STM32F0 DMA串口接收數據

作者:良知猶存

轉載授權以及圍觀->歡迎添加WxId:Allen-Iverson-me-LYN

 

DMA,全稱Direct Memory Access,即直接存儲器訪問。DMA傳輸將數據從一個地址空間複製到另一個地址空間,提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。當CPU初始化這個傳輸動作,傳輸動作本身是由DMA控制器來實現和完成的。DMA傳輸方式無需CPU直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢復現場過程,通過硬件爲RAM和IO設備開闢一條直接傳輸數據的通道,使得CPU的效率大大提高。

主要特點:

 

•DMA上多達7個可獨立配置的通道(請求)

 

•每個通道都連接到專用的硬件DMA請求,軟件觸發爲

每個頻道也都支持。該配置由軟件完成。

•DMA通道的請求之間的優先級是軟件可編程的(4

級別由非常高,高,中,低)或硬件組成(在相等的情況下)

(請求1的優先級高於請求2的優先級,依此類推)

•獨立的源和目標傳輸大小(字節,半字,字),模擬

包裝和拆箱。源/目標地址必須與數據對齊

尺寸。

•支持循環緩衝區管理

•3個事件標誌(DMA半傳輸,DMA傳輸完成和DMA傳輸錯誤)

在每個通道的單箇中斷請求中進行邏輯或運算

•內存到內存的傳輸

•外圍到內存和內存到外圍,以及外圍到外圍

轉移

•訪問閃存,SRAM,APB和AHB外設作爲源和目標

•可編程的數據傳輸數量:最多65535

DMA通道對應的外設情況(F0系列):

 

    很多博文會講解裏面的詳細定義,對於每一處寄存器的詳細操作指導,所以我就不會去多寫了。

    我只想表達,DMA是一種可以快速相關外設數據交互的一種方法,我們學習哪裏用它,怎麼用它,至於細節學習,大家去網上所搜DMA相信息即可。    

    之前講過DMA的數據發送,現在補充上DMA的接收數據部分。

  利用DMA接收串口數據的配置,大致分爲:1.初始化串口並開啓DMAR接收功能,配置DMA的外設到內存的數據接收功能,2.等待串口中斷提示,並進行處理數據,3.清空DMA,重新等待數據

 

01 配置串口與DMA

 

    其中USART2->CR3寄存器的第6 bit用來設置DMA的接收配置,此處比較重要我們設置爲USART_CR3_DMAR。 USART2->CR1寄存器中開啓幀信息接收完成之後的中斷,USART_CR1_IDLEIE,這處可以幫助我們節省CPU的不必要開支,開啓此處中斷類型,我們只需要在每幀信息接收完成之後,usart才觸發中斷,我們再解析。其餘爲正常的usart配置。

GPIOD->MODER |=   GPIO_MODER_MODER5_1|  //USART2_TX                  GPIO_MODER_MODER6_1;  //USART2_RX    /* set baudrate */  USART2->BRR = USART_Baudrate;//115200  USART2->CR3 |= USART_CR3_DMAT|USART_CR3_DMAR;//USART_CR3_OVRDIS;// 不需要覆寫  /*enable usart2 and enable tx*/  USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE;

 

配置DMA:

    首先根據上方的映射表,我們初始化了usart2,而usart2 RX對應的DMA通道爲DMA1 channel5,所以我們進行DMA1 ch5通道配置。

第一個爲DMA1_Channel5->CPAR寄存器,在下表可知,用來設置對應接收數據的外設的地址,而USART的接收數據的寄存器爲RDR,所以寄存器配置爲DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);

 

    設置需要放數據的指定內存位置,根據指導手冊可知CMAR爲DMA通道用來放置數據的內存地址,所以此處設置爲我們定義好的變量的地址。

    設置單次傳送數據量的大小,此處最大可設置爲65535byte的數據大小。

最後開啓DMA通道。

void USART2_DMA_Recive(uint8_t *p_data,uint16_t length){  DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);          //Peripheral address  DMA1_Channel5->CMAR = (uint32_t)p_data;                  //memory address  DMA1_Channel5->CNDTR = length;                            //Set the length  DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_EN;}

 

 

02  等待串口中斷,處理數據

    此時在debug中,在我們定義好的DMA內存變量,地址在RAM初始完成,後續串口數據接收的時候,DMA會直接將數據置於p_data所對應的內存。

    然後在中斷服務函數我們可以將我們數據進行處理,Uart_Channel_isr[1](USART2->RDR);,這個函數爲我自己寫的處理函數,中斷裏面函數都是數據複製,所以我直接在中斷執行,一般大家會在中斷寫個標誌,在主循環進行解析。但是DMA接收配合USART_ISR_IDLE標誌在STM32平臺下並不友好,如果主循環用來解析數據的時候,空閒中斷還沒有產生,本幀數據還尚未接收完成,但是由於內存數據已經在實時寫入,DMA1_Channel5->CNDTR已經有所變化,並早於空閒中斷產生,所以主循環就不能用DMA1_Channel5->CNDTR所接收數據長度進行解析了。一般建議,如果解析數據只是單純的挪移,此時候直接在中斷處理即可,對主程序並沒有大的阻塞。

void USART2_IRQHandler(void){  if ((USART2->ISR & USART_ISR_ORE) == USART_ISR_ORE)   {      USART2->ICR |= USART_ICR_ORECF;  }    if ((USART2->ISR & USART_ISR_IDLE) == USART_ISR_IDLE)  //The new frame data receive   {    USART2->ICR |= USART_ICR_IDLECF;        Uart_Channel_isr[1](USART2->RDR);/*讀取解析數據*/   }  }

 

 

03 恢復DMA,清空CNDTR,等待下次數據到來

 

    處理完數據之後,及時將DMA標誌以及CNDTR清空,否則CNDTR一直不清空,會導致下次接收數據的時候,造成通道的佔用,數據使用過之後,就清理掉CNDTR,這樣保證每次接收數據的通道有足夠的位置。

 

static void Usart2_Dma_Reload(uint16_t length)/*清空數據*/{  DMA1_Channel5->CCR &= ~(DMA_CCR_EN);             //disable the dma   DMA1_Channel5->CNDTR = length;          //Set the length  DMA1_Channel5->CCR |= DMA_CCR_EN;                //enable the dma}

 

                                                                  ————————END————————

                                                           如果大家覺得有用,可以關注我,有更多的文章。

 

 

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