Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: залипает шина I2C в STM32
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
Метценгерштейн
STM32 подключен слейвом к другому процу АРМ на Линуксе.
Все работает, но поведение странное. Периодически отваливается шина I2C и мой STM не получает никакие команды из вне. Написана на регистрах инициализация. Подтяжки шины к +3,3 по 7,5 К.
Куда можно смотреть? Может что не проинициализировал в МК?
Jury093
Цитата(Метценгерштейн @ Oct 6 2016, 11:51) *
Куда можно смотреть? Может что не проинициализировал в МК?

так проведите диагностику с логированием
как на стороне линукса - диагностический выхлоп драйвера i2c в syslog
так и стороне stm32, желательно вывод транзакций с записью с консоли + дамп статусных регистров
ну и осцилл на шину

смотрите работу, фиксируете, возник дефект - изучаете картину, ловите виновного..
может слейв NAK глотает, может тайминги не держит, может машина состояния что-то не формирует
Метценгерштейн
Цитата(Jury093 @ Oct 6 2016, 13:23) *
так проведите диагностику с логированием
как на стороне линукса - диагностический выхлоп драйвера i2c в syslog
так и стороне stm32, желательно вывод транзакций с записью с консоли + дамп статусных регистров
ну и осцилл на шину

смотрите работу, фиксируете, возник дефект - изучаете картину, ловите виновного..
может слейв NAK глотает, может тайминги не держит, может машина состояния что-то не формирует


что значит, NAK глотает? А если тайминги не держит - что значит? Как смотреть?
Логгирование в Линуксе I2C как запустить?
gerber
Изъездил аппаратный 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.
Метценгерштейн
выложу инициализацию 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;
}
gerber
Как по мне, сбрасывать периферию, не включив её клок, бессмысленно, она "мертва".
Последовательность инициализации несколько напрягает - сначала нужно включать клок, потом инитить и включать периферию, только потом конфигурировать "ноги", и уже в самом конце разрешать от неё прерывания. При нарушении такой последовательности возможны всяки-разны казусы, типа невовремя стрельнувшего прерывания и т. п.
Хотя это всё мелочи и придирки, в общем-то. laughing.gif
Ах, да, ещё I2C Slave не нуждается в делителях клока, это всё нужно только мастеру. Slave работает на том клоке SCL, который приходит от мастера.
Метценгерштейн
ну а так в целом- все регистры проинициализированы корректно? Или чего-то забыто?

Цитата(gerber @ Oct 6 2016, 14:59) *
Как по мне, сбрасывать периферию, не включив её клок, бессмысленно, она "мертва".
Последовательность инициализации несколько напрягает - сначала нужно включать клок, потом инитить и включать периферию, только потом конфигурировать "ноги", и уже в самом конце разрешать от неё прерывания. При нарушении такой последовательности возможны всяки-разны казусы, типа невовремя стрельнувшего прерывания и т. п.
Хотя это всё мелочи и придирки, в общем-то. laughing.gif
Ах, да, ещё I2C Slave не нуждается в делителях клока, это всё нужно только мастеру. Slave работает на том клоке SCL, который приходит от мастера.

не понял по включению клока:
Код
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;

вот включил клок, только потом дальше начинаю инициализировать регистры. Не так?
gerber
Цитата(Метценгерштейн @ Oct 6 2016, 15:05) *
ну а так в целом- все регистры проинициализированы корректно? Или чего-то забыто?

В целом, вроде ОК.
Цитата(Метценгерштейн @ Oct 6 2016, 15:05) *
не понял по включению клока:
Код
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;

вот включил клок, только потом дальше начинаю инициализировать регистры. Не так?

Это клок GPIOB, имелся в виду клок I2C-контроллера
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
да, его надо включать в самом начале инициализации, даже до сброса периферии, КМК.
Метценгерштейн
Код
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;


так пойдет?

Здесь мы ловим всех блох, которые глючат. Поэтому любое, что режет глаз- будем править.

Насчет делителя для мастера- можно его не трогать? Я понял, что это просто лишнее написано и не применяется, но не мешает слейву моему.
gerber
Цитата(Метценгерштейн @ Oct 6 2016, 15:16) *
так пойдет?

Я бы снес инициализацию ног после инициализации I2C, хотя это и непринципиально, возможно. Дело вкуса, но подключать неинициализированный I2C к реальной шине, а потом его инициализировать, ИМХО, не очень правильно.
Цитата(Метценгерштейн @ Oct 6 2016, 15:16) *
Насчет делителя для мастера- можно его не трогать? Я понял, что это просто лишнее написано и не применяется, но не мешает слейву моему.

Да, это не мешает слейву, просто лишняя операция.
Метценгерштейн
Цитата(gerber @ Oct 6 2016, 15:25) *
Я бы снес инициализацию ног после инициализации I2C, хотя это и непринципиально, возможно. Дело вкуса, но подключать неинициализированный I2C к реальной шине, а потом его инициализировать, ИМХО, не очень правильно.


Не очень понял- как предлагаете сделать?
gerber
Цитата(Метценгерштейн @ 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 мкс.
Метценгерштейн
Можно как-то попросить поделиться драйвером вашим?
Пока что стоит на прогоне плата у меня- ничего не вылетает. Может просто помыл грязь перед этим.
k155la3
Цитата(Метценгерштейн @ 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".
А слейв это сделал "не в том смысле", или наоборот, не сделал.

Как альтернативный вариант. Может ошибка в мастере ?






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

0 выставил.

Мастер- это Линукс. Должно быть все отлажено. Проц на линуксе, в смысле.

Посмотрим утром. Отпишусь.
k155la3

Может помеха - наводка быть.
Импульсные блоки питания, настольные лампы флюорисцентные и светодиодные.
И еще куча всего в этомже стиле.
Метценгерштейн
Такое ощущение происходило, что именно со стороны Линукса проблема, т.е. мастера.
Т.к. не перегружая STM, перегрузив Линукс- все работало снова нормально.
А в отладчике в модуле I2C что можно посмотреть когда отвалится? На что обратить внимание? На какой регистр?
Метценгерштейн
Ну что. Ночь отстояло. 2 стенда сразу. Ничего нигде не отвалилось. С утра оба все работали.
Но еще понаблюдаю. Может проц был грязный. Помыл его перед тестами хорошенько.
Вопрос пока- если вдруг отвалился I2C, в кейле в отладке на какие регистры и биты смотреть?
k155la3
Цитата(Метценгерштейн @ Oct 6 2016, 20:05) *
Такое ощущение происходило, что именно со стороны Линукса проблема, т.е. мастера.
Т.к. не перегружая STM, перегрузив Линукс- все работало снова нормально.
А в отладчике в модуле I2C что можно посмотреть когда отвалится? На что обратить внимание? На какой регистр?


Исходя из своего опыта долбежа с узлом I2C могу сделать вывод, что без лог. анализатора поиск ошибок
на 1-2 порядка сложнее, если вообще возможен.

При "влете" в ошибку зафиксируйте состояние шины, в смысле какие уровни на линиях SDA SCL.
Например, отключите с шины узел I2C и линии переведите в режим входов.
Если залипла какая-либо линия - это какая-то несостыковка по логике работы master-slave.
Залипнуть может (причем это не сбой а режимы ожидания) как SDA так и SCL.
Несостыковка может быть как устраняемая - когда возможная ошибка или недописка логики на Вашей стороне.
А если кривой мастер - тут ничего не сделаешь. Ситуацию можно только попытаться обойти.

Т.е. надо отловить (устойчиво-гарантированно) при каких обстоятельствах мастер отказывается работать.
Потом отсадить и мучить до победы sm.gif

Возьмите простой лог. анализатор, клон Saleae (10-20 кваксов) на базе CY7C68013A и будет Вам счастье
(там еще куча протоколов анализа, USART etc)


Цитата(Метценгерштейн @ Oct 7 2016, 09:55) *
Ну что. Ночь отстояло. 2 стенда сразу. Ничего нигде не отвалилось. С утра оба все работали.
Но еще понаблюдаю. Может проц был грязный. Помыл его перед тестами хорошенько.
Вопрос пока- если вдруг отвалился I2C, в кейле в отладке на какие регистры и биты смотреть?


Если уж дошло до мытия проц-ра - тогда уж посмотрите выводы на мелкоскопе.

"отвалится" - очень неконкретно.
Первое, на что надо обратить внимание - в каком состоянии линии шины.
Если получается в отладчике остановиться на "отвале"
- проверьте осцилографом или тестером состояние линий.
- переведите линии из I2C на вход и опять проверьте напредмет "залипа" SDA - SCL
Так Вы определите, если это "залип", то кто-есть-ху - мастер или слейв

ps - далее по обстоятельствам.
шина в норме (1-1)
залип со стороны мастера (SDA/SCL)
залип со стороны слейва (SDA/SCL)
Метценгерштейн
а еще такой момент- не указал нигде в коде, что прерывания по I2C имеют высший приоритет.
Код
NVIC_SetPriority(I2C1_IRQn, 0); // так не сделано


может надо указать все-таки? Может из-за этого лагать?
k155la3
Цитата(Метценгерштейн @ Oct 7 2016, 16:02) *
а еще такой момент- не указал нигде в коде, что прерывания по I2C имеют высший приоритет.
Код
NVIC_SetPriority(I2C1_IRQn, 0); // так не сделано


может надо указать все-таки? Может из-за этого лагать?


Не исключено. Мастер он и есть мастер, считает если послан запрос, то обязательно должен быть ответ от слейва.
В противном случае он считает что слейва нет, он неисправен и прочие напасти. Зависит от фантазии писателя.
Например, может переинициализировать свою периферию, и проверить шину на залипание.
Если он видит залипшую шину, то может попытаться ее "разлепить" путем подачи пакета SCL (не помню 8 или 9 клоков до "отлипа").
(такая ситуация у меня была с 24LC16 на отладке).
Если слейв не может ответить, то (слейв) должен перевести шину в режим "ожидания готовности слейва",
кажется это притянуть SCL на 0. Но лучше сразу дать ему что-надо по протоколу - чтобы не заморачиваться с реализацией ожидания.

Приоритет прерывания, скорее всего значения не имеет.
Имеет значение "наполнение" вектора прерывания.

gerber
После RESET, насколько я помню, в STM32 все периферийные прерывания имеют одинаковый наивысший приоритет (нуль), поэтому приоритет можно только понизить. Например, SysTick обычно конфигурируется на самом низком приоритете (15), чтобы не мешать остальным.

Полу-OFF: был в моей практике неприятный трудноотлавливаемый глюк, связанный с неправильной расстановкой приоритетов. В низкоприоритетном коде контроллер выполнял примерно такую конструкцию:
Код
if(NeedSleep)
{
  Sleep();
  NeedSleep = 0;
}

В функции Sleep() контроллер гасил PLL, и уходил в "спячку" WFI(). Выход оттуда - по прерыванию.
Так вот, очень редко, 1-2 раза в день при круглосуточной работе, контроллер "отваливался" и приходилось передергивать питание.
Причина оказалась в том, что если после проверки
Код
if(NeedSleep)

и до ухода в спячку возникало прерывание - оно оставалось необработанным, так как контроллер после него "засыпал".
Поймать такую багу оказалось возможно только с помощью логического анализатора, который тут не раз справделиво советовали.
Вылечилось так
Код
__disable_irq();
if(NeedSleep)
{
  Sleep();
  NeedSleep = 0;
}
__enable_irq();
Метценгерштейн
глюк вот какой замечен.
Связка- мастер Линукс, слейв на STM32.
Запускаю- работает. Был момент, что не трогая STM32, останавливаю программу на Линуксе, потом заново ее стартую. Программа эта шлет в I2C данные, которые мой STM32 принимает. Так вот- STM ничего не принимает больше. Перезагрузка STM тоже не помогает. Помогает только полная перезагрузка Линукса заново и с этим перезапуск программы.
Пока такие наблюдения. А хоть в теории- почему так?
А после каких- то манипуляций с кодом на STM, после перепрошивки его, можно много раз подряд стопить линукс и стартовать заново программу- работает.
В общем, я теряюсь уже.

лог. анализатор- это хорошо. С китайским клоном кто- то работал? Можно его брать?
Но проблема- купить его- долго время уйдет. Нужен сейчас.
Да и не понятно как в прошлом моем сообщении- что он покажет?
k155la3
Цитата(Метценгерштейн @ Oct 7 2016, 18:20) *
глюк вот какой замечен.
Связка- мастер Линукс, слейв на STM32.
Запускаю- работает. Был момент, что не трогая STM32, останавливаю программу на Линуксе, потом заново ее стартую. Программа эта шлет в I2C данные, которые мой STM32 принимает. Так вот- STM ничего не принимает больше. Перезагрузка STM тоже не помогает. Помогает только полная перезагрузка Линукса заново и с этим перезапуск программы.
Пока такие наблюдения. А хоть в теории- почему так?

Чтобы в такой ситуации теоретически "вычислить" глюк, придется выкуривать досконально работу "автоматов" I2C как STM, таки и мастерского.
А также стандарт I2C - у филипса-NXP можно скачать мануал на эту тему.
Каким путем я гонял своих "чертей" описано выше.

Чистым "программным-безаппаратным" методом, без анализатора и осцилографа ловить чертей сложно.
А с их использованием - часто элементарно sm.gif IMHO

Цитата
. . . .
лог. анализатор- это хорошо. С китайским клоном кто- то работал? Можно его брать?
Но проблема- купить его- долго время уйдет. Нужен сейчас.
Да и не понятно как в прошлом моем сообщении- что он покажет?


Вот анализатор которым я пользуюсь Saleae_Clone_LAnalizer_8Channel

Как срочный вариант - используйте 2-лучевой осцилограф + вход синхро.
На синхро подаете строб методом ногодрыга "когданадо", SDA-SCL - на 2 канала.
Но главное - локализировать по месту в алгоритме и времени появление глюка. Я выше Вам это рекомендовал.

ps - что покажет анализатор.
-"байтовую" расшифровку тарфика обмена мастер-слейв.
- удобочитаемые отметки состояний START, STOP, RESTART, ACK NACK
- метки времени
- лог. анализатор (запись) можно стартануть в нужном месте алгоритма методом ногодрыга или с анализируемых линий
Вам потребуется локализовать момент "глюка" с маcтером, а далее промониторить обмен стартуя лог. анализатор
незадолго до появления глюка. Так вы можете проанализировать "предпроцесс" и найти причину глюка-завеса.

Так в каком (извиняюсь за назойливость) состоянии линии I2C после глюка ?
Это можно на отладчике или даже тестером посмотреть.
Метценгерштейн
в понедельник дам ответ. Заодно посмотрим- за выходные не слетела работа девайса.
Глюк не просто отловим, поэтому надо смотреть.
Осцилл, конечно, есть у меня.
Метценгерштейн
k155la3,

все выходные простояло нормально. С утра сегодня все кнопочки работали. Моя прога на МК обрабатывает кнопки и по I2C шлет их в проц на Линуксе.
Но после перезагрузки софта на Линуксе, связь по I2C пропала. Команды от Линукса я не получаю.

В отладчике какие смотреть регистры I2C?
k155la3
Цитата(Метценгерштейн @ Oct 10 2016, 10:41) *
k155la3,
все выходные простояло нормально. С утра сегодня все кнопочки работали. Моя прога на МК обрабатывает кнопки и по I2C шлет их в проц на Линуксе.
Но после перезагрузки софта на Линуксе, связь по I2C пропала. Команды от Линукса я не получаю.

В отладчике какие смотреть регистры I2C?

0. подключиться осцилографом на SDA-SCL
1. запускаете свой проект на отладчике.
2. обеспечиваете "завес" мастером (не понял, что означает "перегрузки софта" - это холодный-горячий ресет или перезаливка флеш ?)
3. после фиксации завеса останавливаете свой слейв отладчиком (можно поставить breakpoint "тамгдеможно" чтоб не остановить в векторе перывания).
4. осцилографом смотрите состояние линий SDA-SCL. Подключиться на линии лучше заранее, чтобы помехой подключения не сбросить ситуацию.
Фиксируете значения уровней. Писал об этом выше.
5. в отладчике, в регистрах управления функцией порта, на который выведен I2C, отключаете узел I2C, а порт переводите в режим входа.
Снова фиксируете значения уровней.

Вместо (5) можно просто разорвать шину I2C и посмотреть ее состояние со стороны мастера.

Если есть "залип" - это диагностика с чьей стороны - мастера ли слейва.

Кстати, на шине I2C только один мастер и один слейв ?
Метценгерштейн
Пробую.
Перезагрузка софта-

есть Линукс. На нем программа, что работает с моим МК.
Запускаю Линукс (включил питание на плату), запускаю эту прогу.
Так вот- было так, что одна перезагрузка проги не дает результата. Только полный рестарт самого Линукса, потом снова запуск проги.

пока вижу одну картину- такое ощущение, что NAK в ответ идет.
Картинку сейчас сделаю.

Одни мастер и один слейв.

картинка
http://prntscr.com/crztu6

хотя картинка из пачки передачи, так что наки там- это нормально.

очень похоже, что именно сам модуль процессора Линукс дурил. Заменил на другой модуль- все ОК.
k155la3
Цитата(Метценгерштейн @ Oct 10 2016, 13:17) *
. . . .
хотя картинка из пачки передачи, так что наки там- это нормально.
очень похоже, что именно сам модуль процессора Линукс дурил. Заменил на другой модуль- все ОК.


Если это пакет от мастера. В этом случае должен быть ACK.
Первый байт мастера в сторону слейва есть <адрес слейва + RW + (N)ACK >.
Поэтому в ответ на этот байт слейв должен выдать подтверждение "я понял свой адрес и режим R/W" и на поле (N)ACK притянуть
шину на 0.
А на осцилограмме ОНО в 1. Т.о. мастер "не увидел" ваш слейв. Дает Stop И пробует его увидеть во второй раз (на осцилограмме).
Адрес слейва 0x7E. Мастер хочет что-то прочитать. После этого должны идти или байты адреса в слейв (как в EEPROM I2C)
или то, что определено в Вашем протоколе обмена.

Метценгерштейн
там идет пачка пакетов. Я выхватил что-то посередине. Нужен анализатор. Уже заказал. месяц пути.
адрес слейва у меня 0х01.
Я тоже вижу 0х7Е.
Значит, не к моему обращается.
Нужен анализатор.
Nixon
Месяц ждать? А на месте купить? Запрос "saleae logic петербург купить" выдает цену ~700 рублей.
Метценгерштейн
сегодня куплю вечером его.
Утром будет у меня уже.

пока давайте еще в осцилл посмотрим.
http://prntscr.com/cs3vat

При нормальной работе идет пачка из 5 пакетов. Адрес моего слейва 0х01

Почему NAK приходит первым пакетом?
gerber
Цитата(Метценгерштейн @ Oct 10 2016, 17:56) *
При нормальной работе идет пачка из 5 пакетов. Адрес моего слейва 0х01

Почему NAK приходит первым пакетом?

Вроде же, ACK там на осциллограмме. 9-й бит защёлкивается ноль, это ACK.
Метценгерштейн
да, правильно. на 9-м бите в нуле зеленый луч. Это АСК.
Вторая пачка всегда в нуле. Тоже нормально?
gerber
Цитата(Метценгерштейн @ Oct 10 2016, 18:04) *
да, правильно. на 9-м бите в нуле зеленый луч. Это АСК.
Вторая пачка всегда в нуле. Тоже нормально?

biggrin.gif
Тут уж Вам виднее. Судя по биту направления, идёт чтение, читается нулевой байт, а правильно это или нет, определяется логикой обмена.
Метценгерштейн
Цитата(gerber @ Oct 10 2016, 17:59) *
Вроде же, ACK там на осциллограмме. 9-й бит защёлкивается ноль, это ACK.

Кстати, не наблюдаете проблем с мастером? Т.е. между фронтами не мало времени? Должен по опыту аппаратный I2C работать?
gerber
Ну, фронты не только мастер определяет, в случае бита ACK/NACK его установка зависит от расторопности слейва, и у вас она на пределе, я бы сказал. Также видно, что мастер меняет SDA практически одновременно с падением вниз SCL, что как раз и может способствовать появлению ложных START/STOP условий, особенно на длинных проводах.
Это как раз то, с чем я боролся, и после чего остановился на софтовом I2C. Неприятная особенность аппаратного I2C в STM32 как раз в этом и заключается, что он очень чувствителен к таким ситуациям, выбрасывает при этом флаг BERR и начинает с начала.
Метценгерштейн
зеленый луч- зачастую слейв отвечает. После SCL слейв дает ответ через микросекунду ровно. Может мастеру быстро?

Разобрал по битам пачку от мастера:
дает адрес 1, далее на чтение.
слейв отвечает 0,7,8,90

а вот мой код:
outgoing[0] = 0x00;
outgoing[1] = 0x07;
outgoing[2] = 8;
outgoing[3] = calcCRC(outgoing, 4);

Все сходится. Вопрос глюков.
Nixon
Мастер растягивать SCL умеет? STM по-умолчанию делает это, возможно мастеру это не подходит.
Метценгерштейн
мастер может и умеет, но я туда не лезу.
Может где в STM указать, чтобы растянул слегка?
Я купил анализатор за 700=, будем завтра изучать вопрос углубленно ))
k155la3
Цитата(Метценгерштейн @ Oct 10 2016, 21:56) *
мастер может и умеет, но я туда не лезу.
Может где в STM указать, чтобы растянул слегка?
Я купил анализатор за 700=, будем завтра изучать вопрос углубленно ))

Теперь Вы и сами, думаю, разберетесь. А если покурите UM10204.pdf Philips - такужточно sm.gif

Я для такой разработки, конечно, вместо черного ящика с линуксом сделал бы его эмуляцию для "нагрузочного" тестирования.
(если в процессоре есть 2 канала I2C - то можно эмулятор мастера сделать и в основном проекте, а соединение с линуксом временно разорвать).

Обратите внимание на содержимое вектора обработки прерываний.
Он должен быть полнофункциональный по обработке ситуаций и минимальный по объему кода.
Только работа с периферийным узлом I2C - команды и состояния обмена.


Метценгерштейн
http://prntscr.com/cseh7b

вот кусок с анализатора. Не понятно- почему 3 в расшифровке? Адрес 7 бит, 0-й бит =1. Еще младше, это 1- на чтение слейва команда.

Содержимое ветки обработчика может и наполнено лишним. Но надо разобраться сначала тут.
Nixon
Анализатор пишет адрес с учетом сдвига. Там еще на боковой панели вам текстовый лог протокола идет.

По поводу SCL STRETCH - по-умолчанию он включен в STM32. Не каждый мастер поддерживает такую возможность (практически все примеры soft-i2c такого не умеют). Выключите этот режим. NOSTRETCH = 1 в CR1 кажется. Но имейте в виду - в таком случае ваши прерывания по I2C в ведомом не должны иметь длительной обработки.
Хотя если судить по биту ACK кажется ваш мастер такое умеет.
Метценгерштейн
может быть, что зависания происходят из-за того, что в закрытом корпусе? Рядом горячий проц на Линуксе (85 градусов). Корпус изделия на ощупь горячий.
Уже все варианты смотрю.

Только что-то не найти в даташите рабочей температуры МК
Jury093
Цитата(Метценгерштейн @ Oct 10 2016, 16:28) *
адрес слейва у меня 0х01.

как минимум, смените адрес слейва на допустимый.. i2cdetect вам в помощь..
Метценгерштейн
Цитата(Jury093 @ Oct 11 2016, 19:12) *
как минимум, смените адрес слейва на допустимый.. i2cdetect вам в помощь..

по стандарту от 0х03 должен быть.
Но с адресом 0х01 работает. И соседнее устройство аналогичное, но на AVR имеет адрес 0х02. Все работает. Возможно, допустима работа в этом диапазоне.

Метценгерштейн
Вроде локализовали проблему.
Прога на Линуксе не всегда отправляла пакеты моему устройству. Допустим, из параллельной консоли сделать i2cset... и команда на STM приходит нормально.

Вопрос по корректной реализации I2C прерывания.
Я вхожу в прерывание
Код
void I2C1_IRQHandler(void){


корректно ли делать внутри цикл for(;;) , чтобы он не выходил из прерывания, а крутился там пока флаги все не обработал? Или так не очень правильно?

Как получается вход в прерывание? Выскочило несколько флагов, он вошел в прерывание? И все там пачкой обработал?
Nixon
C этого и начинать нужно было. Не стоит нагружать прерывание сверх необходимого. Приняли/передали и выскочили. Для примера:
Код
void handler ( void ) {
    uint8_t  byte;
    
    if (_i2c->ISR & I2C_ISR_ADDR) {  // пришел ADDR
      _i2c->ICR = I2C_ISR_ADDR;                                        
    } else if (_i2c->ISR & I2C_ISR_RXNE) {  // прием байта
      byte = _i2c->RXDR;
      _buf_rx.push(byte);
    } else if (_i2c->ISR & I2C_ISR_TXIS) {  // запрос байта
      _buf_tx.pop(byte);
      _i2c->TXDR = byte;
    } else if (_i2c->ISR & I2C_ISR_NACKF) { // трансфер закончен, пришел NACK
      _i2c->ICR = I2C_ISR_NACKF;
    } else if (_i2c->ISR & I2C_ISR_STOPF) { // пришел STOP
      _i2c->ISR |= I2C_ISR_TXE;
      _i2c->ICR = I2C_ISR_STOPF;
    } else if (_i2c->ISR & I2C_ISR_BERR) {  // I2C Bus Error
      _i2c->ICR = I2C_ISR_BERR;
    } else if (_i2c->ISR & I2C_ISR_OVR) {  // I2C Over-Run/Under-Run
      _i2c->ICR = I2C_ISR_OVR;
    } else if (_i2c->ISR & I2C_ISR_ARLO) {  // I2C Arbitration Loss
      _i2c->ICR = I2C_ISR_ARLO;
    }    
  }


P.S. Это для STM32L0
Метценгерштейн
у вас код if else if
другими словами, в прерывании только одно событие обработается.
А не может придти два события сразу?

т.е. нет смысла делать
if
if
if
...
?

и еще - насчет по быстрому обработали прерывание и выскочили-
Код
else if (_i2c->ISR & I2C_ISR_RXNE) {  // прием байта
      byte = _i2c->RXDR;
      _buf_rx.push(byte);
    }

мы тут не сразу выскакиваем, идем сначала в _buf_rx.push(), там что-то делаем, потом только выскакиваем.
Так что можно и в самом прерывание тогда это же делать.
Timmy
Цитата(Метценгерштейн @ Oct 13 2016, 10:53) *
у вас код if else if
другими словами, в прерывании только одно событие обработается.
А не может придти два события сразу?

Если придут два события сразу, они обработаются в двух последовательных входах в обработчик прерывания. Вообще, вариант от Nixon является наилучшим универсальным и имеет смысл при использовании подгрупп приоритетов прерываний, и принадлежности прерывания I2C не к самой приоритетной подгруппе. В этом случае может быть целесообразно выскочить из обработчика прерывания как можно скорее, чтобы передать управление обработчику более привилегированной подгруппы, если возникнет запрос на него. Во всех прочих случаях вариант Nixon, как и большинство универсальных решений sm.gif , создаёт более длинный код и большее время выполнения при одновременной обработке нескольких событий.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.