|
|
  |
При запуске из отладчика программа работает, когда отключаешь и снова включаешь STM32 программа перестает работать |
|
|
|
Apr 5 2018, 13:06
|
Частый гость
 
Группа: Участник
Сообщений: 169
Регистрация: 31-08-05
Из: New York
Пользователь №: 8 118

|
Скорость нарастания питающего напряжения у работающих и не работающих плат одинакова?
--------------------
ASB
|
|
|
|
|
Apr 6 2018, 06:09
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(Alex_Golubev @ Apr 6 2018, 07:48)  Но как влияет в моем случаи I2C мне не ясно. Нашел в исходниках "чудесные" строчки (это обработчик прерывания I2C) Код case TIC_STATE_POS_STOP: I2C2->CR1 = I2C_CR1_PE | I2C_CR1_STOP; tic_state = TIC_STATE_FILL_START; break;
case TIC_STATE_FILL_START: s1 = 0; while(I2C2->SR1 !=0 ) s1++; I2C2->CR1 = I2C_CR1_PE | I2C_CR1_START; tic_state = TIC_STATE_FILL; break; Без дикого while(I2C2->SR1 !=0 ) ничего не работало (вроде, I2C переставал функционировать и никак его из этого ступора было не вытащить). Долго возился, но в итоге переделал все на ногодрыг.
|
|
|
|
|
Apr 6 2018, 07:59
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(adnega @ Apr 6 2018, 09:09)  Без дикого while(I2C2->SR1 !=0 ) ничего не работало (вроде, I2C переставал функционировать и никак его из этого ступора было не вытащить). Долго возился, но в итоге переделал все на ногодрыг. Странно, у меня прекрасно работает как раз на F429 аппаратный I2C (да ещё с DMA) с кучкой висящих на нём разных устройств (FRAM + RTC + FM-тюнер + touch-контроллер) на 400кГц. По многу часов без каких-либо сбоев. И никаких ногодрыгов и тем более - циклов в ISR. Так что у автора явно не МК виноват, а кривые руки.  Код #define TUNER_ADDR_NATIVE_SEQ 0x10 //RDA5807 mode, sequential register address #define TUNER_ADDR_NATIVE_RND 0x11 //RDA5807 mode, random access mode. #define TUNER_ADDR_COMPATIBLE 0x60 //TEA5767 compatible mode #define FRAM_ADDR 0x53 //адрес FRAM-памяти FM31T37x #define RTC_ADDR 0x6B //адрес RTC FM31T37x #define TSC_ADDR 0x41 //адрес touchscreen controller STMPE811
|
|
|
|
|
Apr 6 2018, 08:42
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(adnega @ Apr 5 2018, 17:48)  Или как-то некорректно ожидается "раскочегаривание" кварцев, PLL... + на плате, которая глючная, поменять кварц или генератор. Возможно дело в конкретном экземпляре. Цитата(Forger @ Apr 6 2018, 09:00)  Попробуйте для начала отключить вообще все узлы на этой шине. . . . + Поотключать блоки кода или функции. + Если (вдруг) в коде проекта есть таймауты - подвигать их длительность.
|
|
|
|
|
Apr 6 2018, 09:05
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Apr 6 2018, 10:59)  Странно, у меня прекрасно работает как раз на F429 аппаратный I2C (да ещё с DMA) с кучкой висящих на нём разных устройств (FRAM + RTC + FM-тюнер + touch-контроллер) на 400кГц. Для этого проекта применялся STM32F103RET6. Цитата(jcxz @ Apr 6 2018, 10:59)  По многу часов без каких-либо сбоев. Масштаб "часы" это очень мало. В проекте использовался дисплей TIC154(A) на I2C для постоянной индикации. А сбои вы как-то разруливали? Бывает, slave выдает ACK на линию, а мастеру хочется START устроить, но у него ничего не получится. Тут ногодрыгом нужно slave протолкнуть до 1 на SDA. Я умышленно создавал помехи в линии, чтобы система смогла их обработать. Цитата(jcxz @ Apr 6 2018, 10:59)  И никаких ногодрыгов и тем более - циклов в ISR. Так что у автора явно не МК виноват, а кривые руки.  Никто с этим не спорит. Когда все хорошо (нет помех), то и сценарий работы сложно сделать плохим, но ведь бывают нештатные ситуации... в этом случае корректнее говорить о таланте разруливания, а не о кривизне рук, т.к. работают скорее не руки, а голова. Приведенный кусок - это ситуация когда сразу за STOP от предыдущей посылки формируется START для следующей.
|
|
|
|
|
Apr 6 2018, 10:28
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(adnega @ Apr 6 2018, 12:05)  Для этого проекта применялся STM32F103RET6. У автора как раз F429, а не F103. А у них как я слышал - I2C сильно отличаются. Цитата(adnega @ Apr 6 2018, 12:05)  А сбои вы как-то разруливали? Бывает, slave выдает ACK на линию, а мастеру хочется START устроить, но у него ничего не получится. Тут ногодрыгом нужно slave протолкнуть до 1 на SDA. Я умышленно создавал помехи в линии, чтобы система смогла их обработать. Не понял - что значит "slave выдает ACK на линию"? ACK слэйв выдаёт только для подтверждения приёма данных. Мастер в это время никак не может СТАРТ выдавать, он ACK или не-ACK должен ждать. И не понимаю - зачем тут ногодрыг? Цитата(adnega @ Apr 6 2018, 12:05)  Никто с этим не спорит. Когда все хорошо (нет помех), то и сценарий работы сложно сделать плохим, но ведь бывают нештатные ситуации... в этом случае корректнее говорить о таланте разруливания, а не о кривизне рук, Помех по I2C у меня в том устройстве очень много. По питанию (и возможно по эфиру) от работающего рядом ESP8266. И я долго с ними боролся. Даже сюда писал. Но теперь всё работает стабильно. Цитата(adnega @ Apr 6 2018, 12:05)  Приведенный кусок - это ситуация когда сразу за STOP от предыдущей посылки формируется START для следующей. CODE enum {OPTION_DUPLEX_RS, OPTION_DUPLEX_SS, OPTION_2BYTE, OPTION_TIMER, OPTION_SA}; .. extern "C" void Isr() { u32 i0 = i2cU->SR[0], i1; if (!i0) return; switch (isrStep) { case IS_RESTART: //передан повторный START if (!(i0 &= ~(1 << SR0_BTF | 1 << SR0_TXE))) return; case IS_START: //передан START if (i0 != 1 << SR0_SB) fault(i0); i0 = i2cU->SR[1]; if ((i0 & (1 << SR1_MSL | 1 << SR1_BUSY | 1 << SR1_TRA)) != (1 << SR1_MSL | 1 << SR1_BUSY)) fault(i0 | ERR_BAD_START << 16); if (saddr & 1) if (cnt + 1 < 0) i2cU->CR[0] = 1 << CR0_ENA | 1 << CR0_ACK; i2cU->DR = saddr; if (option & 1 << OPTION_TIMER) actTimer = HrTimerGet(); isrStep = IS_SADDR; return; case IS_SADDR: //передан Slave Address ... case IS_CMD1: //передана вся стартовая последовательность if (i0 == 1 << SR0_TXE) return; if (i0 != (1 << SR0_BTF | 1 << SR0_TXE)) fault(i0); if (option & (1 << OPTION_DUPLEX_SS | 1 << OPTION_DUPLEX_RS)) { saddr++; i2cU->CR[0] = (option >> OPTION_DUPLEX_SS & 1) << CR0_STOP | 1 << CR0_START | 1 << CR0_ENA; isrStep = IS_RESTART; return; } else if (cnt) { i1 = IS_TX; ... Приведённый код - часть ISR-а драйвера I2C, работающего по флагам в регистрах I2C и машине состояний (isrStep). Если для устройства нужна I2C-транзакция с рестартом или повторным стартом (дуплексная транзакция), то перед запуском транзакции I2C в option драйвер устанавливает соответствующий флаг или OPTION_DUPLEX_SS (для переключения вывод->ввод выполнить на шине СТОП-СТАРТ) или OPTION_DUPLEX_RS (для переключения вывод->ввод выполнить на шине ПОВТОРНЫЙ СТАРТ). Что именно нужно OPTION_DUPLEX_SS или OPTION_DUPLEX_RS - выясняется из даташита на слэйв. Так что как видно - обхожусь без циклов в ISR и без ногодрыга.
|
|
|
|
|
Apr 6 2018, 12:09
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ Apr 6 2018, 13:28)  Не понял - что значит "slave выдает ACK на линию"? ACK слэйв выдаёт только для подтверждения приёма данных. Мастер в это время никак не может СТАРТ выдавать, он ACK или не-ACK должен ждать. Мастер выдал на SCL такт, но в результате помехи slave его не принял, а поскольку slave до этого выдавал ACK уровня "0", то получается, что мастер уже такт для ACK выдал и считает транзакцию законченной, а slave ждет такта для проталкивания ACK. Если не понятно, что это редко, но возможно, и без ногодрыга это не решить, то пусть это останется вне рамок обсуждения данной темы. Цитата(jcxz @ Apr 6 2018, 13:28)  Так что как видно - обхожусь без циклов в ISR и без ногодрыга.  Проблема не с повторным стартом (который нужен, например, при чтении), а в идущих друг за другом обычных write-транзакций. Попробуйте нарисовать STOP и START с нулевой паузой между ними. У меня получается лишь "1"-иголка на SDA.
|
|
|
|
|
Apr 6 2018, 12:34
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(adnega @ Apr 6 2018, 15:09)  Мастер выдал на SCL такт, но в результате помехи slave его не принял, а поскольку slave до этого выдавал ACK уровня "0", то получается, что мастер уже такт для ACK выдал и считает транзакцию законченной, а slave ждет такта для проталкивания ACK. Если не понятно, что это редко, но возможно, и без ногодрыга это не решить, то пусть это останется вне рамок обсуждения данной темы. При таких ошибках, драйвер выявляет ошибку на шине и уходит в fault(). Т.е. - рестарт устройства. При желании можно не всё устройство перезапускать, а только I2C-драйвер. А старт драйвера I2C у меня начинается с 15-кратного выполнения операций СТАРТ и СТОП на шине. Ногодрыгом. Что выведет любого слэйва из непонятного состояния. Цитата(adnega @ Apr 6 2018, 15:09)  Попробуйте нарисовать STOP и START с нулевой паузой между ними. У меня получается лишь "1"-иголка на SDA. А я Вам ещё раз повторяю: у меня при выполнении STOP и последующем START (опция OPTION_DUPLEX_SS) в приведённом выше драйвере, на шине наблюдаются нормальные условия СТОП и СТАРТ. А не иголка. Т.е. - I2C-контроллер сам отрабатывает необходимую времянку. И из указанного списка устройств на шине, для TUNER_ADDR_... используется OPTION_DUPLEX_SS, а для остальных - OPTION_DUPLEX_RS.
|
|
|
|
|
Apr 7 2018, 08:04
|

Местный
  
Группа: Участник
Сообщений: 492
Регистрация: 12-11-11
Пользователь №: 68 264

|
Я тоже ничего не понял про ACK во время START... Вообще таких вещей протокол не предусматривает. Есть четкая последовательность выдачи сигналов на линии. Да, бывают сбои по различным причинам - от мощных переключателей вне устройства, например, происходит рассинхронизация приемника и передатчика путем вставки "лишнего" clock-а. Я, например, всегда придерживаюсь стратегии следующего рода: если процессор очень шустрый, периферия медленная, но есть DMA - я всегда работаю с DMA. I2C в STM32F429 также поднимал с обработкой всех ошибок. Я делал так: CODE // HW_ExchangeTransmitData - функция отправки данных по внешнему каналу обмена. // Параметры: // Buffer - указатель на буфер передаваемых данных; // Size - объем данных в байтах. // Возвращаемое значение: // HW_EXCHANGE_TRANSMIT_OK - отправка успешно запущена; // HW_EXCHANGE_TRANSMIT_ERROR_TIMEOUT - канал обмена занят транзакцией данных. unsigned int HW_ExchangeTransmitData(void *Buffer, unsigned int Size) { HW_EXCHANGE_TIMER_START(65535); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) && !ExchangeBusyStatus); HW_EXCHANGE_TIMER_STOP(); unsigned int PhysicalBusy = ExchangeBusyStatus; ExchangeBusyStatus = 0; if(PhysicalBusy) return HW_EXCHANGE_TRANSMIT_ERROR_TIMEOUT; else { ExchangeTxDataQuantity = Size; DMA_MemoryTargetConfig(DMA1_Stream6, (unsigned int)Buffer, DMA_Memory_0); DMA_SetCurrDataCounter(DMA1_Stream6, ExchangeTxDataQuantity); I2C_GenerateSTART(I2C1, ENABLE); HW_EXCHANGE_TIMER_START(1); return HW_EXCHANGE_TRANSMIT_OK; } } При отправке я сначала смотрю, а не занят ли модуль I2C прошлой транзакцией. Чтобы вечно не тупить в цикле ожидания BUSY, покрываю проверку аппаратным таймером на максимальную длину посылки через DMA. Если занят - говорю что вышел таймаут ожидания и выхожу из функции. Чуть позже добавлю сюда просто ожидание семафора, чтобы освободить задачу (эта функция крутится в составе ОСРВ). После того как I2C освободился, заполняю служебные переменные размера передаваемого сообщения, настраиваю DMA, но не включаю его. Формирую условие START и запускаю контролирующий таймер на минимальный интервал передачи одного байта. Теперь выхожу из функции - вся остальная работа будет сделана в прерывании. CODE void I2C1_EV_IRQHandler(void) { if(I2C_GetITStatus(I2C1, I2C_IT_SB) == SET) { ExchangeDirection = HW_EXCHANGE_DIRECTION_TRANSMITTER; HW_EXCHANGE_TIMER_STOP(); I2C_Send7bitAddress(I2C1, HW_EXCHANGE_I2C_ADDRESS_ABONENT, I2C_Direction_Transmitter); HW_EXCHANGE_TIMER_START(1); return; } if(ExchangeDirection == HW_EXCHANGE_DIRECTION_RECEIVER) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; if(I2C_GetITStatus(I2C1, I2C_IT_ADDR) == SET) { if(!ExchangeOverrunStatus) { ExchangeOverrunStatus = 1; DMA_MemoryTargetConfig(DMA1_Stream0, ExchangeRxDataBuffer, DMA_Memory_0); DMA_SetCurrDataCounter(DMA1_Stream0, ExchangeRxBufferSize); DMA_ClearFlag(DMA1_Stream0, DMA_FLAG_TCIF0); DMA_Cmd(DMA1_Stream0, ENABLE); } else System.ExchangeStatus |= SYSTEM_EXCHANGE_STATUS_RX_ERROR_DATA_OVERRUN; (void)I2C_GetFlagStatus(I2C1, I2C_FLAG_TRA); } if(I2C_GetITStatus(I2C1, I2C_IT_BTF) == SET) { (void)I2C_ReceiveData(I2C1); System.ExchangeStatus |= SYSTEM_EXCHANGE_STATUS_RX_ERROR_BUFFER_OVERFLOW; } if(I2C_GetITStatus(I2C1, I2C_IT_STOPF) == SET) { if(DMA_GetCmdStatus(DMA1_Stream0) == ENABLE) DMA_Cmd(DMA1_Stream0, DISABLE); if(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == SET) { (void)I2C_ReceiveData(I2C1); System.ExchangeStatus |= SYSTEM_EXCHANGE_STATUS_RX_ERROR_BUFFER_OVERFLOW; } *ExchangeRxDataQuantity = ExchangeRxBufferSize - DMA_GetCurrDataCounter(DMA1_Stream0); xSemaphoreGiveFromISR(ExchangeRxSemaphoreHandle, &xHigherPriorityTaskWoken); I2C_GenerateSTOP(I2C1, DISABLE); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } else { BaseType_t xHigherPriorityTaskWoken = pdFALSE; if(I2C_GetITStatus(I2C1, I2C_IT_ADDR) == SET) { HW_EXCHANGE_TIMER_STOP(); DMA_ClearFlag(DMA1_Stream6, DMA_FLAG_TCIF6); DMA_Cmd(DMA1_Stream6, ENABLE); (void)I2C_GetFlagStatus(I2C1, I2C_FLAG_TRA); HW_EXCHANGE_TIMER_START(ExchangeTxDataQuantity + 1); } else if(I2C_GetITStatus(I2C1, I2C_IT_BTF) == SET) { HW_EXCHANGE_TIMER_STOP(); I2C_GenerateSTOP(I2C1, ENABLE); ExchangeDirection = HW_EXCHANGE_DIRECTION_RECEIVER; xSemaphoreGiveFromISR(ExchangeTxSemaphoreHandle, &xHigherPriorityTaskWoken); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } В прерывании обрабатываются все события I2C в зависимости от приема или передачи, при этом каждый шаг контролируется защитным временным интервалом в зависимости от количества передаваемых данных. Таким образом, все сошлось к следующему: 1. Настроить DMA. 2. Выдать на линию START-условие. 3. Ждать в прерывании установки события START (SB). 4. Запустить передать физический адрес устройства. 5. Ждать в прерывании установки события окончания передачи адреса (ADDR). 6. Запустить DMA. 7. Ожидать в прерывании установки события BTF. 8. Выдать на линию STOP-условие. На почти каждый шаг накладывается штрафной таймер, контролирующий выполнение текущего шага. Этот код работает очень долго и стабильно. Обработка ошибок возложена на вышестоящие функции, как видно, в системный журнал ошибок (структура System) постоянно сваливаются нужные флаги.
Сообщение отредактировал IgorKossak - Apr 7 2018, 14:03
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!
|
|
|
|
|
Apr 7 2018, 14:31
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Arlleex @ Apr 7 2018, 11:04)  При отправке я сначала смотрю, а не занят ли модуль I2C прошлой транзакцией. Чтобы вечно не тупить в цикле ожидания BUSY, покрываю проверку аппаратным таймером на максимальную длину посылки через DMA. Если занят - говорю что вышел таймаут ожидания и выхожу из функции. Чуть позже добавлю сюда просто ожидание семафора, чтобы освободить задачу (эта функция крутится в составе ОСРВ). В указанном устройстве у меня есть арбитр шины I2C. Он и управляет всеми транзакциями на шине. Это процесс, который ждёт появления флагов запросов от клиентов о выполнении очередной транзакции с одним из устройств на шине. Каждый клиент, когда хочет выполнить транзакцию с одним из I2C-устройств, формирует запрос об этой транзакции, помещает его в один из слотов запросов (свободный слот), выставляет флаг наличия запроса в слоте, пингует I2C-арбитра и становится на ожидание события завершения транзакции. I2C-арбитр, когда освобождается после завершения очередной транзакции, просматривает флаги активных запросов в слотах и выбирает наиболее приоритетный из них и запускает выполнение данной транзакции (программирует периферию I2C, DMA, выполняет последовательность шагов машины состояний в ISR и т.п.). По завершении транзакции, он заносит в слот информацию о результате транзакции (ОК или ошибка или ...), переводит событие готовности слота в сигнальное состояние (клиент выходит из состояния ожидания события) и выбирает следующий слот, ждущий обслуживания. Если такового нет сейчас - уходит в IDLE. Так что всеми транзакциями на шине и всей периферией, связанной с данным портом I2C, у меня управляет один процесс - I2C-арбитр. Клиенты, запрашивающие операции обмена с I2C-слэйвами, работают только через его API. Они изолированы от железа. Цитата(rudy_b @ Apr 7 2018, 12:58)  Именно поэтому каждую операцию приходится окучивать таймаутами, что является полным безобразием. Ничего не могу сказать про 207, но на F429 у меня транзакции по I2C не "окучены" ни какими таймаутами. Единственный таймаут, который отслеживается в коде моего I2C-драйвера - это когда при инициализации драйвера я выполняю многократно СТОП-условие по шине и контролирую наличие залипания линии SCL в низком состоянии слишком долгое время.
|
|
|
|
|
Apr 7 2018, 14:53
|

Местный
  
Группа: Участник
Сообщений: 492
Регистрация: 12-11-11
Пользователь №: 68 264

|
Цитата(jcxz @ Apr 7 2018, 18:31)  Ничего не могу сказать про 207, но на F429 у меня транзакции по I2C не "окучены" ни какими таймаутами. Однако я тогда не совсем понимаю, какая у Вас гарантия того, что данные успешно передадутся, да еще в нужное время. Если в Вашем устройства (на макете) замкнуть SCL на SDA пинцетом, затем убрать - связь восстановится? Если восстановится - то не понимаю, какая магия у Вас мониторит, что нужные флажки не взвелись через определенное время и сбрасывает автомат. Ведь идеологически любая конструкция Код while(ожидание флага); должна сопрягаться с установкой таймаута ожидания (не важно, средствами ОС или же аппаратным таймером). Ведь слова Цитата По завершении транзакции, он заносит в слот информацию о результате транзакции (ОК или ошибка или ...)... ставят встречный вопрос - каким же образом можно узнать результат транзакции? Ведь после установки битов управления (тот же START-бит), нужно некоторое время, чтобы в регистре статуса этот бит был выставлен и сгенерировалось прерывание. Если, как было указано выше, бесконечно ожидать, то получится полная лажа при какой-либо помехе на I2C-шине. Таймаут-то по-любому нужен, как без него...
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|