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