打印

GD32F30x系列---串口通信(USART)基础配置(DMA模式)

[复制链接]
254|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
GD32F30x系列的USART支持DMA功能,但不是所有的都支持,如手册中说明了UART4:


这里使用USART的DMA功能+串口空闲中断进行数据的收发,可以与上一节的串口接收中断收发对比,DMA的原理以及说明这里就不再赘述了,可自行查找资料了解一下。

使用特权

评论回复
沙发
我爱台妹mmd|  楼主 | 2023-9-30 19:22 | 只看该作者
先创建一个usart_dma.c和usart_dma.h文件,并放到对应的文件夹中,如下图所示:

使用特权

评论回复
板凳
我爱台妹mmd|  楼主 | 2023-9-30 19:22 | 只看该作者
并将创建好的文件添加到工程中,以及将gd32f30x_dma.c库文件添加到keil项目工程中,如下图所示:

使用特权

评论回复
地板
我爱台妹mmd|  楼主 | 2023-9-30 19:23 | 只看该作者
2、实现串口的初始化以及DMA收发功能—这里以USART2为例
//usart_dma.c文件
#include "usart_dma.h"

//<<<<<<<<<<<<<<<<宏定义<<<<<<<<<<<<<<<<
#define USART_TX_RX_BUFF_LEN         256//DMA缓存数据大小
#define USART_DMA_IDLE                        0//DMA状态空闲
#define USART_DMA_BUSY                        1//DMA状态忙
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<结构定义<<<<<<<<<<<<<<<<
#pragma pack(1)
typedef struct
{
        uint8_t dma_tx_buf[USART_TX_RX_BUFF_LEN];//缓存DMA要发送的数据
        uint8_t dma_rx_buf[USART_TX_RX_BUFF_LEN];//缓存DMA接收到的数据
        uint8_t dma_tx_state;//DMA发送状态
        uint8_t dma_rx_state;//DMA接收状态
}USART_DataInfo;
#pragma pack()
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
USART_DataInfo g_usart_DataInfo;

/*
        串口初始化,使用DMA模式
*/
void gd32_usart_dma_init(void)
{
        usart_deinit(USART2);
        usart_disable(USART2);
        rcu_periph_clock_enable(RCU_GPIOB);
        rcu_periph_clock_enable(RCU_USART2);
        /* connect port to USARTx_Tx */
        gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
        /* connect port to USARTx_Rx */
        gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
        nvic_irq_enable(USART2_IRQn,1,0);
       
        usart_interrupt_enable(USART2,USART_INT_IDLE);                //空闲中断
        //usart_interrupt_enable(USART2,USART_INT_RBNE);//接收中断
        usart_baudrate_set(USART2,38400);//波特率设置
        usart_parity_config(USART2,USART_PM_NONE);//校验位设置
        usart_word_length_set(USART2,USART_WL_8BIT);//数据位设置
        usart_stop_bit_set(USART2,USART_STB_1BIT);//停止位设置
        usart_hardware_flow_rts_config(USART2, USART_RTS_DISABLE);//
    usart_hardware_flow_cts_config(USART2, USART_CTS_DISABLE);//硬件流控设置
        usart_data_first_config(USART2,USART_MSBF_LSB);//发送模式--LSB
        usart_transmit_config(USART2,USART_TRANSMIT_ENABLE);//发送使能
        usart_receive_config(USART2,USART_RECEIVE_ENABLE);//接收使能
        usart_enable(USART2);//使能串口
        gd32_usart_dma_tx_init();//DMA发送初始化
        gd32_usart_dma_rx_init();//DMA接收初始化
}

使用特权

评论回复
5
我爱台妹mmd|  楼主 | 2023-9-30 19:24 | 只看该作者
/*
        USART2 DMA发送初始化
*/
void gd32_usart_dma_tx_init(void)
{
        dma_parameter_struct dma_init_struct;
        /* enable DMA1 */
        rcu_periph_clock_enable(RCU_DMA0);
    /* deinitialize DMA channel(USART tx) */
    dma_deinit(DMA0, DMA_CH1);
        dma_init_struct.periph_addr  = (uint32_t)(&USART_DATA(USART2));
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory_addr  = NULL;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.number       = 0;
    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA0, DMA_CH1, &dma_init_struct);
    //configure DMA mode
    dma_circulation_disable(DMA0, DMA_CH1);
        dma_memory_to_memory_disable(DMA0, DMA_CH1);
        usart_dma_transmit_config(USART2, USART_DENT_ENABLE);
        nvic_irq_enable(DMA0_Channel1_IRQn,2,0);
        dma_interrupt_enable(DMA0, DMA_CH1, DMA_INT_FTF|DMA_INT_ERR);
        g_usart_DataInfo.dma_tx_state = USART_DMA_IDLE;
        return;
}

使用特权

评论回复
6
我爱台妹mmd|  楼主 | 2023-9-30 19:24 | 只看该作者
/*
        USART2 DMA接收初始化
*/
void gd32_usart_dma_rx_init(void)
{
    dma_parameter_struct dma_parameter;
        /* enable DMA0 */
        rcu_periph_clock_enable(RCU_DMA0);
    /* 接收 dm0 channel5(USART1 rx) */
    dma_deinit(DMA0,DMA_CH2);
        dma_parameter.periph_addr  = (uint32_t)(&USART_DATA(USART2));
    dma_parameter.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_parameter.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
        dma_parameter.memory_addr  = (uint32_t)&g_usart_DataInfo.dma_rx_buf[0];
    dma_parameter.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_parameter.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_parameter.number       = USART_TX_RX_BUFF_LEN;
    dma_parameter.direction    = DMA_PERIPHERAL_TO_MEMORY;
    dma_parameter.priority     = DMA_PRIORITY_ULTRA_HIGH;       
        dma_init(DMA0, DMA_CH2, &dma_parameter);
    /* configure DMA mode */
    dma_channel_enable(DMA0, DMA_CH2);
        dma_circulation_disable(DMA0, DMA_CH2);
        dma_memory_to_memory_disable(DMA0, DMA_CH2);

        usart_dma_receive_config(USART2, USART_DENR_ENABLE);
        nvic_irq_enable(DMA0_Channel2_IRQn, 2, 1);
        dma_interrupt_enable(DMA0, DMA_CH2, DMA_INT_FTF|DMA_INT_ERR);
        g_usart_DataInfo.dma_rx_state = USART_DMA_BUSY;
}

使用特权

评论回复
7
我爱台妹mmd|  楼主 | 2023-9-30 19:24 | 只看该作者
/*
        USART2 DMA发送数据
*/
void gd32_usart_dma_send(uint8_t *p_buff,uint32_t data_len)
{
        dma_channel_enum dma_channel;
       
        if(data_len >= USART_TX_RX_BUFF_LEN || USART_DMA_BUSY == g_usart_DataInfo.dma_tx_state)
                return;
        memcpy(g_usart_DataInfo.dma_tx_buf, p_buff, data_len);
        g_usart_DataInfo.dma_tx_state = USART_DMA_BUSY;
        dma_channel_disable(DMA0, DMA_CH1);
        dma_memory_address_config(DMA0, DMA_CH1,(uint32_t)&g_usart_DataInfo.dma_tx_buf[0]);
        dma_transfer_number_config(DMA0, DMA_CH1, data_len);
        dma_channel_enable(DMA0, DMA_CH1);
}

使用特权

评论回复
8
我爱台妹mmd|  楼主 | 2023-9-30 19:25 | 只看该作者
/*
        USART2 获取DMA接收数据
*/
uint32_t gd32_usart_dma_get_recv_data(uint8_t *recv_buff,uint32_t data_len)
{
        uint32_t recv_len=0;
        if(data_len >= USART_TX_RX_BUFF_LEN || USART_DMA_BUSY == g_usart_DataInfo.dma_rx_state)
        {
                return 0;
        }

        recv_len = USART_TX_RX_BUFF_LEN - dma_transfer_number_get(DMA0, DMA_CH2);
        if(recv_len > 0)
        {
                if(recv_len> data_len)
                {
                        recv_len = data_len;
                }
                memcpy(recv_buff, g_usart_DataInfo.dma_rx_buf, recv_len);
        }
        gd32_usart_dma_rx_init();
        return recv_len;
}

使用特权

评论回复
9
我爱台妹mmd|  楼主 | 2023-9-30 19:25 | 只看该作者
/*
        串口接收中断函数
*/
void USART2_IRQHandler(void)
{
        if(usart_interrupt_flag_get(USART2,USART_INT_FLAG_ERR_ORERR) != RESET
        ||usart_interrupt_flag_get(USART2,USART_INT_FLAG_ERR_NERR) != RESET
        ||usart_interrupt_flag_get(USART2,USART_INT_FLAG_ERR_FERR) != RESET)
        {
                usart_interrupt_flag_clear(USART2,USART_INT_FLAG_ERR_ORERR);
                usart_interrupt_flag_clear(USART2,USART_INT_FLAG_ERR_NERR);
                usart_interrupt_flag_clear(USART2,USART_INT_FLAG_ERR_FERR);
                return;
        }
        if(usart_interrupt_flag_get(USART2,USART_INT_FLAG_IDLE) != RESET
        &&        RESET != usart_flag_get(USART2, USART_FLAG_IDLE))
        {
                usart_interrupt_flag_clear(USART2,USART_INT_FLAG_IDLE);
                usart_flag_clear(USART2,USART_FLAG_IDLE);
                usart_data_receive(USART2);
                usart_interrupt_disable(USART2, USART_INT_IDLE);
                dma_channel_disable(DMA0, DMA_CH2);
                g_usart_DataInfo.dma_rx_state = USART_DMA_IDLE;
        }
}

使用特权

评论回复
10
我爱台妹mmd|  楼主 | 2023-9-30 19:25 | 只看该作者
/*
        USART2 -- DMA发送完成中断
*/
void DMA0_Channel1_IRQHandler(void)
{
        if(dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INTF_ERRIF) != RESET)
        {
                dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INTF_ERRIF);
        }
        if(dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INT_FLAG_FTF) != RESET)
        {
                dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INT_FLAG_FTF);
                g_usart_DataInfo.dma_tx_state = USART_DMA_IDLE;
        }
}

使用特权

评论回复
11
我爱台妹mmd|  楼主 | 2023-9-30 19:26 | 只看该作者
/*
        USART2 -- DMA接收完成中断
*/
void DMA0_Channel2_IRQHandler(void)
{
        if(dma_interrupt_flag_get(DMA0, DMA_CH2, DMA_INTF_ERRIF) != RESET)
        {
                dma_interrupt_flag_clear(DMA0, DMA_CH2, DMA_INTF_ERRIF);
        }
        if(dma_interrupt_flag_get(DMA0, DMA_CH2, DMA_INTF_FTFIF) != RESET)
        {
                dma_interrupt_flag_clear(DMA0, DMA_CH2, DMA_INTF_FTFIF);
                dma_channel_disable(DMA0, DMA_CH2);
        }
}

使用特权

评论回复
12
我爱台妹mmd|  楼主 | 2023-9-30 19:26 | 只看该作者
//usart_dma.h文件
#ifndef __USART_DMA_H__
#define __USART_DMA_H__

#include "gd32f30x.h"
#include "stdio.h"
#include "string.h"

void gd32_usart_dma_init(void);
void gd32_usart_dma_tx_init(void);
void gd32_usart_dma_rx_init(void);
void gd32_usart_dma_send(uint8_t *p_buff,uint32_t data_len);
uint32_t gd32_usart_dma_get_recv_data(uint8_t *recv_buff,uint32_t data_len);
#endif

使用特权

评论回复
13
我爱台妹mmd|  楼主 | 2023-9-30 19:26 | 只看该作者
在main.c文件的main函数中调用串口DMA模式的初始化,并循环调用串口DMA发送,这里循环1S发送字符“1234567890”,连接好硬件板和电脑后,编译下载程序到硬件板中,点击调试运行,如下图所示:

使用特权

评论回复
14
我爱台妹mmd|  楼主 | 2023-9-30 19:27 | 只看该作者
点击运行程序,连接好电脑和硬件板的串口,使用串口调试工具查看当前硬件板发送的数据,如下图所示:

使用特权

评论回复
15
我爱台妹mmd|  楼主 | 2023-9-30 19:27 | 只看该作者
串口助手每隔1S接收一条1234567890字符,说明程序运行成功;

使用特权

评论回复
16
我爱台妹mmd|  楼主 | 2023-9-30 19:27 | 只看该作者
使用串口调试助手发送0x55、0xAA、0x00、0xFF数据到硬件板,设置断点,查看接收到的数据是否正常,如下图所示:

程序接收到的数据与串口助手发送的数据一致,程序运行正常。
这里我们可以看到使用串口连续发送4个字节的数据,在串口空闲中断中就能够查看到DMA缓存的4个字节的数据,只需要进一次中断即可,如果是串口接收则每接收一个字节需要中断一次,手动将数据缓存,因此使用DMA模式在串口收发频繁的情况下能够大大的节省CPU的开销。

使用特权

评论回复
17
我爱台妹mmd|  楼主 | 2023-9-30 19:27 | 只看该作者
注意:我这里没有调用gd32_usart_dma_get_recv_data(uint8_t *recv_buff,uint32_t data_len)去获取DMA的数据,所以接收完一帧数据后就把DMA关闭了,如果要继续接收的话需要把DMA的数据提取出来重新开启DMA接收才可以,这里就不做处理了。

使用特权

评论回复
18
我爱台妹mmd|  楼主 | 2023-9-30 19:28 | 只看该作者
这里在次测试一下使用DMA功能后,用串口连续发送200个字节的数据,查看会占用CPU多少的时间来处理,如下入所示:

使用特权

评论回复
19
我爱台妹mmd|  楼主 | 2023-9-30 19:28 | 只看该作者

使用特权

评论回复
20
我爱台妹mmd|  楼主 | 2023-9-30 19:28 | 只看该作者
这里可以查看到使用DMA发送数据之前当前的systick为6006,使用DMA发送数据完成后,系统当前的systick仍然是6006,并且串口也接收到硬件板发送过来的数据,说明使用DMA发送数据基本上是不占用CPU的时间的。
与之前的串口中断接收缓存和串口发送相比,在数据量大的情况下DMA模式能够大大的节省CPU的开销。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

45

主题

420

帖子

0

粉丝