在hal库使用DMA方式实现对于stm32的串口通信
stm32 在给pc机发送不断的hello windows语句时,我们可以使用中断服务程序来反向控制,是否继续发送,这里使用‘*’代表继续发送‘#’表停止发送。
首先我们需要对应的环境,使用hal库,我们就需要cubemx,可以在st公司官网上下载。我们使用的flymcu只能烧录软件,不能发送信息,所以还需要下载sscom 串口助手来帮助调试。
首先让我们进入cubemx,并且新建一个工程。
选择我们使用的stm32f103c8芯片,
对于DMA设置,DMA实现的是直接对于内存的操控,在操控工程中完全不需要cpu参与,以此可以极大的解放cpu算力,让其可以进行其他工作。我们只需要在一开始设置DMA时候设置cpu就可以了,其余则完全不需要cpu参与,同时DMA由于是直接内存操作,也能让程序执行时间更加简短。
下面我们进入cubemx的对于DMA的相关设置。
首先在cubemx上选择你的芯片
然后再RCC里面设置高速时钟
设置usart1串口数据模式
然后使能串口
然后设置DMA设置,添加两个通道
最后直接create code 然后直接使用keil5打开它
上面就是项目位置 和名称。
这样直接创建代码就行。
直接打开main.c
在代码main函数的while 循环中,就是我们需要写入的输出函数,
但是在这之前,我们需要定义全局变量,把我们预计要发送的语句提前定义上去。
char c;//开始指令 A:停止 B:开始
uint8_t message[]=" hello windows\n";//输出目标信息
char error[]="commanderror\n";
char tips1[]="startcmd...\n";
char tips2[]="stopcmd...\n";
char flag=1;//中断标志 #:停止发送 *.开始发送
好的,第一个c 表示pc机给stm32 发送的变量,是用来触发中断函数的。可以看到message 就是我们要不断发送的hello windows,第二个error是表示在指令不存在时的回馈,tips1 表示*继续或者开始发送。
tips2 则表示# 停止发送。
下面的flag=1时表示是否在发送的状态,1为发送,2为停止。
这里初始定义为1 ,所以串口会在一开始就不断发送message。
接下来回到我们的while语句之前,我们还需要定义中断服务函数
如下
这里还需要解释一下为什么要将message数据类型定义为uint8_t
在串口通信中,数据通常是按照字节(8位)来传输的。
使用 uint8_t
(无符号8位整数)数据类型是为了确保跨多种平台和编译器的兼容性。
当我们将数据转换为 uint8_t
,我们确保数据在所有系统上都是8位的,无论该系统的体系结构如何。这样,不论是在8位、16位、32位还是64位的系统上,该数据类型都能正确地处理8位的数据。
此外,uint8_t
是无符号的,这意味着其所有的8位都用于表示数值,这样可以在传输单个字节时,提供0-255的全部取值范围。因此,它经常被用来在串口通信(Serial Communication)中传输数据。
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1); //设置变量为c的中断,也就是c会承接一个指令执行我们的中断服务函数
//在中断服务函数里面,我们可以设置根据c的不同指令出现的不同操作,比如让flag变化 注意flag变化会直接影响是否正常发送
*好的,让我解释这三个量是什么,首先,这个函数是一个中断函数,它可以让程序在接受数据时还可以执行其他操作。
第一个&huart1 这是一个指向uart 结构体的指针。
第二个(uint8_t )&c 首先&c表示取c的地址,也就是它的指针,前面的括号表示将这个指针强制类型转换为uint8_t 这是一个中断函数要求的类型具体为什么这么要求可以看上文。
第三个 ‘ 1’ 表示要接受的字符长度,同时他也是个条件,当接收到对应长度的字符,此中断才会启用。
接下来说明启动中断过后会如何。
首先,中断条件一旦满足,就会进入中断服务函数,这个函数内,我们可以实现正常的所有函数操作,比如最基本的条件判断if语句,使用这个语句我们就可以判断是否继续发送。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//当输入的指令为#时,发送提示并改变flag
if(c=='#')//这个地方就是设置你需要的开始结束条件,我这里设置的停止条件是#
{
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF);
}
//当输入的指令为*时,发送提示并改变flag
else if(c=='*'){
flag=1;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF);
}
//当输入不存在指令时,发送提示并改变flag
else {
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&error, strlen(error),0xFFFF);
}
//重新设置中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1); //这样中断就不止执行一次了,只需要在中断服务末尾再使用中断就行。
}
好的,可以看到里面有一个transmis函数,这个就是stm32 发送到pc机的函数,我们可以看到,在c分别为#和*时,我们直接在if语句里面发送给pc机了tips1 和tips2 以及error语句。
那么怎么操控我们的message语句呢。
在主函数的while 循环中,我们这样写。
//code start
if(flag==1)
{
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
HAL_Delay(1000);
}
//每次发送完成后检测,如果变量c进入到目标长度字符,就会触发中断服务函数,再利用中断服务来操作是否继续发送所需字符。
好的,可以看到在主函数中,我们使用if语句判断flag是否为1来判断需不需要发送,而我们又将flag定义为全局变量,各个函数的数值改变flag都会导致所有的flag数值改变,所以我们只需要在中断服务函数里面,不断给flag赋值1或者0 就行,只要赋值1 ,则全局变量改变,导致主函数flag数值变化,再导致我们继续发送。
**但是注意在中断服务函数的末尾我们还需要定义一次中断函数,因为一个中断函数只会中断一次,
导致的是我们只能执行一次指令,所以在中断服务函数在定义一次,就可以一直执行指令。**
总体的顺序如下。
定义中断
stm32 发送message
检测中断,检测到变量c出现对应长度的字符。
触发中断>进入中断服务函数,实现我们需要的功能
中断服务函数末尾再次定义中断。
这样达成一个循环。
在烧录程序之前,我们线使用keil的逻辑分析仪看一下usart1串口的变化情况。
如下
然后我们使用sscom串口助手在pc上进行调试。
进去先打开串口,可以看到stm32开始不断发送hello windows语句。
这是因为我们给flag初始赋值为1.
我们先发送#,使其停止。
可以看到我们收到了预先设定的stopcmd语句,这代表中断服务程序被正确的执行了。
我们再使用* 在继续发送。
可以看到我们先收到 设定的startcmd 语句 然后stm32 开始不断向pc机发送我们需要的hello windows语句。
由此可以表示我们程序完全成功。
参考文献
-
https://www.pianshen.com/article/8285571527/
-
https://blog.youkuaiyun.com/qq_47281915/article/details/121053903
-
https://blog.youkuaiyun.com/qq_47281915/article/details/121063896