目录
前言:
1. 用CubeMX配置串口+DMA所需要的环境
(1)打开CubeMAX,点击红框
(2)查找stm32F103C8T6的芯片
(3)配置SYS
(4)配置RCC时钟
(5)配置主频
(6)配置串口
(7)配置DMA
(8)生成工程
2.串口中断与DMA相关重要函数介绍
(1)串口相关
接收完成回调函数:
接收串口的函数:
发送串口的函数:
空闲中断打开函数:
在每次进入中断后,判断是否为空闲中断:
清除空闲标志位
终止当前的接收(会把RxferCount清零)
终止接收回调函数
发送完成回调函数
(2)DMA相关
串口DMA接收函数:
串口DMA发送函数:
3.编写相关代码
(1)项目要求
(2)相关代码编写
1.全局变量
2.main.c
3.stm32f1xx_it.c
4.ATK-LORA-01无线串口的使用
1.使用该无线串口前,需要通过软件调整该串口的参数配置。
2.步骤如下:
总结:
前言:
本文章使用stm32f103c8t6进行HAL库串口程序的使用,可以解决串口接收发送数据问题,也使用了DMA转运,但没有实现无线数据传输(脑子笨了点),此外,本章也会初步的介绍关于ATK-LORA-01无线串口模块的使用,当然,这也指的是透明传输这一方面。
STM32的串口收发信息,标准库的代码b站上江协科技我认为已经讲的非常清楚了,但可惜没有hal库的,其他的博主,还有网络上的一些视频,貌似距离如接收“stop”然后发送“”stop”,接着接收“start”然后发送“start”都有些困难,他们通过只接受一个字节然后进入中断,所以我实际用CUbeMX来带领大家来操作一遍,无脑掌握。
备注:关于串口无限数据传输的方法我认为下面这一篇博客讲的非常厉害,使用串口的空闲中断,而且也给出了解决方法,给了我很大启发,链接如下:超子物联网HAL库笔记:串口篇
1. 用CubeMX配置串口+DMA所需要的环境
(1)打开CubeMAX,点击红框
(2)查找stm32F103C8T6的芯片
C8T6和C8T6TR都可以,双击。
(3)配置SYS
(4)配置RCC时钟
注意如果这个没有配置好的话,就不能配置72MHz的主频。
(5)配置主频
直接双击改写为72,之后敲下Enter,自动配置完毕。
(6)配置串口
一定要使能串口中断!!接收不到信号。
(7)配置DMA
由于要使用DMA,我们先在这里吧DAM配置好。
配置中等速度其他的不用改。
(8)生成工程
以上就是CubeMX所有的准备操作啦,下面是实现的代码。
2.串口中断与DMA相关重要函数介绍
(1)串口相关
HAL库为串口中断提供了很多回调函数。 比如 错误回调、半完成回调、Rx、Tx完成回调。你需要用什么回调,就声明什么回调~
接收完成回调函数:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
其中“RxCpltCallback” 可以分解为以下部分:
- “Rx” 通常代表 “Receive”,即接收。
- “Cplt” 通常代表 “Complete”,即完成。
- “Callback” 即回调,指的是当某个特定事件发生时被调用的函数。
接收串口的函数:
HAL_UART_Receive_IT(&Uart1,Buff,Rx_Size,200)
他的参数分别为:串口配置结构体地址、接收缓冲区(我们自己定义的数组)、预计接收的字节个数(我们自己定义)、超时时间(单位为ms)
接收串口的函数会有四个返回值:
HAL_OK、 代表此次在200ms内接受到了 200 字节。 (也就是与预计相等)
HAL_ERROR 、代表传参错误、 比如超时时间为0、或者数据缓冲区地址为空
HAL_TIMEOUT 、代表未能在规定时间内接收到 200 个字节
往往分为两种情况: 1、接收了但没接收完、2、一点没接收
HAL_BUSY、代表已经在接受了。在忙
注意!!!当接受完size(预计接受的字节)后就会触发串口中断,但如果没有接收完size个字节,而是少接受了字节,就不会触发串口中断,但是数组缓冲区中仍然有数据,这也就是为什么很多博主喜欢把size设置为1,因为一接触到数据就进一次中断。
发送串口的函数:
HAL_UART_Transmit_IT(&huart1,txBuff,200);
他的参数分别为:串口配置结构体地址、发送缓冲区(我们自己定义的数组)、预计发送的字节个数(我们自己定义)。
空闲中断打开函数:
__HAL_UART_ENABLE_IT(&uart1.uart,UART_IT_IDLE);
在每次进入中断后,判断是否为空闲中断:
if(__HAL_UART_GET_FLAG(&uart1.uart, UART_FLAG_IDLE))
清除空闲标志位
__HAL_UART_CLEAR_FLAG(&uart1.uart, UART_FLAG_IDLE);
终止当前的接收(会把RxferCount清零)
HAL_UART_AbortReceive_IT(&uart1.uart);
终止接收回调函数
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)
发送完成回调函数
HAL_UART_TxCpltCallback
(2)DMA相关
DMA(Direct Memory Access)是一种直接内存访问技术,
可以在不经过CPU的情况下实现外设与内存之间的数据传输,提高数据传输效率
同时,DMA同样可以触发串口中断,进入串口中断函数处理任务。
串口DMA接收函数:
HAL_UART_Receive_DMA(&huart1, (uint8_t *)&ReBuff,5);
他的参数分别为:串口配置结构体地址、接收缓冲区(我们自己定义的数组)、预计接收的字节个数(我们自己定义)。
其效果和串口接收函数是一样的,注意的要点也一样,这里就不多描述。
串口DMA发送函数:
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)"stop",5);
他的参数分别为:串口配置结构体地址、发送缓冲区(我们自己定义的数组或字符串)、预计发送的字节个数(我们自己定义)。
3.编写相关代码
(1)项目要求
stm32使用(无线串口)实现收发、判断数据+DMA(HAL库),现象如下:
项目开始:单片机通过串口发送: Hello windows! ,后PC端通过串口助手发送stop,单片机接收到后停止Hello windows!发送,PC端再通过串口助手发送start,单片机接收到后同样发送start,并且开始Hello windows!发送。如下图
(2)相关代码编写
1.全局变量
char ReBuff[5];//接收缓冲区
int flag=1;//对于stop和start的判断标志
uint8_t SendBuff[]="Hello windows! ";//字符串"Hello windows! "封装
const char *str1="start";//字符串"start! "封装
const char *str2="stop";//字符串"stop "封装
char *result1 ;//strstr比较结果
char *result2 ;
2.main.c
在while函数上加上HAL_UART_Receive_DMA(&huart1, (uint8_t *)&ReBuff,5);
接收串口信息,当达到5个数组时触发中断。
下面是while里面的函数
/* USER CODE BEGIN 3 */
result1 = strstr(ReBuff, str1);
result2 = strstr(ReBuff, str2);
if(result1)
{
flag=1;
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)"start",5);
memset(ReBuff, 0, sizeof(ReBuff));
result1 =0;
}
else if(result2)
{
flag=0;
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)"stop",5);
memset(ReBuff, 0, sizeof(ReBuff));
result2 =0;
}
if(flag==1)
{
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)SendBuff,17);
HAL_Delay(1000);
}
}
/* USER CODE END 3 */
这里使用的strstr函数,用于查找子字符串在另一个字符串中的第一次出现,使用此函数时要添加下面的头文件才可以使用。
#include "string.h"
#include "stdio.h"
这里使用的memset函数,用于清零数组。同样要加上述头文件。
下面是,HAL_UART_RxCpltCallback里触发中断后的操作
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)//判断是串口1中断被触发
{
HAL_UART_Receive_DMA(&huart1, (uint8_t *)&ReBuff,5); //用于再次开启串口中断等待
}
}
3.stm32f1xx_it.c
主要修改void USART1_IRQHandler(void),其他不需要修改。
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* 在每次进入中断后,判断是否为空闲中断 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)){
/* 清除空闲标志位 */
//__HAL_UART_CLEAR_FLAG(&uart1.uart, UART_FLAG_IDLE);
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
/* 终止当前的接收(会把RxferCount清零) */
HAL_UART_AbortReceive_IT(&huart1);//放弃串口1中断
HAL_UART_Receive_DMA(&huart1, (uint8_t *)&ReBuff,5);//开启串口1中断
}
/* USER CODE END USART1_IRQn 1 */
}
这里最后放弃串口1中断,是因为不放弃的话,比如串口1接收到长度为4的信号stop,这时候主函数中判断已经发生,且已经将接收缓冲数组中的值清零,但由于没有放弃串口1中断,导致继续接收的数据没有从缓冲数组的首位开始写,使得数据紊乱。所以需要重新关闭再开启接收中断。
至此,软件部分编写完毕。
4.ATK-LORA-01无线串口的使用
1.使用该无线串口前,需要通过软件调整该串口的参数配置。
2.步骤如下:
(1)用UBS-TTL串口连接无线串口进行参数配置
连接MD0引脚后,在上位机按下
等待出现框中回复时,可进行配置,再按下“保存配置”时,配置成功!
注意!!!通讯中的两个ATK-LORA-01模块都要配置相同信道、波特率、地址、功率。
上位机软件如下:
通过网盘分享的文件:上位机软件.rar
链接: https://pan.baidu.com/s/1qQxlQT5BfZvN-iksfvM45g 提取码: ua25
总结:
我们学习知识,要理解运用,将所用到的知识化为自己的知识才是重点。此外,串口通常只是单对单的通信,这一点上我认为不如IIC,希望这篇文章能够帮助到大家,谢谢!
文章程序源码我发在下面了,需要自取。
通过网盘分享的文件:DMA-test1
链接: https://pan.baidu.com/s/1HVK7Me02YbYHvHsmysKrFA 提取码: ike8