Перейти к содержанию
    

Неполадки с очередью в USART

Вот такой код отправки в USART мертво застревает при большой нагрузке, когда большую часть времени задача блокирована на вызове xQueueSendToBack. Проявляется только при оптимизации -O3 -flto.

void USART_putc(int c)
{
    char        xC = (char) c;

    GPIO_set_HIGH(GPIO_LED);

    if (xQueueSendToBack(hal_USART.xTX, &xC, portMAX_DELAY) == pdTRUE) {

        USART3->CR1 |= USART_CR1_TXEIE;
    }

    GPIO_set_LOW(GPIO_LED);
}

Со стороны прерывания вот так.

void irq_USART3()
{
    BaseType_t        xWoken = pdFALSE;
    unsigned int         SR;
    char            xC;

    SR = USART3->SR;

    if (SR & USART_SR_RXNE) {

        xC = USART3->DR;
        xQueueSendToBackFromISR(hal_USART.xRX, &xC, &xWoken);

        GPIO_set_LOW(GPIO_LED); // это для проверки, попадем ли сюда когда все застряло (результат отрицательный)
    }

    if (SR & USART_SR_TXE) {

        if (xQueueReceiveFromISR(hal_USART.xTX, &xC, &xWoken) == pdTRUE) {

            USART3->DR = xC;
        }
        else {
            USART3->CR1 &= ~USART_CR1_TXEIE;
        }
    }

    portYIELD_FROM_ISR(xWoken);
}

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

Изменено пользователем amaora

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 hours ago, amaora said:

Судя по тому, что в прерывание после этого уже не попадаем (даже на прием)

Не попадаем, или висим глухо в этом прерывании по флагу ORE, например?

 

Оператор if в USART_putc() смысла не несет. Использование очереди в качестве кольцевого буфера для периферии - не самая удачная идея.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

24 minutes ago, aaarrr said:

Использование очереди в качестве кольцевого буфера для периферии - не самая удачная идея

+ латентность. По нашим замерам до 800 мкс проиходило между событиями "поместили в очередь" и "приняли из очереди".

5 hours ago, amaora said:

Со стороны прерывания вот так.

Нужно обрабатывать все события от последовательного порта, в т.ч. ошибки (framing error, overrun error и другие).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 hours ago, aaarrr said:

Не попадаем, или висим глухо в этом прерывании по флагу ORE, например?

По другим событиям прерывание я не разрешал. Позже уточню.

3 hours ago, haker_fox said:

+ латентность. По нашим замерам до 800 мкс проиходило между событиями "поместили в очередь" и "приняли из очереди".

Никуда не тороплюсь, это вывод в отладочный usart со скоростью 57600. Для этого и очередь, чтобы данные в ней лежали пока до них дело не дойдет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, amaora said:

Для этого и очередь, чтобы данные в ней лежали пока до них дело не дойдет

Ну таки да) Я просто предупредели, вдруг для вас это важно)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно посмотреть в HAL обработку прерываний от USART и сделать необходимые изменения в коде.

Полчаса хватит для исправления.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 hours ago, amaora said:

По другим событиям прерывание я не разрешал. Позже уточню.

А их и не надо разрешать - такой вот сюрприз.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Очередь для байтов UART'а... чтож, мсье знает толк.

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

Но это тоже плохой вариант при работе из прерывания.

Я бы рекомендовал описать свой вариант очереди без блокировок и с некоторым кольцевым буфером отправки. У себя использую около 4kiB на отправку и 128 байт на приём. Переполнения не ловил уже давно.

Отправляю посредством DMA, принимаю - тоже. Прекращение отправки происходит по событию DMA-Transfer-Complete, обработка приёма - по USART-IDLE. Но в прерываниях только взводятся флаги и запоминаются новые позиции указателей на буфер, а в основном потоке происходит сравнение новых позиций с их копией в потоке (атомарность операций не нарушается). Быстро, достаточно эффективно, относительно безопасно для критических секций и потоков. Во всяком случае, не вызывает зависаний.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

25 minutes ago, AlanDrakes said:

Накладные расходы большие для такого дела.

Иногда почему бы и нет. Сам для себя давно написал кольцевой буфер на шаблонах, использую его для систем сбора данных, и для коммуникационных протоколов. Но средства сигнализации посредством ОС тоже имеются. Да и DMA при желании имеет возможность в него писать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 часов назад, haker_fox сказал:

+ латентность. По нашим замерам до 800 мкс проиходило между событиями "поместили в очередь" и "приняли из очереди".

Странная какая-то очередь. Если это задержки именно из-за её работы, то получается ТС вообще не сможет с ней работать ибо:

10/57600 = 174 мкс, а нужно не менее 800 мкс. У Вас там скорее всего или какая-то другая задача занимала процессор в это время. Или что-то не так с раздачей квантов времени задачам и их пробуждением по событиям объектов синхронизации.

2 часа назад, AlanDrakes сказал:

Очередь для байтов UART'а... чтож, мсье знает толк.

Накладные расходы большие для такого дела.

ТСу торопиться некуда (как он сказал). И ничего что сделанный таким образом "вывод в отладочный usart" завалит работу всего остального ПО.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 hours ago, aaarrr said:

А их и не надо разрешать - такой вот сюрприз. 

И может быть чтобы ORE=1 а при этом RXNE=0 ? В любом случае,

1) Остальные прерывания по ошибкам отключены;

2) В проблемный момент USART ничего не принимает, только передает;

3) Каким-то образом это проявляется только с -O3 и -flto;

4) Добавлю проверку на USART_SR_ORE и понаблюдаю еще;

10 hours ago, AlanDrakes said:

Я бы рекомендовал описать свой вариант очереди без блокировок и с некоторым кольцевым буфером отправки. У себя использую около 4kiB на отправку и 128 байт на приём. Переполнения не ловил уже давно. 

И вылавливать там race condition несколько лет. Переполнения это часть нормальной работы, на все что выводится памяти не хватит.

Upd: Добавил проверку.

    if (SR & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) {

        GPIO_set_LOW(GPIO_LED);
        lowTRACE(EOL "** USART_ERR **" EOL);
    }

Ни разу не сработала, и так же застряло во время нагрузки.

 

Upd2: Обновился до свежей V10.2.0, теперь проблема не воспроизводится. Буду считать, что была ошибка в RTOS, возможно неосторожно внесенная мной. Делал много правок, которые впрочем не затрагивали ничего важного, только удалял ненужное и адаптировал к своим условиям. Исправил сборку с LTO. То же самое я теперь сделал и с новой версией, но с ней проблемы уже нет. :unknw:

Изменено пользователем amaora

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 часов назад, amaora сказал:

Upd2: Обновился до свежей V10.2.0, теперь проблема не воспроизводится. Буду считать, что была ошибка в RTOS, возможно неосторожно внесенная мной. Делал много правок, которые впрочем не затрагивали ничего важного, только удалял ненужное и адаптировал к своим условиям. Исправил сборку с LTO. То же самое я теперь сделал и с новой версией, но с ней проблемы уже нет. :unknw:

Достаточно долго работаю с FreeRTOS и могу вас заверить, что критических ошибок приводящих к мертвым зависаниям в ней нет(с версии 8 точно). То, что после обновления ОС, ошибка пропала, скорее всего говорит о том, что ошибка была "скрыта", а не решена. Скорее всего при следующих правках кода ошибка снова проявит себя. Посмотрите в сторону прерываний hard_fault, bus_fault и им подобным(они включены в не зависимости от хотелок), предположу: при зависании вылетает в них.

 

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

Изменено пользователем Neo_Matrix

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 hours ago, amaora said:

Делал много правок,

Просто прекрасно! А сразу упомянуть о такой несущественной мелочи нельзя было? Это просто свинство, если называть вещи своими именами.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 hours ago, Neo_Matrix said:

Посмотрите в сторону прерываний hard_fault, bus_fault и им подобным(они включены в не зависимости от хотелок), предположу: при зависании вылетает в них.

Правильно! А лучше включить один hardfault и написать для него обработчик. И уже в нём смотреть причину вылета. Также я сделал перехватавалки для сишной библиотеки для функций abort(), exit() и подобных. Иногда у меня проги и туда вылетали. Очень удобное, если выводить LR (видно, откуда туда попали). Ну и стековые канарейки иногда тоже спасают, если версия IAR помогает. При этом во FreeRTOS нужно включить всю отладку (configASSERT, stack overflow protection). Дополнительно на неиспользуемые вектора прерываний можно повесить что-то по-умолчанию для сигнализации.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 hours ago, aaarrr said:

Просто прекрасно! А сразу упомянуть о такой несущественной мелочи нельзя было? Это просто свинство, если называть вещи своими именами.

Правки это например убрал include string.h и заменил на свои объявления mem* функций. Удалил файлы croutine, timers, и другие что не планирую использовать никогда. Добавил аттрибут used одной функции и одной переменной. Исправил инлайн асм вставки в port.c, заменив загрузку адреса через ldr на две movw/movt. Последнюю часть делал из необходимости а не по желанию, лучшего решения не нашел. В остальном стараюсь ничего не править.

А не так, чтобы решил убрать вот здесь критическую сеецию для ускорения.

И да, действительно есть опасения, что проблема скрылась и может проявиться когда-то снова. Ведь причину я не понял.

7 hours ago, Neo_Matrix said:

Посмотрите в сторону прерываний hard_fault, bus_fault и им подобным(они включены в не зависимости от хотелок), предположу: при зависании вылетает в них.

Нет, в них не попадаем. Там есть обработчик, я бы узнал об этом.

Изменено пользователем amaora

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...