文章目录
一、原理
1、中断介绍
中断通常被定义为一个事件,该事件能够改变处理器执行指令的顺序。这样的事件与 CPU 芯片内外部硬件电路产生的电信号相对应。
中断是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。
2、中断作用
速度匹配:可以解决快速的CPU与慢速的外部设备之间传送数据的矛盾。 分时操作:CPU可以分时为多个外部设备服务,提高计算机的利用率。
实时响应:CPU能够及时处理应用系统的随机事件,增强系统的实时性。 可靠性高:CPU可以处理设备故障及掉电等突发事件,提高可靠性。
3、中断优先级
多个中断同时出现时,处理器先响应高优先级的中断 低优先级中断的ISR执行时,可以被高优先级中断再次打断 ISR比App
Code拥有更高的执行优先级
二、中断LED实现
以中断方式模拟开关实现LED灯的亮灭
1、设计思路
以PA1模拟开关
PA1接高电平 | 开关按下LED灯亮 |
---|---|
PA1接低电平 | 开关断开ED灯灭 |
2、创建新项目
(1)进入CubeMX界面,在New Project 中选择ACCESS TO MCU SELECTOR 进行创建项目,会弹出一个小的弹窗选择 NO即进入参数设置界面
(2)参数配置
外设设置
设置指示灯LED引脚PB5,设置引脚模式为输出模式GPIO_Output
设置按键引脚PA1,设置引脚为外部中断功能,PA1与外部中断线EXIT1连接GPIO_EXIT1
在这里插入图片描述
对于LED对应的PB5管脚,默认设置即可,名字设为LED
对于开关对应管脚PA1,设置其触发方式为上升沿触发
External Interrupt Mode with Rising edge trigger detection
上升沿
使能对应的外部中断线,点击Enabled
User Label处设置名字为 A1_EXTI
配置中断优先级
(大多数情况下不必设置中断优先级,直接使用中断编号设置的默认中断优先级)
时钟设置
这里设置了36M
生成工程文件
3、代码编写
当捕获到上升沿,触发中断,就会进入到这个函数里面
然后就会执行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状态
}
}
编译无错
4、电路连接
TXD | A10 |
---|---|
RXD | A9 |
GRD | GRD |
3V3 | 3V3 |
PB5 | LED灯短脚 |
3v3 | LED长脚 |
PA1 | 3V3 LED灯亮 |
PA1 | GED LED灯灭 |
5、实现结果展示
三、中断串口通信
1、字符实现串口通信
当stm32接收到字符“s”时,停止持续发送“hello windows!”; 当接收到字符“t”时,持续发送“hello windows!”
(1)设计思路
采用一个全局标量做信号灯,本次实验选用flag作为全局变量
flag=0 | 停止发送 |
---|---|
flag=1 | 恢复发送 |
指令s | 恢复发送 |
指令t | 停止发送 |
(2)创建新项目
1.进入CubeMX界面,在New Project 中选择ACCESS TO MCU SELECTOR 进行创建项目,会弹出一个小的弹窗选择 NO即进入参数设置界面
2.设置RCC
3.设置SYS
4.设置USART
5.设置NVIC
6.创建项目
项目创建后选择Open Project 进入keil 配置代码
(3)代码编写
1.在main函数前定义全局变量
char c;//指令 s:停止 t:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 0:停止发送 1.开始发送
2.main函数中设置接收中断
函数说明:
函数原型
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能
功能:串口中断接收,以中断方式接收指定长度数据。 大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。
接收到数据时,会触发串口中断。 再然后,串口中断函数处理,直到接收到指定长度数据
而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)
参数
UART_HandleTypeDef *huart UATR的别名
huart1 *pData 接收到的数据存放地址
Size 接收的字节数
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);
3.main函数中的while循环里面添加传输代码
if(flag==1){
//发送信息
HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF);
//延时
HAL_Delay(1000);
}
4.在main函数下面重写中断处理函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//当输入的指令为0时,发送提示并改变flag
if(c=='s'){
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF);
}
//当输入的指令为1时,发送提示并改变flag
else if(c=='t'){
flag=1;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF);
}
//当输入不存在指令时,发送提示并改变flag
else {
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips, strlen(tips),0xFFFF);
}
//重新设置中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);
}
6.main函数中全部代码
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <string.h>
void SystemClock_Config(void);
char c;//指令 s:停止 t:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 0:停止发送 1.开始发送
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
//设置接受中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);
//当flag为1时,每秒发送一次信息
//当flag为0时,停止
while (1)
{
if(flag==1){
//发送信息
HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF);
//延时
HAL_Delay(1000);
}
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//当输入的指令为0时,发送提示并改变flag
if(c=='s'){
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF);
}
//当输入的指令为1时,发送提示并改变flag
else if(c=='t'){
flag=1;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF);
}
//当输入不存在指令时,发送提示并改变flag
else {
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips, strlen(tips),0xFFFF);
}
//重新设置中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);
}
/* USER CODE END 4 */
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
6.编译烧录
(4)结果展示
当发送t
后不断输出“hello Windows
”
当发送s
停止发送
2、字符串实现串口通信
当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”(提示:要将接收到的连续字符保存到一个字符数组里,进行判别匹配。写一个接收字符串的函数
(1)设计思路
设计接收字符串的函数,将接收到的连续字符保存到一个字符数组里,进行判别匹配
(2)创建项目
(1)进入CubeMX界面,在New Project 中选择ACCESS TO MCU SELECTOR 进行创建项目,会弹出一个小的弹窗选择 NO即进入参数设置界面
设置高速外部时钟HSE,选择外部时钟源
设置串口
1)点击USART1
2)设置MODE为异步通信
3)基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1,接收和发送都使能
4)GPIO引脚设置 USART1_RX/USART_TX(这里一般自动设置好了)
5) NVIC Settings 一栏使能接收中断
时钟设置
之后导出Keil文件并打开。
(3)代码编写
printf函数设置
在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 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(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 */
编译无错烧录
烧录时boot0接1
(4)结果展示
四、串口DMA
1、DMA原理
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU,
CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,如果我们把这部分的CPU资源拿出来,让CPU去处理其他的复杂计算事务,是不是能够更好的利用CPU的资源呢?
因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B
不经过CPU的处理
2、创建项目
设置RCC
设置高速外部时钟HSE 选择外部时钟源
,
设置串口USART1
点击USATR1,
设置MODE为异步通信(Asynchronous
),基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1 接收和发送都使能GPIO引脚自动设置 USART1_RX/USART_TX
NVIC Settings
一栏使能接收中断
DMA
设置
点击DMASettings 点击 Add 添加通道,选择USART_RX USART_TX 传输速率设置为中速,DMA传输模式为正常模式,DMA内存地址自增,每次增加一个Byte(字节)
1DMA基础设置
右侧点击System Core 点击DMA
时钟设置
项目文件设置
点击Project Manager
创建工程文件
创建工程
GENERATE CODE
点击Open Project
进入keil进行代码编写
配置下载工具
新建的工程所有配置都是默认的 我们需要自行选择下载模式,勾选上下载后复位运行
3、代码编写
uint8_t Senbuff[] = "Hello world!"; //定义数据发送数组
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
HAL_Delay(1000);
4、结果展示
编译无错后,烧录
总结
本次实验难点主要在于字符串实现中断通信,在while语句中添加字符匹配判别来实现中断。对于中断有了进一步了解与加深。
参考
https://blog.csdn.net/qq_46467126/article/details/121055475?spm=1001.2014.3001.5506
https://blog.csdn.net/qq_47281915/article/details/121053903
https://blog.csdn.net/as480133937/article/details/104827639/
https://blog.csdn.net/qq_46467126/article/details/121076618
DMA