网站建设最贵多少钱,北湖区网站建设公司,网络服务公司有哪些,秦皇岛网站制作与网站建设目录前言一、工程配置二、串口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平台…
目录前言一、工程配置二、串口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.huint8_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增加所用芯片的版本#endif2.头文件UART_DMA.h
#ifndef UART_DMA_UART_DMA_H_
#define UART_DMA_UART_DMA_H_#include main.hextern 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测试
(2022年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, 2021* Author: 乙酸氧铍*/
#include ../NumAndStr/NumAndStr.h
#include stdlib.hint32_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:数字字符串首地址
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 utime
from board import board_info
from Maix import freq
from fpioa_manager import fm
from 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, 2021* Author: Royic*/
#include UART_DMA.h
#include string.h
#include stdarg.h
#include stdio.huint8_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, 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 //串口回显开关
//#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_ */