• MM32F0140 UART1 DMA Interrupt RX and TX (UART1 DMA中断接收和DMA中断发送数据)


    目录:

    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,使能相应外设时钟即可。

  • 相关阅读:
    安卓Frida 脱壳
    centos的root密码忘记或失效的解决办法
    什么是软件EV代码签名证书
    【JUC】线程通信与等待唤醒机制
    检查两个数组在维度,形状以及元素值上是否均等价 numpy.array_equiv()
    518. 零钱兑换 II-动态规划算法
    华为交换机一端口3网段,跨网段通讯
    VMware 系列:戴尔服务器配置(格式数据、RAID5、安装ESXI)以及相关问题
    objectarx + libcurl下载文件遇到的问题
    Podfile、Podfile.lock、Manifest.lock、Podspec
  • 原文地址:https://www.cnblogs.com/armny3264-lcpj/p/16054908.html