STM32实验非正式报告之DMA

前言

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
",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
",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
",dstTable[0],dstTable[1],dstTable[2],dstTable[3]);
 19   DMA_Configuration();
 20     while(DMA_GetFlagStatus(DMA1_FLAG_TC5) == RESET);
 21     printf("%d
",DMA_GetFlagStatus(DMA1_FLAG_GL1));
 22   printf("after:0x%x,0x%x,0x%x,0x%x
",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采样数据存放进内存。这也可行,但对我们来说,没有什么意义。

原文地址:https://www.cnblogs.com/yulongchen/p/3543528.html