zoukankan      html  css  js  c++  java
  • 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采样数据存放进内存。这也可行,但对我们来说,没有什么意义。

  • 相关阅读:
    ubuntu 16.04 tmux
    ubuntu 16.04 samba 文件共享
    ubuntu 16.04 有道词典
    ubuntu bless 16字节每行
    Win7任务栏图标大小调整为等宽
    ubuntu 16.04 vnc server
    ubuntu 16.04 U盘多媒体不自动弹出
    Linux录屏软件
    通过apt-get安装nvidia驱动
    调试X Server
  • 原文地址:https://www.cnblogs.com/yulongchen/p/3543528.html
Copyright © 2011-2022 走看看