Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: При запуске из отладчика программа работает, когда отключаешь и снова включаешь STM32 программа перестает работать
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Alex_Golubev
Привет.
Контроллер STM32f429zi.
Столкнулся с такой проблемой, запускаешь программу из отладчика все работает (в частности USB), когда передергиваешь питание микроконтроллера программа начинает выполнится некорректно (отваливается USB).
Если запустить контроллер из отладчика IAR и сразу выключить отладчик (шнурок программатора-отладчика выдергиваю) не трогая питание STM32, то все нормально программа выполняется корректно.
Но если передернуть питание то опять все отвалилось.

Как можно найти причину этого?
KRS
Цитата(Alex_Golubev @ Apr 5 2018, 06:41) *
Как можно найти причину этого?


Если стартовать при помощи отладчика - происходит задержка, пока отладчик подключается, причина может быть в этом.

Или еще может быть у Вас таблица прерываний (0 элемент - начальный адрес стека, 1 - точка входа) не в том месте расположены!
Forger
Цитата(Alex_Golubev @ Apr 5 2018, 06:41) *
Как можно найти причину этого?

Куда грузится программа: ОЗУ или FLASH?
Alex_Golubev
Цитата
Куда грузится программа: ОЗУ или FLASH?
Не много не понял вопрос.
Но я загружаю программу во FLASH. Если вы говорите про BOOT0, то BOOT0 у меня на земле.
Forger
Создайте пустой новый проект в любимой среде, который, например, тупо моргает какой-нить лампочкой на плате.
Этот проект должен запускаться без отладчика. Если не удается этого добиться, то проблема где-то в железе или настройках среды.
Если удается, то постепенно добавляйте в этот проект исходники и вашего глючного проекта.
Цель - найти то место в ваших кодах, которые приводят к описанной проблеме.

зы. Подобный принцип поиска косяков годится для любой отрасли деятельности sm.gif
SSerge
Цитата(Alex_Golubev @ Apr 5 2018, 13:48) *
Не много не понял вопрос.
Но я загружаю программу во FLASH. Если вы говорите про BOOT0, то BOOT0 у меня на земле.

Симптомы указывают на неправильное положение таблицы векторов. Надо смотреть что там в файле .icf
Alex_Golubev
Есть еще один интересный момент, который меня вгоняет в ступор.
А именно, зашиваю эту же прошивку с помощью STM32 ST-LINK Utility в другой микроконтроллер и там такой проблемы нет. Беру еще один мк. там снова такая проблема беру другой ее нет.

В общем из 100 мк. 10 шт. ведут себя вот так не понятно.
Forger
Цитата(Alex_Golubev @ Apr 5 2018, 11:40) *
Есть еще один интересный момент, который меня вгоняет в ступор.

Любая прошивка себя так ведет на этих МК, или только ваша?
Схемотехника плат в норме? Среди этих 100 плат эти 10 вообще рабочие (все компоненты правильные)?

В такой ситуации может быть масса причин и выяснить это можно лишь локализовав причину, хотя бы грубо: железо или софт.
Выше я описал как это можно сделать, не гадая на "кофейной гуще" и т. п. мифических предположениях.
Baser
Цитата(Alex_Golubev @ Apr 5 2018, 11:40) *
В общем из 100 мк. 10 шт. ведут себя вот так не понятно.

Вариантов много, один из них - забыта инициализация каких-либо переменных или регистров которые после подачи питания имеют случайное состояние.
Alex11
Еще вариант, который у себя наблюдал - медленно заводится внешний кварц, нужна была задержка.
Forger
Цитата(Alex11 @ Apr 5 2018, 13:38) *
Еще вариант, который у себя наблюдал - медленно заводится внешний кварц, нужна была задержка.

В любой нормальной программе старт приложения следует производить по условию готовности генератора (соотв. биты или даже прерывание) с контролем таймаута, а НЕ после истечения некой фиксированной задержки.
Alex_Golubev
Цитата
по условию готовности генератора
у меня так и сделано.
Плюс у меня стоит внешней генератор на 16 МГц. На нем генерация в норме.
Заметил вот еще что.
Беру не рабочей мк. ставлю уровень оптимизации в IAR low вижу не работает мк. ставлю для этого же мк. уровень оптимизации high, size работает.
Но думаю надо поставить везде high и будит счастье.
А нет беру второй мк. а для него подходит уже low.
Чета ваще не чего не понятно.
Forger
Цитата(Alex_Golubev @ Apr 5 2018, 14:05) *
Но думаю надо поставить везде high и будит счастье.
А нет беру второй мк. а для него подходит уже low. Чета ваще не чего не понятно.

Тут есть два варианта:
1) переписать нормально код, изучая соотв. литературу и мануалы
2) воспользоваться бубном smile3046.gif

Нажмите для просмотра прикрепленного файла
ViKo
Если USB из Куба, то мне приходилось размер кучи увеличивать. А еще SysTick таймер программировать. Как оно у них работает, хрен поймешь.
KnightIgor
Цитата(Alex_Golubev @ Apr 5 2018, 09:40) *
Есть еще один интересный момент, который меня вгоняет в ступор.
А именно, зашиваю эту же прошивку с помощью STM32 ST-LINK Utility в другой микроконтроллер и там такой проблемы нет. Беру еще один мк. там снова такая проблема беру другой ее нет.
В общем из 100 мк. 10 шт. ведут себя вот так не понятно.

Мне приходят в голову следующие вещи, из моего опыта.
Первая: при работе отладчика им запускается DWT счетчик. Если в коде он используется для отсчета каких-либо задержек (почему и как именно - писано на форуме много), а код забывает его проинициализировать, то под отладчиком (или после вываливания из отладки) будет все работать, т.к. DWT тикает, но при запуске после сброса - нет.
Вторая: отладчик стартует медленно; за это время может успеть пройти инициализация периферии, которая не успевает при холодном старте без отладчика. У меня это было что-то тактируемое медленными генераторами (RTC) и, кажется, I2C, не помню точно.

В общем и целом опыт мой подсказывает, что чудес не бывает sad.gif . Если что-то ведет себя непредсказуемо и странно, за этим стоит вполне конкретная причина. Ищите!
Aleksandr Baranov
Скорость нарастания питающего напряжения у работающих и не работающих плат одинакова?
adnega
Цитата(Baser @ Apr 5 2018, 12:36) *
Вариантов много, один из них - забыта инициализация каких-либо переменных или регистров которые после подачи питания имеют случайное состояние.

Или как-то некорректно ожидается "раскочегаривание" кварцев, PLL...
Alex_Golubev
Путем отключения и включения разных инициализации периферии, было установлено что проблемы возникают из-за I2C.
Но как влияет в моем случаи I2C мне не ясно.
Forger
Цитата(Alex_Golubev @ Apr 6 2018, 07:48) *
Но как влияет в моем случаи I2C мне не ясно.

Попробуйте для начала отключить вообще все узлы на этой шине.
Если возможно, то покажите схему девайса.
adnega
Цитата(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 переставал функционировать и никак его из этого ступора было не вытащить).
Долго возился, но в итоге переделал все на ногодрыг.
jcxz
Цитата(adnega @ Apr 6 2018, 09:09) *
Без дикого while(I2C2->SR1 !=0 ) ничего не работало (вроде, I2C переставал функционировать и никак его из этого ступора было не вытащить).
Долго возился, но в итоге переделал все на ногодрыг.

Странно, у меня прекрасно работает как раз на F429 аппаратный I2C (да ещё с DMA) с кучкой висящих на нём разных устройств (FRAM + RTC + FM-тюнер + touch-контроллер) на 400кГц.
По многу часов без каких-либо сбоев.
И никаких ногодрыгов и тем более - циклов в ISR.
Так что у автора явно не МК виноват, а кривые руки. laughing.gif
Код
#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
k155la3
Цитата(adnega @ Apr 5 2018, 17:48) *
Или как-то некорректно ожидается "раскочегаривание" кварцев, PLL...

+ на плате, которая глючная, поменять кварц или генератор. Возможно дело в конкретном экземпляре.
Цитата(Forger @ Apr 6 2018, 09:00) *
Попробуйте для начала отключить вообще все узлы на этой шине. . . .

+ Поотключать блоки кода или функции.
+ Если (вдруг) в коде проекта есть таймауты - подвигать их длительность.
adnega
Цитата(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.
Так что у автора явно не МК виноват, а кривые руки. laughing.gif

Никто с этим не спорит. Когда все хорошо (нет помех), то и сценарий работы сложно сделать плохим,
но ведь бывают нештатные ситуации... в этом случае корректнее говорить о таланте разруливания, а не о кривизне рук,
т.к. работают скорее не руки, а голова.

Приведенный кусок - это ситуация когда сразу за STOP от предыдущей посылки формируется START для следующей.
jcxz
Цитата(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 и без ногодрыга. laughing.gif
adnega
Цитата(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 и без ногодрыга. laughing.gif

Проблема не с повторным стартом (который нужен, например, при чтении), а в идущих друг за другом обычных write-транзакций.
Попробуйте нарисовать STOP и START с нулевой паузой между ними. У меня получается лишь "1"-иголка на SDA.
jcxz
Цитата(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.
Arlleex
Я тоже ничего не понял про 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) постоянно сваливаются нужные флаги.
rudy_b
I2C в STM - полное барахло и недоделка, ваш код только подтверждает это. Я внимательно смотрел его на 207 - там автомат сделан принципиально неправильно, он влипает в ситуации из которых выйти не может и без полного сброса его оттуда не вывести. Именно поэтому каждую операцию приходится окучивать таймаутами, что является полным безобразием.

Нормальный автомат обязан сам давать информацию об ошибке, сам (по своим тактам) следить за своими таймаутами и иметь алгоритм нормального (без полного сброса) выхода из ошибочных ситуаций. Stm-ки попытались это сделать, но у них откровенно не получилось.
jcxz
Цитата(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 в низком состоянии слишком долгое время.
Arlleex
Цитата(jcxz @ Apr 7 2018, 18:31) *
Ничего не могу сказать про 207, но на F429 у меня транзакции по I2C не "окучены" ни какими таймаутами.

Однако я тогда не совсем понимаю, какая у Вас гарантия того, что данные успешно передадутся, да еще в нужное время. Если в Вашем устройства (на макете) замкнуть SCL на SDA пинцетом, затем убрать - связь восстановится? Если восстановится - то не понимаю, какая магия у Вас мониторит, что нужные флажки не взвелись через определенное время и сбрасывает автомат.
Ведь идеологически любая конструкция
Код
while(ожидание флага);

должна сопрягаться с установкой таймаута ожидания (не важно, средствами ОС или же аппаратным таймером).
Ведь слова
Цитата
По завершении транзакции, он заносит в слот информацию о результате транзакции (ОК или ошибка или ...)...

ставят встречный вопрос - каким же образом можно узнать результат транзакции? Ведь после установки битов управления (тот же START-бит), нужно некоторое время, чтобы в регистре статуса этот бит был выставлен и сгенерировалось прерывание. Если, как было указано выше, бесконечно ожидать, то получится полная лажа при какой-либо помехе на I2C-шине. Таймаут-то по-любому нужен, как без него...
jcxz
Цитата(Arlleex @ Apr 7 2018, 17:53) *
Если в Вашем устройства (на макете) замкнуть SCL на SDA пинцетом,

А зачем их замыкать? Вы своё ПО пишете с таким расчётом, что кто-то вскроет корпус и замкнёт дорожки? biggrin.gif

Цитата(Arlleex @ Apr 7 2018, 17:53) *
ставят встречный вопрос - каким же образом можно узнать результат транзакции? Ведь после установки битов управления (тот же START-бит), нужно некоторое время, чтобы в регистре статуса этот бит был выставлен и сгенерировалось прерывание. Если, как было указано выше, бесконечно ожидать

Что значит "бесконечно"? Вы же сами пишете: "выставится в регистре бит статуса". Вот тогда ожидание и закончится.
И судя по битам статуса (насколько помню), там не может быть такой ситуации чтобы никакой бит не выставился (манипуляции с пинцетом конечно исключаем), а следовательно - бесконечного ожидания не может быть.

Цитата(Arlleex @ Apr 7 2018, 17:53) *
получится полная лажа при какой-либо помехе на I2C-шине.

"при какой-либо помехе" в регистре статуса появится "какой-либо бит" и сгенерится прерывание. laughing.gif
adnega
Цитата(jcxz @ Apr 7 2018, 21:33) *
А зачем их замыкать? Вы своё ПО пишете с таким расчётом, что кто-то вскроет корпус и замкнёт дорожки? biggrin.gif

Ага. В случае неприятности у изделия нет права продолжать работать как ни в чем не бывало - нужно нештатную ситуацию разрулить.

Не знаю как у кого, а у меня на I2C максимум дисплей висит. Поскольку скорости большие не нужны, а интерфейс синхроннее некуда -
делаешь тупой ногодрыг в районе mainloop'а и не паришься за прерывания, while(condition), таймауты и т.п.
После любых случайных пинцетов изделие рано или поздно прокашляется без лишних арбитров, журналов, алгоритмов сброса.

PS. Не пора ли часть сообщений вынести в отдельную ветку по I2C, ибо к данной теме I2C мало относится?
jcxz
Цитата(adnega @ Apr 7 2018, 22:03) *
Ага. В случае неприятности у изделия нет права продолжать работать как ни в чем не бывало - нужно нештатную ситуацию разрулить.

Как же изделию разрулить такую ситуацию, когда некто влазит в него и начинает тыкать разными металлическими штуками куда ни попадя? biggrin.gif

Цитата(adnega @ Apr 7 2018, 22:03) *
PS. Не пора ли часть сообщений вынести в отдельную ветку по I2C, ибо к данной теме I2C мало относится?

Да ладно Вам - тут каждая первая вторая тема такая: пара постов по теме, а потом - кто в лес кто по дрова.... rolleyes.gif
adnega
Цитата(jcxz @ Apr 7 2018, 22:23) *
Как же изделию разрулить такую ситуацию, когда некто влазит в него и начинает тыкать разными металлическими штуками куда ни попадя? biggrin.gif

В плане I2C я предлагаю ногодрыг - простейшее решение, которое не завесит шину в принципе.
Я бы не встревал, если бы лет 10 назад не столкнулся однократно с ситуацией ACK от слейва и START от мастера.
Долго ломал голову и убеждал себя, что это в нормальной жизни не повториться, но редчайшее событие, напрочь завешивающее обмен не давало покоя.
Кста, тогда это была микросхема памяти серии 24xx. С тех пор у меня настороженное отношение к I2C.

А обвешивать низкоскоростной обмен, полностью синхронный, дык, еще когда ты монопольный мастер всякими таймаутами по-моему много чести.
Alex_Golubev
USB cdc.
До меня не доходит из-за чего вылазит ошибка Error setting parameters from DCB при подключение к COM port tolkit ?
jcxz
Цитата(adnega @ Apr 7 2018, 23:51) *
Я бы не встревал, если бы лет 10 назад не столкнулся однократно с ситуацией ACK от слейва и START от мастера.
Долго ломал голову и убеждал себя, что это в нормальной жизни не повториться, но редчайшее событие, напрочь завешивающее обмен

Видимо это нанесло психологическую травму, которая не заживает уже 10 лет biggrin.gif
Шутка. sm.gif
Вас очень трудно понять, так как Вы не придерживаетесь стандартной терминологии I2C. О чём тут уже говорили.
Как именно может "завесить обмен" обычный СТАРТ или ACK - я думаю тут не только я, но никто не понял.... laughing.gif
adnega
Цитата(jcxz @ Apr 8 2018, 12:04) *
Как именно может "завесить обмен" обычный СТАРТ или ACK - я думаю тут не только я, но никто не понял.... laughing.gif

Если slave в ACK, а master хочет сгенерировать START, то как он это может сделать?
Как система оказалась в этом состоянии - совершенно другой вопрос (кто-то проворный тыкал пинцетом).
jcxz
Цитата(adnega @ Apr 8 2018, 13:28) *
Если slave в ACK, а master хочет сгенерировать START, то как он это может сделать?

ACK или не-ACK - это состояние на шине i2c. Именно состояние шины, а не состояние линии SDA.
А состояния i2c определяются не только по состоянию линий SDA/SCL, а и по фазе работы мастера.
Т.е. - если мастер выдаёт СТАРТ, то на шине в принципе не может быть АСК, просто по определению.
Если при выполнении мастером СТАРТ на SDA держится "0", удерживаемый слэйвом, то это не АСК, это коллизия на шине, которая трактуется как одновременный доступ двух i2c-мастеров к шине. Для отслеживания этого есть соответствующий бит в регистре статуса i2c. И никакие ногодрыги или таймауты для обнаружения этой ситуации в F429 не нужны - всё нормально работает и обнаруживается. Надо внимательно читать доки. laughing.gif
А если уже ваше ПО трактует "0" на шине во время СТАРТ как АСК, то тут проблема только в Вашем ПО.
adnega
Цитата(jcxz @ Apr 8 2018, 13:51) *
Если при выполнении мастером СТАРТ на SDA держится "0", удерживаемый слэйвом, то это не АСК, это коллизия на шине, которая трактуется как одновременный доступ двух i2c-мастеров к шине. Для отслеживания этого есть соответствующий бит в регистре статуса i2c. И никакие ногодрыги или таймауты для обнаружения этой ситуации в F429 не нужны - всё нормально работает и обнаруживается. Надо внимательно читать доки. laughing.gif

И что мастер может сделать в случае такой "коллизии"?
jcxz
Цитата(adnega @ Apr 8 2018, 14:27) *
И что мастер может сделать в случае такой "коллизии"?

Если она вызвана помехой или внезапным перезапуском МК мастера (машина состояния I2C слэйва осталась в режиме вывода бита АСК или бита данных), то я ещё в сообщении https://electronix.ru/forum/index.php?showt...t&p=1555322 писал что я делаю. С поправкой: я выполняю 15 раз условие СТОП. СТАРТ не надо.
Такая операция на I2C при старте ПО у меня делается всегда, а не только при SDA=='0', ведь в слэйве машина состояний может из-за помехи оказаться в ошибочном состоянии вывода например очередного бита данных =='1' и вроде на SDA всё ок, но когда пойдут SCLK будет капец.
А если коллизия из-за наличия другого мастера на I2C, то тут поможет только паяльник. laughing.gif
adnega
Цитата(jcxz @ Apr 8 2018, 15:47) *
писал что я делаю.

Дык, вы и меня поймите: раз без ногодрыга I2C гарантированно работать не может, то в моем случае я вообще отказался от аппаратного I2C
и сделал все ногодрыгом.
Alex_Golubev
Ребята я очень рад что вы так активно обсуждаете I2C.
Но я прошу помочь с USB. У меня с ним проблема.

В кубе сделан проект USB CDC.

При запуске мк. и открытие порта программой COM port tolkit.

Вылетает ошибка Error setting parameters from DCB.
adnega
Цитата(Alex_Golubev @ Apr 9 2018, 03:55) *
Ребята я очень рад что вы так активно обсуждаете I2C.
Но я прошу помочь с USB. У меня с ним проблема.

В кубе сделан проект USB CDC.

При запуске мк. и открытие порта программой COM port tolkit.

Вылетает ошибка Error setting parameters from DCB.

В CDC передается пакет с настройками при открытии порта (bRequest == SET_LINE_CODING).
Он к вам приходит?

Или так.
Baser
Цитата(Alex_Golubev @ Apr 9 2018, 03:55) *
При запуске мк. и открытие порта программой COM port tolkit.

Вылетает ошибка Error setting parameters from DCB.

Я такую ошибку видел только когда случайно установил скорость работы СОМ порта, которую не поддерживал сам СОМ порт. Нужно найти compt.ini в директории установки Тулкита и в ручную подправить скорость на правильную.

Цитата(jcxz @ Apr 8 2018, 12:04) *
Вас очень трудно понять, так как Вы не придерживаетесь стандартной терминологии I2C. О чём тут уже говорили.
Как именно может "завесить обмен" обычный СТАРТ или ACK - я думаю тут не только я, но никто не понял.... laughing.gif

А я прекрасно понимаю laughing.gif потому что на разных МК модули I2C настолько разные, и частенько, настолько кривые, что применять их просто не хочется. На старых ПИКах мастера I2C вообще не было, все в ручную. На АВР-ке я даже один раз сварганил мастера и слейва на прерываниях встроенного модуля, но ПО для этого выглядело настолько чудовищно нечитабельно, что никакого удовлетворения не доставило. На МСП430 были потуги применить аппаратный I2C на прерываниях, даже пару дней убил на это, пока не обнаружил, что они прое*** забыли сделать прерывание на одно состояние. Типа почти все на прерываниях, а вот тут нужно "поллингом" wacko.gif Плюнул, вернул ногодрыг, который по коду коротенький, компактный и понятный. Благо что I2C синхронная, её везде можно приостановить что на 10 мс, что на 1 сек.
На STM32 пока до I2C не добрался, но тоже предстоит...
rudy_b
Я уже писал, что на 207 автомат влипает в глючные состояния из которых без полного сброса он ни сам не выходит, ни прерываний по ошибке не выдает. Отсюда - необходимость окучивания таймаутами.

А по поводу пинцета - даже без него бывают глюки - например еще не подано питание на slave или обращение к нему произошло до того, как завершился его startup. Нормальный I2C должен дать ошибку и возможность привести автомат в нормальное состояние, а не впадать в клиническую смерть.
jcxz
Цитата(adnega @ Apr 8 2018, 17:19) *
Дык, вы и меня поймите: раз без ногодрыга I2C гарантированно работать не может, то в моем случае я вообще отказался от аппаратного I2C
и сделал все ногодрыгом.

Почему не может?
Во-первых: это не штатная работа, а процесс инициализации из произвольного состояния (сброса без снятия питания).
Во-вторых: это только на данном МК так (STM32F4xx), возможно там можно сделать такой сброс и через возможности штатного контроллера I2C, но оно особо не нужно разбираться - это однократная короткая операция при старте, по большому счёту без разницы как она работает. А вот штатный обмен да ещё со множеством устройств да ещё когда среди этих устройств есть FRAM-память с пересылками в несколько десятков КБ и есть устройства выдающие периодически пачки в десятки-сотни байт (тачскрин) - делать это не то что без DMA, а ещё и ногодрыгом - ну это просто какая-то уже совершенно абдуринщина в запущенной стадии. laughing.gif

PS: У меня в текущем проекте на XMC4500, контроллер I2C гораздо мощнее STM-ного. Здесь думаю сделать процедуру сброса при старте (несколько СТОП-условий) силами самого I2C-контроллера. Потому что на XMC4500 это сделать даже проще чем ногодрыгом. У него вообще можно всю транзакцию (СТАРТ+адрес+W, запись адреса регистра, РЕСТАРТ+адрес+R, N-чтений с ACK, 1 чтение NACK, СТОП) - можно записать одним блоком сразу в FIFO I2C и запустить на выполнение на машине состояний I2C-контроллера. А в конце транзакции получить ОДНО прерывание о завершении и вычитать из FIFO результат. Каждое слово (11 бит) в блоке: младшие 8 бит - слэйв-адрес или байт данных, старшие 3 бита - команда-состояние I2C (СТАРТ,РЕСТАРТ,СТОП,передача,...). И ISR очень простой и всего одно прерывание в конце - просто красота по сравнению с STM. Не говоря уже о ногодрыге... При желании можно подключить и DMA для наполнения FIFO I2C.
Все нештатные ситуации - NACK, lost arbitration и т.п. - отслеживаются самим I2C-контроллером, выдаётся нештатное СТОП-условие на шину (или завершение приёма байта, NACK и СТОП если идёт приём) и прерывание с флагами ошибки в регистре статуса. rolleyes.gif

Цитата(Baser @ Apr 9 2018, 12:12) *
А я прекрасно понимаю laughing.gif потому что на разных МК модули I2C настолько разные, и частенько, настолько кривые, что применять их просто не хочется.

Так может нужно выбирать МК с хорошей периферией? rolleyes.gif
Оцените как выглядит и насколько простая реализация обмена по I2C на XMC4500:
CODE
extern "C" void concat(isrUSIC, USIC_UNIT(nUSIC_i2c), _, SRUSIC_SR(nUSIC_SR_i2c_CTRL))()
{
u32 i = io->PSR;
if (i & (B0 | B1 | B5 | B6 | B8 | B11 | B16)) trap(TRAP_I2C_ERROR, i);
io->PSCR = i;
if (!(i & B4)) return;
io->TCSR_b[1] = B0;
IsrEnter();
MboxPost(mbox);
IsrExit();
}

//Запись в регистры MPU-6050.
//ra - начальный адрес регистра MPU-6050 для записи;
//data/len - указатель/длина блока записываемых данных.
//return: !=0 - всё ок.
int GyroWr(uint ra, void const *data, uint len)
{
assert(len <= FIFO_SIZE - 3, TRAPR_GYRO);
SemPend(sem);
io->IN[0] = TDF_START | GYRO_ADDR << 1;
io->IN[0] = TDF_TX | ra;
for (u8 const *s = (u8 const *)data; (int)--len >= 0; ) io->IN[0] = TDF_TX | *s++;
io->IN[0] = TDF_STOP;
io->TCSR_b[1] = B0 | 1 << 2;
MboxPend(mbox);
SemPost(sem);
return 1;
}

//Чтение из регистров MPU-6050.
//ra - начальный адрес регистра MPU-6050 для чтения;
//data/len - указатель/длина блока записываемых данных.
//return: кол-во прочитанных байт; ==0 - произошла ошибка.
int GyroRd(uint ra, void *data, uint len)
{
assert(data && len - 1 <= FIFO_SIZE - 5, TRAPR_GYRO);
SemPend(sem);
io->IN[0] = TDF_START | GYRO_ADDR << 1;
io->IN[0] = TDF_TX | ra;
io->IN[0] = TDF_RSTART | GYRO_ADDR << 1 | B0;
for (ra = len; --ra; ) io->IN[0] = TDF_RX_A;
io->IN[0] = TDF_RX_NA;
io->IN[0] = TDF_STOP;
io->TCSR_b[1] = B0 | 1 << 2;
MboxPend(mbox);
if ((ra = io->TRBSR_b[2]) - 1 >= len) trap(TRAP_I2C_RXOVER, ra);
u8 *s = (u8 *)data;
len = ra;
do *s++ = io->OUTR;
while (--ra);
SemPost(sem);
return len;
}

Первая функция - ISR. Пока нет обработки ошибочных ситуаций (не получен ACK на slave addr, lost arbitration и т.п.), позже добавлю - это мелочи.

Цитата(rudy_b @ Apr 9 2018, 13:26) *
например еще не подано питание на slave или обращение к нему произошло до того, как завершился его startup. Нормальный I2C должен дать ошибку и возможность привести автомат в нормальное состояние, а не впадать в клиническую смерть.

Нормальный драйвер I2C не должен допускать таких ситуаций, ибо то о чём Вы пишете - баги в реализации алгоритма драйвера.
Наделать багов в алгоритме, а потом пытаться фиксить их костылями типа таймаутов - это плохое решение.
pitt
По поводу I2C. До 2010 года активно использовал AVR8 для safety-critical проектов. Машина состояний там имеет аппаратную ошибку, подтвержденную тогда еще Atmel'ом. Сут= ее в том, что помехи на шине приводили к потере арбитража в Multi-master mode и шина зависала. Для решения, одновременно с циклом обмена, запускался таймер в режиме watchdog. Если цикл приема-передачи не завершался вовремя, таймер выключал и тут же включал периферию. После перехода проекта на Cortex-M проверял устойчивость I2C на FPGA(NIOS-II), NXP, Atmel, STM32F2xx/4xx(F1xx сильно отличается). Итого: к NXP нет никаких претензий, Atmel не давал никакой возможности stretch cycle, STM худо-бедно позволяла работать НО без всяких кубов/SPL. При этом, подчеркну, что создавал помехи, подключая на шину GPIO с открытым коллектором, управляемым хаотично(0/1) в короткие промежутки времени(единицы us) и проверял целостность и времена трансферов. The product safety tests were completed succesfully.
Данная информация доведена до вашего сведения, а не для дальнейших дискуссий.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.