DMA 网上的解释一大堆,简单总结一下:
DMA 处理过程全部是由硬件来实现的,速度很快!
DMA 在专门的DMA控制下,实现高速外设与主存储期之间自动成批量的数据交换。
通常有两种交换方式:1 独占总线方式 2 周期挪用方式
DMA 传送过程
DMA 预处理
DMA 数据传送
DMA 结束处理 将总线控制权交换给CPU
DMA 内部寄存器
地址寄存器 : 存放DMA传输是储存单元地址
字节寄存器 : 存放DMA 传输字节数
控制寄存器 : 存放CPU 设定的DMA传输方式
状态寄存器 : 存放DMAC当前的状态
DMA 外设地址 : 基地址 + 偏移地址
两种方法设定地址:
#define USART1_DR_Base 0x40013804
#define USART1_DR_Base (&(USART1->DR)) 觉得还是第二种方法比较直接
DMA 两种中断触发方式 :
DMA_IT_TC1 传输完成中断 :每次更新发生在计数>= 设定值
DMA_IT_HT1 传输过半中断 :每次更新发生在计数> (设定值 / 2)
DMA_IT_GL1 全局中断 :(实验了几次和过半中断传输情况一致)
编程的时候根据DMA请求映像来判断通道号,然后找到目的地址和原地址基本上就算完事儿了。当然得分清是传输是外设和内存,内存和外设,内存和内存三种情况,会有 (DMA_InitTypeDef 结构体).DMA_M2M 进行设置。
下面是DMA串口收发简单例子,并没有用到FIFO精确收发,有时间再好好写写~
DMA_Configuration()是DMA5通道 串口1接收通道配置函数
DMA_Configuration2()是DMA4通道 串口1发送通道配置函数
对于发送 可以每当发送的时候 都执行DMA_Configuration2(),进行发送操作
对于接收 理应采用FIFO,对于接收数组进行处理,这里只是简单的接收,没有进一步处理
/* Includes ------------------------------------------------------------------*/ #include "stm32f10x.h" #include "stm32f10x_tim.h" #include "stm32f10x_rcc.h" #include "stm32f10x_map.h" #include "stm32f10x_it.h" #include "misc.h" #include "bsp_timer.h" #include "stm32f10x_usart.h" #include "stm32f10x_dma.h" #include "stdio.h" #define SENDBUFF_SIZE 10 vu8 SendBuff[SENDBUFF_SIZE]={0}; vu8 TxBuffer[] = "hello xiao lei !!! "; vu8 NbrOfDataToTransfer = sizeof(TxBuffer)-1; GPIO_InitTypeDef GPIO_InitStructure; #define LED1_ON GPIO_SetBits(GPIOB, GPIO_Pin_5); #define LED1_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_5); #define LED2_ON GPIO_SetBits(GPIOD, GPIO_Pin_6); #define LED2_OFF GPIO_ResetBits(GPIOD, GPIO_Pin_6); #define LED3_ON GPIO_SetBits(GPIOD, GPIO_Pin_3); #define LED3_OFF GPIO_ResetBits(GPIOD, GPIO_Pin_3); void RCC_Configuration(void); void LED_Config(void); void Delay(__IO uint32_t nCount); int fputc(int ch, FILE *f) { USART_SendData(USART1, (u8) ch); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) { } return ch; } / * 名 称:void LED_Config(void) * 功 能:LED 控制初始化函数 * 入口参数:无 * 出口参数:无 * 说 明: * 调用方法:无 / void LED_Config(void){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD , ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1 V6 //将V6,V7,V8 配置为通用推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //口线翻转速度为50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_3; //LED2, LED3 V7 V8 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //LCD背光控制 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_ResetBits(GPIOD, GPIO_Pin_13); //LCD背光关闭 } void USART1_Configration() { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); //USART1_TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; 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_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } void DMA_Configuration(void) { DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输 DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(USART1->DR)); DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作源头//外设是作为数据传输的目的地还是来源 DMA_InitStructure.DMA_BufferSize = 5; //DMA缓存的大小 单位在下边设定 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设字节为单位 DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //内存字节为单位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //4优先级之一的(高优先) VeryHigh/High/Medium/Low DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存 DMA_Init(DMA1_Channel5, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道1寄存器 DMA_ITConfig(DMA1_Channel5, DMA_IT_HT, ENABLE); //DMA5半传输完成中断 USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); //使能USART1的接收DMA请求 DMA_Cmd(DMA1_Channel5, ENABLE); DMA_ClearFlag(DMA1_FLAG_TC5); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void DMA_Configuration2(void) { DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输 DMA_DeInit(DMA1_Channel4); DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(USART1->DR)); DMA_InitStructure.DMA_MemoryBaseAddr = (u32)TxBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST ; //外设作源头//外设是作为数据传输的目的地还是来源 DMA_InitStructure.DMA_BufferSize = NbrOfDataToTransfer; //DMA缓存的大小 单位在下边设定 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设字节为单位 DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //内存字节为单位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; //工作在循环缓存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //4优先级之一的(高优先) VeryHigh/High/Medium/Low DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //内存到非内存 DMA_Init(DMA1_Channel4, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道1寄存器 DMA_ITConfig(DMA1_Channel4, DMA_IT_TC , ENABLE); //DMA4传输完成中断 USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能USART1的发送DMA请求 DMA_Cmd(DMA1_Channel4, ENABLE); DMA_ClearFlag(DMA1_FLAG_TC4); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } / * 名 称:int main(void) * 功 能:主函数 * 入口参数:无 * 出口参数:无 * 说 明: * 调用方法:无 / int main(void) { int i,j; RCC_Configuration(); //系统时钟配置 // TIM3_Configuration(); NVIC_Configuration(); LED_Config(); USART1_Configration(); DMA_Configuration(); //DMA_Configuration2(); //LED控制配置 // printf("Start DMA transmission!\r\n"); //LED1_ON ; for(i=0;i<1000000;i++); // for(j=0;j<20;j++); DMA_Configuration2(); while (1) { } } / * 名 称:void RCC_Configuration(void) * 功 能:系统时钟配置为72MHZ * 入口参数:无 * 出口参数:无 * 说 明: * 调用方法:无 / void RCC_Configuration(void) { SystemInit(); } void DMA1_Channel5_IRQHandler(void) { u8 end; int i; printf("Start DMA transmission!\r\n"); / 全传输完成*/ // if(DMA_GetITStatus(DMA1_IT_TC5)) // { // end = DMA_GetCurrDataCounter(DMA1_Channel5); // DMA_ClearITPendingBit(DMA1_IT_GL5);///* 清中断源 // DMA_ClearFlag(DMA1_FLAG_TC5); // // printf("AA%dAA:", end); // printf("%s\r\n", SendBuff); // for(i=0;i<100000;i++); // } // / 半传输完成*/ if(DMA_GetITStatus(DMA1_FLAG_HT5)) { end = DMA_GetCurrDataCounter(DMA1_Channel5); DMA_ClearITPendingBit(DMA1_IT_GL5);///* 清中断源 DMA_ClearFlag(DMA1_IT_TC5); printf("AA%dAA:", end); printf("%s\r\n", SendBuff); for(i=0;i<100000;i++); } // } void DMA1_Channel4_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC4)==SET) { DMA_ClearFlag(DMA1_IT_TC4);//TCIE,TE,RE printf("AAAA!"); } }
上一篇:Mini2440 NRF24L01无线模块驱动
下一篇:STM32关于USART的问题集锦
推荐阅读最新更新时间:2024-03-16 14:56