完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
前言 许多科创比赛中经常会有其他设备与STM32串口通讯的需求,比如可能需要Openmv / K210向STM32串口发送坐标的情况。下面我将介绍一种基于HAL库的串口DMA不定长数据收发和数据解读的方案。 一、工程配置 (示例) 平台: STM32 Cube IDE 1.选择好芯片、配置好时钟和debug模式后,使能要用到的串口。 2.使能该串口的收发收发DMA: 3.使能串口全局中断,并生成工程文件。 二、串口DMA部分代码 本部分代码修改自xia0816大佬写的《真正实现了STM32 HAL串口不定长数据的接收发送功能(DMA方式,不用限定单次接收长度和添加结束标志)》 1.源文件UART_DMA.c #include "UART_DMA.h" #include #include #include uint8_t RxBuffer[UART_RX_BUF_SIZE] = {0}; uint8_t TxBuffer[UART_RX_BUF_SIZE] = {0}; uint8_t sendCompleteSign = 1; uint8_t TxLen = 0; void DataProcess(void) { //在这里加入数据处理的函数 } //到USARTx_IRQHandler中添加,如: //void USART1_IRQHandler(void) //{ // /* USER CODE BEGIN USART1_IRQn 0 */ // if(__HAL_UART_GET_FLAG(&USB_Huart,UART_FLAG_IDLE)) // { // HAL_UART_IdleCallback(&USB_Huart); // } // // /* USER CODE END USART1_IRQn 0 */ // HAL_UART_IRQHandler(&huartx); //} void HAL_UART_IdleCallback(UART_HandleTypeDef *huart) { __HAL_UART_CLEAR_IDLEFLAG(huart); { HAL_UART_DMAStop(huart); ProcessData(); StartUartRxDMA(); } } void ProcessData() { uint32_t len = 0; //得到已经接收了多少个字节 = 总共要接收的字节数 - ?NDTR F1为CNDTR F4为NDTR #ifdef __STM32F1xx_HAL_H len = UART_RX_BUF_SIZE - USB_Huart.hdmarx->Instance->CNDTR; #define ProcessDataOK #endif #ifdef __STM32F4xx_HAL_H len = UART_RX_BUF_SIZE - USB_Huart.hdmarx->Instance->NDTR; #define ProcessDataOK #endif #ifndef ProcessDataOK 增加所用芯片的版本 #endif if(len > 0) { if(sendCompleteSign == 1) { #if UART_RXTX_Switch memset((void *)TxBuffer, 0, sizeof(TxBuffer)); memcpy(TxBuffer, RxBuffer, len); TxLen = len; StartUartTxDMA(); //串口回显 #endif { //在这里面加入数据处理的函数 DataProcess(); } } } } void USB_DMA_printf(const char *format,...) { uint32_t length; va_list args; va_start(args, format); length = vsnprintf((char*)TxBuffer, sizeof(TxBuffer)+1, (char*)format, args); va_end(args); HAL_UART_Transmit_DMA(&USB_Huart,TxBuffer,length); } void USB_printf(const char *format,...) { uint32_t length; va_list args; va_start(args, format); length = vsnprintf((char*)TxBuffer, sizeof(TxBuffer)+1, (char*)format, args); va_end(args); HAL_UART_Transmit(&USB_Huart,TxBuffer,length,0xFFFF); } /** * @brief Tx Transfer completed callbacks. * @param huart Pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @retval None */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ // UNUSED(huart); if(huart == &USB_Huart) { sendCompleteSign = 1; } /* NOTE: This function should not be modified, when the callback is needed, the HAL_UART_TxCpltCallback could be implemented in the user file */ } /** * @brief Rx Transfer completed callbacks. * @param huart Pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @retval None */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ // UNUSED(huart); if(huart == &USB_Huart) { ProcessData(); StartUartRxDMA(); } /* NOTE: This function should not be modified, when the callback is needed, the HAL_UART_RxCpltCallback could be implemented in the user file */ } uint8_t UartTxData(UART_HandleTypeDef *huart, uint8_t *buf, const uint32_t len) { HAL_StatusTypeDef status; uint8_t ret = 1; if(sendCompleteSign == 0 || len == 0) { return 0; } sendCompleteSign = 0; status = HAL_UART_Transmit_DMA(huart, (uint8_t*)buf, len); if(HAL_OK != status) { ret = 0; } return ret; } //启动DMA发送 uint8_t StartUartTxDMA() { return UartTxData(&USB_Huart, TxBuffer, TxLen); } uint8_t UartRxData(UART_HandleTypeDef *huart, uint8_t *buf, const uint32_t len) { HAL_StatusTypeDef status; uint8_t ret = 1; status = HAL_UART_Receive_DMA(huart, (uint8_t*)buf, len); if(HAL_OK != status) { ret = 0; } else { /* 开启空闲接收中断 */ __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); } return ret; } //启动DMA接收 uint8_t StartUartRxDMA() { return UartRxData(&USB_Huart, RxBuffer, UART_RX_BUF_SIZE); } void ProcessData()中可能需要视所用芯片情况作部分修改,目前只测试过STM32F103VET6和STM32F411CEU6 //得到已经接收了多少个字节 = 总共要接收的字节数 - ?NDTR F1为CNDTR F4为NDTR #ifdef __STM32F1xx_HAL_H len = UART_RX_BUF_SIZE - USB_Huart.hdmarx->Instance->CNDTR; #define ProcessDataOK #endif #ifdef __STM32F4xx_HAL_H len = UART_RX_BUF_SIZE - USB_Huart.hdmarx->Instance->NDTR; #define ProcessDataOK #endif #ifndef ProcessDataOK 增加所用芯片的版本 #endif 2.头文件UART_DMA.h #ifndef UART_DMA_UART_DMA_H_ #define UART_DMA_UART_DMA_H_ #include "main.h" extern UART_HandleTypeDef huart1; //修改为所用串口 #define USB_Huart huart1 //修改为所用串口 #define UART_RX_BUF_SIZE 128 #define UART_RXTX_Switch 1 //串口回显开关 /* 要在Cube中开串口全局中断和收发DMA */ extern uint8_t RxBuffer[UART_RX_BUF_SIZE]; extern uint8_t TxBuffer[UART_RX_BUF_SIZE]; extern uint8_t TxLen; void USB_DMA_printf(const char *format,...); //printf DMA方式 void USB_printf(const char *format,...); //printf 普通方式 uint8_t UartTxData(UART_HandleTypeDef *huart, uint8_t *buf, const uint32_t len); uint8_t StartUartRxDMA(); //接收DMA初始化 uint8_t StartUartTxDMA(); //不需要自己调用 void ProcessData(); //在里面添加数据处理函数 void HAL_UART_IdleCallback(UART_HandleTypeDef *huart); //到USARTx_IRQHandler中添加 #endif /* UART_DMA_UART_DMA_H_ */ 3.stm32f1xx_it.c的修改 需要到stm32f1xx_it.c中的USARTx_IRQHandler添加几句话 //... /* USER CODE BEGIN Includes */ #include "../UART_DMA/UART_DMA.h" /* USER CODE END Includes */ //... //... /** * @brief This function handles USART1 global interrupt. */ void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ if(__HAL_UART_GET_FLAG(&USB_Huart,UART_FLAG_IDLE)) { HAL_UART_IdleCallback(&USB_Huart); } /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ } //... 4.串口收发DMA测试 启动串口DMA接收 //... /* USER CODE BEGIN 2 */ StartUartRxDMA(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ //... 进入debug跑起来,将接收区缓存RxBuffer加入 现场表达式 //... uint8_t RxBuffer[UART_RX_BUF_SIZE] = {0}; uint8_t TxBuffer[UART_RX_BUF_SIZE] = {0}; uint8_t sendCompleteSign = 1; uint8_t TxLen = 0; //... 在ProcessData()中的该处打上断点。 打开串口调试助手,选择好参数后发送一段测试字符串,可以发现该字符串已成功存入缓冲区。 随后又成功将数据通过DMA回显 至此串口DMA收发已成功实现。 而源文件中附有的USB_DMA_printf()和USB_printf()分别为DMA方式的printf和普通的printf void USB_DMA_printf(const char *format,...) { uint32_t length; va_list args; va_start(args, format); length = vsnprintf((char*)TxBuffer, sizeof(TxBuffer)+1, (char*)format, args); va_end(args); HAL_UART_Transmit_DMA(&USB_Huart,TxBuffer,length); } void USB_printf(const char *format,...) { uint32_t length; va_list args; va_start(args, format); length = vsnprintf((char*)TxBuffer, sizeof(TxBuffer)+1, (char*)format, args); va_end(args); HAL_UART_Transmit(&USB_Huart,TxBuffer,length,0xFFFF); } 效果如下: 为了进一步处理数据,下面介绍字符串数字提取的方案。 三、字符串数字提取代码 改进型代码见C语言字符串数字提取函数,支持负数、浮点数、科学记数法 实测double数据直接传参数据会出错,故采取了指针的方式。 1.源文件NumAndStr.c: /* * NumAndStr.c * * Created on: Mar 15, 2021 * Author: 乙酸氧铍 */ #include "../NumAndStr/NumAndStr.h" #include int32_t str2int(uint8_t * str, uint8_t flag, uint8_t no) { uint8_t No = 1; uint8_t * Str = str; uint8_t NumTemp[TempIntLen]; while(No!=no) { if(*Str == flag) No++; Str++; } No = 0; while(*Str != flag && *Str != 'r' && *Str != 'n' && *Str != ' |