Ага, иногда в виде бесплатного цирка на такое посмотреть можно, за одним разобрать "вредный пример" из раздела "как нельзя делать".
Вот смотрите, там представлен самый простейший случай коммуникации, судя по скринам состояния регистров CR1, CR2, CR3: базовый UART с включенным передатчиком и приемником, однобайтовая коммуникация без FIFO и DMA, разрешен запрос прерывания только по приему байта (RXNE) и запрещена реакция на переполнение приемника. Всё.
Каким образом должна работать такая конфигурация - при приёме байта выставляется запрос RXNE и вызывается обработчик прерываний USART. То есть, исходя из показанных настроек, должен вестись только прием байтов в однобайтовом режиме.
Но что у автора написано в тексте обработчика прерывания? А там начинается цирковая дичь: безусловная проверка всех подряд флагов, в том числе и самая фатальная ошибка - проверка флага TXE, который всегда, за исключением одного случая, = 1. Следствием этого будет непрерывная передача TX, как я показывал на осциллограмме ранее.
Для решения этой проблемы что делает автор - он просто переносит программную переменную tx_flag = 1 в другое место, под проверку флага завершения передачи TC. По умолчанию после сброса модуля USART этот флаг присутствует, поэтому так же возможен ложный запуск в таких условиях, и это вторая ошибка. Однако, появление TC можно предотвратить, если его предварительно сбросить и ничего не передавать, тогда он больше не будет появляться.
Эта часть кода в блоке if(tx_flag), к сожалению, не показана.
Теперь что касается RXNE.
Автор придумал цирк такого плана:
usart_sr_my = UART5->ISR - то есть, получил независимую копию ISR с флагами.
Далее, он без проверки флагов читает usart_rdr_my = UART5->RDR. При этом в исходном ISR сбрасывается флаг RXNE. Остальные флаги этим действием не сбрасываются. Однако, перед этим участком присутствует блок со сбросом большинства флагов, которые могли выставиться, в том числе и ключевых для приема - NE, FE, PE. То есть, фактически уже неоткуда взять информацию об ошибках приема.
Далее, несмотря на фактический сброс RXNE при чтении RDR, в независимой копии usart_sr_my, полученной ранее, срабатывает if((usart_sr_my & USART_ISR_RXNE). Дальнейшие действия с принятым байтом в представленном коде не показаны.
И после этого следует еще один блок с двухкратным сбросом всех флагов в ISR, сначала записью 0xFFFF, затем "контрольный выстрел в голову" в виде дополнительного сброса уже сброшенных флагов. Про |= уже не говорю, это мелочи.
Что нужно сделать в этой ситуации. Как в анекдоте - оставить скобки { }, а между ними вложить новый код обработчика прерывания. В коде: для передачи - проверка флага TXE совместно с битом разрешения запросов TXEIE, при истинном результате - запись в TDR очередного байта;
для приема - проверка флага RXNE, и затем, после проверки отсутствия NE, FE, PE - считывание байта из RDR и складирование его в массив. При выставленных NE и FE - сброс RXNE и, либо запуск автодетектирования скорости, либо сообщение об ошибке приема. При PE - либо продолжить прием, сбросив PE, а считанный байт пометить как поврежденный, либо остановить прием и вывести сообщение о помехах в связи.
Таким образом будут отсечены большинство коммутационных помех и ошибок неверной скорости.
Так же следует реализовать обработку флага Idle для обнаружения начала блока принимаемых данных.
Про реализацию аппаратной поддержки таймингов модбаса тут пока что речь не идет. Автору нужно научиться правильно управляться с базовым функционалом.
Например, вот, работа с обнаружением коммутационных шумов в линии (петля TX-RX) и остановка передачи TX с завершающим сигналом Break (он же Framing Error на приемной стороне):