Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: stm32 i2c
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Dron_Gus
Что-то не совсем адекватно работает модуль I2C2. Или я что-то не учел.
Кто-нить с ним работал. Есть какие-нить дополнительные грабли, кроме описанных в errat'е?
Симптомы: идет постоянный обмен с 8 устройствами. STM32 мастер. В какой-то момент общение со всеми устройствами выпадает на Ack Failed в момент передачи адреса. Отваливаются сразу все. Через некоторое время обмен восстанавливается.

З.Ы. на первом i2c висят часы и с ними никаких проблем не обнаружено.
Dron_Gus
Покопался на форуме ST. Вообщем, хотели как лучше, получилось как всегда.

З.Ы. ни у кого нет bit bang реализации i2c? И вообще, как на "быстром" проце делать привязку к времени? Не висеть же все время в процедуре обмена. С другой стороны вешать на прерывание таймера - больно часто выходит.
IgorKossak
Посмотрите здесь свежак: STM32F10xxx devices: advanced I²C examples, если ещё не видели.
Dron_Gus
Цитата(IgorKossak @ Nov 6 2009, 21:01) *
Посмотрите здесь свежак: STM32F10xxx devices: advanced I²C examples, если ещё не видели.


Спасибо. Глядел диагонально. Еще пару месяцев назад этот пример содержал реализации на основе прерываний и полинга. Теперь только ДМА. У меня же основная засада в том, что заранее неизвестно, сколько данных отдаст периферийное устройство. Количество данных идет в первом байте. Т.е. ДМА не мой вариант.
brag
У меня тоже он че-то не идет. Работает некоторое время, потом виснет. помогает только полный рестарт i2c + передергивание вручную SCL.
прерывания забустил.
Также есть несколько веток (типа https://my.st.com/public/STe2ecommunities/m...urrentviews=304 ) на их форуме, тоже без решений по сути.
Сделал софтовый I2C, благо, шина не сильно нагружена
rexton
Еще вопрос

Пытаюсь запустить обмен по шине на прерываниях. Так вот не пойму, у них прерывание по Start bit send работает в принципе?
То есть выставляю в I2C1->CR1 бит I2C_CR_START. По идее, после генерации старта на шине я должен попасть в прерывание I2C1_EV_IRQHandler() и там увидеть выставленный фдаг SB в SR1, однако, в прерывание не попадаю. Или я что то не так понял?

Другие прерывания вроде работают (например ADDR TxE).
Прерывания разрешены, переферия затактирована...
adnega
Цитата(rexton @ Jul 10 2012, 11:29) *
Еще вопрос

Пытаюсь запустить обмен по шине на прерываниях. Так вот не пойму, у них прерывание по Start bit send работает в принципе?
То есть выставляю в I2C1->CR1 бит I2C_CR_START. По идее, после генерации старта на шине я должен попасть в прерывание I2C1_EV_IRQHandler() и там увидеть выставленный фдаг SB в SR1, однако, в прерывание не попадаю. Или я что то не так понял?

Другие прерывания вроде работают (например ADDR TxE).
Прерывания разрешены, переферия затактирована...


Резисторы подтягивающие в наличии?
Линии SDA и SCL в норме?
kan35
Рекомендации ST по поводу I2C:
Это периферийное устройство в STM32 - сложное с кучей диагностических флагов и множеством режимов работы. Кроме того, оно чувствительно к таймингам, оттого инженеры саппорта ST обещают наложить проклятие на каждого, кто будет работать с этим модулем напрямую (и потом будет жаловаться на глюки). Вместо этого строго рекомендуется применять библиотеку CPAL, она доступна с сайта st.com. Не факт, что это ваш случай, но вероятность далеко не нулевая.
KnightIgor
Цитата(Dron_Gus @ Nov 8 2009, 20:57) *
Теперь только ДМА. У меня же основная засада в том, что заранее неизвестно, сколько данных отдаст периферийное устройство. Количество данных идет в первом байте. Т.е. ДМА не мой вариант.

Это как так? Мастер не знает, сколько спрашивает? Протокол I2C весьма детерминированный. Мастер определяет, сколько читать/писать. Если у ведомого запросить больше, чем он может дать, он выставит NACK. Это ловится в прерывании, обмен завершается, DMA останавливается. Все чисто. Не глядел еще примеры от ST: в свое время написал свою поддержку, работающую по прерываниям и с использованием DMA, работает стабильно.
DmitryM
Цитата(KnightIgor @ Jul 11 2012, 12:32) *
Если у ведомого запросить больше, чем он может дать, он выставит NACK.


Не, 24LC можно читать до посинения по кругу, только оно никому не надо.
sls_
Прошу прощения, разобрался, напутал в чтении.
LeonVS
Всем доброго времени суток!
Помогите разобраться с опросом MS5611, никак не могу прочитать два байта при опросе, читается старший и все... Может кто сталкивался уже?
KnightIgor
Цитата(LeonVS @ Mar 8 2013, 19:50) *
Всем доброго времени суток!
Помогите разобраться с опросом MS5611, никак не могу прочитать два байта при опросе, читается старший и все... Может кто сталкивался уже?

Что значит "читается старший и все"? Что значит "всё", то есть, как это проявляется: выставляется NACK, либо I2C виснет вообще, либо младший байт неправильный по содержанию?

Как можно вообще так пространно вопросы ставить и потом на ответы надеяться?...
drcepera
Доброго времени суток. Юзаю i2c на STM32F4DISCOVERY для опроса датчика. Использую библиотеку CPAL. Появилась проблема, которая вот уже много времени гложет мозг. Суть в следующем.

CPAL проверяет таймауты, для чего на CPAL у меня выделен TIM3, по прерыванию раз в мс вызывается библиотечная функция CPAL_I2C_TIMEOUT_Manager(). И все вроде хорошо. НО! Возникают моменты, когда программа затыкается на __CPAL_I2C_TIMEOUT(). Это дефайн, который представляет из себя цикл, выход из которого возможен по одному из двух условий: 1) наступило ожидаемое событие (например, скинулся бит BUSY статусного регистра); 2) превышен лимит ожидания. То есть, если щелкает таймер, то рано или поздно цикл будет покинут. НО! При зависании в __CPAL_I2C_TIMEOUT() таймер хоть и работает, и выставлены нужные флаги, но прерывание, и соответственно, подсчет таймингов, не вызываются.

Привожу рисунок, на нем - собственно место затыка во время отладки, а также регистры 3-го таймера. Ниже по коду в комментах - определение самой __CPAL_I2C_TIMEOUT()
Lotor
Цитата(drcepera @ Jul 10 2013, 13:00) *
таймер хоть и работает, и выставлены нужные флаги, но прерывание, и соответственно, подсчет таймингов, не вызываются.

Так определите, кто (где, почему) запретил прерывание, если оно не вызывается.
drcepera
Тээк.. Если прерывание разрешено в таймере (бит Update interrupt flag), плюс не запрещены прерывания в целом (PRIMASK = 0), следовательно дело может быть в запрете в NVIC? Или я еще что-то упускаю?
_Артём_
Цитата(drcepera @ Jul 10 2013, 15:22) *
Если прерывание разрешено в таймере (бит Update interrupt flag), плюс не запрещены прерывания в целом (PRIMASK = 0), следовательно дело может быть в запрете в NVIC?



Нужно вызвать функцию разрешения прерывания в NVIC:

NVIC_EnableIRQ(Номер прерывания таймера);



Цитата(drcepera @ Jul 10 2013, 15:22) *
Или я еще что-то упускаю?


Тактирование таймера включено? Нужные флаги в статусе устанавливаются?




drcepera
Цитата(_Артём_ @ Jul 10 2013, 16:45) *
Нужно вызвать функцию разрешения прерывания в NVIC:

NVIC_EnableIRQ(Номер прерывания таймера);

Тактирование таймера включено? Нужные флаги в статусе устанавливаются?


Товарищи, таймер-то включен и NVIC именно так и проинициализирован, и прерывания происходят. КРОМЕ случая зависания в указанном месте CPAL. Более того, если при этом я разорву линию I2C - программа выйдет из этого цикла, т.к. __CPAL_I2C_HAL_GET_BUSY(pDevInitStruct->CPAL_Dev) возвращает 0 - и дальше программа будет продолжать работать хорошо, в смысле, прерывания таймеров возобновятся.

Цитата
Так определите, кто (где, почему) запретил прерывание, если оно не вызывается.


Вот я и спрашиваю, если в регистрах таймера все в порядке и не запрещены прерывания - то дело может быть в запрете прерывания в NVIC, либо же еще в чем-нибудь?
Lotor
Цитата(drcepera @ Jul 10 2013, 17:08) *
то дело может быть в запрете прерывания в NVIC, либо же еще в чем-нибудь?

Чудес не бывает. Когда снова "зависните" - проанализируйте отладчиком регистры, NVIC в том числе. Может, например, Вы в обработчике прерывания забываете сбрасывать pending бит события таймера или что-то в этом роде.
esaulenka
Цитата(KnightIgor @ Jul 11 2012, 12:32) *
Мастер определяет, сколько читать/писать. Если у ведомого запросить больше, чем он может дать, он выставит NACK.

Я извиняюсь за некропостинг, но это не так.
Если у ведомого запросить больше, он может вообще ничего не делать - посылает подтверждения (или неподтверждения) приёмник, т.е. мастер. В этом случае мы получим много-много FF-ов, если слейв-передатчик отпустит шину вообще. Его дело - правильно подтвердить посылку с собственным адресом, и всё.
drcepera
OMG.. Извиняюсь, товарищи. Нашел таки косяк )
Датчик читался по таймеру А, а таймауты отслеживались по таймеру В. Хоть приоритет у В был и выше, чем А, они были в одной группе. Установил NVIC_PriorityGroup_2 - все, кажется, Ок. Не учел, что прерывания из одной группы не обрывают друг друга.

Спасибо всем за ответы! )
Ruslan-maniak
Хочу поднять эту тему. Пытаюсь завести I2C через CPAL вместе с freeRTOSом. Подскажите как в cpal_conf.h замутить таймер через vApplicationTickHook(), который у меня отсчитывает миллисекунды.
Я так полагаю это делается В от здесь. Но как правильно это дело задефайнить не догоняю.
Код
/*  -- Section 4 :         **** Configure Timeout method, TimeoutCallback ****

    Description: This section allows you to implement your own Timeout Procedure.
                 By default Timeout procedure is implemented with Systick timer and
                 CPAL_I2C_TIMEOUT_Manager is defined as SysTick_Handler.
                 */


#define _CPAL_TIMEOUT_INIT()           SysTick_Config((SystemCoreClock / 1000));\
                                                          NVIC_SetPriority (SysTick_IRQn, 0)
                                       /*<! Configure and enable the systick timer
                                       to generate an interrupt when counter value
                                       reaches 0. In the Systick interrupt handler
                                       the Timeout Error function is called. Time base is 1 ms */

#define _CPAL_TIMEOUT_DEINIT()         SysTick->CTRL = 0        /*<! Disable the systick timer */


#define CPAL_I2C_TIMEOUT_Manager       SysTick_Handler         /*<! This callback is used to handle Timeout error.
                                                                     When a timeout occurs CPAL_TIMEOUT_UserCallback
                                                                     is called to handle this error */
#ifndef CPAL_I2C_TIMEOUT_Manager
   void CPAL_I2C_TIMEOUT_Manager(void);
#else  
   void SysTick_Handler(void);  
#endif /* CPAL_I2C_TIMEOUT_Manager */

/*#define CPAL_TIMEOUT_UserCallback        (void)*/                  /*<! Comment this line and implement the callback body in your
                                                                      application in order to use the Timeout Callback.
                                                                      It is strongly advised to implement this callback, since it
                                                                      is the only way to manage timeout errors. */

В общем основной вопрос как прикрутить к CPAL свой собственный таймер.
kan35
Я убрал всю инициализацию таймеров в CPAL (закомментив тело вами показанных макросов)
Код
#define _CPAL_TIMEOUT_INIT()           /*SysTick_Config((SystemCoreClock / 1000));\
                                       NVIC_SetPriority (SysTick_IRQn, 0) */
                                       /*<! Configure and enable the systick timer
                                       to generate an interrupt when counter value
                                       reaches 0. In the Systick interrupt handler
                                       the Timeout Error function is called. Time base is 1 ms */

#define _CPAL_TIMEOUT_DEINIT()         /*SysTick->CTRL = 0*/        /*<! Disable the systick timer */


/*#define CPAL_I2C_TIMEOUT_Manager       SysTick_Handler*/          /*<! This callback is used to handle Timeout error.
                                                                     When a timeout occurs CPAL_TIMEOUT_UserCallback
                                                                     is called to handle this error */

/*#define CPAL_TIMEOUT_UserCallback        (void)      */            /*<! Comment this line and implement the callback body in your
                                                                      application in order to use the Timeout Callback.
                                                                      It is strongly advised to implement this callback, since it
                                                                      is the only way to manage timeout errors. */

и сделал так:
Код
void SysTick_Handler(void)
{
    xPortSysTickHandler();
    CPAL_I2C_TIMEOUT_Manager();
}
Ruslan-maniak
Понятно. Спасибо.
Ruslan-maniak
А ещё такой вопрос, не касающийся CPAL: может ли быть такое что используя стандартную библиотеку STM в режиме Мастер не отпускает линию что бы принять ACK от слэйва после отправки адреса устройства? Просто некоторое время slave нормально отвечал, затем я немного решил порефакторить код и всё сломалось. Вернул на место, всё равно ничего не работает. Смотрю по осцилограмме - слэйв не отправляет ACK. Хотя в его работе вообще ничего меняться не должно было. Выкладываю код:
Инициализация:
CODE
//Инициализация GPIO
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

// SCL пин
GPIO_InitStruct.GPIO_Pin = I2C1_SCL_PIN;
GPIO_Init(I2C1_SCL_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(I2C1_SCL_PORT, I2C1_SCL_PIN_SOURCE, GPIO_AF_I2C1);

// SDA пин
GPIO_InitStruct.GPIO_Pin = I2C1_SDA_PIN;
GPIO_Init(I2C1_SDA_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(I2C1_SDA_PORT, I2C1_SDA_PIN_SOURCE, GPIO_AF_I2C1);

// Конфигурация I2C
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

I2C_Cmd(I2C1, DISABLE);

I2C_DeInit(I2C1);
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStruct.I2C_OwnAddress1 = I2C1_OWN_ADDRESS;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = I2C_SPEED;
I2C_Init(I2C1, &I2C_InitStruct);

// Включение интерфейса
I2C_Cmd(I2C1, ENABLE);


чтение регистра
CODE
// Проверка готовности линии
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));

// Генерация первого старта
I2C_GenerateSTART(I2Cx, ENABLE);

// Проверка что линия свободна
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

// Отправка адреса подчинённого устройства (запись)
I2C_Send7bitAddress(I2Cx, slaveAddress, I2C_Direction_Transmitter);

// Проверка ACK от подчинённого устройства (есть ли такой адрес на шине)
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

// Отправка адреса регистра чтения
I2C_SendData(I2Cx, regAddress);

// Проверка ACK от подчинённого устройства
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

// Генерация повторного старта
I2C_GenerateSTART(I2Cx, ENABLE);

// Проверка что линия свободна
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

// Отправка адреса подчинённого устройства (чтение)
I2C_Send7bitAddress(I2Cx, slaveAddress, I2C_Direction_Receiver);

// Проверка ACK от подчинённого устройства
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

// Завершаем приём
I2C_AcknowledgeConfig(I2Cx, DISABLE);

// Проверка что байт принят (данные загружены в регистр DR)
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED));

// Генерируем стоп
I2C_GenerateSTOP(I2C1, ENABLE);

return I2C_ReceiveData(I2Cx);


И после первой отправки адреса устройства ACK я так и не дожидаюсь. Устройство STA381BWS.

Добавил к коду сдвиг адреса Slave'а на один бит влево. Ничего не изменилось.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.