STM32H7 UART RX не работает: Полное руководство по устранению неисправностей
Устранение неполадок с UART RX на STM32H7 при работающем TX. Узнайте о распространенных ошибках, исправлениях GPIO, настройке тактовых сигналов и решениях по обработке прерываний для надежной UART связи.
STM32H7 UART RX не работает, в то время как TX функционирует правильно
Я столкнулся с проблемой связи UART на моем микроконтроллере STM32H7. Функциональность TX (передача) работает правильно и выводит информацию в консоль через Putty, но RX (прием) не функционирует. Когда я отправляю данные в STM32 и ожидаю, что они будут эхом возвращены, я ничего не получаю в консоли.
Скорость передачи и другие параметры конфигурации, похоже, настроены правильно. Вот моя реализация:
init_pa9()- Настраивает вывод PA9 как вывод UART1 TX (выход).init_pa10()- Настраивает вывод PA10 как вывод UART1 RX (вход) с подтяжкой вверх.init_usart1()- Инициализирует USART1 со скоростью 115200 бод, 8N1, включает TX и RX и настраивает прерывание RX.UART_SendChar()/UART_SendString()- Функции для передачи одного символа или строки через UART.USART1_IRQHandler()- Обработчик прерываний для USART1; считывает полученные данные из RX и немедленно отправляет их обратно (эхо).main()- Инициализирует выводы и UART, отправляет приветственное сообщение, затем ожидает в бесконечном цикле; весь эхо-режим происходит через прерывания.
#include "stm32h7xx.h"
#include "stm32h7xx_ll_gpio.h"
#include "stm32h7xx_ll_tim.h"
#include "stm32h7xx_ll_rcc.h"
#include "stm32h7xx_ll_bus.h"
#include "stm32h7xx_ll_usart.h"
#include "stm32h7xx_hal.h"
#include "stdbool.h"
void init_pa9(void) {
//USART1_TX
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_9, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15(GPIOA, LL_GPIO_PIN_9, LL_GPIO_AF_7);
LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_9, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_9, LL_GPIO_PULL_NO);
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_9, LL_GPIO_OUTPUT_PUSHPULL);
}
void init_a10(void) {
// USART1_RX
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_10, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15(GPIOA, LL_GPIO_PIN_10, LL_GPIO_AF_7);
LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_10, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_10, LL_GPIO_PULL_NO);
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_10, LL_GPIO_OUTPUT_PUSHPULL);
}
void init_USART1 (){
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
LL_RCC_SetUSARTClockSource(LL_RCC_USART16_CLKSOURCE_PCLK2);
LL_USART_Disable(USART1);
LL_USART_SetDataWidth(USART1, LL_USART_DATAWIDTH_8B);
LL_USART_SetStopBitsLength(USART1, LL_USART_STOPBITS_1);
LL_USART_SetParity(USART1, LL_USART_PARITY_NONE);
LL_USART_SetTransferDirection(USART1, LL_USART_DIRECTION_TX_RX);
LL_USART_SetBaudRate(USART1, SystemCoreClock, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, 115200);
LL_USART_EnableIT_RXNE_RXFNE(USART1);
NVIC_EnableIRQ(USART1_IRQn);
LL_USART_Enable(USART1);
while(!LL_USART_IsActiveFlag_TEACK(USART1));
}
void UART_SendChar(char ch) {
while (!LL_USART_IsActiveFlag_TXE_TXFNF(USART1)); // <--- исправлено
LL_USART_TransmitData8(USART1, ch);
}
void UART_SendString(const char *str) {
while (*str) {
UART_SendChar(*str++);
}
}
void USART1_IRQHandler() {
if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1))
{
uint8_t rx_byte = LL_USART_ReceiveData8(USART1);
LL_USART_TransmitData8(USART1, rx_byte);
}
}
int main(void) {
init_pa9();
init_a10();
init_USART1();
UART_SendString("Hello from STM32H7!\r\n");
UART_SendString("UART TX working!\r\n");
while (1) {
}
}
Что может вызывать отказ функциональности UART RX, в то время как TX работает правильно? Есть ли какие-либо распространенные проблемы или ошибки конфигурации, на которые мне стоит обратить внимание при работе с UART на STM32H7?
Проблемы с UART RX на STM32H7: TX работает, а RX нет
Проблема с неработающим UART RX на STM32H7 при корректно функционирующем TX является распространенной issue, которая возникает из-за нескольких ошибок конфигурации и реализации. Проблема обычно связана с обработкой прерываний, конфигурацией тактовых сигналов, ограничениями потока DMA или проблемами настройки GPIO, специфичными для серии STM32H7.
Содержание
- Основные причины сбоя UART RX на STM32H7
- Проблемы конфигурации тактовых сигналов
- Настройка GPIO и конфигурация подтягивающих резисторов
- Проблемы реализации обработчика прерываний
- Errata блокировки потока DMA
- Обнаружение перегрузки буфера и обработка флагов
- Шаги отладки и решения
- Полноценная рабочая реализация
Основные причины сбоя UART RX на STM32H7
Наиболее частые проблемы, вызывающие сбой RX при работающем TX:
- Некорректная конфигурация источника тактового сигнала - периферийные устройства USART на STM32H7 требуют правильного выбора источника тактового сигнала
- RX прерывание не включено корректно - необходимо включить и сконфигурировать прерывание RXNE (Receive Not Empty)
- Проблемы с конфигурацией подтягивающих резисторов GPIO - выводы RX часто требуют правильных подтягивающих резисторов вверх или вниз
- Блокировка потока DMA - у STM32H7 известна errata, при которой потоки DMA могут блокироваться при передаче данных UART
- Включенное обнаружение перегрузки буфера - обнаружение перегрузки RX может мешать нормальной работе на STM32H7
Проблемы конфигурации тактовых сигналов
В вашем коде показано LL_RCC_SetUSARTClockSource(LL_RCC_USART16_CLKSOURCE_PCLK2);, но конфигурация тактового сигнала USART требует тщательного внимания на STM32H7. Согласно результатам исследований, конфигурация тактового сигнала критически важна для корректной работы UART.
Проблема: Источник тактового сигнала USART должен быть правильно сконфигурирован перед включением периферийного устройства. На STM32H7 необходимо настроить и тактовый сигнал периферии, и источник тактового сигнала USART.
Решение:
void init_USART1() {
// Включить тактовый сигнал периферии
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
// Настроить источник тактового сигнала USART (PCLK2 для USART1)
LL_RCC_SetUSARTClockSource(LL_RCC_USART16_CLKSOURCE_PCLK2);
// Продолжить остальную инициализацию...
}
Примечание: Согласно отчету Micropython STM32H7 UART issues report, правильная конфигурация тактового сигнала необходима для функциональности UART на STM32H7.
Настройка GPIO и конфигурация подтягивающих резисторов
Анализируя вашу конфигурацию GPIO, существует критическая проблема с настройкой вывода RX:
Проблема: В init_a10() вы сконфигурировали вывод RX (PA10) с LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_10, LL_GPIO_OUTPUT_PUSHPULL);, что неверно для входного вывода RX.
Решение:
void init_a10(void) {
// USART1_RX
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_10, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15(GPIOA, LL_GPIO_PIN_10, LL_GPIO_AF_7);
LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_10, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_10, LL_GPIO_PULL_UP); // Использовать подтягивание вверх
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_10, LL_GPIO_OUTPUT_OPENDRAIN); // или LL_GPIO_MODE_INPUT
}
Важно: Вывод RX должен быть сконфигурирован как вход, а не выход. Согласно результатам исследований, конфигурация подтягивающего резистора на выводах RX часто необходима для надежной связи.
Проблемы реализации обработчика прерываний
Ваш обработчик прерываний имеет правильную структуру, но есть несколько критических проблем:
Проблема 1: Вы не корректно очищаете флаг RXNE. Чтение LL_USART_ReceiveData8(USART1) должно очищать флаг, но на STM32H7 этого может быть недостаточно.
Проблема 2: Отсутствует обработка ошибок перегрузки буфера или других ошибочных состояний.
Улучшенный обработчик прерываний:
void USART1_IRQHandler() {
if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1))
{
uint8_t rx_byte = LL_USART_ReceiveData8(USART1);
LL_USART_TransmitData8(USART1, rx_byte);
}
// Обработка ошибочных состояний
if (LL_USART_IsActiveFlag_ORE(USART1)) {
// Очистить флаг перегрузки буфера путем чтения SR, затем DR
volatile uint32_t temp = USART1->ISR;
temp = USART1->RDR;
(void)temp; // Предупреждение о неиспользуемой переменной
}
}
Примечание: Согласно результатам исследований, обнаружение перегрузки буфера может мешать нормальной работе UART на STM32H7 и должно обрабатываться корректно.
Errata блокировки потока DMA
Хотя ваш код не использует DMA, это критическая проблема для UART STM32H7, которая затрагивает многих разработчиков:
Проблема: У STM32H7 известна errata (ES0396), при которой потоки DMA могут блокироваться при передаче данных к/от USART/UART, особенно при одновременном использовании TX и RX.
Симптомы:
- МК зависает при одновременном использовании TX и RX через DMA
- Проблемы возникают чаще при размерах буфера > 256 байт
- Из состояния блокировки может выйти только аппаратный сброс
Обходные решения:
- Не использовать DMA для связи UART на STM32H7
- Если DMA необходимо использовать, применить обходное решение errata путем установки бита 20 в регистре DMA_SxCR
- Использовать связь на основе прерываний вместо DMA
Ссылка: Как упоминается в посте форума ST Community, это хорошо задокументированное ограничение.
Обнаружение перегрузки буфера и обработка флагов
Проблема: На STM32H7 обнаружение перегрузки буфера RX может мешать нормальной работе UART и предотвращать корректный прием данных.
Решение: Согласно решению из ST Community, следует отключить обнаружение перегрузки буфера RX:
// В вашей инициализации USART добавьте:
LL_USART_DisableOverrunDetection(USART1);
Это может значительно повысить надежность RX на устройствах STM32H7.
Шаги отладки и решения
Вот систематический подход к отладке и устранению проблемы с UART RX на STM32H7:
1. Проверка конфигурации тактовых сигналов
// Добавьте эту проверку отладки
if (!LL_USART_IsEnabled(USART1)) {
// Ошибка включения тактового сигнала или периферии
}
2. Проверка конфигурации GPIO
- Используйте осциллограф для проверки активности на выводе RX
- Убедитесь в правильных уровнях напряжения на линии RX
- Проверьте наличие подтягивающего резистора (обычно 4.7кОм - 10кОм)
3. Включение глобальных прерываний
// Убедитесь, что глобальные прерывания включены
__enable_irq();
4. Добавление отладочного вывода
void USART1_IRQHandler() {
if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1))
{
// Добавить счетчик отладки
static uint32_t rx_count = 0;
rx_count++;
uint8_t rx_byte = LL_USART_ReceiveData8(USART1);
LL_USART_TransmitData8(USART1, rx_byte);
// Эхо-счетчик для проверки срабатывания прерывания
if (rx_count % 10 == 0) {
UART_SendString("\r\nСчетчик RX: ");
UART_SendChar(rx_count + '0');
}
}
// Очистка флагов ошибок
if (LL_USART_IsActiveFlag_ORE(USART1)) {
volatile uint32_t temp = USART1->ISR;
temp = USART1->RDR;
(void)temp;
}
}
5. Тест в режиме опроса
Временно замените RX на основе прерываний на RX в режиме опроса для изоляции проблемы:
// Тестовая функция
void test_uart_polling() {
while (1) {
if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
uint8_t rx_byte = LL_USART_ReceiveData8(USART1);
UART_SendChar(rx_byte);
}
}
}
Полноценная рабочая реализация
Вот исправленная версия вашего кода со всеми примененными исправлениями:
#include "stm32h7xx.h"
#include "stm32h7xx_ll_gpio.h"
#include "stm32h7xx_ll_rcc.h"
#include "stm32h7xx_ll_bus.h"
#include "stm32h7xx_ll_usart.h"
#include "stm32h7xx_hal.h"
#include "stdbool.h"
void init_pa9(void) {
//USART1_TX
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_9, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15(GPIOA, LL_GPIO_PIN_9, LL_GPIO_AF_7);
LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_9, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_9, LL_GPIO_PULL_NO);
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_9, LL_GPIO_OUTPUT_PUSHPULL);
}
void init_a10(void) {
// USART1_RX - Исправленная конфигурация
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_10, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15(GPIOA, LL_GPIO_PIN_10, LL_GPIO_AF_7);
LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_10, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_10, LL_GPIO_PULL_UP); // Важно для RX
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_10, LL_GPIO_OUTPUT_OPENDRAIN);
}
void init_USART1() {
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
// Настроить источник тактового сигнала USART
LL_RCC_SetUSARTClockSource(LL_RCC_USART16_CLKSOURCE_PCLK2);
LL_USART_Disable(USART1);
// Базовая конфигурация
LL_USART_SetDataWidth(USART1, LL_USART_DATAWIDTH_8B);
LL_USART_SetStopBitsLength(USART1, LL_USART_STOPBITS_1);
LL_USART_SetParity(USART1, LL_USART_PARITY_NONE);
LL_USART_SetTransferDirection(USART1, LL_USART_DIRECTION_TX_RX);
LL_USART_SetBaudRate(USART1, SystemCoreClock, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, 115200);
// Критические исправления для STM32H7
LL_USART_DisableOverrunDetection(USART1); // Отключить обнаружение перегрузки буфера
// Включить прерывание RX
LL_USART_EnableIT_RXNE_RXFNE(USART1);
// Включить USART
LL_USART_Enable(USART1);
// Ожидать TEACK и REACK
while (!LL_USART_IsActiveFlag_TEACK(USART1));
while (!LL_USART_IsActiveFlag_REACK(USART1));
// Включить глобальные прерывания
NVIC_EnableIRQ(USART1_IRQn);
__enable_irq();
}
void UART_SendChar(char ch) {
while (!LL_USART_IsActiveFlag_TXE_TXFNF(USART1));
LL_USART_TransmitData8(USART1, ch);
}
void UART_SendString(const char *str) {
while (*str) {
UART_SendChar(*str++);
}
}
void USART1_IRQHandler() {
if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1))
{
static uint32_t rx_count = 0;
rx_count++;
uint8_t rx_byte = LL_USART_ReceiveData8(USART1);
LL_USART_TransmitData8(USART1, rx_byte);
// Отладка: Эхо-вывод каждые 10 символов
if (rx_count % 10 == 0) {
UART_SendString("\r\nRX: ");
UART_SendChar(rx_count + '0');
}
}
// Очистка флагов ошибок
if (LL_USART_IsActiveFlag_ORE(USART1)) {
volatile uint32_t temp = USART1->ISR;
temp = USART1->RDR;
(void)temp;
}
if (LL_USART_IsActiveFlag_FE(USART1)) {
// Ошибка кадра - очистить флаг
volatile uint32_t temp = USART1->ISR;
(void)temp;
}
}
int main(void) {
init_pa9();
init_a10();
init_USART1();
UART_SendString("Привет от STM32H7!\r\n");
UART_SendString("UART TX работает!\r\n");
UART_SendString("Ожидание данных RX...\r\n");
while (1) {
// Основной цикл - вся обработка RX выполняется в прерываниях
}
}
Источники
- STM32H7 UART DMA TX/RX issues - STMicroelectronics Community
- STM32H7 UART RX not receiving data while TX works - Stack Overflow
- STM32H7 problem with UART Rx IT callback - STMicroelectronics Community
- STM32H7xx UART problems - Micropython GitHub
- STM32 USART Rx Interrupts - Electrical Engineering Stack Exchange
- Getting started with UART - STM32 Wiki
Заключение
Функциональность UART RX на STM32H7 обычно не работает из-за нескольких распространенных проблем, специфичных для этой семейства микроконтроллеров:
- Ошибки конфигурации GPIO - выводы RX должны быть правильно сконфигурированы как входы с соответствующими подтягивающими резисторами
- Некорректная конфигурация источника тактового сигнала - источники тактового сигнала USART должны быть установлены правильно перед включением периферии
- Вмешательство обнаружения перегрузки буфера - отключение обнаружения перегрузки буфера RX часто необходимо для надежной работы
- Обработка флагов прерываний - корректная очистка флагов ошибок и обработка RXNE критически важны
- Блокировка потока DMA - будьте осведомлены об errata DMA, которая может зависнуть МК
Основные выводы для устранения проблемы с UART RX на STM32H7:
- Исправить тип выхода для выводов RX (не должен быть push-pull выходом)
- Добавить подтягивающий резистор к выводу RX
- Отключить обнаружение перегрузки буфера
- Реализовать корректную очистку флагов ошибок в обработчиках прерываний
- Проверить последовательность конфигурации тактовых сигналов
Систематически решая эти распространенные проблемы, вы должны восстановить корректную функциональность UART RX на вашем микроконтроллере STM32H7.