1、DMA 简介 DMA,全称是 Direct Memory Access,中文意思为直接存储器访问。DMA 可用于实现外设与存储器之间或者存储器与存储器之间数据传输的高效性。之所以称为高效,是因为 DMA 传输数据移动过程无需 CPU 直接操作,这样节省的 CPU 资源就可供其它操作使用。从硬件层面来理解,DMA 就好像是 RAM 与 I/O 设备间数据传输的通路,外设与存储器之间或者存储器与存储器之间可以直接在这条通路上进行数据传输。这里说的外设一般指外设的数据寄存器,比如 ADC、SPI、I2C、DCMI 等外设的数据寄存器,存储器一般是指片内 SRAM、外部存储器、片内 Flash等。 STM32F1 最多有 2 个 DMA 控制器( DMA2 仅存在大容量产品中),DMA1 有7 个通道。DMA2 有 5 个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权。 DMA 具有 12 个独立可编程的通道,其中 DMA1 有 7 个通道, DMA2 有 5 个通道,每个通道对应不同的外设的 DMA 请求。虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。 2、DMA 配置步骤 #include "stm32f10x.h"#include "stdio.h" //-------------------------------------------------------------------------//变量功能 : 定义缓存//-------------------------------------------------------------------------#define send_buf_len 5000u8 send_buf[send_buf_len]; //-------------------------------------------------------------------------//函数功能 : 按键初始化//-------------------------------------------------------------------------void KEY_Init(void){ GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0; //选择你要设置的IO口 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;//下拉输入 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置传输速率 GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */} //-------------------------------------------------------------------------//函数功能 : 重定向//-------------------------------------------------------------------------int fputc(int ch,FILE *p) //函数默认的,在使用printf函数时自动调用{ USART_SendData(USART1,(u8)ch); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); return ch;} //-------------------------------------------------------------------------//函数功能 : USART1初始化//输入说明 : bound为波特率配置参数//-------------------------------------------------------------------------void USART1_Init(u32 bound){ //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //打开时钟 /* 配置GPIO的模式和IO口 */ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //串口输出PA9 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入IO */ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX //串口输入PA10 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入 GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */ //USART1 初始化设置 USART_InitStructure.USART_BaudRate = bound;//波特率设置 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1 USART_Cmd(USART1, ENABLE); //使能串口1 USART_CleaRFlag(USART1, USART_FLAG_TC); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、 } //-------------------------------------------------------------------------//函数功能 : USART1中断函数//-------------------------------------------------------------------------void USART1_IRQHandler(void) //串口1中断服务程序{ u8 r; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 { r =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据 USART_SendData(USART1,r); while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET); } USART_ClearFlag(USART1,USART_FLAG_TC);} //-------------------------------------------------------------------------//函数功能 : 延迟 N ms//输入说明 : time为延时的时间参数//-------------------------------------------------------------------------void delay_nms(u16 time){ u16 i=0; while(time--) { i=12000; //自己定义 while(i--) ; }} //-------------------------------------------------------------------------// 函 数 名 : DMAx_Init// 函数功能 : DMA初始化函数// 输 入 : DMAy_Channelx MA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7// par:外设地址// mar:存储器地址// ndtr:数据传输量// 输 出 : 无//------------------------------------------------------------------------- void DMAx_Init(DMA_Channel_TypeDef* DMAy_Channelx,u32 par,u32 mar,u16 ndtr){ DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1时钟使能 //DMA_DeInit(DMAy_Channelx); /* 配置 DMA */ DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址 DMA_InitStructure.DMA_MemoryBaseAddr = mar;//DMA 存储器0地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存储器到外设模式 DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化DMA }//-------------------------------------------------------------------------// 函 数 名 : DMAx_Enable// 函数功能 : 开启一次DMA传输// 输 入 : DMAy_Channelx MA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7// ndtr:数据传输量// 输 出 : 无//------------------------------------------------------------------------- void DMAx_Enable(DMA_Channel_TypeDef *DMAy_Channelx,u16 ndtr){ DMA_Cmd(DMAy_Channelx, DISABLE); //关闭DMA传输 DMA_SetCurrDataCounter(DMAy_Channelx,ndtr); //数据传输量 DMA_Cmd(DMAy_Channelx, ENABLE); //开启DMA传输 }//-------------------------------------------------------------------------// 函数功能 : DMA中断//-------------------------------------------------------------------------void DMA1_Channel4_IRQHandler(void) { if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=0)//判断通道4传输完成 { DMA_ClearFlag(DMA1_FLAG_TC4); }} //-------------------------------------------------------------------------// 函数功能 : 要发送的数据//-------------------------------------------------------------------------void Send_Data(u8 *p){ u16 i; for(i=0;i<send_buf_len;i++) { *p='5';//填充 5 进数组,让数组所有数据都是字符 5 p++; }} //-------------------------------------------------------------------------//函数功能 : 主函数//-------------------------------------------------------------------------int main( void ){ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组 USART1_Init(9600); KEY_Init(); DMAx_Init(DMA1_Channel4,(u32)&USART1->DR,(u32)send_buf,send_buf_len); Send_Data(send_buf); while(1) { if( (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)) == 0 ) { delay_nms(10); if( GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)) { USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送 DMAx_Enable(DMA1_Channel4,send_buf_len); //开始一次DMA传输! } } }}//------------------------------------------------------------------------- 注意:在项目当中往往都是以模块化编写,是为了让初学者对新学的知识有个全局理解,就只用一个主文件列出所有代码驱动。 3、验证程序,按下按键就会通过DMA发送数据到串口,效果如下图 |
/1
关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )
GMT+8, 2025-11-6 04:27 , Processed in 0.140625 second(s), 28 queries , Gzip On.
地址:深圳市南山区科技生态园2栋A座805 电话:19926409050