В сегодняшней статье рассмотрим как работать с функциями STM32 HAL Uart. подробно разберем каждую функцию, а так же рассмотрим небольшой пример.
STM32 Uart
В микроконтроллерах STM32, в зависимости от количества выводов, может быть от 1 до 6 блоков UART/USART а также LPUART
- UART — Universal asynchronous receiver/transmitter или если по простому асинхронный приема передатчик.
- USART — Universal Synchronous/Asynchronous Receiver/Transmitter или, синхронный/асинхронный приема передатчик.
- LPUART — это тот же UART, только с пониженным энергопотреблением.
Прием и передача по Uart может осуществляться в следующих режимах:
- Прием и передача данных в режиме опроса.
- Прием и передача данных с помощью прерываний.
- Прием и передача данных с помощью DMA.
Для каждого из режимов у STM32 имеются свои функции HAL Uart.
Функции HAL UART в режиме опроса
HAL Uart Transmit
Передача данных в режиме опроса осуществляется с помощью функции HAL_UART_Transmit:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout), где
- huart — указатель на используемый UART.
- pData — массив с данным для отправки.
- Size — количество байт. которые нужно отправить.
- Timeout — время ожидания отправки.
HAL Uart Receive
Для того, что бы принять данные по UART, необходимо использовать функцию HAL_UART_Receive:
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout), где
- huart — указатель на используемый UART.
- pData — указатель на приемный буфер.
- Size — количество байт, которые нужно принять.
- Timeout — время ожидания отправки.
ВНИМАНИЕ! В данном режиме функции приема и передачи данных, блокируют работу основного цикла на время приема или отправки данных. Или на время Timeout.
Функции HAL UART в режиме прерываний
HAL Uart Transmit IT
Передача данных в режиме прерываний осуществляется с помощью функции HAL_UART_Transmit_IT:
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size), где
- huart — указатель на используемый UART.
- pData — массив с данным для отправки.
- Size — количество байт. которые нужно отправить.
HAL Uart Receive IT
Для того, что бы принять данные по UART в режиме прерываний, необходимо использовать функцию HAL_UART_Receive_IT:
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size), где
- huart — указатель на используемый UART.
- pData — указатель на приемный буфер.
- Size — количество байт, которые нужно принять.
В режиме прерываний функции приема и передачи данных не блокируют основной цикл программы, что является плюсом.
Функции HAL в режиме DMA
HAL Uart Transmit DMA
Передача данных в режиме прерываний осуществляется с помощью функции HAL_UART_Transmit_DMA:
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size), где
- huart — указатель на используемый UART.
- pData — массив с данным для отправки.
- Size — количество байт. которые нужно отправить.
HAL Uart Receive DMA
Для того, что бы принять данные по UART в режиме прерываний, необходимо использовать функцию HAL_UART_Receive_DMA:
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size), где
- huart — указатель на используемый UART.
- pData — указатель на приемный буфер.
- Size — количество байт, которые нужно принять.
В режиме DMA функции приема и передачи данных не блокируют основной цикл программы, а так же прием или передача данных осуществляется без участия процессора.
Пример программы на STM32
Рассмотрим пример программы приема передаче через UART c использованием прерываний:
// Использовать будем USART2 uint32_t SIZE_BUFFER = 125; uint8_t BuferRxDEBUG[SIM8xx_SIZE_BUFFER]; //В main после инициализации UART MX_USART2_UART_Init(); //необходимо вызвать функцию приема байта. Этим мы активируем начало работы приема HAL_UART_Receive_IT(&huart2, &getbyteDb, 1); // Для приема данных будем использовать функцию Callback. Данная функция выполняется когда завершен прием 1 байта. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ if(huart==&huart2) { HAL_UART_Receive_IT(&huart2, &getbyteDb, 1); BuferRxDEBUG[pGetByteDb] = getbyteDb; pGetByteDb++; // если достигли признака конца строки, очищаем буфер и обнуляем счетчик принятых байт if (RxBuf_Bytes((uint8_t*)BuferRxDEBUG, (uint8_t *) END_OF_MESSAGE_STRING, SIZE_BUFFER)) { Flush_Buf(BuferRxDEBUG, SIZE_BUFFER); pGetByteDb = 0; } } }
Добрый день, вопрос к вам по поводу функции HAL_UART_Receive(*huart, *pData, Size, Timeout). Столкнулся с тем что параметр Timeout никак не влияет на работу функции. Функция будет выполняться до тех пор пока не примет пакет размера Size, а если пакет не придет или потеряется хотя бы байт, то функция не закончит свою работу даже по прошествию времени Timeout. Т.е. если мы не приняли пакет размера Size, то просто повиснем. Величина В чем может быть причина?
Добрый день! Для того что бы лучше оценить вашу проблему, пришлите часть кода, где вы работаете с этой функцией. Если можно, то всю функцию целиком.
void Config(void)
{
unsigned char _packetReceive[37];
extern unsigned char Packet200[45];
USART1_Init();
HAL_UART_Transmit(&huart1, Packet200, 45, 20);
HAL_UART_Receive(&huart1, _ packetReceive, 37, 40);
}
Отсылаю пакет (пакет задан вне функции) внешнему устройству через UART и жду от него ответа. Пакет передается за 4мс, ещё через 3 мс получаю ответ в течении 3мс. Этот кусок кода работает хорошо пока приходит ответ, если его не будет (например обрыв линии), то программа виснет наглухо в функции UART_WaitOnFlagUntilTimeout
Очень похоже на то, что у вас блокируется SysTick. Посмотрите его приоритет. Выставте значение 0. CubeMX -> NVIC ->System tick timer
Если используете данный код в прерывании IRQ то лучше вести прием так же через прерывание HAL_UART_Receive_IT.
Так же используйте последнюю версию HAL для вашего микроконтроллера.
Компилятор ругается на UART_HandleTypeDef в функции void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart). Где чего надо объявить или ещё чего сделать.
потому что это прототип функции, вместо UART_HandleTypeDef *huart необходимо вставить дескриптор на используемый модуль UART.
Спасибо за оперативный ответ. Как будет выглядеть этот дискриптор в программе и почему в Вашем примере он не требуется. Дело в том, что как-то всё работало и не возникало вопросов по дескрипторам, т.е я до этого момента и не знаю что это такое и вдруг начал ругаться.
Да, работа ведётся по USB
Приведите пример кода, где у вас ругается компилятор.
В нашем примере дескриптор используемого UART это &huart2. Он храниться в файле usart.h и формируется автоматически, если вы генерируете код с помощью CubeMX. Объявлен дескриптор должен быть вот так :
extern UART_HandleTypeDef huart2; Возможно не подключен файл #include "usart.h"
Да, не стал с этим заморачиваться, а необходимую обработку делаю непосредственно в функции прерывания.
Если у вас так как написано. то надо делать так: if (huart->Instance == &USART1)
*huart это указатель, вам же нужно обратиться к содержимому этого указателя. Т.е. необходимо использовать «&»
Спасибо за участие. Компилятору не нравится строчка void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart), а именно UART_HandleTypeDef. Повторюсь, что ранее с этой строчкой проблем не было.
Да, а с & спасибо, прозевал.
Тут я вас ввел в заблуждение, если вы проверяете uart через -> instance, то амперсанд не нужен.
Что пишет компилятор? Случайно не unknown type name ‘UART_HandleTypeDef’; did you mean ‘DMA_HandleTypeDef’?
Да, именно это. Насквозь всё видите.
Это значит, что у вас не подключен модуль UART1. Он включается в CubeMX.
Спасибо. Как уже писал использую только USB в режиме виртуального компорта. Как понимаю, для использования обсуждаемого колбека мне надо подключить UART1. А если он не нужен (только ножки занимает), то что тогда делать?
Что значит не нужен, только ножки занимает? Если вы подключились к пинам Uart то используется калбэк для работы с ним. Если просто подключились, но работать не собираетесь, то удалит данный колбэк.
Но если я правильно понимаю, вы используете переходник с ucb на Com порт. т.е С одной стороны у вас Usb c другой Tx, Rx, Gnd, +5v. А значит вы используете UART а не USB, значит вам его надо включить для дальнейшей работы в колбэке.
Нет, никаких переходников не использую. У STM32 есть возможность сразу на своих ножках организовать USB_DP и USB_DM. В Кубе также есть соответствующие для этого настройки в том числе будет ли он являться виртуальным COM портом или каким иным классом. В случае виртуального COM порта, как понимаю, в нём есть примочка для связи USB и USART. В этом случае обсуждаемый колбек должен работать, но что-то не получается без подключения USART. И как в этом случае его настраивать. Или всёже есть отдельный колбек для USB. Эти мои рассуждения надо рассматривать как дилетантские.
В ST-Link v2, V3 есть режим VCP — виртуальный порт. Работает от UART. И если на плате (Nucleo?) есть два вывода TX/RX, то можно туда подключить UART с МК и передавать в комп. Надо найти на плате, с какого UART МК заведен на МК ст-линка, настроить уарт и просто передавать как по обычному уарту. Изучайте схему на плату. Без USart не обойтись)