前言
DMA即直接内存存取。我理解它就是一个“交通部长”抑或是一个“搬运工”,协助CPU存储或读取数据。既然它的主要工作就是“搬运”数据,服务对象自然就是内存(不太严格的说法吧,STM32中Flash闪存也可成为DMA的服务对象)。
问题1 DMA传输数量寄存器DMA_CNDTRx的含义
描述
在中文版本参考手册里,寄存器DMA_CNDTRx有如下解释:
对于“指示待传输字节数目”的解释,我有些疑惑,因为在参考手册DMA主要特性中又是这么说的:可编程的数据传输数目:最大为65535.同样的,我在英文版本参考手册里也看到如下:
所以寄存器DMA_CNDTRx的内容是代表哪个意义,待传输字节数目还是待传输单位数目?
实验
设计DMA从内存搬运数据到内存,数据为u16类型,往DMA_CNDTRx里写入4. 即
u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567};作为源数据
u16 dstTable[TABLE_LENGTH] = {0};作为目的
可以想象,如果4代表的是4个字节,则只能搬移0x1234,0x2345;否则,全部数据都被复制过去了。
1 #include "STM32f10x_lib.h" 2 #include "stdio.h" 3 4 void RCC_Configuration(void); 5 void GPIO_Configuration(void); 6 void USART_Configuration(void); 7 void DMA_Configuration(void); 8 9 #define TABLE_LENGTH 4 10 u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567}; 11 u16 dstTable[TABLE_LENGTH] = {0}; 12 13 int main(void) 14 { 15 RCC_Configuration(); 16 GPIO_Configuration(); 17 USART_Configuration(); 18 printf("before:0x%x,0x%x,0x%x,0x%x\r\n",dstTable[0],dstTable[1],dstTable[2],dstTable[3]); 19 DMA_Configuration(); 20 while(DMA_GetFlagStatus(DMA1_FLAG_TC5) == RESET); 21 printf("after:0x%x,0x%x,0x%x,0x%x\r\n",dstTable[0],dstTable[1],dstTable[2],dstTable[3]); 22 while(1); 23 } 24 25 /*RCC*/ 26 void RCC_Configuration(void) 27 { 28 ErrorStatus HSEStartUpStatus; 29 /*默认状态*/ 30 RCC_DeInit(); 31 /*HSE使能并等待起震*/ 32 RCC_HSEConfig(RCC_HSE_ON); 33 HSEStartUpStatus = RCC_WaitForHSEStartUp(); 34 /*HSE外部高速晶振启动成功*/ 35 if(HSEStartUpStatus == SUCCESS) 36 { 37 /*配置HCLK,PCLK1,PCLK2分频*/ 38 RCC_HCLKConfig(RCC_SYSCLK_Div1); 39 RCC_PCLK2Config(RCC_HCLK_Div1); 40 RCC_PCLK1Config(RCC_HCLK_Div2); 41 /**/ 42 FLASH_SetLatency(FLASH_Latency_2); 43 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); 44 /*配置PLL*/ 45 RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9); 46 RCC_PLLCmd(ENABLE); 47 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); 48 /*选择SYSCLK时钟源为PLL*/ 49 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); 50 while(RCC_GetSYSCLKSource() != 0x08); 51 } 52 /*使能外设时钟*/ 53 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); 54 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE); 55 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 56 } 57 /*GPIO*/ 58 void GPIO_Configuration(void) 59 { 60 GPIO_InitTypeDef GPIO_InitStructure; 61 /*USART1*/ 62 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;/*USART1 TXD*/ 63 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 64 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 65 GPIO_Init(GPIOA,&GPIO_InitStructure); 66 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;/*USART1 RXD*/ 67 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 68 GPIO_Init(GPIOA,&GPIO_InitStructure); 69 } 70 /*USART*/ 71 void USART_Configuration(void) 72 { 73 USART_InitTypeDef USART_InitStructure; 74 75 USART_InitStructure.USART_BaudRate = 9600; 76 USART_InitStructure.USART_WordLength = USART_WordLength_8b; 77 USART_InitStructure.USART_StopBits = USART_StopBits_1; 78 USART_InitStructure.USART_Parity = USART_Parity_No; 79 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; 80 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 81 USART_Init(USART1,&USART_InitStructure); 82 83 USART_Cmd(USART1,ENABLE); 84 } 85 int fputc(int ch, FILE *f) 86 { 87 USART_SendData(USART1,(u8)ch); 88 while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); 89 return ch; 90 } 91 92 /*DMA*/ 93 void DMA_Configuration(void) 94 { 95 DMA_InitTypeDef DMA_InitStructure; 96 97 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)srcTable; 98 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dstTable; 99 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC ; 100 DMA_InitStructure.DMA_BufferSize = 4; 101 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ; 102 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable ; 103 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; 104 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 105 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; 106 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh ; 107 DMA_InitStructure.DMA_M2M = DMA_M2M_Enable ; 108 DMA_Init(DMA1_Channel5,&DMA_InitStructure); 109 110 DMA_Cmd(DMA1_Channel5,ENABLE); 111 }
软件仿真结果显示,全部数据都被复制过去了
结论
参考手册对DMA_CNDTRx寄存器描述有误,其代表的是待传输单位(依赖于配置寄存器的设置,字节、半字、字)数目。
问题2 DMA的外设请求信号
描述
在DMA请求映像中,固定的几个外设的请求映像,通过或门连接到一个逻辑选择器中,选择器的输入另外还连接着软件可控的MEM2MEM位。此外逻辑选择器有一个EN使能信号。选择器出来就是DMA各个通道的请求。如下图所示。
试想,如果DMA1通道5由USART1_RX产生请求信号,然后DMA将内存上的数据搬移到内存上另外一个地方,这是否可行?
实验
将上一个实验的请求由软件触发换成USART1_RX。
u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567};
u16 dstTable[TABLE_LENGTH] = {0};
如果可行,dstTable将变成srcTable中的值。
1 #include "STM32f10x_lib.h" 2 #include "stdio.h" 3 4 void RCC_Configuration(void); 5 void GPIO_Configuration(void); 6 void USART_Configuration(void); 7 void DMA_Configuration(void); 8 9 #define TABLE_LENGTH 4 10 u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567}; 11 u16 dstTable[TABLE_LENGTH] = {0}; 12 13 int main(void) 14 { 15 RCC_Configuration(); 16 GPIO_Configuration(); 17 USART_Configuration(); 18 printf("before:0x%x,0x%x,0x%x,0x%x\r\n",dstTable[0],dstTable[1],dstTable[2],dstTable[3]); 19 DMA_Configuration(); 20 while(DMA_GetFlagStatus(DMA1_FLAG_TC5) == RESET); 21 printf("%d\r\n",DMA_GetFlagStatus(DMA1_FLAG_GL1)); 22 printf("after:0x%x,0x%x,0x%x,0x%x\r\n",dstTable[0],dstTable[1],dstTable[2],dstTable[3]); 23 while(1); 24 } 25 26 /*RCC*/ 27 void RCC_Configuration(void) 28 { 29 ErrorStatus HSEStartUpStatus; 30 /*默认状态*/ 31 RCC_DeInit(); 32 /*HSE使能并等待起震*/ 33 RCC_HSEConfig(RCC_HSE_ON); 34 HSEStartUpStatus = RCC_WaitForHSEStartUp(); 35 /*HSE外部高速晶振启动成功*/ 36 if(HSEStartUpStatus == SUCCESS) 37 { 38 /*配置HCLK,PCLK1,PCLK2分频*/ 39 RCC_HCLKConfig(RCC_SYSCLK_Div1); 40 RCC_PCLK2Config(RCC_HCLK_Div1); 41 RCC_PCLK1Config(RCC_HCLK_Div2); 42 /**/ 43 FLASH_SetLatency(FLASH_Latency_2); 44 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); 45 /*配置PLL*/ 46 RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9); 47 RCC_PLLCmd(ENABLE); 48 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); 49 /*选择SYSCLK时钟源为PLL*/ 50 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); 51 while(RCC_GetSYSCLKSource() != 0x08); 52 } 53 /*使能外设时钟*/ 54 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); 55 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE); 56 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 57 } 58 /*GPIO*/ 59 void GPIO_Configuration(void) 60 { 61 GPIO_InitTypeDef GPIO_InitStructure; 62 /*USART1*/ 63 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;/*USART1 TXD*/ 64 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 65 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 66 GPIO_Init(GPIOA,&GPIO_InitStructure); 67 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;/*USART1 RXD*/ 68 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 69 GPIO_Init(GPIOA,&GPIO_InitStructure); 70 } 71 /*USART*/ 72 void USART_Configuration(void) 73 { 74 USART_InitTypeDef USART_InitStructure; 75 76 USART_InitStructure.USART_BaudRate = 9600; 77 USART_InitStructure.USART_WordLength = USART_WordLength_8b; 78 USART_InitStructure.USART_StopBits = USART_StopBits_1; 79 USART_InitStructure.USART_Parity = USART_Parity_No; 80 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; 81 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 82 USART_Init(USART1,&USART_InitStructure); 83 /*使能了USART1_RX的DMA请求*/ 84 USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); 85 86 USART_Cmd(USART1,ENABLE); 87 } 88 int fputc(int ch, FILE *f) 89 { 90 USART_SendData(USART1,(u8)ch); 91 while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); 92 return ch; 93 } 94 95 /*DMA*/ 96 void DMA_Configuration(void) 97 { 98 DMA_InitTypeDef DMA_InitStructure; 99 100 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)srcTable; 101 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dstTable; 102 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC ; 103 DMA_InitStructure.DMA_BufferSize = 4; 104 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ; 105 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable ; 106 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 107 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 108 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; 109 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh ; 110 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable ; 111 DMA_Init(DMA1_Channel5,&DMA_InitStructure); 112 113 DMA_Cmd(DMA1_Channel5,ENABLE); 114 }
串口调试助手显示搬运前的dstTable中值全为0.在串口发送任意字符后,打印出了搬运后的数值,与srcTable中无异。
结论
由这个实验现象:DMA1通道5由USART1_RX产生请求信号,使DMA在内存上搬移数据。推广出来,该通道上其它请求信号也可以启动数据的传输。
后记
值得一提的是,DMA不仅支持内存上的数据传输,还支持外设之间,外设到内存,内存到外设的数据传输。说白了,外设、RAM、ROM都是依靠地址寻址的,对DMA来说,无所谓外设或内存,只认地址。如果你设置了ADC作为请求信号,来启动串口数据到内存的传输,而不是将ADC采样数据存放进内存。这也可行,但对我们来说,没有什么意义。
原文:http://www.cnblogs.com/yulongchen/p/3543528.html