MM32F0140 UART1 DMA Interrupt RX and TX (UART1 DMA中斷接收和DMA中斷髮送資料)

Armny發表於2022-03-25

目錄:

1.MM32F0140簡介

2.DMA工作原理簡介

3.初始化MM32F0140 UART1

4.配置MM32F0140 UART1 DMA接收

5.配置MM32F0140 UART1 DMA傳送

6.編寫MM32F0140 UART1 中斷優先順序函式

7.編寫MM32F0140 UART1 DMA中斷函式

8.編寫MM32F0140 UART1 DMA接收資料函式

9.編寫MM32F0140 UART1 DMA傳送資料函式

10.編寫處理MM32F0140 UART1 DMA中斷接收和DMA中斷髮送資料函式

11.測試MM32F0140 UART1 DMA中斷接收和DMA中斷髮送資料函式

提要:

  學習MM32F0140 UART1 DMA 中斷接收和 UART1 DMA中斷髮送資料,通過上位機串列埠助手傳送10位元組的十六進位制資料:0x01,0x02,0x03,0x04,0x05,0x06,0x7,0x08,0x55,0xAA;下位機MM32F0140的UART1的DMA中斷接收到一幀:0x01,0x02,0x03,0x04,0x05,0x06,0x7,0x08,0x55,0xAA 共10位元組資料後,通過UART1 DMA中斷髮送資料函式,原樣傳送到串列埠助手顯示出來。

內容:

1、MM32F0140簡介

  (1)MM32F0140微控制器是基於Arm® Cortex®-M0核心,最高工作頻率可達72MHz;

  (2)供電電壓支援:2.0V - 5.5V;

  (3)多達64KB的Flash,8KB的SRAM;

  (4)1個I2C;

  (5)3個UART;

  (6)1個12位共13通道的ADC;

  (7)2個I2C或I2S;

  (8)1個16位高階定時,1個16位和1個32位的通用定時器,3個16位的基本定時器;

  (9)1個FlexCAN介面;

  (10)1個IWDG和1個WWDG看門狗。

2.DMA工作原理簡介

  DMA的工作原理:

  DMA(Direct Memory Access)即直接儲存器訪問。DMA 控制器通過共享系統匯流排,實現無需 CPU 參與的快速自動資料傳輸。MM32F0140的DMA 控制器有 5 個通道,多個外設 DMA 請求傳送到對應通道上處理。DMA 與 CPU 都是通過系統匯流排實現對儲存器或外設資料的訪問。當 CPU 和 DMA 訪問衝突時,DMA 請求可能會佔用系統匯流排,此時 CPU 只能等待 DMA 傳輸完成釋放匯流排。為了防止匯流排一直被DMA 佔用導致 CPU 無法工作,匯流排仲裁器會執行相關的迴圈排程,以此保證 CPU 至少可以獲得一半的系統匯流排控制權。

  DMA傳輸將資料從一個地址空間搬運到另一個地址空間,支援外設到儲存器之間或者儲存器到外設之間的高速資料傳輸。當CPU初始化這個傳輸動作,傳輸動作本身是由DMA控制器來實現和完成的。DMA傳輸方式無需CPU直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢復現場過程,通過硬體為RAM和IO裝置開闢一條直接傳輸資料的通道,使得CPU的效率大大提高。

  MM32F0140的DMA特性

(1)5 個獨立的通道,可通過暫存器配置相關功能。

(2)硬體發出的 DMA 請求與對應專用 DMA 通道直連。通過軟體配置暫存器的方式也可以觸發DMA 通道請求

(3)可以通過軟體的方式配置暫存器決定 5 個通道請求之間的處理優先順序(共有四級: 很高、高、中等和低),若優先順序相同,則由硬體自動決定,處理順序(低編號通道請求優先處理)。

(4)資料來源頭與目的地的傳輸寬度可獨立配置為位元組、半字、全字。

(5)獨立資料來源頭的寬度配置進行打包,並在目的地按照目的地的寬度配置進行拆包。要求源和目標地址必須根據各自配置的資料傳輸寬度對齊。

(6)支援迴圈緩衝器控制。

(7)每個通道支援 DMA 半傳輸, DMA 傳輸完成和 DMA 傳輸出錯 3 種事件標誌。各通道單獨的中斷請求由這 3 種事件標誌邏輯或起來。

(8)支援儲存器對儲存器傳輸。

(9)支援資料傳輸方向為外設到儲存器,儲存器到外設。

(10)資料訪問的源和目標可以是: SRAM、 APB1、 APB2 和 AHB 匯流排上的外設。

(11)資料的傳輸數量可以通過軟體配置對應暫存器,最大值為 65535

  DMA請求映像:

  外設對DMA的請求映像如下圖1所示,從外設產生的多個傳輸請求,通過 DMAMUX 輸入到 DMA 控制器,為了避免衝突,在一個通道中,同時只能有一個外設 DMA 請求有效。

外設本身的控制暫存器應有對應的 DMA 使能位,來獨立控制外設是否傳送傳輸請求。

  如下圖1表所示,本例項UART1的DMA請求映像選擇DMA1的通道2對應UART1_TX,DMA1通道3對應UART1_RX。

3.初始化MM32F0140 UART1

  MM32F0140 UART1的GPIO初始化,根據MM32F0140的DS資料手冊選擇PA9:UART1_TX,PA10:UART1_RX做為UART1的傳送和接收資料的引腳,具體配置步驟,及其初始化如下所示:

(1)使能GPIOA外設時鐘;

(2)配置IO管腳GPIO_AFx複用為UART1功能(可參考DS手冊引腳定義及複用功能);

(3)配置UARTx IO的管腳;

(4)配置GPIO的輸出速度;

(5)配置IO管腳的工作模式;

(6)根據GPIOA配置的引數整體初始化GPIO各管腳的成員引數。

static void Bsp_UART1_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
    //PA9 AF UART1_TX
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
    //PA10 AF UART1_RX
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);
    
    //PA9:UART1_TX   
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    //PA10:UART1_RX   
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

4.配置MM32F0140 UART1 DMA接收

  配置MM32F0140 UART1 DMA接收功能,配置步驟如下步驟(1)到(19):

(1)使能外設DMA1的時鐘;

(2)復位DMA通道暫存器;

(3)初始化DMA結構體成員為自定義引數;

(4)配置DMA傳輸外設基地址;

(5)配置DMA傳輸記憶體基地址;

(6)配置DMA的傳輸方向;

(7)配置DMA傳輸的快取大小;

(8)配置DMA傳輸外設地址是否遞增;

(9)配置DMA傳輸記憶體地址是否遞增;

(10)配置DMA傳輸外設資料寬度(位元組或半字或字,配置需與記憶體資料寬度一致);

(11)配置DMA傳輸記憶體資料寬度(位元組或半字或字,配置需與外設資料寬度一致);

(12)配置DMA傳輸的工作模式(常規或迴圈模式);

(13)配置DMA傳輸的軟體優先順序(很高、高、中等、低);

(14)配置DMA傳輸是否使能記憶體到記憶體;

(15)配置DMA是否自動裝載傳輸數量暫存器;

(16)根據以上(1)到(15)的配置整體初始化DMA通道的結構體成員引數;

(17)使能DMA傳輸完成中斷;

(18)使能UART的DMA介面;

(19)使能DMA通道。

  定義與MM32F0140 UART1 DMA接收和傳送相關的變數標誌,快取,以及標頭檔案宣告變數標誌、快取,函式宣告,具體程式碼如下所示:

//UART1 receive completion flag
vu8 gUART1_RxComplete = 0;
//UART1 send complete flag
vu8 gUART1_TxComplete = 0;
//UART1 send buffer
u8 gUART1_TxBuf[10] = {0x00};
//UART1 receive buffer
u8 gUART1_RxBuf[100] = {0x00};
//UART1 Baud rate
#define UART1_BAUDRATE  (115200)

//UART1 receive completion flag
extern vu8 gUART1_RxComplete;
//UART1 send complete flag
extern vu8 gUART1_TxComplete;
//UART1 send buffer
extern u8 gUART1_TxBuf[10];
//UART1 receive buffer
extern u8 gUART1_RxBuf[100];

//UART1 Init
void Bsp_UART1_Init(u32 baudrate);
//Configure UART1 DMA transmission
void Bsp_UART1_DMA_NVIC_Send_Config(DMA_Channel_TypeDef* dam_chx, u32 peraddr, u32 memaddr, u16 cndtr);
//Configure UART1 DMA reception
void Bsp_UART1_DMA_NVIC_Recv_Config(DMA_Channel_TypeDef* dam_chx, u32 peraddr, u32 memaddr, u16 cndtr);
//NVIC interrupt priority
void Bsp_NVIC_Init(u8 ch, u8 pri);  
//Process UART1 DMA interrupt to receive and interrupt to transmit data
void Bsp_UART1_DMA_Rx_DMA_Tx_Task(void);
//UART1 DMA send data
void Bsp_UART1_DMA_SendData(DMA_Channel_TypeDef* dam_chx, u32 pBuf, u16 length);
//UART1 DMA receive data
void Bsp_UART1_DMA_RecvData(DMA_Channel_TypeDef* dam_chx, u32 pBuf, u16 length);

  根據以上(1)到(19)配置步驟配置MM32F0140 UART1 DMA接收功能的程式碼如下所示:

void Bsp_UART1_DMA_NVIC_Recv_Config(DMA_Channel_TypeDef* dam_chx, u32 peraddr, u32 memaddr, u16 cndtr)
{
    DMA_InitTypeDef DMA_InitStruct;

    //Enable DMA1 Clock
    RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE);
    
    //Deinitializes the DMA Channeln registers to their default reset
    DMA_DeInit(dam_chx);
    DMA_StructInit(&DMA_InitStruct);
    //DMA transfer peripheral address
    DMA_InitStruct.DMA_PeripheralBaseAddr = peraddr;
    //DMA transfer memory address
    DMA_InitStruct.DMA_MemoryBaseAddr = memaddr;
    //DMA transfer direction from peripheral to memory
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
    //DMA cache size
    DMA_InitStruct.DMA_BufferSize = cndtr;
    //After receiving the data, the peripheral address is forbidden to move
    //backward
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    //After receiving the data, the memory address is shifted backward
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    //Define the peripheral data width to 8 bits
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
    //M2M mode is disabled
    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_reload = DMA_Auto_Reload_Enable;
    DMA_Init(dam_chx, &DMA_InitStruct);

    //Enable UARTx DMA1 Channel Transfer complete interrupt
    DMA_ITConfig(dam_chx, DMA_IT_TC, ENABLE);

    UART_DMACmd(UART1, UART_GCR_DMA, ENABLE);
    // UARTx DMA1 Channel Enable
    DMA_Cmd(dam_chx, ENABLE);
}

5.配置MM32F0140 UART1 DMA傳送

  配置MM32F0140 UART1 DMA傳送功能,配置步驟如下步驟(1)到(19):

(1)使能外設DMA1的時鐘;

(2)復位DMA通道暫存器;

(3)初始化DMA結構體成員為自定義引數;

(4)配置DMA傳輸外設基地址;

(5)配置DMA傳輸記憶體基地址;

(6)配置DMA的傳輸方向;

(7)配置DMA傳輸的快取大小;

(8)配置DMA傳輸外設地址是否遞增;

(9)配置DMA傳輸記憶體地址是否遞增;

(10)配置DMA傳輸外設資料寬度(位元組或半字或字,配置需與記憶體資料寬度一致);

(11)配置DMA傳輸記憶體資料寬度(位元組或半字或字,配置需與外設資料寬度一致);

(12)配置DMA傳輸的工作模式(常規或迴圈模式);

(13)配置DMA傳輸的軟體優先順序(很高、高、中等、低);

(14)配置DMA傳輸是否使能記憶體到記憶體;

(15)配置DMA是否自動裝載傳輸數量暫存器;

(16)根據以上(1)到(15)的配置整體初始化DMA通道的結構體成員引數;

(17)使能DMA傳輸完成中斷;

(18)使能UART的DMA介面;

(19)使能DMA通道。

  根據以上(1)到(19)配置步驟配置MM32F0140 UART1 DMA傳送功能的程式碼如下所示:

void Bsp_UART1_DMA_NVIC_Send_Config(DMA_Channel_TypeDef* dam_chx, u32 peraddr, u32 memaddr, u16 cndtr)
{
    DMA_InitTypeDef DMA_InitStruct;
    
    //Enable DMA1 Clock
    RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE);
    //Deinitializes the DMA Channeln registers to their default reset
    DMA_DeInit(dam_chx);
    DMA_StructInit(&DMA_InitStruct);
    //DMA transfer peripheral address
    DMA_InitStruct.DMA_PeripheralBaseAddr = peraddr;
    //DMA transfer memory address
    DMA_InitStruct.DMA_MemoryBaseAddr = memaddr;
    //DMA transfer direction from peripheral to memory
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;
    //DMA cache size
    DMA_InitStruct.DMA_BufferSize = cndtr;
    //After receiving the data, the peripheral address is forbidden to move
    //backward
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    //After receiving the data, the memory address is shifted backward
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    //Define the peripheral data width to 8 bits
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
    //M2M mode is disabled
    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_reload = DMA_Auto_Reload_Enable;
    DMA_Init(dam_chx, &DMA_InitStruct);

    //Enable UARTx_DMA1_Channel Transfer complete interrupt
    DMA_ITConfig(dam_chx, DMA_IT_TC, ENABLE);
    UART_DMACmd(UART1, UART_GCR_DMA, ENABLE);
    // UARTx DMA1 Channel enable
    DMA_Cmd(dam_chx, DISABLE);
}

6.編寫MM32F0140 UART1 DMA中斷優先順序函式

  MM32F0140 NVIC中斷優先順序函式程式碼如下所示,配置MM32F0140 UART1 DMA中斷優先順序參考以上圖1或UM手冊的DMA通道對映表即可,從查表得知ch:DMA1_Channel2_3_IRQn,通道2可配置為對應UART1的DMA傳送,通道3可配置為對應UART1的DMA接收。

void Bsp_NVIC_Init(u8 ch, u8 pri)
{
    NVIC_InitTypeDef  NVIC_InitStruct;
    //Channel
    NVIC_InitStruct.NVIC_IRQChannel = ch;
    //Priority
    NVIC_InitStruct.NVIC_IRQChannelPriority = pri;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStruct);
}

7.編寫MM32F0140 UART1 DMA中斷函式

  MM32F0140 UART1 DMA中斷函式程式碼如下所示,其中DMA1_IT_TC2為UART1 DMA傳送中的傳輸完成,DMA1_IT_TC3為UART1接收中斷傳輸完成,傳輸完成後分別作標誌。

void DMA1_Channel2_3_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC2)) 
    {
        //Clears the DMA Channeln's interrupt pending bits.
        DMA_ClearITPendingBit(DMA1_IT_TC2);

        //UART1 send complete flag
        gUART1_TxComplete = 1;
    }
    if(DMA_GetITStatus(DMA1_IT_TC3)) 
    {
        //Clears the DMA Channeln's interrupt pending bits.
        DMA_ClearITPendingBit(DMA1_IT_TC3);

        //UART1 receive completion flag
        gUART1_RxComplete = 1;
    }
}

8.編寫MM32F0140 UART1 DMA接收資料函式

  MM32F0140 UART1 DMA接收資料函式程式碼如下所示:

void Bsp_UART1_DMA_RecvData(DMA_Channel_TypeDef* dam_chx, u32 pBuf, u16 length)
{
    //DMA channel x memory address register
    dam_chx->CMAR = pBuf;
    //DMA channel x number of data register
    dam_chx->CNDTR = length;
    //Enables or disables the specified DMA Channeln interrupts.
    DMA_Cmd(dam_chx, ENABLE);
}

9.編寫MM32F0140 UART1 DMA傳送資料函式

  MM32F0140 UART1 DMA傳送資料函式程式碼如下所示:

void Bsp_UART1_DMA_SendData(DMA_Channel_TypeDef* dam_chx, u32 pBuf, u16 length)
{
    //DMA channel x memory address register
    dam_chx->CMAR = pBuf;
    //DMA channel x number of data register
    dam_chx->CNDTR = length;
    DMA_Cmd(dam_chx, ENABLE);
}

10.編寫處理MM32F0140 UART1 DMA中斷接收和DMA中斷髮送資料函式

處理MM32F0140 UART1 DMA中斷接收和DMA中斷髮送資料函式程式碼如下所示:

void Bsp_UART1_DMA_Rx_DMA_Tx_Task(void)
{
    //UART1 receive completion flag
    if(gUART1_RxComplete == 1) 
    {
        gUART1_RxComplete = 0;
        //Enable DMA1 Channel3:UART1 RX 
        DMA_Cmd(DMA1_Channel3, ENABLE);
        //UART1 receive buffer

      if((gUART1_RxBuf[0] == 0x01) && (gUART1_RxBuf[1] == 0x02) && (gUART1_RxBuf[2] == 0x03) && (gUART1_RxBuf[7] == 0x08)\
      && (gUART1_RxBuf[8] == 0x55) && (gUART1_RxBuf[9] == 0xAA))

        {
            Bsp_UART1_DMA_SendData(DMA1_Channel2,(u32)gUART1_RxBuf,10);
        }
    }
    //UART1 send complete flag
    if(gUART1_TxComplete == 1) 
    {
        gUART1_TxComplete = 0;
    }
    while(!UART_GetFlagStatus(UART1, UART_FLAG_TXEPT));
}

11.測試MM32F0140 UART1 DMA中斷接收和DMA中斷髮送資料函式

  在main函式中分別呼叫UART1初始化函式、UART1 DMA NVIC中斷優先順序初始化函式、配置UART1 DMA NVIC接收資料函式、配置UART1 DMA NVIC傳送資料函式,然後在while(1)

主迴圈中呼叫處理MM32F0140 UART1 DMA中斷接收和DMA中斷髮送資料函式,主迴圈迴圈檢測UART1 DMA中斷是否收到資料,如果收到上位機串列埠助手傳送一幀10位元組的十六進位制資料:0x01,0x02,0x03,0x04,0x05,0x06,0x7,0x08,0x55,0xAA,通過UART1 DMA中斷髮送資料函式,原樣傳送到串列埠助手顯示出來,測試結果如下圖2所示:

int main(void)
{
    //UART1 Init Baud rate:115200
    Bsp_UART1_Init(UART1_BAUDRATE);
    //UART1 DMA NVIC Init
    Bsp_NVIC_Init(DMA1_Channel2_3_IRQn,1);
    //Configure UART1 DMA reception
    Bsp_UART1_DMA_NVIC_Recv_Config(DMA1_Channel3, (u32)&UART1->RDR, (u32)gUART1_RxBuf, 10);
    //Configure UART1 DMA transmission
    Bsp_UART1_DMA_NVIC_Send_Config(DMA1_Channel2, (u32)&UART1->TDR, (u32)gUART1_TxBuf, 10);
    
    while(1) 
    {
        //Process UART1 DMA interrupt to receive and interrupt to transmit data
        Bsp_UART1_DMA_Rx_DMA_Tx_Task();
    }
}

                  圖2

總結:

  學習MM32F0140 UART1 DMA 中斷接收和 UART1 DMA中斷髮送資料,通過上位機串列埠助手傳送10位元組的十六進位制資料:0x01,0x02,0x03,0x04,0x05,0x06,0x7,0x08,0x55,0xAA;下位機MM32F0140的UART1的DMA中斷接收到一幀:0x01,0x02,0x03,0x04,0x05,0x06,0x7,0x08,0x55,0xAA ;共10位元組資料後,通過UART1 DMA中斷髮送資料函式,原樣傳送到串列埠助手顯示出來。

注意事項:

  (1)MM32F0140每個外設都有自己獨立的時鐘,需使能UART1 傳送和接收引腳的GPIO時鐘;

  (2)使能UART1外設時鐘;

  (3)配置GPIOA的 PA9和PA10複用成UART1功能(可參考DS手冊的引腳定義及複用功能);

  (4)使能DMA時鐘,復位DMA通道暫存器;

  (5)配置DMA傳輸外設基地址;

  (6)配置DMA傳輸記憶體基地址;

  (7)配置DMA的傳輸方向;

  (8)配置DMA傳輸外設地址是否遞增;

  (9)配置DMA傳輸記憶體地址是否遞增;

  (10)配置DMA傳輸外設資料寬度(位元組或半字或字,配置需與記憶體資料寬度一致);

  (11)配置DMA傳輸記憶體資料寬度(位元組或半字或字,配置需與外設資料寬度一致);

  (12)配置DMA是否自動裝載傳輸數量暫存器;

  (13)UART2和UART3的操作方法與UART1的方法一樣,可參考以上UART1把對應的UART1引數改成UART2或UART3,使能相應外設時鐘即可。

相關文章