【GD32】07 - UART串口通信

GD32F407中的UART

今天我用的型号是GD32F407,用其他型号的小伙伴在使用UART的时候注意一下自己手上板子的资源就行,我们使用固件库就算是不同型号其实也是没有什么太大差别的。

我们废话不多说,直接开始讲怎么使用UART。

首先我们先确定串口的TX和RX是哪些引脚。

我使用USART0,那么用到的引脚就是GPIOB的6和7号引脚。

那么我们要做的就是打开GPIOB的外设时钟;开启GPIO的引脚复用;设置GPIO的模式。

这边就不一一介绍这些函数了,因为之前的文章都有,并且也不是本文的重点。

cpp 复制代码
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_af_set(GPIOB,GPIO_AF_7,GPIO_PIN_6|GPIO_PIN_7);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_6|GPIO_PIN_7);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_6|GPIO_PIN_7);

接下来我们配置UART,首先也是要先打开对应的外设时钟。

cpp 复制代码
    rcu_periph_clock_enable(RCU_USART0);

接下来是对串口设置。

复位串口,一般情况下我们在配置之前都调用一下,第一次配置的时候不调用也没关系。

cpp 复制代码
usart_deinit(USART0); 

设置串口的波特率,理论上想设置啥值都可以,但是我们要考虑到通信对方,因此我们填入常用的波特率,如9600,115200等。

cpp 复制代码
usart_baudrate_set(USART0,9600);

设置校验位,一般选择无校验。

cpp 复制代码
usart_parity_config(USART0,USART_PM_NONE);

设置数据位长度,一般都是8bit。

cpp 复制代码
usart_word_length_set(USART0,USART_WL_8BIT);

设置停止位,一般都是1bit。

cpp 复制代码
usart_stop_bit_set(USART0,USART_STB_1BIT);

使能串口,直接调用不用传参。

cpp 复制代码
usart_enable(USART0);

使能串口发送。

cpp 复制代码
usart_transmit_config(USART0,USART_TRANSMIT_ENABLE);

使能串口接收。

cpp 复制代码
usart_receive_config(USART0, USART_RECEIVE_ENABLE);

发送数据,注意这边参数的取值范围,发送数据的范围居然是0~0x1FF,类型是uint32_t。但是在实践的时候发现,类型还是uint16_t。可能是固件库版本不同的缘故。

因此我们使用的时候还是以uint16_t的为准。

接收数据用的函数在类型的问题上和上面发送数据是一样的。

当然了,光有上面的函数可收不到数据,我们需要开启接收中断。

我们要接收数据,那么就选择读数据缓冲区不为空的时候中断。

cpp 复制代码
usart_interrupt_enable(USART0,USART_INT_RBNE);

除了用上面这个函数使能,我们还需要配置NVIC,就用下面这两行(中断优先级可以修改)。

cpp 复制代码
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);           //中断等级分组
nvic_irq_enable(USART0_IRQn,1,1);                           //设置串口中断等级

如果我们收到了数据,那么就会进入中断执行函数,我们可以在启动文件中寻找。

进入中断执行函数之后,我们需要做两个操作,一个是检测中断标志位,另一个是清除中断标志位。

cpp 复制代码
void USART0_IRQHandler(void){
    if((RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE))){
        

        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
    }
}

除了在接收数据需要读取清除标志位,我们发送数据同样需要。

我们注意到这个函数也可以或许到读数据缓冲区非空的标志位,差别就在于一个是中断中使用,而另一个(这个)是查询使用的。

cpp 复制代码
void sendData(uint16_t data){
    while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
    usart_data_transmit(USART0,data);  
}

通过上面这些函数我们就可以使用GD32来收发数据了。

下面这个例子我配置了串口0,收到数据之后会将数据再发回去。

cpp 复制代码
#include "board.h"

void sendData(uint16_t data){
    while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
    usart_data_transmit(USART0,data);  
}

void USART0_IRQHandler(void){
    if((RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE))){
        uint8_t data = (uint8_t)usart_data_receive(USART0);
        sendData(data);
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
    }
}

int main(void){
    board_init();
    
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_USART0);
    
    gpio_af_set(GPIOB,GPIO_AF_7,GPIO_PIN_6|GPIO_PIN_7);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_6|GPIO_PIN_7);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_6|GPIO_PIN_7);
    
    usart_deinit(USART0);                                       // 复位串口    
    usart_baudrate_set(USART0,9600);                            // 设置波特率    
    usart_parity_config(USART0,USART_PM_NONE);                  // 无校验    
    usart_word_length_set(USART0,USART_WL_8BIT);                // 8位数据位    
    usart_stop_bit_set(USART0,USART_STB_1BIT);                  // 1位停止位
    usart_enable(USART0);                                       // 使能串口
    usart_transmit_config(USART0,USART_TRANSMIT_ENABLE);        // 使能串口发送
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);         // 使能串口接收
    usart_interrupt_enable(USART0,USART_INT_RBNE);              //使能串口中断
    
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);           //中断等级分组
    nvic_irq_enable(USART0_IRQn,1,1);                           //设置串口中断等级
    
    while (1){
        delay_ms(1000);
    }
}

可以正常收发数据。

根据之前STM32串口的经验,我们可以将printf重定向到串口上,在STM32中我们直接重写fputc,然后在Keil的设置中勾选Use MicroLlB就行了,但是在GD32F407中勾选Use MicroLlB在编译后会有两个错误。

解决方案也不是没有,那就是我们不勾选Use MicroLlB了,我们除了重写fputc之外再加上一些代码即可。

cpp 复制代码
#if !defined(__MICROLIB)
//不使用微库的话就需要添加下面的函数
#if (__ARMCLIB_VERSION <= 6000000)
//如果编译器是AC5  就定义下面这个结构体
struct __FILE
{
        int handle;
};
#endif

FILE __stdout;

//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
        x = x;
}
#endif

int fputc(int ch, FILE *f){
    sendData(ch);
    return ch;
}

接下来我们实验一下,在主循环里使用printf打印一句话(记得#include<stdio.h>)。

可以发现重写fputc之后可以直接使用printf了,这样子我们在调试程序的时候就可以把信息打印到串口助手上了。

相关推荐
嗯嗯=3 小时前
STM32单片机学习篇9
stm32·单片机·学习
松涛和鸣8 小时前
DAY63 IMX6ULL ADC Driver Development
linux·运维·arm开发·单片机·嵌入式硬件·ubuntu
想放学的刺客11 小时前
单片机嵌入式试题(第23期)嵌入式系统电源管理策略设计、嵌入式系统通信协议栈实现要点两个全新主题。
c语言·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆11 小时前
【Linux 驱动开发】五. 设备树
linux·arm开发·驱动开发·stm32·嵌入式硬件·mcu·硬件工程
jghhh0112 小时前
基于上海钜泉科技HT7017单相计量芯片的参考例程实现
科技·单片机·嵌入式硬件
恶魔泡泡糖13 小时前
51单片机外部中断
c语言·单片机·嵌入式硬件·51单片机
意法半导体STM3213 小时前
【官方原创】如何基于DevelopPackage开启安全启动(MP15x) LAT6036
javascript·stm32·单片机·嵌入式硬件·mcu·安全·stm32开发
v_for_van13 小时前
STM32低频函数信号发生器(四通道纯软件生成)
驱动开发·vscode·stm32·单片机·嵌入式硬件·mcu·硬件工程
电化学仪器白超13 小时前
③YT讨论
开发语言·python·单片机·嵌入式硬件
乡野码圣14 小时前
【RK3588 Android12】硬件中断IRQ
单片机·嵌入式硬件