|
|
  |
залипает шина I2C в STM32, на МК режим slave |
|
|
|
Oct 6 2016, 10:23
|
Знающий
   
Группа: Участник
Сообщений: 959
Регистрация: 11-01-06
Из: Санкт-Петербург
Пользователь №: 13 050

|
Цитата(Метценгерштейн @ Oct 6 2016, 11:51)  Куда можно смотреть? Может что не проинициализировал в МК? так проведите диагностику с логированием как на стороне линукса - диагностический выхлоп драйвера i2c в syslog так и стороне stm32, желательно вывод транзакций с записью с консоли + дамп статусных регистров ну и осцилл на шину смотрите работу, фиксируете, возник дефект - изучаете картину, ловите виновного.. может слейв NAK глотает, может тайминги не держит, может машина состояния что-то не формирует
|
|
|
|
|
Oct 6 2016, 10:59
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Изъездил аппаратный I2C Slave на STM32 вдоль и поперек. Сталкивался с тем, что иногда I2C Slave ловит ложное STOP-условие, то есть перепад Low->High на SDA при высоком SCL. Это может происходить, когда мастер меняет SDA близко к фронтам SCL, и учитывая емкостные свойства линий SCL/SDA, аппаратный контроллер фиксирует STOP-условие посреди посылки, что приводит к установке бита 8 (BERR - Bus Error, Misplaced Start or Stop condition) в регистре статуса. Посылка при этом, естественно, теряется, и конечный автомат ждёт снова START-условия, адреса и т. п. Поэтому, задействуя аппаратный контроллер I2C, необходимо включать прерывания по ошибочным состояниям и корректно обрабатывать их. Самое печальное в этом то, что отключить такое поведение (отслеживание Bus Error) нельзя, и это очень мешает нормальной работе, особенно в приложениях, где мастер "не любит" повторять запросы, и при неответе слейва тупо фиксирует ошибку. Что касается меня, то я, изрядно повозившись с аппаратным I2C Slave на STM32, переписал драйвер таким образом, что аппаратный I2C делает для меня только START+сравнение адреса, после чего "будит" контроллер прерыванием "Address matched", после чего ноги переназначаются на GPIO, и дальнейшая работа идёт софтовой эмуляцией I2C Slave.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Oct 6 2016, 11:34
|
Профессионал
    
Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079

|
выложу инициализацию I2C. Может у кого глаз зацепится за что-то некорректное. Проц STM32F030 На всякий случай сейчас еще раз перевыкачал CubeMX, так с ним шину I2C Линукс даже не сканирует. Сразу глючит. HAL вообще здесь не применим. Код void init(void) { i2c.max_rx_count = sizeof(incoming); i2c.state = st_idle; uint_fast32_t bus_clk; uint_fast32_t scale; static const struct gpio_configuration pinconf = { .open_drain=1, .output=0, .analog=0, .alter=1, .speed=0, .pullup=0, // было 1 .pulldn=0, .altfunc = 1, // convenient i2c .lock=0 };
RCC->AHBENR |= RCC_AHBENR_GPIOBEN; gpio_pin_conf(GPIOB, 6,1,&pinconf); gpio_pin_conf(GPIOB, 7,1,&pinconf); RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST; RCC->APB1RSTR ^= RCC_APB1RSTR_I2C1RST; RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; SystemCoreClockUpdate(); // get sysclk for apb1 if((RCC->CFGR3 & RCC_CFGR3_I2C1SW) == 0) // RCC_CFGR3_I2C1SW если 1, то от SystemCoreClock { bus_clk = HSI_VALUE; } else { bus_clk = SystemCoreClock; if(RCC->CFGR & RCC_CFGR_PPRE_2) { uint_fast32_t factor = RCC->CFGR & (RCC_CFGR_PPRE_0 | RCC_CFGR_PPRE_1); if(factor & RCC_CFGR_PPRE_0) factor |= 1; if(factor & RCC_CFGR_PPRE_1) factor |= 2; factor &= 3; bus_clk >>= factor + 1; } } // general scale factor to 8mhz scale = bus_clk / (HSI_VALUE/2); scale -= 1; scale &= 0x0f; myI2C->TIMINGR = scale << 28; // tclk = 250ns refman page 556 - place config from table 100khz myI2C->TIMINGR |= 0x13 //scll | (0x0f << 8) //sclh | (0x02 << 16) //sdadel | (0x04 << 20); // scldel //conf i2c NVIC_EnableIRQ(I2C1_IRQn); //set int mask myI2C->CR1 = I2C_CR1_ADDRIE | I2C_CR1_RXIE | I2C_CR1_TXIE
//| I2C_CR1_NACKIE | I2C_CR1_STOPIE | I2C_CR1_TCIE | I2C_CR1_ERRIE; // enable analog noise filtering or digital // myI2C->CR1 |= I2C_CR1_ANFOFF;// currently analog // clock stretch enabled
// set own address myI2C->OAR1 = I2C_OAR1_OA1EN | ((get_slave_address() & 0x7F) << 1); // enable myI2C->CR1 |= I2C_CR1_PE; } Код void gpio_pin_conf( GPIO_TypeDef *gp, uint8_t num, uint8_t value, const struct gpio_configuration *conf) { volatile uint32_t *altreg; uint32_t msk2 = 1 << (num*2); uint32_t msk4 = 1 << ((num & 0x07)*4); num &= 0x0f; gp->MODER &= ~(3*msk2); if(conf->output) gp->MODER |= msk2; if(conf->analog) gp->MODER |= 3*msk2; if(conf->alter) gp->MODER |= 2*msk2;
if(conf->open_drain) gp->OTYPER |= 1 << num; else gp->OTYPER &= ~(1 << num);
gp->OSPEEDR &= ~(3*msk2); gp->OSPEEDR |= conf->speed * msk2;
gp->PUPDR &= ~(3*msk2); if(conf->pullup) gp->PUPDR |= msk2; if(conf->pulldn) gp->PUPDR |= 2*msk2;
//set value gp->BSRR |= 1 << (num + value?0:16); //set af altreg = &gp->AFR[(num >= 8)]; *altreg &= ~(0x0F * msk4); *altreg |= conf->altfunc * msk4; }
|
|
|
|
|
Oct 6 2016, 11:59
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Как по мне, сбрасывать периферию, не включив её клок, бессмысленно, она "мертва". Последовательность инициализации несколько напрягает - сначала нужно включать клок, потом инитить и включать периферию, только потом конфигурировать "ноги", и уже в самом конце разрешать от неё прерывания. При нарушении такой последовательности возможны всяки-разны казусы, типа невовремя стрельнувшего прерывания и т. п. Хотя это всё мелочи и придирки, в общем-то.  Ах, да, ещё I2C Slave не нуждается в делителях клока, это всё нужно только мастеру. Slave работает на том клоке SCL, который приходит от мастера.
Сообщение отредактировал gerber - Oct 6 2016, 12:01
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Oct 6 2016, 12:05
|
Профессионал
    
Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079

|
ну а так в целом- все регистры проинициализированы корректно? Или чего-то забыто? Цитата(gerber @ Oct 6 2016, 14:59)  Как по мне, сбрасывать периферию, не включив её клок, бессмысленно, она "мертва". Последовательность инициализации несколько напрягает - сначала нужно включать клок, потом инитить и включать периферию, только потом конфигурировать "ноги", и уже в самом конце разрешать от неё прерывания. При нарушении такой последовательности возможны всяки-разны казусы, типа невовремя стрельнувшего прерывания и т. п. Хотя это всё мелочи и придирки, в общем-то.  Ах, да, ещё I2C Slave не нуждается в делителях клока, это всё нужно только мастеру. Slave работает на том клоке SCL, который приходит от мастера. не понял по включению клока: Код RCC->AHBENR |= RCC_AHBENR_GPIOBEN; вот включил клок, только потом дальше начинаю инициализировать регистры. Не так?
|
|
|
|
|
Oct 6 2016, 12:08
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(Метценгерштейн @ Oct 6 2016, 15:05)  ну а так в целом- все регистры проинициализированы корректно? Или чего-то забыто? В целом, вроде ОК. Цитата(Метценгерштейн @ Oct 6 2016, 15:05)  не понял по включению клока: Код RCC->AHBENR |= RCC_AHBENR_GPIOBEN; вот включил клок, только потом дальше начинаю инициализировать регистры. Не так? Это клок GPIOB, имелся в виду клок I2C-контроллера RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; да, его надо включать в самом начале инициализации, даже до сброса периферии, КМК.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Oct 6 2016, 12:16
|
Профессионал
    
Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079

|
Код RCC->AHBENR |= RCC_AHBENR_GPIOBEN; RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; gpio_pin_conf(GPIOB, 6,1,&pinconf); gpio_pin_conf(GPIOB, 7,1,&pinconf); RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST; RCC->APB1RSTR ^= RCC_APB1RSTR_I2C1RST; так пойдет? Здесь мы ловим всех блох, которые глючат. Поэтому любое, что режет глаз- будем править. Насчет делителя для мастера- можно его не трогать? Я понял, что это просто лишнее написано и не применяется, но не мешает слейву моему.
|
|
|
|
|
Oct 6 2016, 12:25
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(Метценгерштейн @ Oct 6 2016, 15:16)  так пойдет? Я бы снес инициализацию ног после инициализации I2C, хотя это и непринципиально, возможно. Дело вкуса, но подключать неинициализированный I2C к реальной шине, а потом его инициализировать, ИМХО, не очень правильно. Цитата(Метценгерштейн @ Oct 6 2016, 15:16)  Насчет делителя для мастера- можно его не трогать? Я понял, что это просто лишнее написано и не применяется, но не мешает слейву моему. Да, это не мешает слейву, просто лишняя операция.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Oct 6 2016, 12:38
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(Метценгерштейн @ Oct 6 2016, 15:30)  Не очень понял- как предлагаете сделать? myI2C->CR1 |= I2C_CR1_PE; потом gpio_pin_conf(GPIOB, 6,1,&pinconf); gpio_pin_conf(GPIOB, 7,1,&pinconf); и лишь потом NVIC_EnableIRQ(I2C1_IRQn); Но, повторюсь, это всё дело вкуса, и вряд ли решит вопрос стабильности работы I2C. ИМХО, аппаратный I2C Slave в STM32 пригоден для работы лишь с идеальным I2C-мастером, соблюдающим "километровые" тайминги, и не переключающим SDA вблизи фронтов SCL. Вблизи - это на расстоянии менее 2-3 мкс.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Oct 6 2016, 13:56
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(Метценгерштейн @ Oct 6 2016, 11:51)  STM32 подключен слейвом к другому процу АРМ на Линуксе. Все работает, но поведение странное. Периодически отваливается шина I2C и мой STM не получает никакие команды из вне. Написана на регистрах инициализация. Подтяжки шины к +3,3 по 7,5 К. Куда можно смотреть? Может что не проинициализировал в МК? Подтяжки шины к +3,3 по 7,5 К. - нормальные, но все равно посмотрите осцилграфом форму фронтов (SDA-SCL). Мало ли что могло затянуть. Захват данных идет по фронту. Если поплыло - на осцилографе будет видно. На скорости 300 kHz у меня работает нормально на 10к. В чем проявляется "отваливание" ? Я свой колхоз с I2C HAL (MSP430) отлаживал с активным использованием лог. анализатора. Сильно рекомендую. Ваш проект "slave" - а он, на мой взгляд существенно сложнее master. Особенно обратите внимание на ACK - NACK квитирование. Если не соотв-ет протоколу I2C и slave отработал не корректно (по логике протокола) то можно войти в ступор. Например вход в режим "ожидания готовности slave". А слейв это сделал "не в том смысле", или наоборот, не сделал. Как альтернативный вариант. Может ошибка в мастере ?
|
|
|
|
|
Oct 6 2016, 14:54
|
Профессионал
    
Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079

|
на ночь оставлю на прогоне девайс- посмотрим к утру. Правки внес, что gerber предложил. Еще убрал внутреннюю подтяжку программную Код .pullup=0, // было 1 0 выставил. Мастер- это Линукс. Должно быть все отлажено. Проц на линуксе, в смысле. Посмотрим утром. Отпишусь.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|