STM32中断与DMA通信编程

STM32中断与DMA通信编程

文章目录

一、题目内容

学习stm32中断、DMA通信原理和编程方法。使用stm32tubemx和HAL库分别完成以下编程练习:

  1. 用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
  2. 采用串口中断方式重做上周的串口通信作业。
  3. STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据。

二、CubeMX中断方式点亮LED灯

1、CubeMX工程

进入STMCubeMX,选择新项目:
STM32中断与DMA通信编程
STM32中断与DMA通信编程
点击sys,将debug选项改为Serial Wire。
STM32中断与DMA通信编程
在RCC里的HSE选择Crystal/Ceramic Resonator
STM32中断与DMA通信编程
STM32中断与DMA通信编程
STM32中断与DMA通信编程
对于开关对应管脚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上升沿和下降沿

User Label处设置名字为 A1_EXTI:
STM32中断与DMA通信编程
使能对应的外部中断线,点击Enabled
STM32中断与DMA通信编程
配置中断优先级
(大多数情况下不必设置中断优先级,直接使用中断编号设置的默认中断优先级)
STM32中断与DMA通信编程
时钟设置:设置为36M
STM32中断与DMA通信编程
project里把toolchain那里改为MDK-ARM,版本选择最新的就行了,项目名和项目路径自己选择
STM32中断与DMA通信编程
选择生成初始化文件,然后选择生成代码就好了
STM32中断与DMA通信编程

2、代码

在Keil文件中的gpio.c文件可以找到中断服务函数

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)

当捕获到上升沿,触发中断,就会进入到这个函数里面
然后就会执行HAL_GPIO_EXTI_Callback(GPIO_Pin)函数,此函数为回调函数,我们打开可以发现前面有个weak。
第561行前面的 __weak 表示此函数为虚函数,需要用户重写的。
STM32中断与DMA通信编程
那么我们在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状态
	}
}


STM32中断与DMA通信编程
编译一下,没有错
STM32中断与DMA通信编程
将代码烧录到C8T6核心板中。
STM32中断与DMA通信编程

3、电路以及效果

GPIO默认是3.3V高电平,当按下按键,GPIO变为低电平,此过程是下降沿。松开按键,GPIO又变为高电平,此过程为上升沿。因为设置的是上升沿中断,也就是松开按键的时候,灯会变化。
STM32中断与DMA通信编程

三、串口中断方式

1、中断

(一)什么是中断

STM32中断与DMA通信编程
中断通常被定义为一个事件,该事件能够改变处理器执行指令的顺序。这样的事件与 CPU 芯片内外部硬件电路产生的电信号相对应。中断是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。
中断分为同步中断和异步中断。同步中断:是当指令执行时由 控制单元产生的,之所以称为同步,是因为只有在一条指令终止执行后 CPU 才会发出中断。异步中断:是由其他硬件设备依照 CPU 时钟信号随机产生的。
中断数据传输方式:
STM32中断与DMA通信编程
中断程序执行过程:
STM32中断与DMA通信编程
中断响应过程:
STM32中断与DMA通信编程

(二)中断作用

STM32中断与DMA通信编程

(三)中断优先级

STM32中断与DMA通信编程
中断的类型:硬中断和软中断。硬中断:通过处理器中断信号线产生的中断。软中断:通过非法指令或特殊指令触发的中断。
中断优先级:多个中断同时出现时,处理器先响应高优先级的中断;低优先级中断的ISR执行时,可以被高优先级中断再次打断;ISR比App Code拥有更高的执行优先级。

2、工程设置

STM32中断与DMA通信编程
设置RCC:设置高速外部时钟HSE,选择外部时钟源
STM32中断与DMA通信编程
设置串口:
1)点击USART1
2)设置MODE为异步通信
3)基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1,接收和发送都使能
4)GPIO引脚设置 USART1_RX/USART_TX(这里一般自动设置好了)
5) NVIC Settings 一栏使能接收中断

STM32中断与DMA通信编程
STM32中断与DMA通信编程
时钟设置
STM32中断与DMA通信编程

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 */

STM32中断与DMA通信编程
STM32中断与DMA通信编程
在main.c中添加如下定义,用来接收串口数据

uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_RxBuff[256];		//接收缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
uint8_t	cAlmStr[] = "数据溢出(大于256)\r\n";



STM32中断与DMA通信编程
添加开启接收中断的语句

/* 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 */

STM32中断与DMA通信编程
编译成功,开始烧录
STM32中断与DMA通信编程

4、串口助手通信利用XCOM进行通信

利用XCOM进行通信
STM32中断与DMA通信编程

四、HAL库UART函数库介绍

UART结构体定义:UART_HandleTypeDef huart1;
串口发送/接收函数:

HAL_UART_Transmit();串口发送数据,使用超时管理机制
HAL_UART_Receive();串口接收数据,使用超时管理机制
HAL_UART_Transmit_IT();串口中断模式发送
HAL_UART_Receive_IT();串口中断模式接收
HAL_UART_Transmit_DMA();串口DMA模式发送
HAL_UART_Transmit_DMA();串口DMA模式接收

功能:串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)
串口中断函数:

HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少)
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
HAL_UART_ErrorCallback();串口接收错误函数

功能:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码,串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改。
串口查询函数:HAL_UART_GetState();判断UART的接收是否结束,或者发送数据是否忙碌。

五、DMA

1、DMA介绍

DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。
STM32中断与DMA通信编程
DMA原理:
DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。
在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。

2、DMA传输

DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:

外设到内存
内存到外设
内存到内存
外设到外设

普通模式:传输结束后(即要传输数据的数量达到零),将不再产生DMA操作。若开始新的DMA传输,需在关闭DMA通道情况下,重新启动DMA传输。
循环模式:可用于处理环形缓冲区和连续数据流(例如ADC扫描模式)。当激活循环模式后,每轮传输结束时,要传输的数据数量将自动用设置的初始值进行加载, 并继续响应DMA请求。
DMA传输参数: 数据的源地址,数据传输位置的目标地址, 传递数据多少的数据传输量,进行多少次传输的传输模式。
当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时 达到传输终点,结束DMA传输 ,当然,DMA 还有循环传输模式 当到达传输终点时会重新启动DMA传输。也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输。
DMA数据传输的四个要素:
① 传输源 :DMA数据传输的来源。
② 传输目标:DMA数据传输的目的。
③ 传输数量:DMA传输数据的数量。
④ 触发信号:启动一次DMA数据传输的动作。
DMA工作框图:
STM32中断与DMA通信编程

3、DMA方式的接口函数

STM32中断与DMA通信编程
STM32中断与DMA通信编程
STM32中断与DMA通信编程

六、串口DMA方式向上位机发送数据

1、设置工程

STM32中断与DMA通信编程
设置RCC
STM32中断与DMA通信编程
设置串口
STM32中断与DMA通信编程
使能中断
STM32中断与DMA通信编程
DMA设置:点击DMA Settings的Add添加通道,传输速率设置为中速Medium。
STM32中断与DMA通信编程
模式设置为Normal,右侧选择Memory
STM32中断与DMA通信编程
在System view下选择DMA
STM32中断与DMA通信编程
时钟设置
STM32中断与DMA通信编程

2、代码

在main.c文件添加代码

uint8_t Senbuff[] = "Hello world!";  //定义数据发送数组

STM32中断与DMA通信编程

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

STM32中断与DMA通信编程
烧录
STM32中断与DMA通信编程

3、串口发送数据

STM32中断与DMA通信编程

七、实验总结

通过这次实验我们学会了很多新的东西,也知道了DMA它在传输的过程不会占用CPU资源,可以边传输边运行其他任务,在实验过程中也遇到了许多的问题但是通过网上查找资料、大佬博客然后得到了解决,总之这次实验还是收获挺多的。

八、参考资料

https://blog.csdn.net/as480133937/article/details/104827639/
https://www.cnblogs.com/breezy-ye/articles/12157442.html
https://blog.csdn.net/junseven164/article/details/121071585?spm=1001.2014.3001.5501
https://blog.csdn.net/junseven164/article/details/121066120?spm=1001.2014.3001.5501

上一篇:HAL库源文件stm32h4xx_hal_qspi.c学习笔记


下一篇:Android 概述