题外话
原本今天的帖子是打算写ADC功能的。但是想了下,写ADC功能的话没有串口或者屏幕的话不是很好能够观察到现象。所以今天的内容是关于串口。
不知道官方是出于什么考虑,在MCU附近针孔预留的都是2.0MM的。很不方便。我已经下单买了一些2.0MM的排母。和一些排针。突然意识到这个杜邦线也必须要使用2.0MM的。不然根本插不到排母里面去。
本文参考如下资料:
1- GD32H759I-EVAL-V1.3.pdf
2- GD32H759I-EVAL评估板使用指南_Rev1.2.pdf
3- GD32H737_757_759_User_Manual_Rev1.3_CN.pdf
4- GD32H759xx_Datasheet_Rev1.5.pdf
正文
这款GD32H759IMK6 一共支持8个串口同时工作。这款芯片非常强大。引脚数太多了。如果需要详细的引脚定义的话,请查看 GD32H759xx_Datasheet_Rev1.5.pdf
本文主要实现用户手册的4.19章已经引出为Mini-usb接口的USART0
数据手册PIN定义如下:
接下来开始编码,复制上一章的工程,重命名为03-UART
#include "gd32h7xx.h"
#include "systick.h"
static void cache_enable(void)
{
/* Enable I-Cache */
SCB_EnableICache();
/* Enable D-Cache */
SCB_EnableDCache();
}
int main(void)
{
/* Enable the CPU Cache */
cache_enable();
/* Configure systick */
systick_config();
while(1) {
}
}
下面是借助ChatGPT翻译的关于串口function 定义的中文注释
/* 函数声明 */
/* 初始化函数 */
/* 复位 USART */
void usart_deinit(uint32_t usart_periph);
/* 配置 USART 波特率值 */
void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval);
/* 配置 USART 校验功能 */
void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg);
/* 配置 USART 字长 */
void usart_word_length_set(uint32_t usart_periph, uint32_t wlen);
/* 配置 USART 停止位长度 */
void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen);
/* 使能 USART */
void usart_enable(uint32_t usart_periph);
/* 禁用 USART */
void usart_disable(uint32_t usart_periph);
/* 配置 USART 发送器 */
void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig);
/* 配置 USART 接收器 */
void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig);
/* USART 正常模式通信 */
/* 数据按 LSB/MSB 顺序传输/接收 */
void usart_data_first_config(uint32_t usart_periph, uint32_t msbf);
/* USART 反转配置 */
void usart_invert_config(uint32_t usart_periph, usart_invert_enum invertpara);
/* 使能 USART 过载功能 */
void usart_overrun_enable(uint32_t usart_periph);
/* 禁用 USART 过载功能 */
void usart_overrun_disable(uint32_t usart_periph);
/* 配置 USART 过采样模式 */
void usart_oversample_config(uint32_t usart_periph, uint32_t oversamp);
/* 配置采样位方法 */
void usart_sample_bit_config(uint32_t usart_periph, uint32_t osb);
/* 使能接收超时 */
void usart_receiver_timeout_enable(uint32_t usart_periph);
/* 禁用接收超时 */
void usart_receiver_timeout_disable(uint32_t usart_periph);
/* 配置接收超时阈值 */
void usart_receiver_timeout_threshold_config(uint32_t usart_periph, uint32_t rtimeout);
/* USART 发送数据函数 */
void usart_data_transmit(uint32_t usart_periph, uint16_t data);
/* USART 接收数据函数 */
uint16_t usart_data_receive(uint32_t usart_periph);
/* 使能 USART 命令 */
void usart_command_enable(uint32_t usart_periph, uint32_t cmdtype);
/* 多处理器通信 */
/* 使能地址 0 匹配模式 */
void usart_address_0_match_mode_enable(uint32_t usart_periph);
/* 禁用地址 0 匹配模式 */
void usart_address_0_match_mode_disable(uint32_t usart_periph);
/* 使能地址 1 匹配模式 */
void usart_address_1_match_mode_enable(uint32_t usart_periph);
/* 禁用地址 1 匹配模式 */
void usart_address_1_match_mode_disable(uint32_t usart_periph);
/* 配置 USART 地址 0 */
void usart_address_0_config(uint32_t usart_periph, uint8_t addr);
/* 配置 USART 地址 1 */
void usart_address_1_config(uint32_t usart_periph, uint8_t addr);
/* 配置地址 0 检测模式 */
void usart_address_0_detection_mode_config(uint32_t usart_periph, uint32_t addmod);
/* 配置地址 1 检测模式 */
void usart_address_1_detection_mode_config(uint32_t usart_periph, uint32_t addmod);
/* 使能静默模式 */
void usart_mute_mode_enable(uint32_t usart_periph);
/* 禁用静默模式 */
void usart_mute_mode_disable(uint32_t usart_periph);
/* 配置静默模式下的唤醒方法 */
void usart_mute_mode_wakeup_config(uint32_t usart_periph, uint32_t wmethod);
/* LIN 模式通信 */
/* 使能 LIN 模式 */
void usart_lin_mode_enable(uint32_t usart_periph);
/* 禁用 LIN 模式 */
void usart_lin_mode_disable(uint32_t usart_periph);
/* LIN 断字符检测长度 */
void usart_lin_break_detection_length_config(uint32_t usart_periph, uint32_t lblen);
/* 半双工通信 */
/* 使能半双工模式 */
void usart_halfduplex_enable(uint32_t usart_periph);
/* 禁用半双工模式 */
void usart_halfduplex_disable(uint32_t usart_periph);
/* 同步通信 */
/* 使能时钟 */
void usart_clock_enable(uint32_t usart_periph);
/* 禁用时钟 */
void usart_clock_disable(uint32_t usart_periph);
/* 配置 USART 同步模式参数 */
void usart_synchronous_clock_config(uint32_t usart_periph, uint32_t clen, uint32_t cph, uint32_t cpl);
/* 智能卡通信 */
/* 配置智能卡模式的守卫时间值 */
void usart_guard_time_config(uint32_t usart_periph, uint32_t guat);
/* 使能智能卡模式 */
void usart_smartcard_mode_enable(uint32_t usart_periph);
/* 禁用智能卡模式 */
void usart_smartcard_mode_disable(uint32_t usart_periph);
/* 使能智能卡模式中的 NACK */
void usart_smartcard_mode_nack_enable(uint32_t usart_periph);
/* 禁用智能卡模式中的 NACK */
void usart_smartcard_mode_nack_disable(uint32_t usart_periph);
/* 使能智能卡模式中的早期 NACK */
void usart_smartcard_mode_early_nack_enable(uint32_t usart_periph);
/* 禁用智能卡模式中的早期 NACK */
void usart_smartcard_mode_early_nack_disable(uint32_t usart_periph);
/* 配置智能卡自动重试次数 */
void usart_smartcard_autoretry_config(uint32_t usart_periph, uint32_t scrtnum);
/* 配置块长度 */
void usart_block_length_config(uint32_t usart_periph, uint32_t bl);
/* IrDA 通信 */
/* 使能 IrDA 模式 */
void usart_irda_mode_enable(uint32_t usart_periph);
/* 禁用 IrDA 模式 */
void usart_irda_mode_disable(uint32_t usart_periph);
/* 配置 IrDA 低功耗或智能卡模式下的外设时钟预分频器 */
void usart_prescaler_config(uint32_t usart_periph, uint32_t psc);
/* 配置 IrDA 低功耗模式 */
void usart_irda_lowpower_config(uint32_t usart_periph, uint32_t irlp);
/* 硬件流控制 */
/* 配置硬件流控制 RTS */
void usart_hardware_flow_rts_config(uint32_t usart_periph, uint32_t rtsconfig);
/* 配置硬件流控制 CTS */
void usart_hardware_flow_cts_config(uint32_t usart_periph, uint32_t ctsconfig);
/* 一致性控制 */
/* 配置硬件流控制一致性模式 */
void usart_hardware_flow_coherence_config(uint32_t usart_periph, uint32_t hcm);
/* 使能 RS485 驱动 */
void usart_rs485_driver_enable(uint32_t usart_periph);
/* 禁用 RS485 驱动 */
void usart_rs485_driver_disable(uint32_t usart_periph);
/* 配置驱动使能断言时间 */
void usart_driver_assertime_config(uint32_t usart_periph, uint32_t deatime);
/* 配置驱动使能取消断言时间 */
void usart_driver_deassertime_config(uint32_t usart_periph, uint32_t dedtime);
/* 配置驱动使能极性模式 */
void usart_depolarity_config(uint32_t usart_periph, uint32_t dep);
/* USART DMA */
/* 配置 USART DMA 接收 */
void usart_dma_receive_config(uint32_t usart_periph, uint32_t dmacmd);
/* 配置 USART DMA 发送 */
void usart_dma_transmit_config(uint32_t usart_periph, uint32_t dmacmd);
/* 禁用接收错误的 DMA */
void usart_reception_error_dma_disable(uint32_t usart_periph);
/* 使能接收错误的 DMA */
void usart_reception_error_dma_enable(uint32_t usart_periph);
/* 使能 USART 从深度睡眠模式唤醒 MCU */
void usart_wakeup_enable(uint32_t usart_periph);
/* 禁用 USART 从深度睡眠模式唤醒 MCU */
void usart_wakeup_disable(uint32_t usart_periph);
/* 配置 USART 从深度睡眠模式唤醒模式 */
void usart_wakeup_mode_config(uint32_t usart_periph, uint32_t wum);
/* USART FIFO */
/* 使能 FIFO */
void usart_fifo_enable(uint32_t usart_periph);
/* 禁用 FIFO */
void usart_fifo_disable(uint32_t usart_periph);
/* 配置发送 FIFO 阈值 */
void usart_transmit_fifo_threshold_config(uint32_t usart_periph, uint32_t txthreshold);
/* 配置接收 FIFO 阈值 */
void usart_receive_fifo_threshold_config(uint32_t usart_periph, uint32_t rxthreshold);
/* 读取接收 FIFO 计数器数量 */
uint8_t usart_receive_fifo_counter_number(uint32_t usart_periph);
/* 标志和中断函数 */
/* 获取 STAT/FCS 寄存器中的标志 */
FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag);
/* 清除 USART 状态 */
void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag);
/* 使能 USART 中断 */
void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum interrupt);
/* 禁用 USART 中断 */
void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum interrupt);
/* 获取 USART 中断和标志状态 */
FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_enum int_flag);
/* 清除 USART 中断标志 */
void usart_interrupt_flag_clear(uint32_t usart_periph, usart_interrupt_flag_enum int_flag);
其实并不需要关注那么多,让我来移除那些不在本章节讨论内容外的函数定义
/* 函数声明 */
/* 初始化函数 */
/* 复位 USART */
void usart_deinit(uint32_t usart_periph);
/* 配置 USART 波特率值 */
void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval);
/* 配置 USART 校验功能 */
void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg);
/* 配置 USART 字长 */
void usart_word_length_set(uint32_t usart_periph, uint32_t wlen);
/* 配置 USART 停止位长度 */
void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen);
/* 使能 USART */
void usart_enable(uint32_t usart_periph);
/* 禁用 USART */
void usart_disable(uint32_t usart_periph);
/* 配置 USART 发送器 */
void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig);
/* 配置 USART 接收器 */
void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig);
/* USART 正常模式通信 */
/* 数据按 LSB/MSB 顺序传输/接收 */
void usart_data_first_config(uint32_t usart_periph, uint32_t msbf);
/* USART 反转配置 */
void usart_invert_config(uint32_t usart_periph, usart_invert_enum invertpara);
/* 使能 USART 过载功能 */
void usart_overrun_enable(uint32_t usart_periph);
/* 禁用 USART 过载功能 */
void usart_overrun_disable(uint32_t usart_periph);
/* 配置 USART 过采样模式 */
void usart_oversample_config(uint32_t usart_periph, uint32_t oversamp);
/* 配置采样位方法 */
void usart_sample_bit_config(uint32_t usart_periph, uint32_t osb);
/* 使能接收超时 */
void usart_receiver_timeout_enable(uint32_t usart_periph);
/* 禁用接收超时 */
void usart_receiver_timeout_disable(uint32_t usart_periph);
/* 配置接收超时阈值 */
void usart_receiver_timeout_threshold_config(uint32_t usart_periph, uint32_t rtimeout);
/* USART 发送数据函数 */
void usart_data_transmit(uint32_t usart_periph, uint16_t data);
/* USART 接收数据函数 */
uint16_t usart_data_receive(uint32_t usart_periph);
/* 使能 USART 命令 */
void usart_command_enable(uint32_t usart_periph, uint32_t cmdtype);
/* 半双工通信 */
/* 使能半双工模式 */
void usart_halfduplex_enable(uint32_t usart_periph);
/* 禁用半双工模式 */
void usart_halfduplex_disable(uint32_t usart_periph);
/* 标志和中断函数 */
/* 获取 STAT/FCS 寄存器中的标志 */
FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag);
/* 清除 USART 状态 */
void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag);
/* 使能 USART 中断 */
void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum interrupt);
/* 禁用 USART 中断 */
void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum interrupt);
/* 获取 USART 中断和标志状态 */
FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_enum int_flag);
/* 清除 USART 中断标志 */
void usart_interrupt_flag_clear(uint32_t usart_periph, usart_interrupt_flag_enum int_flag);
这样看起来代码就简介很多了。接下来让我们来初始化这个USART0
按照如下步骤
1 - 开启时钟
/************************Clock Start ***************************/
/* Turn on GPIO A clock */
rcu_periph_clock_enable(GPIO_PORT_A);
/* Turn on USART0 clock*/
rcu_periph_clock_enable(USART0_CLOCK);
/************************Clock end ***************************/
2- 重映射GPIOA的PIN9 和PIN 10 作为Usart0
/************************GPIO re-maping start ***************************/
/* TX */
gpio_af_set(GPIOA, GPIO_AF_7, USART0_TX_PIN);
/* RX */
gpio_af_set(GPIOA, GPIO_AF_7, USART0_RX_PIN);
/************************GPIO re-maping end ***************************/
3- 初始化GPIO, 注意! 这里初始化GPIO的时候就不能选择输入或者输出模式了。需要配置当前的PIN为复用模式GPIO_MODE_AF
/************************Init GPIO start ***************************/
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, USART0_TX_PIN);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, USART0_TX_PIN);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, USART0_RX_PIN);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, USART0_RX_PIN);
/************************Init GPIO end ***************************/
4- 初始化USART0
/************************Init USART0 start ***************************/
usart_deinit(USART0);
/*配置波特率*/
usart_baudrate_set(USART0, 115200U);
/*配置数据长度*/
usart_word_length_set(USART0, USART_WL_8BIT);
/*配置停止位长度*/
usart_stop_bit_set(USART0, USART_STB_1BIT);
/*配置校验*/
usart_parity_config(USART0, USART_PM_NONE);
/*开启接收*/
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
/*开启发送*/
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
/*开启USART0*/
usart_enable(USART0);
/************************Init USART0 end ***************************/
程序到这里,USART的初始化已经完成了。
接下来我们简单的进行测试, 通过调用usart_data_transmit 函数间隔一秒向上位机发送数据
#include "gd32h7xx.h"
#include "systick.h"
#include "bsp_usart.h"
static void cache_enable(void)
{
/* Enable I-Cache */
SCB_EnableICache();
/* Enable D-Cache */
SCB_EnableDCache();
}
int main(void)
{
/* Enable the CPU Cache */
cache_enable();
/* Configure systick */
systick_config();
usart_init();
while(1) {
delay_1ms(1000);
usart_data_transmit(USART0, 0x41);
}
}
你也许会注意到,这样的方式很不友好,且每次只能发送一个字符。
让我们简单的对这个发送函数做一下封装。使其可以支持发送字符串。
void usart_send_string(uint32_t usart_periph, const char *str) {
// 循环直到字符串结束(即遇到空字符 '\0')
while (*str) {
// 发送当前字符
usart_data_transmit(usart_periph, (uint8_t)*str);
// 这确保当前字符已经传输完成,可以发送下一个字符
while (usart_flag_get(usart_periph, USART_FLAG_TBE) == RESET);
// 移动到下一个字符
str++;
}
}
再次测试,发现串口可以正常的发送字符串。
同时我也提供了另外一种发送字符串的方式,即重写fputchar函数,使其用户调用printf函数可以直接把消息发送到上位机
// 重写 fputchar 函数,使其通过 USART 发送字符
int fputc(int ch, FILE *f) {
// 发送字符
usart_data_transmit(USART0, (uint8_t)ch);
// 等待发送缓冲区为空
while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET);
return ch;
}
代码如下:
接下来我们对代码进行一点点小小的修改,使其能够支持串口接收中断,并且将接收到的数据转发出去。
增加初始化串口接收中断方法
void usart_receive_init(void) {
nvic_irq_enable(USART0_IRQn, 0, 0);
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
usart_interrupt_enable(USART0, USART_INT_RBNE);
}
串口中断回调函数
void USART0_IRQHandler(void) {
if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
// 读取接收缓冲区中的数据并打印
uint16_t received_data = usart_data_receive(USART0);
printf("Received data: %c\r\n", (char)received_data);
// 清除接收缓冲区非空中断标志
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
}
}
测试效果:
注意: 千万千万千万要再中断中调用usart_data_receive 函数,否则会一直触发中断!
代码如下: