引言
前面我们使用轮询的方式实现了STM32与计算机的串口通讯案例,实现了STM32与计算机之间经过下载器中内置的USB转串口进行数据的发送与接收。本次,我们准备换一种方式对该案例进行二次实现,即利用串口中断的方式来实现数据的接收功能 ,这样可以减少CPU阻塞,提高执行效率。 当然,本次案例将基于上一个轮询方式实现的串口通讯案例来进行实现,因此如果大家不清楚的话可以参考下面博客直接跳转至轮询串口通讯案例文章中。USART_串口通讯轮询案例(一)(寄存器实现)-CSDN博客https://blog.csdn.net/2301_79475128/article/details/145222170?spm=1001.2014.3001.5501
一、案例需求描述
二、硬件电路设计与分析
由于本次案例和上次的案例大部分都相同,所以这里就不再赘述。大家如有不清楚可点击上面附上的相关文章链接直接跳转即可,感谢支持!
三、软件设计
根据前面轮询案例的分析,我们发现在进行字符串收发的时候形成的循环阻塞相对会多一些,一方面有单字符内部接收时的轮询、另一方面还有我们循环每一次的单字符接收,这样的轮询接收字符的方式就会大大降低CPU的执行效率。
当然由于发送和接收的中断使用是类似的,所以本次我们就只讲接收中断的使用,关于发送利用中断处理的思路大家可以依照接收的中断来自己实现。
3.1 相关寄存器
前面轮询案例已经介绍了串口主要使用的一些寄存器,这里我们将使用中断来进行串口通讯,所以避免不了的是开启串口相关中断使能,按照前面介绍的寄存器,如果看得仔细的朋友应该能发现,在控制寄存器 中有一位就是表示的串口中断使能,我们看看手册中串口的控制寄存器便知:
显然,在控制寄存器USART_CR1中 包含了五个串口中断使能位。我们在手册中找到关于串口中断的介绍部分,能够发现其实32芯片提供的串口中断一共是有8个,也就是说还有几个中断使能应该是在另外的串口控制寄存器中。如下图
当然,这个手册中关于接收数据就绪可读中断请求标志字母写错了,应该是RXNEIE哈,大家稍微注意一下就行。我们当前是为了借助接收中断来接收计算机发给32芯片的数据 ,所以本次我们将会使用RXNE以及IDLE的中断请求,对应在寄存器上要开始的使能就是RXNEIE位和IDLEIE位,如下图
3.2 程序实现
基于上一次轮询案例的代码,我们本次需要修改一些什么呢?既然使用中断去实现数据接收,所以我们首先要注释掉前面轮询接收数据的代码,大家可以自己再次创建新的工程去实现。
3.2.1 串口初始化函数的变化
然后使用中断 ,自然少不了中断的配置,在前面案例代码基础上,我们需要对串口初始化进行一些增加,即开启相关中断使能,然后调用NVIC库函数来分配优先级组与子优先级、设置各自优先级以及开启NVIC使能。这些配置比较简单,这里我们直接附上增加的代码,如有不懂,欢迎大家私信或者评论区讨论。
新加上后的USART_Init();串口初始化总代码如下:
// 初始化
void USART_Init(void)
{
// 1. 开启GPIO时钟 PA9 PA10
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
// 2. 设置GPIO工作模式
// PA9 TX 输出,复用推挽输出 MODE-11 CNF-10
// PA10 RX 输入,浮空输入 MODE-00 CNF-01
GPIOA->CRH |= GPIO_CRH_MODE9;
GPIOA->CRH |= GPIO_CRH_CNF9_1;
GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
GPIOA->CRH &= ~GPIO_CRH_MODE10;
GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
GPIOA->CRH |= GPIO_CRH_CNF10_0;
// 3. 串口配置
// 3.1 设置波特率
USART1->BRR = 0x271; // 115.2 Kpbs
// 3.2 开启模块及收发使能
USART1->CR1 |= USART_CR1_UE;
USART1->CR1 |= USART_CR1_TE;
USART1->CR1 |= USART_CR1_RE;
// 3.3 其他配置(字长、奇偶校验、停止位)
USART1->CR1 &= ~USART_CR1_M;
USART1->CR1 &= ~USART_CR1_PCE;
USART1->CR2 &= ~USART_CR2_STOP;
// 3.4 开启串口中断使能(接收中断使能、空闲中断使能)
USART1->CR1 |= USART_CR1_RXNEIE;
USART1->CR1 |= USART_CR1_IDLEIE;
// 4. NVIC的调用(优先级组分配、各优先级设置、使能)
NVIC_SetPriorityGrouping(3);
NVIC_SetPriority(USART1_IRQn, 2);
NVIC_EnableIRQ(USART1_IRQn);
}
3.3.2 发送单字符或字符串
由于本次我们只讲述如何使用中断来接收数据,因此这里我们不修改前面案例实现的发送一个字符或者字符串的函数,故这里直接附上参考代码,若有朋友不太清楚怎么实现,可点击文章开头附上的文章跳转查看前一次的案例实现。
// 发送一个字符
void USART_SendChar(uint8_t ch)
{
// 当发送的数据不为空时等待,TXE为1则可以继续写入数据
while ((USART1->SR & USART_SR_TXE) == 0)
{}
// 发送一个字符
USART1->DR = ch;
}
// 发送一个字符串
void USART_SendString(uint8_t *str, uint8_t size)
{
// 循环发送字符串中的每一个字符
for (uint8_t i = 0; i < size; i++)
{
USART_SendChar(str[i]);
}
}
3.2.3 接收单字符或字符串部分的变化
初始化做好后,我们中断使能已经开启,接下来我们就可以开始写中断处理函数的内容了。由于我们案例使用的串口是USART1,所以我们首先得去知道USART1的中断处理函数叫啥,前面讲述中断的时候说过:可以直接去启动文件中的汇编文件中的中断向量表中查到中断处理函数声明,即下图所示
找到USART1中断处理函数头以后,我们直接在串口源文件中,即usart.c中实现就行
// 串口中断处理函数
void USART1_IRQHandler(void)
{
}
现在要补充串口中断处理函数,首先我们要仔细分析一下这个中断产生的时机是啥?前面我们开启了两个串口中断的使能,分别是RXNEIE以及IDLEIE,意思是接收数据就绪中断和总线空闲(出现空闲帧)中断,即甭管是俩啥中断先不说,我们肯定可能触发两种中断请求。所以呢,咱编写中断处理函数首先要进行中断类型判断,判断的方法显然就是看RXNE或者IDLE是否被置1了,因为这俩中断的触发就是分别由于可接收数据非空和检测到总线空闲嘛,所以说这里首先我们写俩判断分别判断RXNE和IDLE是否被置1,参考代码如下
// 串口中断处理函数
void USART1_IRQHandler(void)
{
// 判断中断类型
if (USART1->SR & USART_SR_RXNE) // RXNE置1则执行
{
}
else if (USART1->SR & USART_SR_IDLE) // IDLE被置1则执行
{
}
}
然后,我们想想,RXNE置1时也就是发生RXNE中断,即接收数据就绪了,可以开始接收数据了,所以我们这时候直接接收一个字符即可。当然哈,这里为了方便起见,我们直接创建一个全局变量buffer[100]来存放字符,同时创建一个全局变量size记录数据长度。然后直接用buffer[]存放接收到的字符、同时size++就好了。
IDLE置1时也就是发生了IDLE中断,即检测到空闲帧了,可以结束数据接收了,所以我们这时候将IDLE清零,同时发送我们接收到的数据,接着将size清零 就OK了。当然,为了防止中断处理函数中过于笨重(因为在中断处理函数中调用发送接受的字符的话会涉及到循环等问题,这将使中断执行时间较长,其实会显得代码并不优雅),因此我们这里在中断中借助一个全局标志位isToSend 来控制外部进行数据发送和相关的清零操作。
参考代码如下
1、全局变量的定义
2、中断处理函数中的完善
// 串口中断处理函数
void USART1_IRQHandler(void)
{
// 判断中断类型
if (USART1->SR & USART_SR_RXNE)
{
// 接收单个字符
buffer[size] = USART1->DR;
size++;
}
else if (USART1->SR & USART_SR_IDLE)
{
// 接收字符串
// 清除IDLE标志位
USART1->DR;
// 触发接收标志位
isToSend = 1;
}
}
3、main.c中的实现
3.2.4 整体测试
实现好中断处理函数以及main.c中的一些细节以后,实际上我们整个中断实现串口通讯的需求就基本完成了,现在我们可以编译烧录测试一下
显然,我们本次实现基本成功了,甚至可以发中文,只要保证串口助手这边的编码和VSCode编码一致就不会出现乱码。
当然我们发现这个过程比前面简单许多,主要还是因为本次我们没有重复前面编写的发送函数,所以大家有时间的话可以自己试试完整把代码都实现一遍,这样的话,相信大家会有新的收获以及更深刻的理解!
以上就是本次串口通讯中断案例的内容,祝愿我们都能坚持学习,得偿所愿,共同进步!
以上便是本次文章的所有内容,欢迎各位朋友在评论区讨论,本人也是一名初学小白,愿大家共同努力,一起进步吧!
鉴于笔者能力有限,难免出现一些纰漏和不足,望大家在评论区批评指正,谢谢!