GD32H759 USART 使能IDLE+DMA接受 以及DMA发送

使用GD32H759,处于cortexm7内核,和STM32H7同一个内核,所以配置基本上相同,由于GD32系列更新,所以工艺更高,发热等等控制更好.GD32官方没有提供类似于STM32的HAL库,只有标准库,同时配置方法与STM32有一点不同.

进入正文,工程结构,本次文章的内容主要集中在SYS文件夹,BSP文件夹的内容可以删除.

思路

在USART接收完成以后,也就是数据没有继续发送时,USART会产生一个IDLE 空闲中断,其标志位为USART_FLAG_IDLE,发生IDLE中断以后,我们可以在其中读取缓冲区,通知接受完成等等,结合DMA功能自动将数据搬运到缓冲区,在IDLE中断中获取数据长度,清除中断标志位,DMA传输标志位,以及开始下一次DMA搬运.

所有我们需要先配置USART的IDLE中断,确认能够进入中断以后编写DMA的参数,在IDLE中启动DMA,当再次发生IDLE中断时,可认为本次串口发送完毕,使用DMA再将数据发送到USART_TD发送寄存器发送出去.

串口配置

首先根据基本的示例代码,配置我们的串口基本参数,USART对应端口自行选择修改.

由于我们需要在IDLE中断中完成数据接收,所以配置串口中断的NVIC.

void USART_Init(uint32_t bound)
{
    /* IO 及 时钟配置 */
    rcu_periph_clock_enable(USART_TX_GPIO_CLK);     /* 使能串口TX脚时钟 */
    rcu_periph_clock_enable(USART_RX_GPIO_CLK);     /* 使能串口RX脚时钟 */
    rcu_periph_clock_enable(RCU_USART1);          /* 使能串口时钟 */
    USART_RX_DMA_Config();
    USART_TX_DMA_Config();
    /* 设置USARTx_TX的复用功能选择 */
    gpio_af_set(USART_TX_GPIO_PORT, USART_TX_GPIO_AF, USART_TX_GPIO_PIN);
    /* 设置USARTx_RX的复用功能选择 */
    gpio_af_set(USART_RX_GPIO_PORT, USART_RX_GPIO_AF, USART_RX_GPIO_PIN);
    /* USARTx_TX的模式设置 */
    gpio_mode_set(USART_TX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, USART_TX_GPIO_PIN);
    gpio_output_options_set(USART_TX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, USART_TX_GPIO_PIN);
    /* USARTx_RX的模式设置 */
    gpio_mode_set(USART_RX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, USART_RX_GPIO_PIN);
    gpio_output_options_set(USART_RX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, USART_RX_GPIO_PIN);
    /* 配置USART的参数 */
    usart_deinit(USART1);                                   /* 复位USARTx */
    usart_baudrate_set(USART1, bound);                      /* 设置波特率 */
    usart_stop_bit_set(USART1, USART_STB_1BIT);             /* 一个停止位 */
    usart_word_length_set(USART1, USART_WL_8BIT);           /* 字长为8位数据格式 */
    usart_parity_config(USART1, USART_PM_NONE);             /* 无奇偶校验位 */
    
    usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);   /* 使能发送 */
    usart_dma_transmit_config(USART1, USART_TRANSMIT_DMA_ENABLE);
    
    usart_receive_config(USART1, USART_RECEIVE_ENABLE);     /* 使能接收 */
    usart_dma_receive_config(USART1, USART_RECEIVE_DMA_ENABLE);
    
    usart_flag_clear(USART1, USART_FLAG_IDLE);              //没啥用
    usart_interrupt_enable(USART1, USART_INT_IDLE);
    /* 配置NVIC,并设置中断优先级 */
    nvic_irq_enable(USART1_IRQn, 3, 3);                     /* 组2,抢占优先级3,子优先级3 */
    usart_enable(USART1);                                    /* 使能串口 */
    usart_flag_clear(USART1, USART_FLAG_IDLE);              //没啥用
}

DMA配置

DMA0_CH0---USART_TX

先配置TX的DMA通道,由于cortexm7内核具有DMAMUX,DMA请求多路复用器,可以理解为所有DMA通道和外设都连接到这个外设,这个外设可以自动实现多路DMA通道与任意外设的连接,不需要参考DMA映射表.通过查看定义,得知USART1_TX对应DMA0外设的0-7通道,选择DMA0_CH0.

具体的代码注释可以参考RX的代码

static void USART_TX_DMA_Config()
{
    dma_single_data_parameter_struct dma_init_struct;
    rcu_periph_clock_enable(RCU_DMA0);
    rcu_periph_clock_enable(RCU_DMAMUX);
    dma_deinit(DMA0, DMA_CH0);
    dma_single_data_para_struct_init(&dma_init_struct);
    dma_init_struct.request = DMA_REQUEST_USART1_TX;
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
    //dma_init_struct.memory0_addr = 0; 不需要指定地址 使用时再指定
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
    //dma_init_struct.number = 512; 不需要指定数量 使用时再指定
    dma_init_struct.periph_addr = (uint32_t)&USART_TDATA(USART1);
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
    dma_single_data_mode_init(DMA0, DMA_CH0, &dma_init_struct);
    
    dma_circulation_disable(DMA0, DMA_CH0);
    dma_channel_disable(DMA0, DMA_CH0);
}

DMA0_CH1---USART_RX

//RX --DMA0-CH1
static void USART_RX_DMA_Config()
{
    dma_single_data_parameter_struct dma_init_struct;
    rcu_periph_clock_enable(RCU_DMA0);
    rcu_periph_clock_enable(RCU_DMAMUX);
    dma_deinit(DMA0, DMA_CH1);
    dma_single_data_para_struct_init(&dma_init_struct);
    dma_init_struct.request = DMA_REQUEST_USART1_RX;                    //DMA请求通道
    dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;                   //外设到内存
    dma_init_struct.memory0_addr = (uint32_t)Usart1_RX;                 //缓冲区
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;            //允许内存地址自增
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;        //外设宽度八位
    dma_init_struct.number = USART_REC_LEN;                             //接受字节512
    dma_init_struct.periph_addr = (uint32_t)&USART_RDATA(USART1);       //外设地址USART1的RD寄存器
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;           //关闭外设地址自增
    dma_init_struct.priority = DMA_PRIORITY_MEDIUM;                     //优先级中等
    dma_single_data_mode_init(DMA0, DMA_CH1, &dma_init_struct);
    dma_circulation_disable(DMA0, DMA_CH1);                             //关闭循环模式 实测开启关闭没影响
    dma_channel_enable(DMA0, DMA_CH1);
    /*
    使能DMA 由于系统启动会产生一个IDLE中断,所以可以不用使能,在第一个IDLE中断中使能
    */
}

由于我们不需要DMA中断,所有就不使能了.

中断处理函数

字符串的定义当读取到\0时默认字符串末尾,所以不需要memset清空缓冲区,只需在末尾设置'\0'.

DMA传输即使不开启Full Transmit中断也会产生对应标志位,所以需要清除.

void USART1_IRQHandler(void)
{   
    
    if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) 
    {
        dma_channel_disable(DMA0, DMA_CH1);
        usart_interrupt_flag_clear(USART1,USART_INT_FLAG_IDLE);
        dma_flag_clear(DMA0, DMA_CH1, DMA_FLAG_FTF);
        Usart1_RX[512-dma_transfer_number_get(DMA0,DMA_CH1)]='\0';
        INFO("%s",Usart1_RX);
        dma_channel_enable(DMA0, DMA_CH1);
        
    }
}

USART发送

void USART_DMA_Transmit(char *data)
{   
    int len=strlen(data);
    SCB_CleanDCache_by_Addr(data, len);
    dma_flag_clear(DMA0, DMA_CH0, DMA_FLAG_FTF);
    DMA_CHCNT(DMA0, DMA_CH0) = len;
    DMA_CHM0ADDR(DMA0, DMA_CH0) = (uint32_t)data;
    dma_channel_enable(DMA0, DMA_CH0);
}

完整c代码

最后的清除IDLE中断写错了应该修改为usart_interrupt_flag_clear(USART1,USART_INT_FLAG_IDLE);

 
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "string.h"
#include "gd32h7xx_dma.h"
#include "main.h"

/******************************************************************************************/
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */

#if 1

#if (__ARMCC_VERSION >= 6010050)            /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t");  /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");    /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */

#else
#pragma import(__use_no_semihosting)

struct __FILE
{
    int handle;
};

#endif

int _ttywrch(int ch)
{
    ch = ch;
    return ch;
}
void _sys_exit(int x)
{
    x = x;
}
char *_sys_command_string(char *cmd, int len)
{
    return NULL;
}

FILE __stdout;
FILE __stderr;
int fputc(int ch, FILE *f)
{
    while (RESET == usart_flag_get(USART1, USART_FLAG_TC));       /* 等待上一个字符发送完成 */
    usart_data_transmit(USART1, (uint8_t)ch);                     /* 将要发送的字符 ch 写入到TDATA寄存器 */  
    
    return ch;
}
#endif



uint8_t Usart1_RX[USART_REC_LEN];


void USART_DMA_Transmit(char *data)
{   
    int len=strlen(data);
    SCB_CleanDCache_by_Addr(data, len);
    dma_flag_clear(DMA0, DMA_CH0, DMA_FLAG_FTF);
    DMA_CHCNT(DMA0, DMA_CH0) = len;
    DMA_CHM0ADDR(DMA0, DMA_CH0) = (uint32_t)data;
    dma_channel_enable(DMA0, DMA_CH0);
}

void USART1_IRQHandler(void)
{   
    
    if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) 
    {
        dma_channel_disable(DMA0, DMA_CH1);
        usart_interrupt_flag_clear(USART1,USART_INT_FLAG_IDLE);
        dma_flag_clear(DMA0, DMA_CH1, DMA_FLAG_FTF);
        Usart1_RX[512-dma_transfer_number_get(DMA0,DMA_CH1)]='\0';
        INFO("%s",Usart1_RX);
        dma_channel_enable(DMA0, DMA_CH1);
        
    }
}

//RX --DMA0-CH1
static void USART_RX_DMA_Config()
{
    dma_single_data_parameter_struct dma_init_struct;
    rcu_periph_clock_enable(RCU_DMA0);
    rcu_periph_clock_enable(RCU_DMAMUX);
    dma_deinit(DMA0, DMA_CH1);
    dma_single_data_para_struct_init(&dma_init_struct);
    dma_init_struct.request = DMA_REQUEST_USART1_RX;                    //DMA请求通道
    dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;                   //外设到内存
    dma_init_struct.memory0_addr = (uint32_t)Usart1_RX;                 //缓冲区
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;            //允许内存地址自增
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;        //外设宽度八位
    dma_init_struct.number = USART_REC_LEN;                             //接受字节512
    dma_init_struct.periph_addr = (uint32_t)&USART_RDATA(USART1);       //外设地址USART1的RD寄存器
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;           //关闭外设地址自增
    dma_init_struct.priority = DMA_PRIORITY_MEDIUM;                     //优先级中等
    dma_single_data_mode_init(DMA0, DMA_CH1, &dma_init_struct);
    dma_circulation_disable(DMA0, DMA_CH1);                             //关闭循环模式 实测开启关闭没影响
    dma_channel_enable(DMA0, DMA_CH1);
    /*
    使能DMA 由于系统启动会产生一个IDLE中断,所以可以不用使能,在第一个IDLE中断中使能
    */
}

static void USART_TX_DMA_Config()
{
    dma_single_data_parameter_struct dma_init_struct;
    rcu_periph_clock_enable(RCU_DMA0);
    rcu_periph_clock_enable(RCU_DMAMUX);
    dma_deinit(DMA0, DMA_CH0);
    dma_single_data_para_struct_init(&dma_init_struct);
    dma_init_struct.request = DMA_REQUEST_USART1_TX;
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
    //dma_init_struct.memory0_addr = 0; 不需要指定地址 使用时再指定
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
    //dma_init_struct.number = 512; 不需要指定数量 使用时再指定
    dma_init_struct.periph_addr = (uint32_t)&USART_TDATA(USART1);
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
    dma_single_data_mode_init(DMA0, DMA_CH0, &dma_init_struct);
    
    dma_circulation_disable(DMA0, DMA_CH0);
    dma_channel_disable(DMA0, DMA_CH0);
}


/**
 * @brief       串口X初始化函数
 * @param       bound: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void USART_Init(uint32_t bound)
{
    /* IO 及 时钟配置 */
    rcu_periph_clock_enable(USART_TX_GPIO_CLK);     /* 使能串口TX脚时钟 */
    rcu_periph_clock_enable(USART_RX_GPIO_CLK);     /* 使能串口RX脚时钟 */
    rcu_periph_clock_enable(RCU_USART1);          /* 使能串口时钟 */
    USART_RX_DMA_Config();
    USART_TX_DMA_Config();
    /* 设置USARTx_TX的复用功能选择 */
    gpio_af_set(USART_TX_GPIO_PORT, USART_TX_GPIO_AF, USART_TX_GPIO_PIN);
    /* 设置USARTx_RX的复用功能选择 */
    gpio_af_set(USART_RX_GPIO_PORT, USART_RX_GPIO_AF, USART_RX_GPIO_PIN);
    /* USARTx_TX的模式设置 */
    gpio_mode_set(USART_TX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, USART_TX_GPIO_PIN);
    gpio_output_options_set(USART_TX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, USART_TX_GPIO_PIN);
    /* USARTx_RX的模式设置 */
    gpio_mode_set(USART_RX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, USART_RX_GPIO_PIN);
    gpio_output_options_set(USART_RX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, USART_RX_GPIO_PIN);
    /* 配置USART的参数 */
    usart_deinit(USART1);                                   /* 复位USARTx */
    usart_baudrate_set(USART1, bound);                      /* 设置波特率 */
    usart_stop_bit_set(USART1, USART_STB_1BIT);             /* 一个停止位 */
    usart_word_length_set(USART1, USART_WL_8BIT);           /* 字长为8位数据格式 */
    usart_parity_config(USART1, USART_PM_NONE);             /* 无奇偶校验位 */
    
    usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);   /* 使能发送 */
    usart_dma_transmit_config(USART1, USART_TRANSMIT_DMA_ENABLE);
    
    usart_receive_config(USART1, USART_RECEIVE_ENABLE);     /* 使能接收 */
    usart_dma_receive_config(USART1, USART_RECEIVE_DMA_ENABLE);
    
    usart_flag_clear(USART1, USART_FLAG_IDLE);              //没啥用
    usart_interrupt_enable(USART1, USART_INT_IDLE);
    /* 配置NVIC,并设置中断优先级 */
    nvic_irq_enable(USART1_IRQn, 3, 3);                     /* 组2,抢占优先级3,子优先级3 */
    usart_enable(USART1);                                    /* 使能串口 */
    usart_flag_clear(USART1, USART_FLAG_IDLE);              //没啥用
}





完整h代码



#ifndef __USART_H
#define __USART_H

#include "stdio.h"
#include "./SYSTEM/sys/sys.h"

/*
使用IDLE +DMA接收/DMA发送
USART1_RX ---->DMA0_CH1
USART1_TX ---->DMA0_CH0
由于DMA发送完成以后会发生FULL Transmit 标志位,即使禁用FTF中断也会产生导致DMA无法发送 ,所以在发送完成以后清除标志位
*/

#define USART_TX_GPIO_PORT              GPIOA
#define USART_TX_GPIO_PIN               GPIO_PIN_2
#define USART_TX_GPIO_AF                GPIO_AF_7
#define USART_TX_GPIO_CLK               RCU_GPIOA   /* GPIOA时钟使能 */

#define USART_RX_GPIO_PORT              GPIOA
#define USART_RX_GPIO_PIN               GPIO_PIN_3
#define USART_RX_GPIO_AF                GPIO_AF_7
#define USART_RX_GPIO_CLK               RCU_GPIOA   /* GPIOA时钟使能 */


#define USART_REC_LEN               512             /* 定义最大接收字节数 200 */



void USART_Init(uint32_t bound);                    /* 串口初始化函数 */
void USART_DMA_Transmit(char *data);
#endif


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值