【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 小时前
51单片机嵌入式开发:STC89C52操作GPIO口LED灯
c语言·开发语言·单片机·嵌入式硬件·51单片机·dsp开发
KINO323 小时前
51单片机外部中断(按键识别)
单片机·51单片机·嵌入式
番茄灭世神13 小时前
利用STM32的定时器输出PWM方波
stm32·单片机·嵌入式硬件
m0_6442226113 小时前
HarmonyOS开发实战:UDP通讯示例规范
网络·驱动开发·嵌入式硬件·udp·harmonyos·鸿蒙·harmonyos next
人才程序员14 小时前
【51单片机入门】速通定时器
c语言·c++·单片机·嵌入式硬件·mcu·51单片机·proteus
XD74297163617 小时前
【TB作品】步进电机控制器,ATMEGA16单片机,Proteus仿真
单片机·proteus·仿真·步进电机·atmega16
南耿先生17 小时前
我在高职教STM32——LCD液晶显示(1)
stm32·单片机·嵌入式硬件
xiaoyaoyou.xyz17 小时前
嵌入式UI开发-lvgl+wsl2+vscode系列:5、事件(Events)
单片机·ui·lvgl
莫邪博客18 小时前
【STM32修改串口波特率】
stm32·单片机·嵌入式硬件
电气_空空19 小时前
基于单片机技术的按键扫描电路分析
单片机·嵌入式硬件·毕业设计·毕设