STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)
目錄
- 前言
- 一、工程配置
- 二、串口DMA部分代碼
- 1.源文件UART_DMA.c
- 2.頭文件UART_DMA.h
- 3.stm32f1xx_it.c的修改
- 4.串口收發(fā)DMA測(cè)試
- 三、字符串?dāng)?shù)字提取代碼
- 1.源文件NumAndStr.c:
- 2.頭文件NumAndStr.h:
- 3.測(cè)試:
- 四、Openmv / K210 發(fā)送、STM32接收測(cè)試
- 總結(jié)
- 修訂版本
- UART_DMA.c
- UART_DMA.h
平臺(tái): STM32 Cube IDE
前言
許多科創(chuàng)比賽中經(jīng)常會(huì)有其他設(shè)備與STM32串口通訊的需求,比如可能需要Openmv / K210向STM32串口發(fā)送坐標(biāo)的情況。下面我將介紹一種基于HAL庫(kù)的串口DMA不定長(zhǎng)數(shù)據(jù)收發(fā)和數(shù)據(jù)解讀的方案。
一、工程配置
1.選擇好芯片、配置好時(shí)鐘和debug模式后,使能要用到的串口。
2.使能該串口的收發(fā)收發(fā)DMA:
3.使能串口全局中斷,并生成工程文件。
二、串口DMA部分代碼
本部分代碼修改自xia0816大佬寫的《真正實(shí)現(xiàn)了STM32 HAL串口不定長(zhǎng)數(shù)據(jù)的接收發(fā)送功能(DMA方式,不用限定單次接收長(zhǎng)度和添加結(jié)束標(biāo)志)》
1.源文件UART_DMA.c
#include "UART_DMA.h" #include <string.h> #include <stdarg.h> #include <stdio.h>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) {//在這里加入數(shù)據(jù)處理的函數(shù)}//到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;//得到已經(jīng)接收了多少個(gè)字節(jié) = 總共要接收的字節(jié)數(shù) - ?NDTR F1為CNDTR F4為NDTR#ifdef __STM32F1xx_HAL_Hlen = UART_RX_BUF_SIZE - USB_Huart.hdmarx->Instance->CNDTR;#define ProcessDataOK#endif#ifdef __STM32F4xx_HAL_Hlen = UART_RX_BUF_SIZE - USB_Huart.hdmarx->Instance->NDTR;#define ProcessDataOK#endif#ifndef ProcessDataOK增加所用芯片的版本#endifif(len > 0){if(sendCompleteSign == 1){ #if UART_RXTX_Switchmemset((void *)TxBuffer, 0, sizeof(TxBuffer));memcpy(TxBuffer, RxBuffer, len);TxLen = len;StartUartTxDMA(); //串口回顯 #endif{//在這里面加入數(shù)據(jù)處理的函數(shù)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; }//啟動(dòng)DMA發(fā)送 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{/* 開(kāi)啟空閑接收中斷 */__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);}return ret; }//啟動(dòng)DMA接收 uint8_t StartUartRxDMA() {return UartRxData(&USB_Huart, RxBuffer, UART_RX_BUF_SIZE); }void ProcessData()中可能需要視所用芯片情況作部分修改,目前只測(cè)試過(guò)STM32F103VET6和STM32F411CEU6
//得到已經(jīng)接收了多少個(gè)字節(jié) = 總共要接收的字節(jié)數(shù) - ?NDTR F1為CNDTR F4為NDTR#ifdef __STM32F1xx_HAL_Hlen = UART_RX_BUF_SIZE - USB_Huart.hdmarx->Instance->CNDTR;#define ProcessDataOK#endif#ifdef __STM32F4xx_HAL_Hlen = UART_RX_BUF_SIZE - USB_Huart.hdmarx->Instance->NDTR;#define ProcessDataOK#endif#ifndef ProcessDataOK增加所用芯片的版本#endif2.頭文件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 //串口回顯開(kāi)關(guān)/* 要在Cube中開(kāi)串口全局中斷和收發(fā)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(); //不需要自己調(diào)用 void ProcessData(); //在里面添加數(shù)據(jù)處理函數(shù) 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.串口收發(fā)DMA測(cè)試
(2022年1月25日補(bǔ)充)新版Cube MX有BUG,生成的初始化代碼順序有問(wèn)題,見(jiàn)STM32 HAL串口DMA發(fā)送一直失敗 —— 攻城獅_鯊魚(yú),故建議在生成的初始化代碼前手動(dòng)
MX_DMA_Init();MX_USART1_UART_Init();如
啟動(dòng)串口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 */ //...進(jìn)入debug跑起來(lái),將接收區(qū)緩存RxBuffer加入 現(xiàn)場(chǎng)表達(dá)式
//...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()中的該處打上斷點(diǎn)。
打開(kāi)串口調(diào)試助手,選擇好參數(shù)后發(fā)送一段測(cè)試字符串,可以發(fā)現(xiàn)該字符串已成功存入緩沖區(qū)。
隨后又成功將數(shù)據(jù)通過(guò)DMA回顯
至此串口DMA收發(fā)已成功實(shí)現(xiàn)。
而源文件中附有的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); }效果如下:
為了進(jìn)一步處理數(shù)據(jù),下面介紹字符串?dāng)?shù)字提取的方案。
三、字符串?dāng)?shù)字提取代碼
改進(jìn)型代碼見(jiàn)C語(yǔ)言字符串?dāng)?shù)字提取函數(shù),支持負(fù)數(shù)、浮點(diǎn)數(shù)、科學(xué)記數(shù)法
實(shí)測(cè)double數(shù)據(jù)直接傳參數(shù)據(jù)會(huì)出錯(cuò),故采取了指針的方式。
1.源文件NumAndStr.c:
/** NumAndStr.c** Created on: Mar 15, 2021* Author: 乙酸氧鈹*/ #include "../NumAndStr/NumAndStr.h" #include <stdlib.h>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 != '\0' && No < (TempIntLen - 1)){NumTemp[No] = *Str;Str++;No++;}NumTemp[No] = '\0';return atoi(NumTemp); }void str2double(uint8_t * str, uint8_t flag, uint8_t no, double * Output) {uint8_t No = 1;uint8_t * Str = str;uint8_t NumTemp[TempDoubleLen];uint8_t NumTemp_int[TempDoubleLen];double OutputNum;while(No!=no){if(*Str == flag)No++;Str++;}No = 0;while(*Str != flag && *Str != '\r' && *Str != '\n' && *Str != '\0' && No < (TempDoubleLen - 1)){NumTemp[No] = *Str;Str++;No++;}NumTemp[No] = '\0';NumTemp[(TempDoubleLen - 1)] = 0;No = 0;while(NumTemp[NumTemp[(TempDoubleLen - 1)]] != '\0' && NumTemp[(TempDoubleLen - 1)] < (TempDoubleLen - 1)){if(NumTemp[NumTemp[(TempDoubleLen - 1)]] == '.'){NumTemp[(TempDoubleLen - 1)]++;NumTemp_int[(TempDoubleLen - 1)] = NumTemp[(TempDoubleLen - 1)];}NumTemp_int[No] = NumTemp[NumTemp[(TempDoubleLen - 1)]];No++;NumTemp[(TempDoubleLen - 1)]++;}NumTemp_int[No]='\0';NumTemp[(TempDoubleLen - 1)] = NumTemp_int[(TempDoubleLen - 1)]++;OutputNum = (double)atoi(NumTemp_int);while(NumTemp[NumTemp[(TempDoubleLen - 1)]] != '\0'){OutputNum /= 10;NumTemp[(TempDoubleLen - 1)] ++;}*Output = OutputNum; }2.頭文件NumAndStr.h:
/** NumAndStr.h** Created on: Mar 15, 2021* Author: 乙酸氧鈹*/#ifndef NUMANDSTR_NUMANDSTR_H_ #define NUMANDSTR_NUMANDSTR_H_#include "main.h"#define TempDoubleLen 18 #define TempIntLen 11/* str:數(shù)字字符串首地址 flag:分隔符 no:第no個(gè)數(shù)字 從1開(kāi)始計(jì) Output: 小數(shù)存放地址*/ extern int32_t str2int(uint8_t * str, uint8_t flag, uint8_t no); extern void str2double(uint8_t * str, uint8_t flag, uint8_t no, double * Output);#endif /* NUMANDSTR_NUMANDSTR_H_ */str:數(shù)字字符串首地址
flag:分隔符
no:第no個(gè)數(shù)字 從1開(kāi)始計(jì)
Output: 小數(shù)存放地址
3.測(cè)試:
修改UART_DMA.c中的DataProcess()函數(shù)
#include "../NumAndStr/NumAndStr.h" //包含頭文件int32_t a,b,c; double d,e,f; void DataProcess(void) {//在這里加入數(shù)據(jù)處理的函數(shù)a = str2int(RxBuffer, ' ', 1);b = str2int(RxBuffer, ' ', 2);c = str2int(RxBuffer, ' ', 3);str2double(RxBuffer, ' ', 4, &d);str2double(RxBuffer, ' ', 5, &e);str2double(RxBuffer, ' ', 6, &f); }進(jìn)入debug模式,監(jiān)視變量a、b、c、d、e、f,使用串口調(diào)試助手再次發(fā)送一段測(cè)試字符串
效果如圖所示:
可以看到六個(gè)數(shù)據(jù)都已成功存入對(duì)應(yīng)的變量中,并成功回顯。
且多次測(cè)試都能成功解讀
四、Openmv / K210 發(fā)送、STM32接收測(cè)試
(示例) 平臺(tái): MaixPy IDE、K210 Maix Bit
K210 串口測(cè)試程序
延時(shí)500ms時(shí)
修改UART_DMA.c中的DataProcess()函數(shù)
int32_t Tube_X = 0, Tube_Y = 0; double Tube_Angle = 0; void DataProcess(void) {//在這里加入數(shù)據(jù)處理的函數(shù)Tube_X = str2int(RxBuffer, ' ', 1);Tube_Y = str2int(RxBuffer, ' ', 2);str2double(RxBuffer, ' ', 3, &Tube_Angle); }進(jìn)入debug,如圖所示,數(shù)據(jù)提取成功
延時(shí)15ms時(shí)
總結(jié)
本文介紹了一種STM32 串口DMA收發(fā)并解讀的方案,對(duì)CPU要求較小,只需自己選擇分隔符號(hào),不需要設(shè)計(jì)復(fù)雜的通信協(xié)議就能得到對(duì)應(yīng)位置的數(shù)據(jù),應(yīng)該可以應(yīng)用到使用STM32的多種科創(chuàng)比賽項(xiàng)目中去。
修訂版本
UART_DMA.c
/** UART_DMA.c** Created on: Mar 14, 2021* Author: Royic*/ #include "UART_DMA.h" #include <string.h> #include <stdarg.h> #include <stdio.h>uint8_t RxBuffer[UART_RX_BUF_SIZE] = {0}; uint8_t TxBuffer[UART_RX_BUF_SIZE] = {0}; uint8_t sendCompleteSign = 1; uint8_t TxLen = 0; uint8_t USE_UART_DMA = 0;void DataProcess(UART_HandleTypeDef *huart, uint32_t Len) {//在這里加入數(shù)據(jù)處理的函數(shù) #ifdef USB_Huart_1if(huart == &USB_Huart_1){;} #endif #ifdef USB_Huart_2if(huart == &USB_Huart_2){;} #endif }//到USARTx_IRQHandler中添加,如: //void USART1_IRQHandler(void) //{ // /* USER CODE BEGIN USART1_IRQn 0 */ // if(__HAL_UART_GET_FLAG(&USB_Huart_1,UART_FLAG_IDLE)) // { // HAL_UART_IdleCallback(&USB_Huart_1); // } // // /* 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(huart);StartUartRxDMA(huart);} }void ProcessData(UART_HandleTypeDef *huart) {uint32_t len = 0;len = UART_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx);if(len > 0){if(sendCompleteSign == 1){ #if UART_RXTX_Switchmemset((void *)TxBuffer, 0, sizeof(TxBuffer));memcpy(TxBuffer, RxBuffer, len);TxLen = len;StartUartTxDMA(huart); //串口回顯 #endif}{//在這里面加入數(shù)據(jù)處理的函數(shù)DataProcess(huart, len);}} }void USB_DMA_printf(UART_HandleTypeDef *huart, 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(huart,TxBuffer,length); }void USB_printf(UART_HandleTypeDef *huart, 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(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 */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 */ProcessData(huart);StartUartRxDMA(huart);/* 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; }//啟動(dòng)DMA發(fā)送 uint8_t StartUartTxDMA(UART_HandleTypeDef *huart) {return UartTxData(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{/* 開(kāi)啟空閑接收中斷 */__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);}return ret; }//啟動(dòng)DMA接收 uint8_t StartUartRxDMA(UART_HandleTypeDef *huart) {USE_UART_DMA = 1;return UartRxData(huart, RxBuffer, UART_RX_BUF_SIZE); }UART_DMA.h
/** UART_DMA.h** Created on: Mar 14, 2021* Author: Royic*/#ifndef UART_DMA_UART_DMA_H_ #define UART_DMA_UART_DMA_H_#include "main.h"#define USB_Huart_1 huart1 //修改為所用串口 extern UART_HandleTypeDef USB_Huart_1;#define USB_Huart_2 huart2 //修改為所用串口 extern UART_HandleTypeDef USB_Huart_2;#define UART_RX_BUF_SIZE 128#define UART_RXTX_Switch 0 //串口回顯開(kāi)關(guān) //#define UART_DMA_Switch 0 /* 要在Cube中開(kāi)串口全局中斷和收發(fā)DMA*/extern uint8_t RxBuffer[UART_RX_BUF_SIZE]; extern uint8_t TxBuffer[UART_RX_BUF_SIZE]; extern uint8_t TxLen; extern uint8_t USE_UART_DMA;void USB_DMA_printf(UART_HandleTypeDef *huart, const char *format,...); //printf DMA方式 void USB_printf(UART_HandleTypeDef *huart, const char *format,...); //printf 普通方式 uint8_t UartTxData(UART_HandleTypeDef *huart, uint8_t *buf, const uint32_t len); uint8_t StartUartRxDMA(UART_HandleTypeDef *huart); //接收DMA初始化 uint8_t StartUartTxDMA(UART_HandleTypeDef *huart); //不需要自己調(diào)用 void ProcessData(UART_HandleTypeDef *huart); //在里面添加數(shù)據(jù)處理函數(shù) void HAL_UART_IdleCallback(UART_HandleTypeDef *huart); //到USARTx_IRQHandler中添加#endif /* UART_DMA_UART_DMA_H_ */總結(jié)
以上是生活随笔為你收集整理的STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《帝王三部曲》——二月河
- 下一篇: MFS学习总结