【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了,这样子我们在调试程序的时候就可以把信息打印到串口助手上了。

相关推荐
scan13 小时前
单片机串口接收状态机STM32
stm32·单片机·串口·51·串口接收
Qingniu014 小时前
【青牛科技】应用方案 | RTC实时时钟芯片D8563和D1302
科技·单片机·嵌入式硬件·实时音视频·安防·工控·储能
深圳市青牛科技实业有限公司5 小时前
【青牛科技】应用方案|D2587A高压大电流DC-DC
人工智能·科技·单片机·嵌入式硬件·机器人·安防监控
Mr.谢尔比6 小时前
电赛入门之软件stm32keil+cubemx
stm32·单片机·嵌入式硬件·mcu·信息与通信·信号处理
LightningJie6 小时前
STM32中ARR(自动重装寄存器)为什么要减1
stm32·单片机·嵌入式硬件
西瓜籽@7 小时前
STM32——毕设基于单片机的多功能节能窗控制系统
stm32·单片机·课程设计
远翔调光芯片^138287988729 小时前
远翔升压恒流芯片FP7209X与FP7209M什么区别?做以下应用市场摄影补光灯、便携灯、智能家居(调光)市场、太阳能、车灯、洗墙灯、舞台灯必看!
科技·单片机·智能家居·能源
极客小张10 小时前
基于STM32的智能充电桩:集成RTOS、MQTT与SQLite的先进管理系统设计思路
stm32·单片机·嵌入式硬件·mqtt·sqlite·毕业设计·智能充电桩
m0_7393128713 小时前
【STM32】项目实战——OV7725/OV2604摄像头颜色识别检测(开源)
stm32·单片机·嵌入式硬件
嵌入式小章13 小时前
基于STM32的实时时钟(RTC)教学
stm32·嵌入式硬件·实时音视频