STM32 UART篇
UART异步收发
通用同步异步收发器 USART。串行收发。
起始位为低电平 停止位为高电平。
轮询
static UART_HandleTypeDef g_uart1Handler;static void UART1_GPIO_Init(void)
{// GPIO初始化GPIO_InitTypeDef gpioInitStructure;__HAL_RCC_GPIOA_CLK_ENABLE();gpioInitStructure.Mode = GPIO_MODE_AF_PP; // 复用推挽输出gpioInitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;gpioInitStructure.Pin = GPIO_PIN_9; // TXHAL_GPIO_Init(GPIOA, &gpioInitStructure);gpioInitStructure.Mode = GPIO_MODE_AF_INPUT;gpioInitStructure.Pull = GPIO_NOPULL; // 浮空输入gpioInitStructure.Speed = GPIO_SPEED_FREQ_LOW;gpioInitStructure.Pin = GPIO_PIN_10; // RXHAL_GPIO_Init(GPIOA, &gpioInitStructure);
}void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{if (huart->Instance == USART1) { // 初始化串口1__HAL_RCC_USART1_CLK_ENABLE(); // 开启串口1的时钟UART1_GPIO_Init();} else if (huart->Instance == USART2) {// 串口2初始化}
}static void USART1_DMA_Init(void)
{}void UART1_HandlerInit(uint32_t baudrate)
{g_uart1Handler.Instance = USART1;g_uart1Handler.Init.BaudRate = baudrate;g_uart1Handler.Init.WordLength = UART_WORDLENGTH_8B;g_uart1Handler.Init.StopBits = UART_STOPBITS_1;g_uart1Handler.Init.Parity = UART_PARITY_NONE;g_uart1Handler.Init.Mode = UART_MODE_TX_RX;g_uart1Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;HAL_UART_Init(&g_uart1Handler);USART1_DMA_Init();
}uint32_t USART1_SendData(uint8_t *data, uint32_t len, uint32_t timeout)
{HAL_StatusTypeDef ret;uint32_t sendDataLen = 0;ret = HAL_UART_Transmit(&g_uart1Handler, data, len, timeout);if (ret != HAL_OK) {// 错误报警if (ret == HAL_TIMEOUT && ((g_uart1Handler.TxXferSize - 1) != g_uart1Handler.TxXferCount)) {sendDataLen = g_uart1Handler.TxXferSize - g_uart1Handler.TxXferCount - 1;return sendDataLen;} else {// LOG_ERROR("");}return sendDataLen;}return len;
}uint32_t USART1_ReceiveData(uint8_t *data, uint32_t len, uint32_t timeout)
{HAL_StatusTypeDef ret;uint32_t recvDataLen = 0;ret = HAL_UART_Receive(&g_uart1Handler, data, len, timeout);if (ret != HAL_OK) {// 错误报警if (ret == HAL_TIMEOUT && ((g_uart1Handler.RxXferSize - 1) != g_uart1Handler.RxXferCount)) { // 超时了,但有接受到数据recvDataLen = g_uart1Handler.RxXferSize - g_uart1Handler.RxXferCount - 1; // 接受到的数据长度return recvDataLen;} else {// LOG_ERROR("");}return recvDataLen;}return len;
}UART_HandleTypeDef USART1_GetUartHandle(void)
{return g_uart1Handler;
}
main.c
void HardWareInit(void)
{// 硬件相关初始化HAL_Init();MCS_APT_RCCClock_Init();UART1_HandlerInit(115200);
}void SoftWareInit(void)
{}int main()
{HardWareInit();SoftWareInit();uint8_t recvData[200];uint32_t len = 0;while(1){len = USART1_ReceiveData(recvData, 200, 200);if (len > 0) // 说明接收到了数据USART1_SendData(recvData, len, 200);HAL_Delay(2);}
}
中断定长数据
HAL_NVIC_SetPriority(USART1_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn); // 开启相应的中断
HAL_UART_Receive_IT(&g_uart1Handle, recvBuff, 20);void MainTaskLoop(void)
{// 主函数loop执行while(1) {if(recvStat == 1) {recvStat = 0;HAL_UART_Transmit_IT(USART1_GetUartHandle(), sendBuff, 20);log_i("%s", sendBuff);}}
}
// 接收完成中断 UART_IT_RXNE
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART1) {memcpy(sendBuff, recvBuff, 20);recvStat = 1;HAL_UART_Receive_IT(USART1_GetUartHandle(), recvBuff, 20);}
}void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART1) {}
}void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&g_uart1Handle);
}
多指针定位 + 循环使用收发缓冲区
#define U1_TX_SIZE 2048
#define U1_RX_SIZE 2048
#define U1_RX_MAX 256 // 假设每次接收数据的最大接受量为255typedef struct{uint8_t *start;uint8_t *end;
} LCB;typedef struct{uint32_t RxCounter; // 总共接收到的长度uint32_t TxCounter; // 总共发送的长度LCB RxLocation[10];LCB TxLocation[10];LCB *RxInPtr; // 接收到的数据的指针LCB *RxOutPtr; // 接受到后处理的指针LCB *RxEndPtr; // 接收搬运结束的指针LCB *TxInPtr; // 发送的指针LCB *TxOutPtr; // 发送完成的指针LCB *TxEndPtr; // 发送搬运end指针UART_HandleTypeDef uartHandle;
} UCB;UART_HandleTypeDef g_uart1Handle;
uint8_t recvBuff[U1_RX_SIZE];
uint8_t sendBuff[U1_TX_SIZE];
uint8_t recvStat = 0;
UCB uart1;
中断不定长接收+循环接收缓冲区
HAL_NVIC_SetPriority(USART1_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn); // 开启相应的中断
HAL_UART_Receive_IT(&g_uart1Handle, recvBuff, U1_RX_MAX);__HAL_UART_ENABLE_IT(&huart1.uartHandle, UART_IT_IDLE); // 使能空闲中断(判断一包数据结束)void UART1_HandlerInit(uint32_t baudrate)
{g_uart1Handle.Instance = USART1;g_uart1Handle.Init.BaudRate = baudrate;g_uart1Handle.Init.WordLength = UART_WORDLENGTH_8B;g_uart1Handle.Init.StopBits = UART_STOPBITS_1;g_uart1Handle.Init.Parity = UART_PARITY_NONE;g_uart1Handle.Init.Mode = UART_MODE_TX_RX;g_uart1Handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;HAL_UART_Init(&g_uart1Handle);UART1_PtrInit();// 初始化uart1__HAL_UART_ENABLE_IT(&g_uart1Handle, UART_IT_IDLE); // 使能空闲中断HAL_UART_Receive_IT(&uart1.uartHandle, uart1.RxInPtr->start, U1_RX_MAX);
}void UART1_PtrInit(void)
{uart1.RxInPtr = &uart1.RxLocation[0];uart1.RxOutPtr = &uart1.RxLocation[0];uart1.RxEndPtr = &uart1.RxLocation[9];uart1.RxCounter = 0;uart1.RxInPtr->start = &recvBuff[0];uart1.TxInPtr = &uart1.TxLocation[0];uart1.TxOutPtr = &uart1.TxLocation[0];uart1.TxEndPtr = &uart1.TxLocation[9];uart1.TxCounter = 0;uart1.TxInPtr->start = &sendBuff[0];uart1.uartHandle = g_uart1Handle;
}// 每次发送数据对要发送的数据的填充
void U1_Txdata(uint8_t *data, uint32_t data_len) {if ((U1_TX_SIZE - uart1.TxCounter) >= data_len) {uart1.TxInPtr->start = &sendBuff[uart1.TxCounter];} else {uart1.TxCounter = 0;uart1.TxInPtr->start = &sendBuff[uart1.TxCounter];}memcpy(uart1.TxInPtr->start, data, data_len);uart1.TxCounter += data_len;uart1.TxInPtr->end = &sendBuff[uart1.TxCounter - 1];uart1.TxInPtr++;if (uart1.TxInPtr == uart1.TxEndPtr) {uart1.TxInPtr = &uart1.TxLocation[0];}
}void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&(uart1.uartHandle));if (__HAL_UART_GET_FLAG(&(uart1.uartHandle), UART_FLAG_IDLE)) { // 进入空闲中断__HAL_UART_CLEAR_IDLEFLAG(&(uart1.uartHandle));uart1.RxCounter += (U1_RX_MAX - uart1.uartHandle.RxXferCount); // 接收到的数据HAL_UART_AbortReceive_IT(&(uart1.uartHandle)); // 停止接收数据,在其中会将uart1.uartHandle.RxXferCount重新置为0,也可以直接置0uart1.RxInPtr->end = &recvBuff[uart1.RxCounter - 1]; // 标记接收的数据的结束地方uart1.RxInPtr++; // 下一个接收区间if (uart1.RxInPtr == uart1.RxEndPtr) { // 接收数据到达了末尾uart1.RxInPtr = &uart1.RxLocation[0];}if ((U1_RX_SIZE - uart1.RxCounter) < U1_RX_MAX) { // 判断数据是否够存储的,不够就回卷uart1.RxCounter = 0;uart1.RxInPtr->start = recvBuff;} else {uart1.RxInPtr->start = &recvBuff[uart1.RxCounter];}HAL_UART_Receive_IT(&uart1.uartHandle, uart1.RxInPtr->start, U1_RX_MAX);}
}// 发送完成中断
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART1) {uart1.TxState = 0;}
}void MainTaskLoop(void)
{// 主函数loop执行while(1) {if (uart1.RxOutPtr != uart1.RxInPtr) { // 接收有数据log_i("memcpy len == %d", uart1.RxOutPtr->end - uart1.RxOutPtr->start + 1);U1_Txdata(uart1.RxOutPtr->start, uart1.RxOutPtr->end - uart1.RxOutPtr->start + 1);uart1.RxOutPtr++;if (uart1.RxOutPtr == uart1.RxEndPtr) {uart1.RxOutPtr = &uart1.RxLocation[0];}}if ((uart1.TxOutPtr != uart1.TxInPtr) && (uart1.TxState == 0)) { // 发送有数据uart1.TxState = 1;HAL_UART_Transmit_IT(&uart1.uartHandle, uart1.TxOutPtr->start,uart1.TxOutPtr->end - uart1.TxOutPtr->start + 1);uart1.TxOutPtr++;if (uart1.TxOutPtr == uart1.TxEndPtr) {uart1.TxOutPtr = &uart1.TxLocation[0];}}}
}
DMA
直接存储器存取DMA,用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输,无需CPU干预,数据可以通过DMA快速的移动,节省CPU的资源来做其他的操作。
DMA_HandleTypeDef DMA_InitTypeDef
typedef struct __DMA_HandleTypeDef
{DMA_Channel_TypeDef *Instance; /*!< Register base address */DMA_InitTypeDef Init; /*!< DMA communication parameters */HAL_LockTypeDef Lock; /*!< DMA locking object */__IO HAL_DMA_StateTypeDef State; /*!< DMA transfer state */void *Parent; /*!< Parent object state */void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA transfer complete callback */void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA Half transfer complete callback */void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA transfer error callback */void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA transfer abort callback */__IO uint32_t ErrorCode; /*!< DMA Error code */DMA_TypeDef *DmaBaseAddress; /*!< DMA Channel Base Address */uint32_t ChannelIndex; /*!< DMA Channel Index */
} DMA_HandleTypeDef; typedef struct
{uint32_t Direction; /*!< Specifies if the data will be transferred from memory to peripheral, from memory to memory or from peripheral to memory.This parameter can be a value of @ref DMA_Data_transfer_direction */uint32_t PeriphInc; /*!< Specifies whether the Peripheral address register should be incremented or not.This parameter can be a value of @ref DMA_Peripheral_incremented_mode */uint32_t MemInc; /*!< Specifies whether the memory address register should be incremented or not.This parameter can be a value of @ref DMA_Memory_incremented_mode */uint32_t PeriphDataAlignment; /*!< Specifies the Peripheral data width.This parameter can be a value of @ref DMA_Peripheral_data_size */uint32_t MemDataAlignment; /*!< Specifies the Memory data width.This parameter can be a value of @ref DMA_Memory_data_size */uint32_t Mode; /*!< Specifies the operation mode of the DMAy Channelx.This parameter can be a value of @ref DMA_mode@note The circular buffer mode cannot be used if the memory-to-memorydata transfer is configured on the selected Channel */uint32_t Priority; /*!< Specifies the software priority for the DMAy Channelx.This parameter can be a value of @ref DMA_Priority_level */
} DMA_InitTypeDef;
__HAL_LINKDMA
定义在stm32f1xxx_hal_def.h文件中
#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \do{ \(__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \(__DMA_HANDLE__).Parent = (__HANDLE__); \} while(0U)
串口DMA
typedef struct{uint32_t RxCounter;uint32_t TxCounter;uint32_t TxState; // 0 空闲 1忙碌LCB RxLocation[10];LCB TxLocation[10];LCB *RxInPtr;LCB *RxOutPtr;LCB *RxEndPtr;LCB *TxInPtr;LCB *TxOutPtr;LCB *TxEndPtr;UART_HandleTypeDef uartHandle;DMA_HandleTypeDef dmaTx;DMA_HandleTypeDef dmaRx;
} UCB;void UART_GPIO_Init(UART_HandleTypeDef *huart)
{GPIO_InitTypeDef gpioInitStructure;if (huart->Instance == USART1) { // 串口1引脚初始化UART1_RCC_ENABLE; // 串口1时钟初始化__HAL_RCC_USART1_CLK_ENABLE(); // 打开串口1的时钟gpioInitStructure.Mode = GPIO_MODE_AF_PP; // 复用推挽输出gpioInitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;gpioInitStructure.Pin = UART1_TX_PIN; // TXHAL_GPIO_Init(UART1_TX_PORT, &gpioInitStructure);gpioInitStructure.Mode = GPIO_MODE_AF_INPUT;gpioInitStructure.Pull = GPIO_NOPULL; // 浮空输入gpioInitStructure.Speed = GPIO_SPEED_FREQ_LOW;gpioInitStructure.Pin = UART1_RX_PIN; // RXHAL_GPIO_Init(UART1_RX_PORT, &gpioInitStructure);HAL_NVIC_SetPriority(USART1_IRQn, 3, 0);HAL_NVIC_EnableIRQ(USART1_IRQn);__HAL_RCC_DMA1_CLK_ENABLE(); // 打开DMA时钟uart1.dmaTx.Instance = DMA1_Channel4;uart1.dmaTx.Init.Direction = DMA_MEMORY_TO_PERIPH;uart1.dmaTx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不递增uart1.dmaTx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增(缓冲区连续存储)uart1.dmaTx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;uart1.dmaTx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 外设数据宽度:字节uart1.dmaTx.Init.Mode = DMA_NORMAL; // 正常模式(非循环)uart1.dmaTx.Init.Priority = DMA_PRIORITY_MEDIUM; // 中等优先级__HAL_LINKDMA(huart, hdmatx, uart1.dmaTx);HAL_DMA_Init(&uart1.dmaTx);HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 3, 0);HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);uart1.dmaRx.Instance = DMA1_Channel5;uart1.dmaRx.Init.Direction = DMA_PERIPH_TO_MEMORY; // 从外设到内存uart1.dmaRx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不递增uart1.dmaRx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增(缓冲区连续存储)uart1.dmaRx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;uart1.dmaRx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 外设数据宽度:字节uart1.dmaRx.Init.Mode = DMA_NORMAL; // 正常模式(非循环)uart1.dmaRx.Init.Priority = DMA_PRIORITY_MEDIUM; // 中等优先级__HAL_LINKDMA(huart, hdmarx, uart1.dmaRx);HAL_DMA_Init(&uart1.dmaRx);HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 3, 0);HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);} else if (huart->Instance == USART2) {// 串口2__HAL_RCC_USART2_CLK_ENABLE();}
}void UART1_HandlerInit(uint32_t baudrate)
{g_uart1Handle.Instance = USART1;g_uart1Handle.Init.BaudRate = baudrate;g_uart1Handle.Init.WordLength = UART_WORDLENGTH_8B;g_uart1Handle.Init.StopBits = UART_STOPBITS_1;g_uart1Handle.Init.Parity = UART_PARITY_NONE;g_uart1Handle.Init.Mode = UART_MODE_TX_RX;g_uart1Handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;HAL_UART_Init(&g_uart1Handle);UART1_PtrInit();__HAL_UART_ENABLE_IT(&g_uart1Handle, UART_IT_IDLE); // 使能空闲中断HAL_UART_Receive_DMA(&uart1.uartHandle, uart1.RxInPtr->start, U1_RX_MAX);
}void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&(uart1.uartHandle));if (__HAL_UART_GET_FLAG(&(uart1.uartHandle), UART_FLAG_IDLE)) { // 进入空闲中断__HAL_UART_CLEAR_IDLEFLAG(&(uart1.uartHandle));
// uint32_t count = __HAL_DMA_GET_COUNTER(&uart1.dmaRx); // 剩余未DMA的量log_i("get the data len == %d \n", U1_RX_MAX - __HAL_DMA_GET_COUNTER(&uart1.dmaRx));uart1.RxCounter += (U1_RX_MAX - __HAL_DMA_GET_COUNTER(&uart1.dmaRx)); // 接收到的数据HAL_UART_AbortReceive_IT(&(uart1.uartHandle)); // 停止接收数据uart1.RxInPtr->end = &recvBuff[uart1.RxCounter - 1]; // 标记接收的数据的结束地方uint8_t data[100] = {0};memcpy(data, uart1.RxInPtr->start, uart1.RxInPtr->end - uart1.RxInPtr->start + 1);log_i("%s %d\n", data, uart1.RxInPtr->end - uart1.RxInPtr->start + 1);uart1.RxInPtr++; // 下一个接收区间if (uart1.RxInPtr == uart1.RxEndPtr) {uart1.RxInPtr = &uart1.RxLocation[0];}if ((U1_RX_SIZE - uart1.RxCounter) < U1_RX_MAX) {uart1.RxCounter = 0;uart1.RxInPtr->start = recvBuff;} else {uart1.RxInPtr->start = &recvBuff[uart1.RxCounter];}HAL_UART_Receive_DMA(&uart1.uartHandle, uart1.RxInPtr->start, U1_RX_MAX);}
}void DMA1_Channel4_IRQHandler(void)
{HAL_DMA_IRQHandler(&uart1.dmaTx);
}void DMA1_Channel5_IRQHandler(void)
{HAL_DMA_IRQHandler(&uart1.dmaRx);
}
printf
void u1_printf(char *fmt, ...)
{// https://www.runoob.com/cprogramming/c-macro-va_start.htmluint8_t tempBuff[256];uint16_t i;va_list ap;va_start(ap, fmt);vsprintf((char *)tempBuff, fmt, ap);va_end(ap);for(i = 0; i < strlen((char *)tempBuff); i++) {while(!__HAL_UART_GET_FLAG(&uart1.uartHandle, UART_FLAG_TXE));uart1.uartHandle.Instance->DR = tempBuff[i];}while(!__HAL_UART_GET_FLAG(&uart1.uartHandle, UART_FLAG_TC)); // 等待发送完成
}
--------------------------------
--------------------------------
高效串口方案
学习链接:https://www.bilibili.com/video/BV18BMdzVEB9?spm_id_from=333.788.videopod.sections&vd_source=d24d156785113ae486126f531d567353
DMA(DMA半/全传输中断) + 串口空闲中断 + 环形队列
对于每次中断后的数据,都将其放置到环形队列中,再来处理数据。
核心代码:
#define DMA_RX_BUF_SIZE 1024
static uint8_t dma_rx_buf[DMA_RX_BUF_SIZE];
static uint16_t last_dma_rx_size = 0; // 记录上一次接收到的数据量的大小static uin8_t queue_rx = {.wrIdx = 0,.rdIdx = 0,.size = DMA_RX_BUF_SIZE,.buf = queue_buf,
}
串口通信协议
串口通信的数据包由发送设备的 TXD 接口传输到接收设备的 RXD 接口。在串口通信的协 议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的 数据包格式要约定一致才能正常收发数据。
常见波特率:9600 115200 384000
起始位、停止位、有效数据位、校验位
uart相关结构体
UART_HandleTypeDef 结构体
typedef struct
{USART_TypeDef *Instance; /* UART 寄存器基地址指针 */// USART1~ USART3、USART6、UART4、UART5、UART7、UART8 UART_InitTypeDef Init; /* UART 初始化配置 */// 配置通讯参数 波特率 数据位数 停止位等等UART_AdvFeatureInitTypeDef AdvancedInit; /* 高级功能初始化配置(可选) */uint8_t *pTxBuffPtr; /* 发送缓冲区指针 */uint16_t TxXferSize; /* 发送数据的大小 */__IO uint16_t TxXferCount; /* 发送数据的个数 */uint8_t *pRxBuffPtr; /* 接收缓冲区指针 */uint16_t RxXferSize; /* 接收数据的大小 */__IO uint16_t RxXferCount; /* 接收数据的个数 */uint16_t Mask; /* UART 数据接收寄存器掩码 */uint32_t FifoMode; /* 指定是否使用 FIFO 模式 */uint16_t NbRxDataToProcess; /* 接收执行期间要处理的数据数*/uint16_t NbTxDataToProcess; /* 发送执行期间要处理的数据数*/__IO HAL_UART_RxTypeTypeDef ReceptionType; /* 持续接收类型 */ void (*RxISR)(struct __UART_HandleTypeDef *huart); /* 处理器上的函数指针*/void (*TxISR)(struct __UART_HandleTypeDef *huart); /* 处理器上的函数指针*/DMA_HandleTypeDef *hdmatx; /* 发送 DMA 句柄 */DMA_HandleTypeDef *hdmarx; /* 接收 DMA 句柄 */HAL_LockTypeDef Lock; /* 互斥锁,用于防止并发访问 */__IO HAL_UART_StateTypeDef gState; /* UART 全局状态 */__IO HAL_UART_StateTypeDef RxState; /* 接收状态 */__IO uint32_t ErrorCode; /* 错误码 */
} UART_HandleTypeDef;
UART_InitTypeDef 结构体
typedef struct
{uint32_t BaudRate; /* 波特率 */uint32_t WordLength; /* 字长 */// 可选 UART_WORDLENGTH_8B 或 UART_WORDLENGTH_9Buint32_t StopBits; /* 停止位 UART_STOPBITS_1、UART_STOPBITS_2 */uint32_t Parity; /* 校验位 UART_PARITY_NONE、UART_PARITY_EVEN、UART_PARITY_ODD */uint32_t Mode; /* UART 模式 UART_MODE_TX、UART_MODE_RX 或 UART_MODE_TX_RX */uint32_t HwFlowCtl; /* 硬件流设置 UART_HWCONTROL_NONE、UART_HWCONTROL_RTS、UART_HWCONTROL_CTS、UART_HWCONTROL_RTS_CTS */uint32_t OverSampling; /* 过采样设置 可选 UART_OVERSAMPLING_16 或 UART_OVERSAMPLING_8*/uint32_t OneBitSampling; /* 采样位方法选择 可选 */uint32_t ClockPrescaler; /* 时钟源的预分频值 可选 */
}UART_InitTypeDef
HAL_UART_StateTypeDef
表示uart的状态
#define HAL_UART_STATE_RESET 0x00000000U
#define HAL_UART_STATE_READY 0x00000020U
#define HAL_UART_STATE_READY 0x00000020U
#define HAL_UART_STATE_BUSY_TX 0x00000021U
#define HAL_UART_STATE_BUSY_RX 0x00000022U
#define HAL_UART_STATE_BUSY_TX_RX 0x00000023U
#define HAL_UART_STATE_TIMEOUT 0x000000A0U
#define HAL_UART_STATE_ERROR 0x000000E0Utypedef uint32_t HAL_UART_StateTypeDef;
UART相关函数
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData,uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData,uint16_t Size);
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData,uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);
三个uart中断标志
// 使能串口的空闲中断,当串口在接收数据过程中,超过一定时间(通常是 1 个字符传输时间)
// 没有收到新数据时,就会触发这个中断。进入HAL_UART_RxCpltCallback中处理数据
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 空闲中断// UART_IT_TC 是 “发送完成” 的中断标志,当串口把数据全部发送出去后
// (比如调用 HAL_UART_Transmit_IT 或 HAL_UART_Transmit_DMA 发送数据,且所有数据都发完了),
// 硬件会触发这个中断。
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TC);// 使能“接收非空”中断(收到数据时触发)
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);执行的流程
HAL_UART_Init -> HAL_UART_MspInit -> 发生中断 USART1_IRQn -> HAL_UART_IRQHandler -> HAL_UART_RxCpltCallbackHAL_UART_RxCpltCallback 会在 “中断 / DMA 接收完预设长度的数据” 时自动进入,
是处理固定长度数据的常用方式,需要自己重写才能实现具体功能
当然我们可以设置RxXferSize为1,那么每次接收到一个字节的数据就会进入出发中断,
调用HAL_UART_RxCpltCallback。
示例代码
void MX_USART1_UART_Init(void)
{// 1. 配置UART基本参数huart1.Instance = USART1;huart1.Init.BaudRate = 115200; // 波特率115200huart1.Init.WordLength = UART_WORDLENGTH_8B; // 8位数据位huart1.Init.StopBits = UART_STOPBITS_1; // 1位停止位huart1.Init.Parity = UART_PARITY_NONE; // 无校验huart1.Init.Mode = UART_MODE_TX_RX; // 收发模式huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 16倍过采样if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler(); // 初始化失败处理(需自行实现)}// 2. 配置DMA接收(这里使用DMA2,通道4,具体通道需根据芯片手册确认)hdma_usart1_rx.Instance = DMA2_Stream2; // DMA2 Stream2(需根据芯片修改)hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4; // 通道4(对应USART1_RX)hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; // 外设到内存(接收)hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不递增hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增(缓冲区连续存储)hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据宽度:字节hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 内存数据宽度:字节hdma_usart1_rx.Init.Mode = DMA_NORMAL; // 正常模式(非循环)hdma_usart1_rx.Init.Priority = DMA_PRIORITY_MEDIUM; // 中等优先级hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; // 禁用FIFOif (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK){Error_Handler();}// 3. 将DMA与UART绑定__HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx);// 4. 使能UART相关中断(可选,用于处理接收完成/错误)__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能空闲中断(判断一包数据结束)HAL_NVIC_EnableIRQ(USART1_IRQn); // 使能USART1中断HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); // 设置中断优先级// 5. 启动DMA接收(接收100字节,存到uart_rx_buf)HAL_UART_Receive_DMA(&huart1, uart_rx_buf, 100);// HAL_UART_DMAStop(&huart1);
}// 串口1中断服务函数(在stm32f4xx_it.c中)
void USART1_IRQHandler(void)
{// 先调用HAL库的通用中断处理函数(处理其他中断,如RXNE、TC等)HAL_UART_IRQHandler(&huart1);// 手动检测空闲中断标志if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET){// 清除空闲中断标志(必须先读SR,再读DR才能清除)__HAL_UART_CLEAR_IDLEFLAG(&huart1);// 处理空闲中断:表示当前一包数据接收完成HAL_UART_DMAStop(&huart1); // 停止DMA接收// 计算接收长度:缓冲区总大小 - DMA剩余未接收的数量rx_len = 100 - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 标记接收完成,供主程序判断rx_complete = 1;// 重新启动DMA接收,等待下一包数据HAL_UART_Receive_DMA(&huart1, uart_rx_buf, 100);}
}
不定长数据接收
- 定义全局变量
rx_data
作为串口接收缓冲区
由于是不定长数据的接收,因此缓冲区大小可以根据实际需求调整,只能大不能小,否则可能会丢失数据
// 串口接收缓冲区
uint8_t rx_data[256] = {0};
- 在 main 函数中,使用
HAL_UARTEx_ReceiveToIdle_DMA
函数开启不定长数据DMA接收
注意:需要关闭DMA传输过半中断,我们只需要接收完成中断
此函数是以空闲中断作为接收完成的标志,而不是接收长度,因此可以接收任意长度的数据
// 使用Ex函数,接收不定长数据
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, rx_data, sizeof(rx_data));
// 关闭DMA传输过半中断(HAL库默认开启,但我们只需要接收完成中断)
__HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT);
- 在中断函数
HAL_UARTEx_RxEventCallback
中,处理接收到的数据
所有的串口接收和发送操作都在中断函数中进行,不会阻塞主程序
// 不定长数据接收完成回调函数
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART2){// 使用DMA将接收到的数据发送回去HAL_UART_Transmit_DMA(&huart2, rx_data, Size);// 重新启动接收,使用Ex函数,接收不定长数据HAL_UARTEx_ReceiveToIdle_DMA(&huart2, rx_data, sizeof(rx_data));// 关闭DMA传输过半中断(HAL库默认开启,但我们只需要接收完成中断)__HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT);}
}