2021年11月5日 stm32之中断和DMA通信
一、什么是中断
1.中断即打断,实至CPU再执行当前程序时,由于系统出现了某种需要处理的紧急情况,CPU暂停正在执行的程序,转而去执行另一段特殊程序来处理的出现的紧急事务,处理结束后CPU自动返回到原先暂停的程序中去继续执行,这种执行过程由于外界的的原因被中间打断的情况成为中断。
2.中断的作用:
中断使得计算机系统具备应对对处理突发事件的能力,提高了CPU的工作效率,如果没有中断系统,CPU就只能按照原来的程序编写的先后顺序,对各个外设进行查询和处理,即轮询工作方式,轮询方法貌似公平,但实际工作效率却很低,却不能及时响应紧急事件。

3.中断编程
在配置每个中断的时候一般有 3个编程要点:
1、使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都
由串口控制寄存器的相关中断使能位控制。
2、初始化 NVIC_InitTypeDef结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求。
3、编写中断服务函数
在启动文件 startup_stm32f429_439xx.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只
是初始化中断向量表。实际的中断服务函数都需要我们重新编写,中断服务函数我们统一写在 stm32f4xx_it.c这个库文件中。关
于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口直
接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。

二、什么是NVIC
NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M3内核里面的NVIC进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC是 Cortex-M3 的 NVIC 的一个子集。
优先级分组真值表:

2.优先级定义:
在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx(在 F429 中,x=0…90)用来配置外部中断的优先级,IPR宽度为
8bit,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。
三、STM32CUBEMX生成中断点灯代码
1.首先打开STM32CUBEMX,新建一个工程
选择芯片*STM32F103C8
2.点击开始工程,配置系统调试接口 SYS,选择 Serial Wire

3.配置外设 RCC ,选择 HSE (外部高速时钟)为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)

4.设置LED灯引脚为PA1的输出模式 GPIO_Output,按键引脚为PB15的外部中断模式GPIO_EXIT1

LED对应PA1管脚,User Label处设置为LED

开关对应PB15管脚,GPIO mode处设置为上升沿触发,User Label处设置为 KEY

注: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 上升/下降沿
使能外部中断线,点击Enabled

设置时钟树,选择PLLCLK,修改HCLK为36MHz

设置项目名称和路径,修改Toolchain/IDE为MDK-ARM

打开keil,打开建好的工程LED,在main.c文件中添加以下代码
/*
* @brief EXTI line detection callbacks.
* @param GPIO_Pin: Specifies the pins connected EXTI line,
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if( GPIO_Pin == KEY_Pin ) // 判断外部中断源
{
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 翻转LED状态
}
}

注:本任务只设置一个外部中断引脚,因此不需要判断多个引脚。如果系统中设置了多个外部中断,建议使用 switch-case 进行多分支判断。

打开串口调试助手:


实际效果:

四、STM32CUBEMX生成中断通信代码
1.创建新工程
点击 File->New Pioject或ACCEE TO MCU SELECTOR,创建新工程
选择 STM32F103C8 芯片,点击 Start Project 进入工程

设置时钟RCC,点击HSE,选择Crystal/Ceramic Resonator

设置USART1,点击Mode,选择Asynchronous

设置中断,在NVIC Settings中点击Enabled。
点击Clock Configuration选择HSE和PLLCLK,修改HCLK值

设置项目名称和路径,修改Application Structure为Basic,Toolchain/IDE为MDK-ARM,生成项目

2.程序编写
1)stm32f1xx_hal.c文件
添加头文件
#include <stdio.h>
extern UART_HandleTypeDef huart1; //声明串口

重写fget和fput函数
/**
* 函数功能: 重定向c库函数printf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}

2)main.c文件
在主函数main的while循环中添加发送数据
printf("hello windows!\r\n");
HAL_Delay(1000);

添加定义,接收串口数据
#include "stdio.h"
#include <string.h>
#define RXBUFFERSIZE 256 //最大接收字节数
char RxBuffer[RXBUFFERSIZE]; //接收数据
uint8_t aRxBuffer; //接收中断缓冲
uint8_t Uart1_Rx_Cnt = 0; //接收缓冲计数
在主函数main中添加开启接收中断的语句
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);

添加中断回调函数
/* USER CODE BEGIN 4 */
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(RxBuffer,0x00,sizeof(RxBuffer));
HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF);
}
else
{
RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer; //接收数据转存
if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
{
HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
Uart1_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
}
/* USER CODE END 4 */


3)波形观察
打开Serial Windows中的UART #1窗口和逻辑分析仪,添加要观察的端口,修改各个显示方式,将Display Type处设置为Bit。
3.烧录操作

4.实际效果

五、DMA通信
1.DMA介绍
DMA,全称Direct Memory Access,即直接存储器访问。
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。

DMA数据传输方式:

STM32的DMA控制器
① STM32F411微控制器具备两个DMA控制器:DMA1和DMA2,每个控制器有8个数据流,每个数据流可以映射到8个通道(或请求);
② 每一个DMA控制器用于管理一个或多个外设的存储器访问请求,并通过总线仲裁器来协调各个DMA请求的优先级;
③ 数据流(stream)是用于连接传输源和传输目标的数据通路,每个数据流可以配置为不同的传输源和传输目标,这些传输源和传输目标称为通道(Channel) ;
④ 具备16字节的FIFO。使能FIFO功能后,源数据先送入FIFO,达到FIFO的触发阈值后,再传送到目标地址。

2.创建DMA工程
基本步骤:
点击 File->New Pioject或ACCEE TO MCU SELECTOR,创建新工程
选择 STM32F103C8 芯片,点击 Start Project 进入工程
设置时钟RCC,点击HSE,选择Crystal/Ceramic Resonator
设置USART1,点击Mode,选择Asynchronous,在NVIC Settings中点击Enabled(中断)
在USART1中,点击DMA Settings的Add,添加USART_RX 和USART_TX,传输速率设置为中速Medium,模式设置为Normal,右侧选择Memory;在最右侧的System view中选择DMA,点击Add,添加MEMTOMEM。



设置时钟源,点击Clock Configuration选择HSE和PLLCLK。

设置项目文件,修改Application Structure为Basic,Toolchain/IDE为MDK-ARM。


3.编写编程
main.c文件的主函数main
uint8_t Senbuff[] = "Goodbye\r\n"; //定义数据发送数组
主函数main的while循环
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
HAL_Delay(1000);


3.调试和野火操作
1)连接
使用USB-TTL直接进行串口下载,将USB-TTL的GND和3.3V接入STM32系统板的GND和3.3V,然后TXD和RXD分别接入A10和A9引脚。
2)烧录
BOOT0置1,BOOT1置0,打开mcuisp,选择文件路径,开始烧录。

4.实际效果

六、总结
本次实验我发现DMA运输并不占CPU资源,工作相比中断更加高效,也学会了了解了stm32中断、DMA通信原理和编程方法,收获很丰富,这几次实验的成果让我终于感觉像是个入门级的嵌入式菜鸟,路很长,但步伐很沉稳。
更多推荐



所有评论(0)