前言 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;否则,全部数据都被复制过去了。
- #include "STM32f10x_lib.h"
- #include "stdio.h"
- void RCC_Configuration(void);
- void GPIO_Configuration(void);
- void USART_Configuration(void);
- void DMA_Configuration(void);
- #define TABLE_LENGTH 4
- u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567};
- u16 dstTable[TABLE_LENGTH] = {0};
- int main(void)
- {
- RCC_Configuration();
- GPIO_Configuration();
- USART_Configuration();
- printf("before:0x%x,0x%x,0x%x,0x%xrn",dstTable[0],dstTable[1],dstTable[2],dstTable[3]);
- DMA_Configuration();
- while(DMA_GetFlagStatus(DMA1_FLAG_TC5) == RESET);
- printf("after:0x%x,0x%x,0x%x,0x%xrn",dstTable[0],dstTable[1],dstTable[2],dstTable[3]);
- while(1);
- }
- /*RCC*/
- void RCC_Configuration(void)
- {
- ErrorStatus HSEStartUpStatus;
- /*默认状态*/
- RCC_DeInit();
- /*HSE使能并等待起震*/
- RCC_HSEConfig(RCC_HSE_ON);
- HSEStartUpStatus = RCC_WaitForHSEStartUp();
- /*HSE外部高速晶振启动成功*/
- if(HSEStartUpStatus == SUCCESS)
- {
- /*配置HCLK,PCLK1,PCLK2分频*/
- RCC_HCLKConfig(RCC_SYSCLK_Div1);
- RCC_PCLK2Config(RCC_HCLK_Div1);
- RCC_PCLK1Config(RCC_HCLK_Div2);
- /**/
- FLASH_SetLatency(FLASH_Latency_2);
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
- /*配置PLL*/
- RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
- RCC_PLLCmd(ENABLE);
- while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
- /*选择SYSCLK时钟源为PLL*/
- RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
- while(RCC_GetSYSCLKSource() != 0x08);
- }
- /*使能外设时钟*/
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
- }
- /*GPIO*/
- void GPIO_Configuration(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- /*USART1*/
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;/*USART1 TXD*/
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;/*USART1 RXD*/
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
- }
- /*USART*/
- void USART_Configuration(void)
- {
- USART_InitTypeDef USART_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_Mode = USART_Mode_Rx | USART_Mode_Tx;
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
- USART_Init(USART1,&USART_InitStructure);
-
- USART_Cmd(USART1,ENABLE);
- }
- int fputc(int ch, FILE *f)
- {
- USART_SendData(USART1,(u8)ch);
- while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
- return ch;
- }
- /*DMA*/
- void DMA_Configuration(void)
- {
- DMA_InitTypeDef DMA_InitStructure;
-
- DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)srcTable;
- DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dstTable;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC ;
- DMA_InitStructure.DMA_BufferSize = 4;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
- DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh ;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Enable ;
- DMA_Init(DMA1_Channel5,&DMA_InitStructure);
-
- DMA_Cmd(DMA1_Channel5,ENABLE);
- }
复制代码
结论 参考手册对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中的值。
- #include "STM32f10x_lib.h"
- #include "stdio.h"
- void RCC_Configuration(void);
- void GPIO_Configuration(void);
- void USART_Configuration(void);
- void DMA_Configuration(void);
- #define TABLE_LENGTH 4
- u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567};
- u16 dstTable[TABLE_LENGTH] = {0};
- int main(void)
- {
- RCC_Configuration();
- GPIO_Configuration();
- USART_Configuration();
- printf("before:0x%x,0x%x,0x%x,0x%xrn",dstTable[0],dstTable[1],dstTable[2],dstTable[3]);
- DMA_Configuration();
- while(DMA_GetFlagStatus(DMA1_FLAG_TC5) == RESET);
- printf("%drn",DMA_GetFlagStatus(DMA1_FLAG_GL1));
- printf("after:0x%x,0x%x,0x%x,0x%xrn",dstTable[0],dstTable[1],dstTable[2],dstTable[3]);
- while(1);
- }
- /*RCC*/
- void RCC_Configuration(void)
- {
- ErrorStatus HSEStartUpStatus;
- /*默认状态*/
- RCC_DeInit();
- /*HSE使能并等待起震*/
- RCC_HSEConfig(RCC_HSE_ON);
- HSEStartUpStatus = RCC_WaitForHSEStartUp();
- /*HSE外部高速晶振启动成功*/
- if(HSEStartUpStatus == SUCCESS)
- {
- /*配置HCLK,PCLK1,PCLK2分频*/
- RCC_HCLKConfig(RCC_SYSCLK_Div1);
- RCC_PCLK2Config(RCC_HCLK_Div1);
- RCC_PCLK1Config(RCC_HCLK_Div2);
- /**/
- FLASH_SetLatency(FLASH_Latency_2);
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
- /*配置PLL*/
- RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
- RCC_PLLCmd(ENABLE);
- while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
- /*选择SYSCLK时钟源为PLL*/
- RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
- while(RCC_GetSYSCLKSource() != 0x08);
- }
- /*使能外设时钟*/
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
- }
- /*GPIO*/
- void GPIO_Configuration(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- /*USART1*/
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;/*USART1 TXD*/
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;/*USART1 RXD*/
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
- }
- /*USART*/
- void USART_Configuration(void)
- {
- USART_InitTypeDef USART_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_Mode = USART_Mode_Rx | USART_Mode_Tx;
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
- USART_Init(USART1,&USART_InitStructure);
- /*使能了USART1_RX的DMA请求*/
- USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
-
- USART_Cmd(USART1,ENABLE);
- }
- int fputc(int ch, FILE *f)
- {
- USART_SendData(USART1,(u8)ch);
- while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
- return ch;
- }
- /*DMA*/
- void DMA_Configuration(void)
- {
- DMA_InitTypeDef DMA_InitStructure;
-
- DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)srcTable;
- DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dstTable;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC ;
- DMA_InitStructure.DMA_BufferSize = 4;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
- DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh ;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable ;
- DMA_Init(DMA1_Channel5,&DMA_InitStructure);
-
- DMA_Cmd(DMA1_Channel5,ENABLE);
- }
复制代码
串口调试助手显示搬运前的dstTable中值全为0.在串口发送任意字符后,打印出了搬运后的数值,与srcTable中无异。
结论 由这个实验现象:DMA1通道5由USART1_RX产生请求信号,使DMA在内存上搬移数据。推广出来,该通道上其它请求信号也可以启动数据的传输。
后记
值得一提的是,DMA不仅支持内存上的数据传输,还支持外设之间,外设到内存,内存到外设的数据传输。说白了,外设、RAM、ROM都是依靠地址寻址的,对DMA来说,无所谓外设或内存,只认地址。如果你设置了ADC作为请求信号,来启动串口数据到内存的传输,而不是将ADC采样数据存放进内存。这也可行,但对我们来说,没有什么意义。
0
|
|
|
|