DMA简述与使用实例

之后要学:SPI / IIC+DMA

学习的这位up主的视频:全网最清楚的DMA讲解,三种搬运模式三个例子讲清楚(STM32教程基于HAL库和CUBEIDE)_哔哩哔哩_bilibili

目录

01-基本信息

1-概述

2-方向

3-模式

正常模式

轮询模式

4-地址自增

02-实例一:串口读取陀螺仪

01-基本信息

1-概述

DMA能将外设/内存与外设/内存间建立直接的通道进行数据传输,而无需CPU进行数据的传输,可以将资源用在更合适的地方

2-方向

外设-》内存、内存-》外设,内存-》内存

3-模式

正常模式

只执行一次DMA数据传输,在接收到制定的数据大小之后,结束DMA

轮询模式

可以一直进行数据的传输,不过要注意防止数据的覆盖等,需要特殊的算法进行数据的结算(以后学)

4-地址自增

有这四种自增方式

02-实例一:串口读取陀螺仪

认为适用于接收连续的数据包,协议

1-cubemx配置

配置为正常模式,外设地址不变,内存地址自增

正常模式需要每次调用完成,重新开启DMA,但算法相对简单

当前外设为串口接收寄存器,只有一个地址无需自增

内存需要存放多个数据,所以需要自增,不能覆盖

数据宽度也很重要,但是了解不深,以后再说

串口中断也要打开

在后续处理中,需要用串口中断来进行DMA接收数据完成的判断

2-代码编写

1-初始化

		__HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_IDLE); //使能IDLE

        //开启DMA接收,
		HAL_UART_Receive_DMA(&hlpuart1,uart1_rx_buf, UART1_PACK_SIZE);
        

DMA接收,只需要设定好初始位置,定义好数组大小、接收数据数目比接收的一帧数据大就行,后续会进行处理

使能IDLE,即空闲中断,之后在中断处理函数中进行处理

2-中断处理函数

在stm32g4xx_it.c中

void LPUART1_IRQHandler(void)
{
  /* USER CODE BEGIN LPUART1_IRQn 0 */
	extern uint8_t uart1_rx_buf[UART1_PACK_SIZE];//16Byte
  extern uint16_t uart1_rx_size;
  extern uint8_t uart1_rx_cplt_flag;
	
  uint32_t tmp_flag = 0;
	uint32_t temp;
	tmp_flag =__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE); //获取IDLE标志位
	if((tmp_flag != RESET))//idle标志被置位
	{ 
			__HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);//清除标志位
			
			HAL_UART_DMAStop(&hlpuart1);
			temp  =  __HAL_DMA_GET_COUNTER(&hdma_lpuart1_rx);// 获取DMA中未传输的数据个数   
			uart1_rx_size =  sizeof(uart1_rx_buf) - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
			uart1_rx_cplt_flag = 1;	// 接受完成标志位置1

	}
  /* USER CODE END LPUART1_IRQn 0 */
  HAL_UART_IRQHandler(&hlpuart1);
  /* USER CODE BEGIN LPUART1_IRQn 1 */

  /* USER CODE END LPUART1_IRQn 1 */
}

使能IDLE中断后,在串口空闲下便会进入此中断(其他中断会进吗)然后判断串口当前是否空闲,若空闲,代表一帧数据接收完毕,然后结束DMA(之前接收函数比具体数据数量大也没关系就是这个原因,之后会重新开启),使能标志位,之后进行到串口数据处理函数中,便可以进行处理。

ps:一帧数据代表一次发送所有信息,如55 51 aa aa aa 55 52 aa aa aa,此时接受了两个帧头,但因为是一遍发过来的,所以为一帧。

ps:空闲中断只有在接收完成数据后才会打开,发送不会。

ps:所以单片机,openmv的printf,print发送的是什么呢,是一帧还是一个个发送的呢,或许之后可以用打包函数,直接打包数据发送出去就是一帧

3-串口数据处理函数

uint8_t uart1_receiveData_parse(void)
{
		uint8_t ret = 1;
		//如果接收到数据包
		if(uart1_rx_cplt_flag)
		{
            //数据处理
			for(uint8_t i=0;i<99;i++)
			{
					jy901_read_data(uart1_rx_buf[i]);
			}

			//清空数据包与标志位
			memset(uart1_rx_buf, 0, UART1_PACK_SIZE);
			uart1_rx_cplt_flag = 0;
			uart1_rx_size = 0;
			//重新打开DMA接收
			HAL_UART_Receive_DMA(&hlpuart1,uart1_rx_buf, UART1_PACK_SIZE);	
		}
		
		return ret;
}

注意,这并不是什么接收完成中断之类的,而是一个简单的函数的,需要外界的调用,我将其放在定时器中断函数中,每1ms调用一次函数,则1ms读取一次数据。

03-实例二:另一种DMA收发数据的方案

发送数据:关闭DMA在buffer与串口寄存器的通道,然后将数据写入缓冲区(buffer),全部写入写入后开启通道,则会将所有的数据发出去

接收数据:

若是发送指令后立即读取其反馈,则可以默认关闭DMA通道,然后发送完成指令后立即打开。在空闲中断服务函数中判断,若有,则将缓冲区数组的数据取出并进行计算

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值