首页 > 代码库 > cortex_m3_stm32嵌入式学习笔记(十九):DMA实验(高速传输)
cortex_m3_stm32嵌入式学习笔记(十九):DMA实验(高速传输)
DMA,全称为: Direct Memory Access,即直接存储器访问。 DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高。
看这一句
这是初始化DMA的语句,第一个参数填DMA的通道CHx ,(本次实验用到的是通道4),第二个参数是外设地址,我们是要向串口发送数据,所以要填串口接受发送数据存储器 USART1->DR 的地址,第三个参数填存储器地址,(可以理解为待发送数据的地址),第四个参数填数据传输量(即待发送数据的大小)
主函数
有一个函数
获得当前数据量剩余大小(还有多少没发送完)
即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。 一个完整的DMA传输过程必须经过下面的4个步骤。 1.DMA请求 CPU对DMA控制器初始化,并向I/O接口发出操作命令,I/O接口提出DMA请求。 2.DMA响应 DMA控制器对DMA请求判别优选级及屏蔽,向总线裁决逻辑提出总线请求。当CPU执行完当前总线周期即可释放总线控制权。此时,总线裁决逻辑输出总线应答,表示DMA已经响应,通过DMA控制器通知I/O接口开始DMA传输。 3.DMA传输 DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。 4.DMA结束 当完成规定的成批数据传送后,DMA控制器即释放总线控制权,并向I/O接口发出结束信号。当I/O接口收到结束信号后,一方面停 止I/O设备的工作,另一方面向CPU提出中断请求,使CPU从不介入的状态解脱,并执行一段检查本次DMA传输操作正确性的代码。最后,带着本次操作结果及状态继续执行原来的程序。
简单的说就是一种高速传输数据的方式啦
从外设(TIMx、 ADC、 SPIx、 I2Cx 和 USARTx)产生的 DMA 请求,通过逻辑或输入到DMA 控制器,这就意味着同时只能有一个请求有效。外设的 DMA 请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。
下面是是DMA1各通道一览表:
本次实验实现串口1的DMA传送,即用到通道4
配置DMA大体步骤:
1)使能 DMA 时钟
2) 初始化 DMA 通道 4 参数
3)使能串口 DMA 发送
4)使能 DMA1 通道 4,启动传输。
5)查询 DMA 传输状态
配置DMA的dma.c
#include "dma.h" DMA_InitTypeDef DMA_InitStructure; u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度 //DMA1的各通道配置 //这里的传输形式是固定的,这点要根据不同的情况来修改 //从存储器->外设模式/8 位数据宽度/存储器增量模式 //DMA_CHx:DMA通道CHx //cpar:外设地址 //cmar:存储器地址 //cndtr:数据传输量 void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能 DMA 时钟 DMA_DeInit(DMA_CHx);//将DMA的通道1寄存器重设为缺省值 DMA1_MEM_LEN=cndtr; DMA_InitStructure.DMA_PeripheralBaseAddr=cpar;//DMA外设串口基地址 DMA_InitStructure.DMA_MemoryBaseAddr=cmar;//DMA内存基地址 DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;//数据传输方向内存到外设 DMA_InitStructure.DMA_BufferSize=cndtr;//DMA通道的DMA缓存的大小 DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址不变 DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址寄存器递增 //数据宽度为8位 DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; //数据宽度为8位 DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//工作在正常缓存模式 DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//DM 通道拥有中优先级 DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//非内存到内存传输 DMA_Init(DMA_CHx,&DMA_InitStructure);//初始化 DMA 的通道 } //开启一次DMA传输 void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx) { DMA_Cmd(DMA_CHx,DISABLE );//关闭 USART1 TX DMA1所指示的通道 DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);//设置 DMA 缓存的大小 DMA_Cmd(DMA_CHx, ENABLE); //使能 USART1 TX DMA1 所指示的通道 }
看这一句
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
这是初始化DMA的语句,第一个参数填DMA的通道CHx ,(本次实验用到的是通道4),第二个参数是外设地址,我们是要向串口发送数据,所以要填串口接受发送数据存储器 USART1->DR 的地址,第三个参数填存储器地址,(可以理解为待发送数据的地址),第四个参数填数据传输量(即待发送数据的大小)
dma.h
#ifndef _DMA_H #define _DMA_H #include "sys.h" void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr); void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx); #endif
#include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "lcd.h" #include "key.h" #include "dma.h" const u8 TEXT_TO_SEND[]={"DMA test~~~~~~"}; #define TEXT_LENTH sizeof(TEXT_TO_SEND)-1 u8 SendBuff[(TEXT_LENTH+2)*100]; u16 i;u8 t;float pro=0; void init(void) { delay_init(); uart_init(9600); LED_Init(); KEY_Init(); LCD_Init(); //DMA1通道4,外设为串口1,存储器为SendBuff,长(TEXT_LENTH+2)*100. MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,(TEXT_LENTH+2)*100); POINT_COLOR=RED; LCD_ShowString(60,40,200,24,24," DMA Test~^~"); LCD_ShowString(60,70,200,16,16," M Difficult"); LCD_ShowString(60,90,200,16,16," By--yh"); LCD_ShowString(60,110,200,16,16," 2015/1/25"); LCD_ShowString(60,130,200,16,16,"KEY0:Start"); t=0; for(i=0;i<(TEXT_LENTH+2)*100;i++)//填充缓存区(生成待发送数据) { if(t>=TEXT_LENTH)//添加换行 { SendBuff[i++]=0x0d; SendBuff[i]=0x0a; t=0; } else SendBuff[i++]=TEXT_TO_SEND[t++]; } } int main(void) { init();i=0; POINT_COLOR=BLUE;//设置字体为蓝色 while(1) { t=KEY_Scan(0); if(t==KEY0_PRES) { LCD_ShowString(60,150,200,16,16,"Start Transimit...."); LCD_ShowString(60,170,200,16,16," %");//显示百分号 printf("\r\nDMA DATA: \r\n "); USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输! //等待 DMA 传输完成,此时我们来做另外一些事,点灯 //实际应用中,传输数据期间,可以执行另外的任务 while(1) { if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET)//等待通道 4 传输完成 { DMA_ClearFlag(DMA1_FLAG_TC4);//清除通道 4 传输完成标志 break; } pro=DMA_GetCurrDataCounter(DMA1_Channel4);//当前还剩余数据量 pro=1-pro/((TEXT_LENTH+2)*100);//得到百分比 pro*=100; //扩大 100 倍 LCD_ShowNum(60,170,pro,3,16); //LED1=!LED1; } LCD_ShowNum(60,170,100,3,16);//显示100% LCD_ShowString(60,150,200,16,16,"Transimit Finished!");//传送完成 } i++; delay_ms(10); if(i==20) { LED0=!LED0; i=0; } } }
有一个函数
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
获得当前数据量剩余大小(还有多少没发送完)
其实最重要的当传输数据时MCU的CPU是可以干别的事情的,这才我们利用DMA传输的优势(虽然本实验没有体现出来)
我们可以在串口助手里看到如下内容
说明串口接收到了DMA传输的数据了。。
此实验可以对比之前的串口通信的实验,那次是普通的发送
cortex_m3_stm32嵌入式学习笔记(十九):DMA实验(高速传输)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。