嵌入式STM32入门之STM32中断——点灯实验、串口通信
STM32中断实验
一、前言
紧接上回实验,我们介绍了STM32的串口通信实验,学习了串口协议和RS-232标准,RS232电平与TTL电平的区别,以及"USB/TTL转232"模块(以CH340芯片模块为例)的工作原理。这里使用的STM32串口通信都是简单的通信,只需要STM32不断发送Hello Windows而已,接下来我们来介绍利用中断来点亮LED灯、以及可以完成简单交互的串口通信(依然使用USB转接口)。
二、实验要求
- 用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
- 采用串口中断方式重做上周的串口通信作业,分别实现:1)当stm32接收到字符“s”时,停止持续发送“Hello Windows!”; 当接收到字符“t”时,持续发送“Hello Windows!”(提示:采用一个全局标量做信号灯);2)当stm32接收到字符“stop stm32!”时,停止持续发送“Hello Windows!”; 当接收到字符“go stm32!”时,持续发送“Hello Windows!”(提示:要将接收到的连续字符保存到一个字符数组里,进行判别匹配,写一个接收字符串的函数。
三、中断介绍
与51单片机的中断基本相似,只是在51中我们的中断不是那么复杂,相对来说STM32的中断更加复杂。
M32 中断非常强大,每个外设都可以产生中断,所以中断的讲解放在哪一个外设里面去讲都不合适,这里单独抽出一章来做一个总结性的介绍。
中断:把正常正在运行的程序打断,运行中断服务函数,运行完之后再回到主程序。
**外部中断,体现在外设水平,系统异常,体现在内核水平。**中断就是异常,异常就是中断。
中断由NVIC这个外设来控制。(下列会特意说明STM32CuBe怎么设置)
NVIC:嵌套向量中断控制器,属于内核外设,管理着包括部分内核和片上所有外设的中断相关的功能。 两个重要的库文件:core_cm3.h(定义所有内核上面的寄存器,NVIC寄存器就在这里面)和misc.h(相关的函数)。
对于中断而言,最重要的是优先级,由NVIC->IPRx(中断优先级寄存器)设定。
介绍一下抢占优先级、响应优先级的区别:
- 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
- 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
- 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
- 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
所以,有中断的嵌套:
除此之外还要注意:
打断的情况只会与抢占优先级有关, 和响应优先级无关!
一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
四、CuBe实现中断点灯
用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替,利用杜邦线的连接断开)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
- 核心板为STM32F103C8T6最小核心板,设置PA1端接开关,PB5接LED
- 对于按键电路,设置上拉式按键
- 按键按下,引脚PA1读到低电平;按键释放,引脚PA1读到高电平 LED灯的触发方式
- 按键按下瞬间,形成下降沿;按键释放瞬间,形成上升沿

(1)新建工程
与原来相同,选择自己使用的芯片
(2)设置管脚
- 设置指示灯LED引脚PB5,设置引脚模式为输出模式GPIO_Output
- 设置按键引脚PA1,设置引脚为外部中断功能,PA1与外部中断线EXIT1连接GPIO_EXIT1
- 对于LED对应的PB5管脚,默认设置即可,名字设为LED

设置PA1为上升沿触发,并且将其重命名为User Label处设置名字为 A1_NETI
对于开关对应管脚PA1,设置其触发方式为上升沿触发
External Interrupt Mode with Rising edge trigger detection——上升沿
External Interrupt Mode with Falling edge trigger detection——下降沿
External Interrupt Mode with Rising/Falling edge trigger detection——上升沿和下降沿

注意!!!!
在这里的命名与代码的部分是相互对应的,所以各位在参考的时候自定义了命名,找到代码里面相对应的部分修改即可。
(3)设置NVIC
使能对应的外部中断线,点击Enabled
(4)配置中断优先级
(大多数情况下不必设置中断优先级,直接使用中断编号设置的默认中断优先级)
(5)时钟设置
这里设置了36M
(6)代码撰写
这里稍微有一点点复杂,我参考了一些博客,发现这里讲解都不是很清楚。
查找:在Keil文件中的STM32xxx_gpio.c(具体名字我就不再明说)文件可以找到中断服务函数
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
当捕获到上升沿,触发中断,就会进入到这个函数里面,然后就会执行HAL_GPIO_EXTI_Callback(GPIO_Pin)函数,此函数为回调函数,我们打开可以发现前面有个weak,前面的 __weak 表示此函数为虚函数,需要用户重写的。
那么我们在main.c文件中找个地方重新写一下。
位置在main函数下方。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if( GPIO_Pin == A1_EXTI_Pin)//判断外部中断源
{
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);//翻转LED状态
}
}

这里可能放进去会报错,但是你编译之后会发现没有错,之后就会消除错误的。之后将其烧入即可:
烧录过程我就不再描述。
(7)实践
电路连接:
USB转接口(与之前相同)
LED长脚——3V3
LED短脚——PB5
PA1——母对母杜邦线(这里控制其接3.3V或者接GND)
烧录之后:
五、中断方式实现串口通信
之前我们简单的学习了串口通信,今天我们要介绍使用中断的方式去实现
(1)题目要求
完成一个STM32的USART串口通讯程序(查询方式即可,暂不要求采用中断方式),要求:
1)设置波特率为115200,1位停止位,无校验位;
2)STM32系统给上位机(win10)连续发送“hello windows!”。win10采用“串口助手”工具接收。
根据题目,我设置,串口自动发送Hello windows!,如果接收到其他数据,则返回其他数据,之后继续发送Hello windows
(2)工程设置
1、设置RCC
设置高速外部时钟HSE,选择外部时钟源
2、设置串口
1)点击USART1
2)设置MODE为异步通信
3)基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1,接收和发送都使能
4)GPIO引脚设置 USART1_RX/USART_TX(这里一般自动设置好了)
5) NVIC Settings 一栏使能接收中断
3、时钟设置
之后导出Keil文件并打开。
(3)代码撰写
在main.c和usart.c中添加头文件#include “stdio.h”
之后,在usart.c文件中,添加如下代码,进行重定义
/* USER CODE BEGIN 1 */
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#if 1
//#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0x0001);
return ch;
}
#endif
/* USER CODE END 1 */

在main.c主函数中,添加发送数据
/* USER CODE END WHILE */
printf("Hello windows!\r\n");
HAL_Delay(500);
/* USER CODE BEGIN 3 */
在main.c中添加如下定义,用来接收串口数据
uint8_t aRxBuffer; //接收中断缓冲
uint8_t Uart1_RxBuff[256]; //接收缓冲
uint8_t str1[20] = "s";
uint8_t str2[20] = "t";
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
uint8_t cAlmStr[] = "数据溢出(大于256)\r\n";
添加开启接收中断的语句
这里记得添加在主函数中,不是在外面,!!!注意:在这里添加的时候如果你添加的位置不对运行之后会报错,所以如果在你添加之后编辑有错误,那么很大可能是你添加的位置出错。!!
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
/* USER CODE END 2 */

在main.c下部添加中断回调函数
/* USER CODE BEGIN 4 */
/**
* @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);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_UART_TxCpltCallback could be implemented in the user file
*/
if (strcmp(Uart1_RxBuff, str1) == 0) flag = 0;
if (strcmp(Uart1_RxBuff, str2) == 0) flag = 1;
//if(Uart1_RxBuff[0]=='g') flag = 1;
//if(Uart1_RxBuff[0]=='s') flag = 0;
if(Uart1_Rx_Cnt >= 255) //溢出判断
{
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer; //接收数据转存
if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
{
HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空数组
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
}
/* USER CODE END 4 */

代码思路解释
这里其实主要的就是对于中断函数的重写,定义出一个中断接收的数据,在中断函数判断接收的数据是否为s,t,或者是go STM32,stop STM32,这里无法直接判断,就是因为类型不一样,无法使用if语句进行==判断,所以利用字符复制判断是否相等即可,所以我换了个思路,当复制的字符相等的时候,标志符就为1,这样通过0,1来判断终止。
(4)执行
这里点击编译就可以了:

出现警告但是编译没有问题,将其烧录一下:
进入XCOM串口试验:
中断串口通信
注意:这个是我导入的视频,如果无法观看请到我的主页公开视频查看!!!
六、总结
对于本次实验,在整体上其实并不是很难,主要是复现之前的操作。注意一定就是如果无法正常实现,那么肯定就是哪里没有注意细节上 的东西,中断方式不必等待数据的传输过程,只需要在每字节数据收发完成后,由中断标志位触发中断,在中断服务程序中放入新的一字节数据或者读取接收到的一字节数据。
七、参考
更多推荐

所有评论(0)