|
STM32 Вопрос про I2C, непонимаю даташит или что-то не то |
|
|
|
Aug 25 2011, 10:22
|
Участник

Группа: Участник
Сообщений: 47
Регистрация: 9-03-11
Пользователь №: 63 481

|
Пытаюсь послать два байта слейву по I2C: 1. Выставляем СТАРТ 2. Получаем в прерывании что старт прошел (Событие 5 ) I2C_EVENT_MASTER_MODE_SELECT: Шлем 7битный адрес слейва по этому прерыванию 3. Получаем прерывание что отослан адрес. Игнорируем, так как должно сразу придти прерывание TxE 4. По прерыванию о пустоте буфера ( События 8 и 8_2 ) case I2C_EVENT_MASTER_BYTE_TRANSMITTING: case I2C_EVENT_MASTER_BYTE_TRANSMITTED: Если первый раз пришло, то шлем БАЙТ1. Если второй раз пришло, то шлем БАЙТ2. Если третий раз пришло то:
Вот тут хочется послать стоп. Но это прерывание пришло так как TxE = 1. Реально байт еще не ушел. Когда он уйдет, то, судя по эрате, должен выставиться битик BTF. Поэтому я считаю что его надо ждать. Я бы написал так: Если третий раз пришло то: Если BTF == 1 то шлем СТОП Но этот бит не устанавливается. Прерывания приходят и приходят, БТФ = 0. Почему?
В принципе в эрате написано что СТОП следует выставлять когдв либо TxE = 1 либо BTF = 1; Если я пошлю СТОП когда TxE = 1 а BTF = 0 то не получится ли так, что СТОП оборвет последний байт?
PS
И непонятно еще такая штука, у меня сложилось понимание, что: когда срабатывает прерывание TxE, байт начинает слаться. Если ничего не кинуть в буфер, то прерывание не сбросится, и будет постоянно вызываться, пока не установится бит BTF? Т.е. контроллер будет ждать пока уйдет последний байт в любом случае...
Сообщение отредактировал Vladimir Prokofiev - Aug 25 2011, 10:31
|
|
|
|
|
Aug 25 2011, 14:14
|
Участник

Группа: Участник
Сообщений: 70
Регистрация: 5-04-07
Из: Санкт-Петербург
Пользователь №: 26 789

|
Ну и как получилось завести I2C? Может есть вариант правильной инициации?
|
|
|
|
|
Aug 25 2011, 15:22
|
Участник

Группа: Участник
Сообщений: 47
Регистрация: 9-03-11
Пользователь №: 63 481

|
Цитата(DmitryDI @ Aug 25 2011, 18:14)  Ну и как получилось завести I2C? Может есть вариант правильной инициации? Инициализация вроде понятная, тут вопрос в странном поведении флагов и событий. Все работало (вроде) -- два датчика по i2C опрашивались. Добавил в проект СД карточку (изменив при этом частоты ) и перестало работать и то и другое. Даже не то чтобы перестало, а перебрасывается каким-то количеством пакетов и получает странные события, типо, например lastEvent = 1; Код инициализации CODE void API2C_Init( void ){ I2C_InitTypeDef I2C_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure;
RCC_ClocksTypeDef RCC_Clocks;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB , ENABLE); //I2C Peripheral clock enable RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1); RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &GPIO_InitStructure); /* NVIC configuration */ /* Configure the Priority Group to 1 bit */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* Configure the I2C event priority */ NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* Configure I2C error interrupt to have the higher priority */ NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn; NVIC_Init(&NVIC_InitStructure); RCC_GetClocksFreq(&RCC_Clocks); SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);
//#define I2C_SPEED 340000 #define I2C_SPEED 1000 #define I2C_DUTYCYCLE I2C_DutyCycle_16_9 /* I2C De-initialize */ I2C_DeInit(I2C1); /*!< I2C Struct Initialize */ I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DUTYCYCLE; I2C_InitStructure.I2C_OwnAddress1 = 0xA0; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /*!< I2C Initialize */ I2C_Init(I2C1, &I2C_InitStructure); /* Enable Error Interrupt */ I2C_ITConfig(I2C1, I2C_IT_ERR , ENABLE); /* I2C ENABLE */ I2C_Cmd(I2C1, ENABLE); /* Enable Error and Buffer Interrupts */ I2C_ITConfig(I2C1, (I2C_IT_EVT | I2C_IT_BUF), ENABLE); API2C_SendCmd( I2CCMD_WRITE, LIS_ADDRESS, LIS_REG_CTRL1, LIS_REG_CTRL1_POWERON ); while( API2C_IsTransferDone() == 0);
}
Сообщение отредактировал Vladimir Prokofiev - Aug 25 2011, 15:23
|
|
|
|
|
Aug 29 2011, 08:59
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(Vladimir Prokofiev @ Aug 25 2011, 17:22)  Инициализация вроде понятная, тут вопрос в странном поведении флагов и событий. Все работало (вроде) -- два датчика по i2C опрашивались. Добавил в проект СД карточку (изменив при этом частоты ) и перестало работать и то и другое. Даже не то чтобы перестало, а перебрасывается каким-то количеством пакетов и получает странные события... I2C в STM32F сделан настолько паршиво, что требуется либо запрет прерываний при обработке некоторых событий I2C, либо присвоение прерыванию от I2C наивысшего приоритета. Я предполагаю, что как только Вы добавили SD карточку, у Вас начали прерываться критические фрагменты I2C. Посмотрите внимательно мануал. Особенно хреново дело обстоит при транзакциях с приемо-передачей 1-го или 2-х байтов: это особый случай для данного I2C. Похвастаюсь, что я-таки разобрался со всей хренотенью I2C STM32F и написал своего рода библиотечку, которая предлагает более-менее высокоуровневые процедуры обмена по I2C, а приемо-передача может осуществляться в том числе с использованием DMA. Выборы режима происходят на этапе трансляции путем определений в *.H файле. Если очень нужно, выложу код. Сразу не могу, т.к. там у меня многое завязано на мои общие проектные макросы, и надо бы изолировать I2C от них.
|
|
|
|
|
Aug 29 2011, 11:19
|
Участник

Группа: Участник
Сообщений: 47
Регистрация: 9-03-11
Пользователь №: 63 481

|
Цитата(KnightIgor @ Aug 29 2011, 12:59)  I2C в STM32F сделан настолько паршиво, что требуется либо запрет прерываний при обработке некоторых событий I2C, либо присвоение прерыванию от I2C наивысшего приоритета. Я предполагаю, что как только Вы добавили SD карточку, у Вас начали прерываться критические фрагменты I2C. Посмотрите внимательно мануал. Особенно хреново дело обстоит при транзакциях с приемо-передачей 1-го или 2-х байтов: это особый случай для данного I2C. Похвастаюсь, что я-таки разобрался со всей хренотенью I2C STM32F и написал своего рода библиотечку, которая предлагает более-менее высокоуровневые процедуры обмена по I2C, а приемо-передача может осуществляться в том числе с использованием DMA. Выборы режима происходят на этапе трансляции путем определений в *.H файле. Если очень нужно, выложу код. Сразу не могу, т.к. там у меня многое завязано на мои общие проектные макросы, и надо бы изолировать I2C от них. Основной обмен как раз 2-байтовый) Конечно, хотелось бы увидеть код. Тем более что сам пытаюсь написать такую библиотечку... Если даже и не использовать, то очень бы хотелось посмотреть на правильный и рабочий код.
|
|
|
|
|
Aug 29 2011, 12:15
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(Vladimir Prokofiev @ Aug 29 2011, 13:19)  Конечно, хотелось бы увидеть код. Тем более что сам пытаюсь написать такую библиотечку... Если даже и не использовать, то очень бы хотелось посмотреть на правильный и рабочий код. Намек понял. Я постараюсь на неделе СОБИРАЮЩИЙСЯ код выложить. Сейчас Вы можете глянуть в код, чтобы понять основные ходы обработки I2C и особых случаев. Код насыщен макросами. Причина их применения - не захламлять текст сложными конструкциями обращений к регистрам или библиотечным функциям. Имена самих макросов достаточно прозрачно говорят об их назначении. Это мой стиль, критиковать бесполезно, т.к. о вкусах не спорят; важен результат. Кроме того, применяются некоторые собственные функции, которые пока не приводятся. Нужно учитывать следующее: 1). Сделано под KEIL, с применением его Configuration Wizzard для настроек определений в *.H файле. 2). По причине выше попытки гарантировать собираемость другими компиляторами не делались; 3). Библиотека в нынешнем виде может работать только с одним из I2C (в продвинутых STM32F имеется несколько I2C), выбор которого осуществляется перед трансляцией через заголовки; 4). Коммуникация только с 7-битной I2C периферией (ADDR10 не поддерживается). 5). Есть аппаратно-независимый заголовочный файл HAL_I2C.h и аппаратно-зависимый stm32f10x_iic.h. 6). Библиотека использует как функции из STM32F Peripheral Library (когда надо удобно), так и регистровый доступ (когда хочется по-быстрее), а также некоторые собственные функции (HAL_Proc.h и stm32f10x_proc.h). void I2Cx_EV_IRQHandler(void) это то, куда смотреть в первую очередь, чтобы понять алгоритм. Разбирайтесь, спрашивайте.
Сообщение отредактировал KnightIgor - Aug 29 2011, 12:26
|
|
|
|
|
Aug 30 2011, 08:31
|
Участник

Группа: Участник
Сообщений: 47
Регистрация: 9-03-11
Пользователь №: 63 481

|
Большое спасибо! Скачал, пошел смотреть!
|
|
|
|
|
Aug 30 2011, 15:25
|
Участник

Группа: Участник
Сообщений: 47
Регистрация: 9-03-11
Пользователь №: 63 481

|
Вроде заработало, но, кажется, основная ошибка была совсем в другом... Я посылал один пакет в след за другим сразу. Гипотеза, что слейв не успевал поймать старт после стопа. Может такое быть? Поэтому он не подтверждал прием, срабатывало прерывание на ошибку. ошибка сбрасывалась, но что при этом с передачей происходит дальше я не знаю. Поставил паузу перед отправкой. Но как то это стремно, контроллер тратит свеё время на ожидание... Хотя меня тревожат сомненья что это не причина. Может просто условия изменились и пока ошибки не возникает... Разобрался с BTF. Он не приходит после отправки адреса. Вроде, я даже это где-то читал. Осталась пока одна странность, MASTER_BYTE_TRANSMITTED приходит лишний раз уже после STOP. При этом если его ловить и смотреть регистры, то они нулевые. Т.е. запоздалое прерывание какое-то приходит... PS Прочитал у Вас в коде, а потом в эррате, про то, когда читать или не читать SR2 чтобы не сбросить ADDR. У меня 215 контроллер, там другая эррата, и ничего про это явно не написано. Я сейчас не проверяю, уже 10 минут работает  Но это не показатель... Надо ли в 2xx серии заниматься этим, не знаете? В любом случае, прогресс налицо, большое спасибо за помощь!
Сообщение отредактировал Vladimir Prokofiev - Aug 30 2011, 15:27
|
|
|
|
|
Aug 30 2011, 16:49
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(Vladimir Prokofiev @ Aug 30 2011, 17:25)  Вроде заработало, но, кажется, основная ошибка была совсем в другом... Я посылал один пакет в след за другим сразу. Гипотеза, что слейв не успевал поймать старт после стопа. Может такое быть? Думаю, такое вполне возможно: slave имеет конечный автомат, и как и насколько быстро он там работает, знает только разработчик чипа, если вообще тестировал такую последовательность. Гляньте для Вашего slave, может там есть описание минимально допустимых времен указанной последовательности. Цитата Разобрался с BTF. Он не приходит после отправки адреса. Вроде, я даже это где-то читал. Не приходит, потому как "они" понимают под "byte transferred" именно байт данных, а завершение передачи первого байта после старта, - то есть байта адреса, - сигнализируется битом ADDR в SR2. Отвечая на Ваш PS здесь скажу, что как там с 2xx серией дело обстоит, я не знаю, еще не работал, но уверен, что I2C там такой же. Хотя бы для совместимости. Суть "не сбрасывать" ADDR состоит в том, что при приеме байтов от slave надо заранее подготовить ему ACK/NACK или STOP еще перед тем, как автомат начнет тактирование приема. Тактирование же приема начинается аппаратно сразу, как только ADDR был сброшен. И если сбросить ADDR (чтением SR2) уже при входе в обработчик прерывания, то нет гарантии, что тут же не "рванет" другое прерывание более высокого уровня, и процессор, вернувшись оттуда, уже не успеет подготовить ACK/NACK/STOP, а байт от slave будет аппаратно уже в пути или еще хуже - уже принят. Поэтому я задерживаю сброс ADDR, не считывая SR2, этим самым предотвращаю начало тактирования очередного байта от slave, спокойно готовлю ACK/NACK/STOP, и лишь после этого "отпускаю" аппаратуру I2С. Цитата Осталась пока одна странность, MASTER_BYTE_TRANSMITTED приходит лишний раз уже после STOP. При этом если его ловить и смотреть регистры, то они нулевые. Т.е. запоздалое прерывание какое-то приходит... Может у меня такое было вначале, но мой автомат запрещает прерывание сразу после выдачи STOP (см. последний else ветви case I2C_EVENT_MASTER_BYTE_TRANSMITTED, где вызывается I2C_SUCCESS_HANDLER, который и запрещает прерывание). А перед началом очередной транзакции вызывается INIT_I2C_FLAGS, который там все возможно висящие флаги прочищает. Цитата В любом случае, прогресс налицо, большое спасибо за помощь! Был рад посодействовать!
Сообщение отредактировал KnightIgor - Aug 30 2011, 20:54
|
|
|
|
|
Sep 15 2011, 10:47
|
Участник

Группа: Участник
Сообщений: 47
Регистрация: 9-03-11
Пользователь №: 63 481

|
Уже считал что все получилось, но тут опять столкнулся с проблемой. На слейве есть набор регистров, поэтому бывает нужно отправить слейву 2 байта: Адрес слейва + Адрес регистра + Новое значение регистра. Если вызвать два раза функцию записи, то после второго старта прерывание приходит, шлем адрес а дальше все встает. прерывания больше не приходят, флага BUSY нету. Идешь по шагам -- все ок) Методом научного тыка получилось заставить работать -- вставить маленькую задержку после генерации стопа. Код обработки прерывания стал такой: CODE lastEvent = I2C_Event( isReadSR2 ); #define E_ADDR (2) // SR1_1 #define E_SB (1) // SR1_0 #define E_BTF (4) // SR1_2 #define E_TxE (128) // SR1_2 #define E_RxNE (64) // SR1_2 if( CheckBit( lastEvent, E_SB ) ){ I2C_Send7bitAddress(I2C1, (uint8_t)curCmd.devAddr, I2C_Direction_Transmitter); i2cstate = STATE_START; isReadSR2 = 1; return; } // Àäðåñ îòîñëàí è ñëåéâ ïîäòâåðäèë. Ïðîñòî ñáðàñûâàåì, æäåì TxE if( CheckBit( lastEvent, E_ADDR ) ){ i2cstate = STATE_DATA1; isReadSR2 = 1; return; } // TxE -- áóôåð íà îòïðàâêó ïóñò, çàïîëíÿåì åãî ïåðâûì èëè âòîðûì áàéòîì. if( CheckBit( lastEvent, E_TxE ) ){ if( i2cstate == STATE_DATA1 ){ I2C_SendData( I2C1, curCmd.regAddr ); i2cstate = STATE_DATA2; isReadSR2 = 1; return; } else if( i2cstate == STATE_DATA2 ){ I2C_SendData( I2C1, curCmd.regVal ); i2cstate = STATE_NOTBUSY; isReadSR2 = 1; return; } } if( CheckBit( lastEvent, E_BTF ) ){ if( i2cstate == STATE_NOTBUSY ){ I2C_GenerateSTOP(I2C1, ENABLE); for( i = 0; i < 100; i++ ); isTransferDone = 1; } isReadSR2 = 1; return; } Ну и отправляется это так: API2C_SendCmd( I2CCMD_WRITE, LIS_ADDRESS, LIS_REG_CTRL1, LIS_REG_CTRL1_POWERON ); while( API2C_IsTransferDone() == 0); API2C_SendCmd( I2CCMD_WRITE, LIS_ADDRESS, LIS_REG_CTRL1, LIS_REG_CTRL1_POWERON ); while( API2C_IsTransferDone() == 0); сразу после инита I2C. Функция отправки заполняет структурку curCmd и генерирует СТАРТ последней командой. Тык вот работает только так, с дурацкой задержкой в прерывании. Вынести эту задержку в API2C_SendCmd не получается -- не работает, хотя на осциллографе одно и тоже получается...
|
|
|
|
|
Feb 5 2013, 18:28
|
Группа: Участник
Сообщений: 13
Регистрация: 10-01-11
Пользователь №: 62 132

|
Здравствуйте! Сейчас разбираюсь с похожей проблемой на i2c. Отдельно работает, а если запустить еще какое нибудь прерывание, зависает. KnightIgor а нельзя ли выложить ваши файлы (HAL_Proc.h и stm32f10x_proc.h). Чтобы полностью попытаться разобратьсяв алгоритме.
|
|
|
|
|
Feb 5 2013, 21:41
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(dfyz.s @ Feb 5 2013, 19:28)  Сейчас разбираюсь с похожей проблемой на i2c. Отдельно работает, а если запустить еще какое нибудь прерывание, зависает. KnightIgor а нельзя ли выложить ваши файлы Чтобы полностью попытаться разобратьсяв алгоритме. Со времени моих постов сюда я понемногу усовершенствовал "драйвер" I2C, оставаясь при stm32f10x. 1). Нынешний вариант теперь поддерживает работу через I2C1 и I2C2, для чего в интерфейсные функции добавлен параметр номера "порта" I2C. 2). Не поддерживается 10-битный адресный режим. Да я и не встречал еще устройств таких. 3). Решение использовать DMA или нет (отдельно для TX и RX) принимается на этапе трансляции определением символов. Для этого используется Configuration Wizzard под KEIL. Кто на других системах работает, смотрите тексты в - board.h,
- project.h,
- HAL_I2C.h и
- stm32f10x_iic.h.
Ввиду иерархии включения файлов символы, определенные в предыдущем в списке файле, имеют приоритет над последующими в списке (там условное переопределение). Например, если USE_I2C_TX_DMA1 в файле project.h определен в 1, а этот же символ в stm32f10x_iic.h определен в 0, то так как приоритет будет за project.h, символ останется равным 1. 4). Если разрешен DMA, скажем, для RX, то он будет разрешен как для I2C1, так и I2C2, то есть каналы №7 и №5 DMA1 будут заняты. Так как в stm32f каналов DMA как-то маловато будет, и они альтернативно используются разными перифериями, надо в конкретном проекте смотреть внимательно, чтобы не "наложилось". Наличие board.h и project.h - это следствие попытки вычленить это все I2C хозяйство из существующего проекта, где все сильно друг на друга завязано. Я попробовал странслировать stm32f10x_iic.c изолированно. Вроде получилось. Нужно обращать внимание на пути включения заголовков, особенно библиотеки (standart peripheral library) для stm32f10x, а также не забыть определить для проекта символы USE_STDPERIPH_DRIVER и STM32F10X_HD или какой другой для используемого процессора. Кстати, board.h - это "вычлененка" из stm32f10x_conf.h, который используют те, кто верен предложенному ST стандарту в части иерархии standart peripheral library. Я просто укоротил до минимума stm32f10x_conf.h, который включается в "главном" процессорном файле stm32f10x.h, но вставил туда включение board.h, который уже находится в каталоге проекта. В общем, попробуйте приложенный архив. Будут проблемы - спрашивайте. P.S. ВНИМАНИЕ! Пристыкованный к этому посту архив ПОВРЕЖДЕН! Смотрите перезаливку в последующем посте!
Сообщение отредактировал KnightIgor - Feb 6 2013, 12:07
|
|
|
|
|
Feb 6 2013, 05:40
|
Группа: Участник
Сообщений: 13
Регистрация: 10-01-11
Пользователь №: 62 132

|
Большое спасибо за подробное описание! Не могли ли вы перезалить архив, а то у меня пишет, что поврежден
Сообщение отредактировал dfyz.s - Feb 6 2013, 05:41
|
|
|
|
|
Feb 6 2013, 12:05
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(dfyz.s @ Feb 6 2013, 06:40)  Не могли ли вы перезалить архив, а то у меня пишет, что поврежден Очень странно, но действительно поврежден. Возможно при заливке. Это уже вопрос к порталу. Проверил архив локально. Все ОК. Перезаливаю. Если опять будет бяка, сообщите, выложу куда-нибудь на fileshare.
|
|
|
|
|
Feb 6 2013, 20:20
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(dfyz.s @ Feb 6 2013, 06:40)  Большое спасибо за подробное описание! На Ваш вопрос мне в личку я не могу отправить ответ в личку Вам, т.к. мне идет сообщение, что личка у Вас или отключена, или переполнена. Что бум делать? А пока сюда. Извините, если много букав. 1. Функция Init_I2C() должна быть вызвана на этапе инициализации программы. Если предполагается работа с I2C1, то port равен 0, если работа с I2C2, то port равен 1, а если нужно будет общаться в проекте по двум шинам, надо вызвать Init_I2C() два раза: Init_I2C(0, 0); Init_I2C(1, 0); Если второй параметр равен 0, будет установлена скорость I2C в 400kHz. Далее, при необходимости произвести обмен по I2C1 или I2C2, нужно вызывать функции, указав им номер порта для обмена. Например: Чтение 10-ти байт с устройства I2C с адресом 0x58 на шине I2C2 (port = 1) простейшей функцией ReadW_I2C выглядит так: Код { unsigned char buffer[1+10] = {0x58}; int result;
result = ReadW_I2C(1, buffer, 0, 10); // 0 bytes to write to before, 10 bytes to read if (result == 10) { // full success, all bytes read, // the received data are in buffer[1] to buffer[10] memcpy(myglobalbuffer, &buffer[1], result); // copy the received bytes to some global buffer } else { // some error occurred: the result contains an error code if < 0 or // maybe less than 10 bytes has been read if result >= 0 and < 10. // make a decision what to do... } } Другой пример чтения 16-ти байт из I2C EEPROM размером до 64KB с адреса 0xAABB в этой памяти, и пусть EEPROM сидит на I2C1 (port = 0). Сама EEPROM имеет адрес 0xA0 на шине. Код { unsigned char buffer[1+2+16] = {0xA0}; // one byte for the I2C ID, 2 bytes for memory address, 16 bytes for data. int result;
buffer[1] = 0xAABB / 256; // MSB goes first! buffer[2] = 0xAABB % 256; // LSB
result = ReadW_I2C(0, buffer, 2, 16); // 2 bytes of memory address to write to before, 16 bytes to read afterwards if (result == 16) { // full success, all bytes read, // the received data are in buffer[3] to buffer[18] memcpy(myglobalbuffer, &buffer[3], result); // copy the received bytes to some global buffer } else { // some error occurred: the result contains an error code if < 0 or // maybe less than 16 bytes has been read if result >= 0 and < 16. // make a decision what to do... } } Следует помнить, что функция ReadW_I2C() - блокирующая, то есть не возвращает управление, пока не проделает всю работу. Если устройство "затянет" шину (а такое бывает), все повиснет. Чтобы такое не случилось, следует использовать, например, ReadC_I2C(), которая будет вызывать call back, пока крутится в цикле ожидания. Таким образом, можно в этом call back анализировать время работы и оборвать процесс, если он затянулся. См. комментарии к ReadC_I2C() в коде. 2. О конструкции control[port].rcc и функции RCC_Configuration() заморачиваться пока не нужно. Это моя функция, которая обрабатывает поле структуры control для указанного порта (то есть, для I2C1 или I2C2) с целью включить тактирование периферии, связанной с I2C1 или I2C2. Кстати, коль Вы упомянули, что недавно программируете этот процессор, укажу в этой связи на типичное недопонимание сути его работы (как следует из вопросов на форуме): прежде, чем начать работать с тем или иным периферийным устройством - I2C, USART и т.п. - необходимо сначало включить ему тактирование через узел RCC. Вот этим и занимается RCC_Configuration(), а структура control содержить всякие адреса, флаги и прочие вещи, необходимые для тех или иных операций с I2C. Короче, обо всем "позабочено", см. пункт 1). ответа. Успехов.
Сообщение отредактировал KnightIgor - Feb 6 2013, 21:10
|
|
|
|
|
Feb 8 2013, 17:10
|
Группа: Участник
Сообщений: 13
Регистрация: 10-01-11
Пользователь №: 62 132

|
Да, какая то фигня с личными сообщениями, хотя в настройках стоит, что разрешены, тоже ничего не могу отправлять. А помощь на сайте вообще не работает) (( Попытался на почту вопрос задать, но не пойму дошел ли он или нет. При использовании библиотеки возникают проблемы с чтением BTF. Может я чего то упустил при инициализации? Моя последовательность действий. Код ... Init_I2C(1, 100000);// Реально все инициализируется. Пробовал стандартными функциями отправлять - работает, но при работе //с прерываниями все равно виснет DWT_Init();// счетчик ядра - крутая вещь! ...
u8_t ReadReg(u8_t deviceAddr, u8_t Reg, u8_t* Data) { unsigned char buffer[2+1]; unsigned int result; buffer[0] = deviceAddr; buffer[1] = Reg; buffer[2] = 0;
result = ReadW_I2C(1, buffer, 1, 1);
if(result != 1) return MEMS_ERROR; else { Data = &buffer[2]; return MEMS_SUCCESS; } }
... Заходит в обрабоку прерывания Код // ATTENTION! - since 03.11.11. does not work without the simple // case I2C_EVENT_MASTER_BYTE_TRANSMITTING below as if // I2C_EVENT_MASTER_BYTE_TRANSMITTING occures sometimes // before I2C_EVENT_MASTER_BYTE_TRANSMITTED. // Possible reasons (due to the following changes): // 1). Interrupt priority of I2C has been changed to the highest one; // 2). The I2C speed has been reduced down to 100kHz from 400kHz, // the driver has been tested before. // case I2C_EVENT_MASTER_BYTE_TRANSMITTING: // Data being SENT; TRA, BSY, MSL and { // TxE but BTF are set. uint8_t ex = 0; int32_t tp = I2C_SBTimeoutInit(port, 2*BITS_ONE_BYTE); // Two I2C bytes to wait while (!I2C_BTF(i2c) && !(ex = I2C_SBTimeoutExpired(&tp))); // Wait for BTF! It MUST arrive else // it were a severe hardware error... if (ex) // BTF still off: expired! { I2C_ERROR_HANDLER(port, ctrl->code = ctrl->evnt); break; } Не Возвращает BTF. По таймауту переходит в I2C_ERROR_HANDLER Никто не пробовал работать с данными функциями? Видно, что напсано все очень грамотно, учтены все косяки. Прочитал, что на адрес не приходит BTF. Получается здесь как раз обращение к адресу устройства и к его внутренему регистру. Может он и не должен приходить? Был бы благодарен за помощь!
|
|
|
|
|
Feb 8 2013, 18:04
|
Группа: Участник
Сообщений: 13
Регистрация: 10-01-11
Пользователь №: 62 132

|
)))) Согласен. Но это не решает проблемы. Сейчас посмотрел еще раз отладчиком. Сейчас происходит так. Шлет старт -> Отсылает адрес(нормально приходит) -> Дальше бы надо послать адрес регистра, а он переходит на проверку BTF, который пока точно не придет. Код // Send I2C ID (slave Address to receive) // with R/W bit ==1 I2C_SEND_ADDR(i2c, ctrl->ptr[0] | I2C_Direction_Receiver);
// Prevent reading of SR2 to avoid resetting of ADDR bit, // if two or less bytes to read (cases N == 1 or N == 2). // See RM0008.PDF, Doc ID 13902 Rev 12, Page 739.
if (ctrl->icnt <= 2) ctrl->readsr2 = 0; Как я понял из документации надо запомнить, что пришел ответ от адреса. Но этого не происходит( Стирается, то, что пришел ответ от адреса и прерывание обрабатывается по неправильной ветке автомата
Сообщение отредактировал dfyz.s - Feb 8 2013, 19:23
|
|
|
|
|
Feb 8 2013, 20:25
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(dfyz.s @ Feb 8 2013, 19:04)  Сейчас происходит так. Шлет старт -> Отсылает адрес(нормально приходит) -> Дальше бы надо послать адрес регистра, а он переходит на проверку BTF, который пока точно не придет. Если предполагается операция [START]-[ADDR]-[WRITE]-[RESTART]-[READ]-[STOP], то автомат должен сработать так: После старта попадаем в: case I2C_EVENT_MASTER_MODE_SELECT: // MASTER, (RE)START sent: BUSY, MSL and SB are set где (ctrl->wcnt) будет больше нуля, поэтому вызовется I2C_SEND_ADDR(i2c, ctrl->ptr[0] & ~I2C_Direction_Receiver); для посылки адреса устройства со сброшенным битом чтения, то есть, будет начата операция записи! После этого автомат должен попасть в case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: // ADDR sent with /W bit и лишь после посылки байта там (если не DMA) I2C_WRITE_NEXT(ctrl, ctrl->ptr[ctrl->indx++]); // .wnt-- inside... когда-то попадем в case I2C_EVENT_MASTER_BYTE_TRANSMITTING: // Data being SENT; TRA, BSY, MSL and где после ожидания BTF прямиком далее в case I2C_EVENT_MASTER_BYTE_TRANSMITTED: // Data byte's been SENT; TRA, BSY, MSL and Если посылался только один байт в режиме [WRITE], то (ctrl->wcnt == 0), посему будет выдан рестарт I2C_START(i2c); И т.д. ВНИМАНИЕ! Отладчик нарушает временные характеристики на шине I2C с точки зрения SLAVE, что может приводить к странным результатам! Я заметил также, что отладчик может считывать регистр статуса, что приводит к преждевременному сбросу бита ADDR.
Сообщение отредактировал IgorKossak - Feb 9 2013, 18:15
Причина редактирования: лишние пробельные строки!!!
|
|
|
|
|
Feb 10 2013, 19:31
|
Группа: Участник
Сообщений: 13
Регистрация: 10-01-11
Пользователь №: 62 132

|
Игорь, большое спасибо за ваши обяснения!
Мне сейчас необходимо записать адресс устройства на шине, а потом послать адресс регистра, изкоторого считать информацию. (ST -> SAD+W -> ................. -> SubAddres.......... ST -> SAD+R -> ........................ NMAK -> STOP-> ......................... <- answ SAK .................. <-SAK...........................<-SAK <-DATA.......................... )
Поделитесь опытом, ка вы отлаживали программу, т.к. отладчиком нормально не получается посмотеть (и вправду сбрасывает биты как BTF, так и ADDR). Только при помощи осцилографа?
Сообщение отредактировал dfyz.s - Feb 10 2013, 19:46
|
|
|
|
|
Feb 10 2013, 20:07
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(dfyz.s @ Feb 10 2013, 21:31)  отладчиком нормально не получается посмотеть (и вправду сбрасывает биты как BTF, так и ADDR). Отладчик сбрасывает нужные биты всегда или только когда открыто окно просмотра регистров i2c? Цитата(dfyz.s @ Feb 10 2013, 21:31)  Только при помощи осцилографа? Вариантов множество: вывод в comport (или любой другой интерфейс, который можно выделить для отладки); запись лога обмена в ОЗУ, с последующим просмотром лога после завершения обмена; и тд и тп.
|
|
|
|
|
Feb 11 2013, 11:02
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(dfyz.s @ Feb 10 2013, 20:31)  Мне сейчас необходимо записать адресс устройства на шине, а потом послать адресс регистра, изкоторого считать информацию. Сообщите же, что за датчик? Я хоть гляну, какой протокол. Вообще, функция ReadW_I2C с параметром wcnt > 0 и предназначена для последовательности START-ADDR[W]-WRITE-RESTART-ADDR[R]-READ-STOP. Цитата Поделитесь опытом, ка вы отлаживали программу, т.к. отладчиком нормально не получается посмотеть (и вправду сбрасывает биты как BTF, так и ADDR). Только при помощи осцилографа? По-всякому. И осциллографом, и проскакиванием каких-то участков кода без остановки с последующим анализом содержимого памяти. Муторное дело было. Сейчас на устройстве на I2C EEPROM работает файловая система (причем, как EFS KEIL, так и Chan-FS пробовалась). Все очень стабильно. Я бы сказал, мы тут уже забыли об отладке, просто все работает как надо. И быстро (с учетом DMA).
|
|
|
|
|
Feb 11 2013, 12:46
|
Группа: Участник
Сообщений: 13
Регистрация: 10-01-11
Пользователь №: 62 132

|
Да, скорее всего я где-то напортачил. Надо протестить нормально. Свободного времени не очень много, когда будет - поплотнее займусь По i2c опрашиваются датчики (акселерометр + магнетометр) lsm303dlhc и гироскоп (l3gd20). Сами они опрашивались, используя стандартные либы, проблема была в том, что когда запускаю параллельно какие нибудь измерения от таймера по прерыванию - перестает работать. Видать некоторые ответы от слейва теряются и и2с перестает нормально работать. Цитата Отладчик сбрасывает нужные биты всегда или только когда открыто окно просмотра регистров i2c? Реально, сбрасывает только при просмотре регистров! Пробежал по алгоритму - пошагово нормально работает. А когда запускаю с проскакиванием участков возвращает ошибку. Часто слетатет на i2c busy.
Сообщение отредактировал dfyz.s - Feb 11 2013, 20:05
|
|
|
|
|
Feb 13 2013, 06:34
|
Группа: Участник
Сообщений: 13
Регистрация: 10-01-11
Пользователь №: 62 132

|
Да, с схемотехнической точки зрения похоже все хорошо, т.к. один и2с без других прерываний работает нормально. Резисторы 5 КОм на обоих линиях, и2с на плате, длина линий очень небольшая. Забежал к другу посмотреть на осциллографе, что происходит на шине. Правильно ли я понимаю, что при попытке чтения из регистра нормально передается адрес устройства и адрес регистра , а после рестарта для чтения байта не приходит ответ от слейва? Цитата Вроде заработало, но, кажется, основная ошибка была совсем в другом... Я посылал один пакет в след за другим сразу. Гипотеза, что слейв не успевал поймать старт после стопа. Может такое быть? Поэтому он не подтверждал прием, срабатывало прерывание на ошибку. ошибка сбрасывалась, но что при этом с передачей происходит дальше я не знаю. Поставил паузу перед отправкой. Но как то это стремно, контроллер тратит свеё время на ожидание... Хотя меня тревожат сомненья что это не причина. Может просто условия изменились и пока ошибки не возникает... Спасибо всем огромное, а особенно KnightIgor!!! Кажись заработало! Тоже вставил такую задержку и стало работать! Дома попробую с прерываниями. С прерываниями работает на отлично!)
Сообщение отредактировал dfyz.s - Feb 13 2013, 18:24
Эскизы прикрепленных изображений
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|