900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > STM32 串口DMA接收 Openmv / K210 整数 小数字符串数据 (基于HAL库)

STM32 串口DMA接收 Openmv / K210 整数 小数字符串数据 (基于HAL库)

时间:2024-02-27 17:03:50

相关推荐

STM32 串口DMA接收 Openmv / K210 整数 小数字符串数据 (基于HAL库)

目录

前言一、工程配置二、串口DMA部分代码1.源文件UART_DMA.c2.头文件UART_DMA.h3.stm32f1xx_it.c的修改4.串口收发DMA测试三、字符串数字提取代码1.源文件NumAndStr.c:2.头文件NumAndStr.h:3.测试:四、Openmv / K210 发送、STM32接收测试总结修订版本UART_DMA.cUART_DMA.h

平台: STM32 Cube IDE


前言

许多科创比赛中经常会有其他设备与STM32串口通讯的需求,比如可能需要Openmv / K210向STM32串口发送坐标的情况。下面我将介绍一种基于HAL库的串口DMA不定长数据收发和数据解读的方案。


一、工程配置

1.选择好芯片、配置好时钟和debug模式后,使能要用到的串口。

2.使能该串口的收发收发DMA:

3.使能串口全局中断,并生成工程文件。

二、串口DMA部分代码

本部分代码修改自xia0816大佬写的《真正实现了STM32 HAL串口不定长数据的接收发送功能(DMA方式,不用限定单次接收长度和添加结束标志)》

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){//在这里加入数据处理的函数}//到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_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{//在这里面加入数据处理的函数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_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增加所用芯片的版本#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测试

(1月25日补充)新版Cube MX有BUG,生成的初始化代码顺序有问题,见STM32 HAL串口DMA发送一直失败 —— 攻城狮_鲨鱼,故建议在生成的初始化代码前手动

MX_DMA_Init();MX_USART1_UART_Init();

启动串口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, *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, *Author: 乙酸氧铍*/#ifndef NUMANDSTR_NUMANDSTR_H_#define NUMANDSTR_NUMANDSTR_H_#include "main.h"#define TempDoubleLen 18#define TempIntLen 11/*str:数字字符串首地址flag:分隔符no:第no个数字 从1开始计Output: 小数存放地址*/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:数字字符串首地址

flag:分隔符

no:第no个数字 从1开始计

Output: 小数存放地址

3.测试:

修改UART_DMA.c中的DataProcess()函数

#include "../NumAndStr/NumAndStr.h"//包含头文件int32_t a,b,c;double d,e,f;void DataProcess(void){//在这里加入数据处理的函数a = str2int(RxBuffer, ' ', 1);b = str2int(RxBuffer, ' ', 2);c = str2int(RxBuffer, ' ', 3);str2double(RxBuffer, ' ', 4, &d);str2double(RxBuffer, ' ', 5, &e);str2double(RxBuffer, ' ', 6, &f);}

进入debug模式,监视变量a、b、c、d、e、f,使用串口调试助手再次发送一段测试字符串

效果如图所示:

可以看到六个数据都已成功存入对应的变量中,并成功回显。

且多次测试都能成功解读

四、Openmv / K210 发送、STM32接收测试

(示例) 平台: MaixPy IDE、K210 Maix Bit

K210 串口测试程序

延时500ms时

import utimefrom board import board_infofrom Maix import freqfrom fpioa_manager import fmfrom machine import UARTimport randomfm.register(9,fm.fpioa.UART1_TX)fm.register(10,fm.fpioa.UART1_RX)UART_USB = UART(UART.UART1, 115200, 8, None, 1, timeout = 1000, read_buf_len = 128)while(True):Tube_X = random.randint(-200,200)Tube_Y = random.randint(-200,200)Tube_Angle = random.random()print('%d %d %f'%(Tube_X, Tube_Y, Tube_Angle))UART_USB.write('%d %d %f\r\n'%(Tube_X, Tube_Y, Tube_Angle))utime.sleep_ms(500)

修改UART_DMA.c中的DataProcess()函数

int32_t Tube_X = 0, Tube_Y = 0;double Tube_Angle = 0;void DataProcess(void){//在这里加入数据处理的函数Tube_X = str2int(RxBuffer, ' ', 1);Tube_Y = str2int(RxBuffer, ' ', 2);str2double(RxBuffer, ' ', 3, &Tube_Angle);}

进入debug,如图所示,数据提取成功

延时15ms时

#...while(True):Tube_X = random.randint(-200,200)Tube_Y = random.randint(-200,200)Tube_Angle = random.random()print('%d %d %f'%(Tube_X, Tube_Y, Tube_Angle))UART_USB.write('%d %d %f\r\n'%(Tube_X, Tube_Y, Tube_Angle))utime.sleep_ms(15)#...

总结

本文介绍了一种STM32 串口DMA收发并解读的方案,对CPU要求较小,只需自己选择分隔符号,不需要设计复杂的通信协议就能得到对应位置的数据,应该可以应用到使用STM32的多种科创比赛项目中去。

修订版本

UART_DMA.c

/** UART_DMA.c** Created on: Mar 14, *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){//在这里加入数据处理的函数#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}{//在这里面加入数据处理的函数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;}//启动DMA发送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{/* 开启空闲接收中断 */__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);}return ret;}//启动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, *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//串口回显开关//#define UART_DMA_Switch 0/*要在Cube中开串口全局中断和收发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);//不需要自己调用void ProcessData(UART_HandleTypeDef *huart);//在里面添加数据处理函数void HAL_UART_IdleCallback(UART_HandleTypeDef *huart);//到USARTx_IRQHandler中添加#endif /* UART_DMA_UART_DMA_H_ */

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。