stm32f407之DMA(操作寄存器)

八、DMA

       直接內存訪問(DMA)是用來以提供外設和內存、內存和內存之間的高速數據傳輸的。數據可以在沒有任何CPU干預下通過的DMA進行傳輸。這使得CPU資源更傾重與其他操作。

       DMA控制器基於一個複雜的總線矩陣架構,結合了功能強大的雙AHB主總線架構與獨立的FIFO,以優化系統帶寬。

        兩個DMA控制器共有16個數據流(stream),每個數據流可以編程與規定的通道中的一個搭配。

 


DMA的工作模式

1.  單次傳輸

2. 多次傳輸(burst):把數據分成多次傳輸

 

 

DMA的工作模式

 

1. 循環模式:循環模式是可用來處理循環緩衝區和連續的數據流(如ADC掃描模式)。啓此功能可以設置DMA_SxCR寄存器的CIRC位啓用。

 

在循環模式,在burst方式下,它必須遵循下面的規則

DMA_SxNDTR 等於 ((Mburst beat) × (Msize)/(Psize))的整數倍。

 

2.     雙緩衝模式:雙緩衝模式通過設置在DMA_SxCR寄存器的DBM位啓用。

雙緩衝模式與單緩衝模式的區別在於它有兩個地址,當栓緩衝模式被使能,循環模式會被自動使能,每次傳輸完成,內存地址將會被交換。當一個內存區域被DMA控制器使用時,另一個可供程序使用。

 

如果需要改變內存地址,需要遵循以下規則:

DMA_SxCR 寄存器的CT 位爲 0時,DMA_SxM1AR寄存器可以被改變

DMA_SxCR 寄存器的CT 位爲 1時,DMA_SxM0AR寄存器可以被改變

 

 


    設置步驟:

1.     使能相關時鐘。

2.     如果數據流已啓用,通過重置在DMA_SxCR寄存器的EN位禁用它,然後讀取該位,以確認有沒有持續的數據流操作。向該位寫0不會立即生效,因爲它實際上是在全部轉移完成時才被清除的。當EN位讀爲0,這意味着數據流是準備好,可以進行配置了。因此,任何數據流配置之前,有必要等待EN位被清除。在數據流重新啓動前,前一次DMA傳輸中的狀態寄存器(DMA_LISR and DMA_HISR)所有數據流專用的位應該被清除。

3.      在DMA_SxPAR寄存器中設置外設端口寄存器的地址。

4.     在DMA_SxMA0R寄存器中設置內存的地址(在雙緩衝模式的情況下,還需在DMA_SxMA1R寄存器中設置內存的地址)。

5.     在DMA_SxNDTR寄存器中設置傳輸的數據總數,每個外設的事件或每次burst傳輸之後,這個值是將會遞減。

6.      在DMA_SxCR寄存器使用CHSEL[2:0] 選擇DMA通道。

7.      如果外設爲流量控制器,它支持此功能,設置DMA_SxCR寄存器中PFCTRL的位。

8.      在DMA_SxCR寄存器的PL[1:0]位配置數據流優先級。

9.     配置FIFO(啓用或禁用,發送和接收的閥值)。

10.  在DMA_SxCR寄存器中配置數據傳輸的方向,外設和內存遞增/固定的模式,單個或burst傳輸,外設和存儲器的數據寬度,循環模式,雙緩衝模式和完成幾分之幾後中斷。

11.   激活設置數據流在DMA_SxCR寄存器的EN位。

12.  使能相關外設寄存器的DMA模式位,啓動傳輸。

 

 

程序:

/*********************************************
    標題:操作DMA的練習
    軟件平臺:IAR for ARM6.21
    硬件平臺:stm32f4-discovery
    主頻:168M
    
    描述:從其他設備接收數據,再把數據發送出去
          USART3接收中斷,發射用DMA
    author:小船
    data:2012-02-03
**********************************************/

#include <stm32f4xx.h> 

u8 USART_DMA_Completed;
u8 Rx_Completed;
u8 Rx_data_counter;
u8 usart3_buffer[100];

void USART3_DMA_config(void);
void USART3_config(void);

void main ()
{ 
  
  SCB->AIRCR = 0x05FA0000 | 0x400;  //中斷優先級分組 搶佔:響應=3:1
  
  RCC->AHB1ENR |= ( (1<<3) | (1<<21) ); //使能GPIOD時鐘,使能DMA1時鐘
  RCC->APB1ENR |= (1<<18);  //使能usart3時鐘
  
  USART3_DMA_config();
  USART3_config();
  
  USART_DMA_Completed = 1;
  
  while(1)
  {  
    if(USART_DMA_Completed & Rx_Completed)  //之前數據已經發送完成,接收到新的數據
    {  
      DMA1_Stream3->CR &= 0xFFFFFFFE; //除能DMA1_Stream3
      while(DMA1_Stream3->CR & 0x00000001);//確保DMA可以被設置     
      DMA1->LIFCR |= 0x0f800000;//傳送前清空DMA1_Stream3所有中斷標誌      
      DMA1_Stream3->NDTR = Rx_data_counter; //設置dma傳輸數據的數量
      if((USART3->SR & (1<<7))) //發送數據寄存器空
      {
        USART3->CR3 &= ~(1<<7);//除能usartdma發送
        USART_DMA_Completed = 0;
        DMA1_Stream3->NDTR = Rx_data_counter; //設置dma傳輸數據的數量
        DMA1_Stream3->CR |= 1;//使能dma
        USART3->CR3 |= (1<<7);//使能usartdma發送
        Rx_Completed = 0;
        Rx_data_counter = 0;
      }
    }
  }
}

/****************************************
  函數名:USART3_DMA_config
  參數:無
  返回值:無
  功能:DMA1數據流3與usart3關聯的相關配置
****************************************/
void USART3_DMA_config(void)
{
  DMA1_Stream3->CR &= 0xFFFFFFFE; //除能DMA1_Stream3
  while(DMA1_Stream3->CR & 0x00000001);//確保DMA可以被設置
  
  DMA1->LIFCR |= 0x0f800000;//傳送前清空DMA1_Stream3所有中斷標誌
  
  DMA1_Stream3->PAR = (uint32_t)&USART3->DR;//設置外設地址USART3->DR地址0x40004804
  DMA1_Stream3->M0AR = (uint32_t)usart3_buffer; //設置內存地址
  DMA1_Stream3->NDTR = Rx_data_counter; //設置dma傳輸數據的數量
  //DMA1_Stream3->FCR |= 0x00000007;//設置fifo
  /*
    設置dma通道4,即usart3tx
    優先級Medium
    傳輸方向內存到外設
    內存遞增模式
    傳輸完成中斷使能
  */
  DMA1_Stream3->CR |= ( 0x08000000 |0x00010000 | (1<<6)
                        | (1<<10) | (1<<4) ); 

  USART3->CR3 &= ~(1<<7);//usart3 dma發送模式除能
  
  NVIC->IP[14] = 0xA0;
  NVIC->ISER[0] |= (1<<14);
  
}

/**************************
  函數名:USART3_config
  參數:無
  返回值:無
  功能:配置usart3
************************/
void USART3_config(void)
{
  USART3->BRR = 0x0000016C;   //波特率115200
  /*
   使能usart3
  usart3發送使能
  usart3接收使能
  接收緩衝區非空中斷使能
  8bit
  一位停止位
  無校驗
  */
  USART3->CR1 |= (( 1<<13 ) | ( 1<<3 ) | ( 1<<2 ) | ( 1<<5 )); 
  
  GPIOD->AFR[1] |= 0x00000077;//選擇PD8,9複用功能 
  
  GPIOD->MODER &= 0xFFF0FFFF; //設置PD8,9,複用模式
  GPIOD->MODER |= 0x000A0000; 
  
//  GPIOD->OTYPER &= 0xFFFFDFFF; //設置PD9推輓輸出
  
  GPIOD->OSPEEDR &= 0xFFFCFFFF; //PD8速度50m
  GPIOD->OSPEEDR |= 0x00020000;
  
  GPIOD->PUPDR &= 0xFFFCFFFF; //PD8
  GPIOD->PUPDR |= 0x00010000;
  
  NVIC->IP[39] = 0xf0; //最低搶佔優先級,最低響應優先級1111
  NVIC->ISER[1] |= (1<<(39-32)); //使能中斷線39,也就是usart3中斷
}


void USART3_IRQHandler(void)
{
  if(USART3->SR & (1<<5)) //接收數據寄存器非空
  {
    usart3_buffer[Rx_data_counter] = USART3->DR;
    Rx_data_counter++;
    if(usart3_buffer[Rx_data_counter - 1] == '\r')
    {
      USART3->CR1 &= ~(1<<5); //除能接收中斷
      Rx_Completed = 1;
    }
   } 
}

void DMA1_Stream3_IRQHandler(void)
{
  if(DMA1->LISR & 0x08000000)//DMA傳輸完成
  {
    USART_DMA_Completed = 1;
    USART3->CR1 |= 1<<5;  //使能usart3接收中斷
    DMA1->LIFCR |= 0x08000000;//清除中斷標誌
  }
}

運行結果:






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章