ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

STM32中断与DMA通信编程

2021-11-05 19:31:46  阅读:135  来源: 互联网

标签:DMA HAL 传输 中断 编程 STM32 UART 串口


STM32中断与DMA通信编程

文章目录

一、题目内容

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

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

二、CubeMX中断方式点亮LED灯

1、CubeMX工程

进入STMCubeMX,选择新项目:
在这里插入图片描述
在这里插入图片描述
点击sys,将debug选项改为Serial Wire。
在这里插入图片描述
在RCC里的HSE选择Crystal/Ceramic Resonator
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于开关对应管脚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:
在这里插入图片描述
使能对应的外部中断线,点击Enabled
在这里插入图片描述
配置中断优先级
(大多数情况下不必设置中断优先级,直接使用中断编号设置的默认中断优先级)
在这里插入图片描述
时钟设置:设置为36M
在这里插入图片描述
project里把toolchain那里改为MDK-ARM,版本选择最新的就行了,项目名和项目路径自己选择
在这里插入图片描述
选择生成初始化文件,然后选择生成代码就好了
在这里插入图片描述

2、代码

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

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)

当捕获到上升沿,触发中断,就会进入到这个函数里面
然后就会执行HAL_GPIO_EXTI_Callback(GPIO_Pin)函数,此函数为回调函数,我们打开可以发现前面有个weak。
第561行前面的 __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状态
	}
}


在这里插入图片描述
编译一下,没有错
在这里插入图片描述
将代码烧录到C8T6核心板中。
在这里插入图片描述

3、电路以及效果

GPIO默认是3.3V高电平,当按下按键,GPIO变为低电平,此过程是下降沿。松开按键,GPIO又变为高电平,此过程为上升沿。因为设置的是上升沿中断,也就是松开按键的时候,灯会变化。
在这里插入图片描述

三、串口中断方式

1、中断

(一)什么是中断

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

(二)中断作用

在这里插入图片描述

(三)中断优先级

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

2、工程设置

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

在这里插入图片描述
在这里插入图片描述
时钟设置
在这里插入图片描述

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

在这里插入图片描述
编译成功,开始烧录
在这里插入图片描述

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

利用XCOM进行通信
在这里插入图片描述

四、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 对于其他的工作来说就无法使用。
在这里插入图片描述
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工作框图:
在这里插入图片描述

3、DMA方式的接口函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

1、设置工程

在这里插入图片描述
设置RCC
在这里插入图片描述
设置串口
在这里插入图片描述
使能中断
在这里插入图片描述
DMA设置:点击DMA Settings的Add添加通道,传输速率设置为中速Medium。
在这里插入图片描述
模式设置为Normal,右侧选择Memory
在这里插入图片描述
在System view下选择DMA
在这里插入图片描述
时钟设置
在这里插入图片描述

2、代码

在main.c文件添加代码

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

在这里插入图片描述

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

在这里插入图片描述
烧录
在这里插入图片描述

3、串口发送数据

在这里插入图片描述

七、实验总结

通过这次实验我们学会了很多新的东西,也知道了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

标签:DMA,HAL,传输,中断,编程,STM32,UART,串口
来源: https://blog.csdn.net/qq_48641886/article/details/121162276

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有