Другое

STM32H7 UART RX не работает: Полное руководство по устранению неисправностей

Устранение неполадок с UART RX на STM32H7 при работающем TX. Узнайте о распространенных ошибках, исправлениях GPIO, настройке тактовых сигналов и решениях по обработке прерываний для надежной UART связи.

STM32H7 UART RX не работает, в то время как TX функционирует правильно

Я столкнулся с проблемой связи UART на моем микроконтроллере STM32H7. Функциональность TX (передача) работает правильно и выводит информацию в консоль через Putty, но RX (прием) не функционирует. Когда я отправляю данные в STM32 и ожидаю, что они будут эхом возвращены, я ничего не получаю в консоли.

Скорость передачи и другие параметры конфигурации, похоже, настроены правильно. Вот моя реализация:

  1. init_pa9() - Настраивает вывод PA9 как вывод UART1 TX (выход).
  2. init_pa10() - Настраивает вывод PA10 как вывод UART1 RX (вход) с подтяжкой вверх.
  3. init_usart1() - Инициализирует USART1 со скоростью 115200 бод, 8N1, включает TX и RX и настраивает прерывание RX.
  4. UART_SendChar() / UART_SendString() - Функции для передачи одного символа или строки через UART.
  5. USART1_IRQHandler() - Обработчик прерываний для USART1; считывает полученные данные из RX и немедленно отправляет их обратно (эхо).
  6. main() - Инициализирует выводы и UART, отправляет приветственное сообщение, затем ожидает в бесконечном цикле; весь эхо-режим происходит через прерывания.
c
#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

Наиболее частые проблемы, вызывающие сбой RX при работающем TX:

  1. Некорректная конфигурация источника тактового сигнала - периферийные устройства USART на STM32H7 требуют правильного выбора источника тактового сигнала
  2. RX прерывание не включено корректно - необходимо включить и сконфигурировать прерывание RXNE (Receive Not Empty)
  3. Проблемы с конфигурацией подтягивающих резисторов GPIO - выводы RX часто требуют правильных подтягивающих резисторов вверх или вниз
  4. Блокировка потока DMA - у STM32H7 известна errata, при которой потоки DMA могут блокироваться при передаче данных UART
  5. Включенное обнаружение перегрузки буфера - обнаружение перегрузки RX может мешать нормальной работе на STM32H7

Проблемы конфигурации тактовых сигналов

В вашем коде показано LL_RCC_SetUSARTClockSource(LL_RCC_USART16_CLKSOURCE_PCLK2);, но конфигурация тактового сигнала USART требует тщательного внимания на STM32H7. Согласно результатам исследований, конфигурация тактового сигнала критически важна для корректной работы UART.

Проблема: Источник тактового сигнала USART должен быть правильно сконфигурирован перед включением периферийного устройства. На STM32H7 необходимо настроить и тактовый сигнал периферии, и источник тактового сигнала USART.

Решение:

c
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.

Решение:

c
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: Отсутствует обработка ошибок перегрузки буфера или других ошибочных состояний.

Улучшенный обработчик прерываний:

c
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 байт
  • Из состояния блокировки может выйти только аппаратный сброс

Обходные решения:

  1. Не использовать DMA для связи UART на STM32H7
  2. Если DMA необходимо использовать, применить обходное решение errata путем установки бита 20 в регистре DMA_SxCR
  3. Использовать связь на основе прерываний вместо DMA

Ссылка: Как упоминается в посте форума ST Community, это хорошо задокументированное ограничение.


Обнаружение перегрузки буфера и обработка флагов

Проблема: На STM32H7 обнаружение перегрузки буфера RX может мешать нормальной работе UART и предотвращать корректный прием данных.

Решение: Согласно решению из ST Community, следует отключить обнаружение перегрузки буфера RX:

c
// В вашей инициализации USART добавьте:
LL_USART_DisableOverrunDetection(USART1);

Это может значительно повысить надежность RX на устройствах STM32H7.


Шаги отладки и решения

Вот систематический подход к отладке и устранению проблемы с UART RX на STM32H7:

1. Проверка конфигурации тактовых сигналов

c
// Добавьте эту проверку отладки
if (!LL_USART_IsEnabled(USART1)) {
    // Ошибка включения тактового сигнала или периферии
}

2. Проверка конфигурации GPIO

  • Используйте осциллограф для проверки активности на выводе RX
  • Убедитесь в правильных уровнях напряжения на линии RX
  • Проверьте наличие подтягивающего резистора (обычно 4.7кОм - 10кОм)

3. Включение глобальных прерываний

c
// Убедитесь, что глобальные прерывания включены
__enable_irq();

4. Добавление отладочного вывода

c
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 в режиме опроса для изоляции проблемы:

c
// Тестовая функция
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);
        }
    }
}

Полноценная рабочая реализация

Вот исправленная версия вашего кода со всеми примененными исправлениями:

c
#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 выполняется в прерываниях
    }
}

Источники

  1. STM32H7 UART DMA TX/RX issues - STMicroelectronics Community
  2. STM32H7 UART RX not receiving data while TX works - Stack Overflow
  3. STM32H7 problem with UART Rx IT callback - STMicroelectronics Community
  4. STM32H7xx UART problems - Micropython GitHub
  5. STM32 USART Rx Interrupts - Electrical Engineering Stack Exchange
  6. Getting started with UART - STM32 Wiki

Заключение

Функциональность UART RX на STM32H7 обычно не работает из-за нескольких распространенных проблем, специфичных для этой семейства микроконтроллеров:

  1. Ошибки конфигурации GPIO - выводы RX должны быть правильно сконфигурированы как входы с соответствующими подтягивающими резисторами
  2. Некорректная конфигурация источника тактового сигнала - источники тактового сигнала USART должны быть установлены правильно перед включением периферии
  3. Вмешательство обнаружения перегрузки буфера - отключение обнаружения перегрузки буфера RX часто необходимо для надежной работы
  4. Обработка флагов прерываний - корректная очистка флагов ошибок и обработка RXNE критически важны
  5. Блокировка потока DMA - будьте осведомлены об errata DMA, которая может зависнуть МК

Основные выводы для устранения проблемы с UART RX на STM32H7:

  • Исправить тип выхода для выводов RX (не должен быть push-pull выходом)
  • Добавить подтягивающий резистор к выводу RX
  • Отключить обнаружение перегрузки буфера
  • Реализовать корректную очистку флагов ошибок в обработчиках прерываний
  • Проверить последовательность конфигурации тактовых сигналов

Систематически решая эти распространенные проблемы, вы должны восстановить корректную функциональность UART RX на вашем микроконтроллере STM32H7.

Авторы
Проверено модерацией
Модерация