使用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
1042

被折叠的 条评论
为什么被折叠?



