STM32中的DMA

概念

全称是Direct Memory Access,中文意思为直接存储器访问。
DMA可用于实现外设与存储器之间或者存储器与存储器之间数据传输的高效性。之所以称为高效,是因为DMA传输数据移动过程无需CPU直接操作,这样节省的 CPU 资源就可供其它操作使用。从硬件层面来理解,DMA就好像是RAM与I/O设备间数据传输的通路,外设与存储器之间或者存储器与存储器之间可以直接在这条通路上进行数据传输。这里说的外设一般指外设的数据寄存器,比如ADC、 SPI、 I2C等外设的数据寄存器,存储器一般是指片内SRAM、外部存储器、片内 Flash 等。

 利用DMA获取数据的流程

  • UART控制器读取外设发送的数据
  • DMA硬件上自动读取UART数据寄存器的数据
  • DMA硬件上自动将获取的数据搬移到内存中
  • UART控制器给CPU发送中断信号通知CPU数据读取完毕
  • CPU可以访问内存中的数据

总结:CPU无需频繁的访问数据寄存器,CPU只关心内存

 利用DMA发送数据的流程

  • CPU将数据写入指定的内存中
  • DMA硬件上自动从指定的内存中获取要发送的数据
  • DMA硬件上自动将数据搬移到数据寄存器中
  • UART控制器硬件上自动将数据发送出去
  • DMA给CPU发送中断信号通知数据发送完毕
     

结论:CPU无需频繁的访问数据寄存器,CPU只关心内存

DMA的特性

  • STM32F103有 2 个 DMA 控制器,分别是DMA1和DMA2
  • DMA1 有 7 个通道
  • DMA2 有 5个通道
  • 每个通道专门用来管理来自于一个或多个外设对存储器访问的请求

在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。举个例子,通道2优先于通道4

 代码实现

标准库函数和结构体

typedef struct
{
  uint32_t DMA_PeripheralBaseAddr; 
  uint32_t DMA_MemoryBaseAddr;     
  uint32_t DMA_DIR;                
  uint32_t DMA_BufferSize;         
  uint32_t DMA_PeripheralInc;      
  uint32_t DMA_MemoryInc;         
  uint32_t DMA_PeripheralDataSize; 
  uint32_t DMA_MemoryDataSize;     
  uint32_t DMA_Mode;              
  uint32_t DMA_Priority;          
  uint32_t DMA_M2M;               
}DMA_InitTypeDef;

 初始化

采用DMA发送数据完成后,使用中断发送给CPU结束信号,需要匹配中断和DMA已经NVIC控制器

void My_DMA_Init(void)
{
	// 1.打开DMA1控制器时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	// 2.配置DMA1通道4作为串口1发送 : 内存 -> 寄存器
	DMA_InitTypeDef DMA_Config;
	DMA_Config.DMA_MemoryBaseAddr = (u32)UART1DMA_TxBuff; // 内存缓冲区首地址
	DMA_Config.DMA_PeripheralBaseAddr = (u32)&USART1->DR; // 寄存器首地址
	DMA_Config.DMA_BufferSize = UART1DMA_TXBUFF_SIZE; // 内存缓冲区大小
	DMA_Config.DMA_DIR = DMA_DIR_PeripheralDST; // 内存->寄存器
	DMA_Config.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 寄存器不自增
	DMA_Config.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存自增
	DMA_Config.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存搬移字节为单位
	DMA_Config.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 寄存器字节位单位
	DMA_Config.DMA_Mode = DMA_Mode_Normal; // 普通模式
	DMA_Config.DMA_Priority = DMA_Priority_Medium; // 中等
	DMA_Config.DMA_M2M = DMA_M2M_Disable; // 禁止内存之间拷贝
	DMA_Init(DMA1_Channel4, &DMA_Config);
	
	// 3.配置DMA1通道4支持DMA_IT_TC中断 
	DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
	
	// 4.配置NVIC支持DMA1通道4中断 
	NVIC_InitTypeDef NVIC_Config;
	NVIC_Config.NVIC_IRQChannel = DMA1_Channel4_IRQn; // DMA1通道4中断 
	NVIC_Config.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_Config.NVIC_IRQChannelSubPriority = 2;
	NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Config);
}

测试函数

// 串口1发送测试函数 
void UART1_DMA_Tx_Test(void)
{
	// 1.初始化内存缓冲区 
	u32 i;
	for(i = 0; i < UART1DMA_TXBUFF_SIZE; i++)
		UART1DMA_TxBuff[i] = 'A';
	// 2.关闭DMA1通道4
	DMA_Cmd(DMA1_Channel4, DISABLE);
	// 3.配置串口1支持DMA的发送 
	USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
	// 4.设置搬移数据的长度
	DMA_SetCurrDataCounter(DMA1_Channel4, UART1DMA_TXBUFF_SIZE);
	
	// 5.打开DMA1通道4
	DMA_Cmd(DMA1_Channel4, ENABLE);
	// 打开该功能后, DMA1通道4就会自动从内存缓冲区中搬移数据到串口1的DR中
	// 搬移完成会触发DMA1通道4 TC中断
	
	DMA_TcFlag = 0; // 标识数据还没发完
	
	// 6.采用中断的方式判断数据是否搬移完毕 ??
	while(1){
		if(DMA_TcFlag){ // 
			printf("\n DMA TX SUCCESS\n");
			break;
		}
		// 做其它业务 ...
		LED0 = !LED0;
		delay_ms(200);
	}
}

中断处理函数

// DMA1通道4触发的中断 
// 发送完成触发中断
void DMA1_Channel4_IRQHandler(void){
	// 1.判断是哪个中断 
	if(DMA_GetITStatus(DMA1_IT_TC4) != RESET){
		// 2.清除中断到来位 
		DMA_ClearITPendingBit(DMA1_IT_TC4);
		// 3.关闭DMA1通道4
		DMA_Cmd(DMA1_Channel4, DISABLE);
		// 4.标志数据发送完毕
		DMA_TcFlag = 1;
	}
}

初始化函数和cmd函数中需要将DMA相关添加进来

init.c中
static PINIT_T init_func[] = {
	LED_Init,			// led灯初始化
	BEEP_Init,		// beep初始化 
	Systick_init,	// 滴答定时器初始化
	KEY_Init,			// 按键初始化
	My_EXTI_Init,	// 中断初始化 
	UART_Init,		// 串口初始化
	AT24C02_Init, // AT24C02初始化
	DS18B20_Init, // 温度传感器的初始化
	My_DMA_Init,  // DMA初始化
	0
};


cmd.c中
cmd_t cmd[] = {
	{"led on", LED_On},
	{"led off", LED_Off},
	{"beep on", BEEP_On},
	{"beep off", BEEP_Off},
	{"EEPROM R", AT24C02_ReadOne}, // 读取单字节
	{"EEPROM W", AT24C02_WriteOne}, //写入单字节
	{"EEPROM RS", AT24C02_ReadMul}, // 读取多字节
	{"EEPROM WS", AT24C02_WriteMul},	// 写入多字节
	{"temp", DS18B20_Test},  // 获取温度命令
	{"rom", DS18B20_ReadRom},	// 读取ROM值命令
	{"dma tx", UART1_DMA_Tx_Test} // DMA发送数据命令
};

实验结果

在串口工具中发送dma tx,串口工具能够显示通过内存写入到寄存器中的数据,本文中是写了1024和'A'

  • 16
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32DMA(Direct Memory Access)和TIM(Timer)是两个不同的功能模块,拥有不同的作用和用途。 DMA是一种直接内存访问技术,它允许外设直接与内存进行数据传输,而无需CPU的干预。通过使用DMA,可以在数据传输过程减少CPU的负载,提高系统的效率。在STM32DMA可以与各种外设(如UART、SPI、I2C等)进行数据传输,通过配置DMA通道和外设的地址以及传输长度等参数,可以实现高速数据传输。 TIM(Timer)是用于计时和生成定时断的模块。它提供了多个定时器通道,可以用于产生精确的时间间隔、测量外部信号的频率等应用。通过配置TIM的工作模式、预分频系数和计数器的重载值,可以实现各种计时和定时的功能。 区别: 1. 功能不同:DMA用于数据传输,而TIM用于计时和定时。 2. 应用场景不同:DMA通常用于大量数据的高速传输,如音频、图像等;而TIM通常用于需要精确计时和定时的应用,如PWM输出、定时触发等。 3. 配置方式不同:DMA需要配置DMA通道、外设地址、传输长度等参数;而TIM需要配置工作模式、预分频系数、计数器重载值等参数。 4. DMA可以与多种外设进行数据传输,而TIM主要用于定时和计时,不直接与外设进行数据传输。 总之,DMA和TIM在STM32是两个不同的功能模块,分别用于数据传输和计时/定时,具有不同的应用场景和配置方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值