STM32中断与DMA通信编程

一、中断与DMA通信原理

1.中断

1.1.什么是中断
中断是由内核外部产生的,一般由硬件引起,比如外设中断和外部中断等。
通常在CPU 执行程序时,由于发生了某种随机的事件(外部或内部),引起CPU暂时中断正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),来处理该事件,该事件处理完成后又返回被中断的程序继续执行。
其过程如下:
在这里插入图片描述
1.2.中断通道
微控制器片内集成了很多外设,对于单个外设而言,它通常具备若干个可以引起中断的中断源,而该外设的所有中断源只能通过指定的中断通道向内核申请中断。
例如STM32支持84个中断(16个内部,68个外部);16级可编程的中断优先级设置。
1.3.外部中断
EXTI(External interrupt/event controller)——外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

外部中断控制器结构框图如下:
在这里插入图片描述

2.DMA通信原理

2.1.DMA的基本定义
DMA,全称Direct Memory Access,即直接存储器访问。

DMA传输将数据从一个地址空间复制到另一个地址空间,-用于在外设与存储器之间以及存储器与存储器之间进行高速数据传输。DMA传输过程的初始化和启动由CPU完成,传输过程由DMA控制器来执行,无需CPU参与,从而节省CPU资源,提高利用率。

2.2.DMA数据传输的四个要素
① 传输源 :DMA数据传输的来源
② 传输目标:DMA数据传输的目的
③ 传输数量:DMA传输数据的数量
④ 触发信号:启动一次DMA数据传输的动作

当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时 达到传输终点,结束DMA传输 ,当然,DMA 还有循环传输模式 当到达传输终点时会重新启动DMA传输。  
也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输。
在这里插入图片描述
2.3.STM32的DMA控制器特点

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

2.4.DMA的主要特征
每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发,这些功能通过软件来配置。

  • 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推);
  • 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐;
  • 支持循环的缓冲器管理;
  • 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求;
  • 存储器和存储器间的传输、外设和存储器、存储器和外设之间的传输;
  • 闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标;
  • 可编程的数据传输数目:最大为65535。

2.4.DMA数据传输方式

2.4.1.普通模式
传输结束后(即要传输数据的数量达到零),将不再产生DMA操作。若开始新的DMA传输,需在关闭DMA通道情况下,重新启动DMA传输。
2.4.2.循环模式
可用于处理环形缓冲区和连续数据流(例如ADC扫描模式)。当激活循环模式后,每轮传输结束时,要传输的数据数量将自动用设置的初始值进行加载, 并继续响应DMA请求。

关于更多DMA通信原理的了解,可参考博客:https://blog.csdn.net/as480133937/article/details/104927922
小编理解有限,这里便不做过多说明。

二、中断控制LED灯点亮熄灭

本例使用的是外部中断控制一个LED灯的变化。

1.新建工程

打开STM32cubeMX程序,点击File下的New Project新建一个工程
在这里插入图片描述
Part Number处选择芯片,这里以STMC8T6为例,然后点击中间出现的一列芯片的信息,再点击Start Project就行了
在这里插入图片描述
点击System Core,进入里面的SYS,在debug那里选择Serial Wire
在这里插入图片描述
接下来就是配置时钟了,进入上面的RCC,将HSE那里设置为Crystal/Ceramic Resonator
在这里插入图片描述
来到Clock Configuration界面,把PLLCLK右边选上
在这里插入图片描述
接下来就是设置输出寄存器,进入GPIO界面,点击相应的引脚,选择GPIO_Output,我这里选择的是PA4
在这里插入图片描述
PA4配置如下:
在这里插入图片描述

2.配置外部中断

接着初始化一个GPIO端口模拟按键
我这里选择的是PB10,右击选择GPIO_EXTI10模式
在这里插入图片描述
在左边的GPIO Mode and Configuration中点击刚刚选择的PB10,配置如下图所示
在这里插入图片描述

3.配置中断优先级

NVIC Mode and Configuration界面下,点击NVIC,勾选图示位置,使能外部中断,并配置优先级(本例一个管脚,默认为0)
在这里插入图片描述

4.完成创建

接着在Project Manager–>Project下,配置好自己的项目名和路径,然后IDE那项改为MDK-ARM,版本根据自己的需求选择
在这里插入图片描述
进入Code Generator界面,勾选生成初始化.c/.h文件
在这里插入图片描述
最后点击GENERATE CODE,生成代码
在这里插入图片描述

5.编写中断函数

在刚才自定义的路径下,用MDK5打开生成的工程
在这里插入图片描述
我们可以在stm32f1xx_it.c文件中,找到已配置好的中断处理函数
在这里插入图片描述
接下来要将中断处理函数放到一个名为void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)的函数中即可,这是用户自定义的一个函数,也是我们唯一要编写的代码
代码如下:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	
  switch(GPIO_Pin)
  {
    case GPIO_PIN_10: 
		{
			HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);	//LED为自定义的引脚名
		}
		break;
    default: break;		
  }
}

当然你也可以使用其他判断语句进行编写,例如if语句,这里不多做阐述
加入main.c函数如下:
在这里插入图片描述

6.程序烧录

添加代码后,点击左上角的按钮进行编译,生成相应的.hex文件
在这里插入图片描述
接着打开mcuisp软件进行程序烧录:
选择刚刚生成的.hex文件,点击开始编程进行烧录
在这里插入图片描述
烧录成功
在这里插入图片描述

7.运行效果

由于使用c8t6最小系统板,板上没有多余的按键,所以使用杜邦线代替。
本例使用杜邦线将PB10引出,PB10每从高电平到低电平,LED灯翻转一下
在这里插入图片描述

三、使用串口中断发送“Hello windows!”

上篇博客我们在while循环里连续发送“Hello windows!”,但是这样程序执行效率低,本例我们使用串口的方式来发送数据。

1.新建工程

关于时钟和相关GPIO的配置我这里就跳过了,前面已经详细描述了,这里主要是配置USART1的中断
点击Connnectivity,选择USART1,Mode选择Asynchronous(异步模式)
在这里插入图片描述
在该界面下选择NVIC Settings,勾选中断
在这里插入图片描述
在HAL库中,串口的接收中断、发送中断都需要开启这个中断
中断配置完成,接下来就是生成工程代码,这里也不再赘述了。

2.编写用户函数

找到刚刚创建的工程,利用MDK5打开它
我们使用一个数组来保存我们需要发送的数据:

uint8_t temp[]="Hello windowns\n";

回调函数HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)

uint8_t i;

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(i<=50)
	{
		HAL_UART_Transmit_IT(&huart1, temp, sizeof(temp));
		i++;   //每调用一次,i++,当调用超过50次时,不再发送数据
	}	
}

我们在回调函数中发送再次发送相同的数据,在合适的条件下结束发送。
在这里插入图片描述
在main函数中调用HAL_UART_Transmit_IT(&huart1, temp, sizeof(temp))将数组中的内容发送出去,发送完成后,自动调用发送完成回调函数
在这里插入图片描述

3.程序烧录

添加代码后,点击左上角的按钮进行编译,生成相应的.hex文件
在这里插入图片描述
使用mcuisp软件,选择刚刚生成的.hex文件,点击开始编程进行烧录
在这里插入图片描述

4.运行效果

打开野火串口调试助手程序
在这里插入图片描述
可以看到上位机接收了832个字节就不再接收了,实验成功。

四、采用串口DMA方式发送“Hello windows!”

1.新建工程

点击System Core->SYS,在debug那里选择Serial Wire
在这里插入图片描述
进入RCC,将HSE设置为Crystal/Ceramic Resonator
在这里插入图片描述
接着点击Connnectivity,选择USART1,Mode选择Asynchronous,此时在下面就可以看到配置好的一些基础参数:波特率为115200 Bits/s;传输数据长度为8 Bit;无奇偶检验;停止位为1。
在这里插入图片描述
点击NVIC Settings ,勾选使能接收中断
在这里插入图片描述
接下来进行DMA设置:
在同一界面下点击DMA setting->Add添加通道
在这里插入图片描述
添加USART_TXUSART_RX,配置如下:
在这里插入图片描述
来到Clock Configuration界面,点击HSE右边,PLLCLK右边,设置APB1分频器为/2
在这里插入图片描述
到这工程配置基本完成,生成工程的步骤这里便不再赘述。

2.添加代码

打开刚刚创建的工程,找到mian.c文件,定义一个数组来保存我们需要发送的数据:

uint8_t Senbuff[] = "Hello windows!\n";  //定义一个数组,将想要发送的数据放到数组中去

在这里插入图片描述
在main函数的while循环语句中加入以下代码:

HAL_UART_Transmit_DMA(&huart1,Senbuff, sizeof(Senbuff));
HAL_Delay(1000);

在这里插入图片描述

3.程序烧录

添加代码后,点击左上角的按钮进行编译,生成相应的.hex文件
在这里插入图片描述
使用mcuisp软件,选择刚刚生成的.hex文件,点击开始编程进行烧录
在这里插入图片描述

4.运行效果

打开野火串口调试助手程序
在这里插入图片描述
可以看见,上位机持续接收“Hello windows!”,实验成功。

五、小结

通过这次实验,可以更加体会到STM32CubeMX给我们提供初始化代码的便捷,深入了解了stm32中断和DMA通信的原理,在过程中,也遇到了代码烧录不成功、LED灯持续闪烁等问题,但通过同学的帮助,最终顺利解决,完成实验。

六、参考链接

1.https://blog.csdn.net/weixin_52288941/article/details/121016740?spm=1001.2014.3001.5501
2.https://blog.csdn.net/weixin_52288941/article/details/121046513?spm=1001.2014.3001.5501
3.https://blog.csdn.net/as480133937/article/details/104827639/

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值