九、串口打印信息
1.配置流程
一般我们使用串口,都需要有以下几个步骤。
- 开启时钟(包括串口时钟和GPIO时钟)
- 配置GPIO复用模式
- 配置GPIO的模式
- 配置GPIO的输出
- 配置串口(配置一些参数)
- 使能串口(串口使能和发送使能)
1.1.开启时钟
使用串口0的话就是PA9和PA10引脚。那第一步就是先开启端口A的时钟,在库函数点灯那一章节给大家介绍了使能时钟的函数rcu*periph_clock_enable,只需要传入对应的参数即可。使能端口A的时钟就把RCU_GPIOA当做参数传入。第二步就是开启串口的时钟,把对应的串口0的时钟RCU_USART0传入即可。为了方便后续修改,把端口A的时钟和串口0的时钟用宏定义定义,如下:
#define BSP_USART_RCU RCU_USART0
#define BSP_USART_TX_RCU RCU_GPIOA
#define BSP_USART_RX_RCU RCU_GPIOA
2
3
然后对应的使能时钟的代码就是
rcu_periph_clock_enable(BSP_USART_RCU); // 开启串口时钟
rcu_periph_clock_enable(BSP_USART_TX_RCU); // 开启端口时钟
rcu_periph_clock_enable(BSP_USART_RX_RCU); // 开启端口时钟
2
3
1.2.配置GPIO复用模式
GD32的引脚是可以有复用功能的,就是说单个引脚可有很多个功能,默认的功能一般都是作为GPIO使用。在gd32e23x_gpio.h中可以查找到设置复用的函数
void gpio_af_set(uint32_t gpio_periph, uint32_t alt_func_num, uint32_t pin);
这个函数有三个参数,第一个参数就是要配置的引脚端口,第二个参数就是要复用的功能,第三个参数就是要配置的引脚。第一个参数和第三个参数我们知道分别为GPIOA,GPIO_PIN_9,GPIOA,GPIO_PIN_10。关于第二个参数可以到数据手册的第33页进行查找,如图1-2-1所示。
从图1-2-1可以看到PA9对应的USART0_TX的功能复用为AF1,PA10对应的USART0_RX的功能复用为AF1,那第二个参数我们也知道了。接下来就可以开启PA9,PA10的复用功能了。代码编写如下。首先定义一下端口和复用功能的宏定义。
#define BSP_USART_TX_PORT GPIOA
#define BSP_USART_TX_PIN GPIO_PIN_9
#define BSP_USART_RX_PORT GPIOA
#define BSP_USART_RX_PIN GPIO_PIN_10
#define BSP_USART_AF GPIO_AF_1 // 串口是引脚复用功能1
2
3
4
5
然后调用复用函数使能复用功能。
/* 配置复用功能 */
gpio_af_set(BSP_USART_TX_PORT,BSP_USART_AF,BSP_USART_TX_PIN);
gpio_af_set(BSP_USART_RX_PORT,BSP_USART_AF,BSP_USART_RX_PIN);
2
3
通过上面两句就可以把PA9,PA10设置为串口功能了。
1.3.配置GPIO的模式
配置GPIO的模式还是使用gpio_mode_set这个函数,不同的是第二个参数要配置为复用功能而不是输出功能,第三个参数要配置为上拉,转化为代码如下。
/* 配置TX为复用模式 上拉模式 */
gpio_mode_set(BSP_USART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, BSP_USART_TX_PIN);
/* 配置RX为复用模式 上拉模式 */
gpio_mode_set(BSP_USART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, BSP_USART_RX_PIN);
2
3
4
1.4.配置GPIO的输出
配置GPIO的输出也还是用gpio_output_options_set这个函数,这个和之前库函数点灯配置的一样,修改一下引脚就可以直接套用。
// 配置TX为推挽输出 50MHZ
gpio_output_options_set(BSP_USART_TX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, BSP_USART_TX_PIN);
// 配置RX为推挽输出 50MHZ
gpio_output_options_set(BSP_USART_RX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, BSP_USART_RX_PIN);
2
3
4
1.5.配置串口
和之前库函数点灯不同的是,使用串口还需要进行串口的配置。前面只是对GPIO的引脚做了配置,然后就可以操作GPIO进行输入输出,但是要使用串口,还需要设置串口的一些参数,后面使用其它的资源也是如此。
在使用一个外设之前,可以先复位一下,防止一些不必要的事情发生。在对应的外设库文件中一般都会有这个函数。在gd32e23x_uart.h头文件中有
void usart_deinit(uint32_t usart_periph);
这个函数复位串口。有一个参数,就是要复位的串口。
void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval);
这个函数设置波特率。有两个参数,分别为要设置的串口和要设置的波特率。
void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg);
这个函数配置校验功能。有两个参数,第一个就是要配置的串口,第二个就是设置校验方式。关于校验方式的选项如图1-5-1所示。
从图1-5-1可以看到,校验方式可以选择奇校验、偶检验和无校验。一般配置为无校验即可。
void usart*word_length_set(uint32_t usart_periph, uint32_t wlen);
这个函数设置数据位的长度。有两个参数,第一个参数就是要配置的串口,第二个参数就是要设置的数据位的长度,关于数据位长度的选项如图1-5-2所示。
从图1-5-2可以知道关于数据长度有两个选项,一般选择8位即可。
void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen);
这个函数设置停止位的位数。有两个参数,第一个参数就是要配置的串口,第二个参数就是要设置的停止位的位数,关于停止位的位数的选项如图1-5-3所示。
停止位一般选择1位即可。 经过上面的学习,可编写代码如下。
/* 串口配置*/
usart_deinit(BSP_USART); // 复位串口
usart_baudrate_set(BSP_USART,dwbaud_rate); // 设置波特率
usart_parity_config(BSP_USART,USART_PM_NONE); // 没有校验位
usart_word_length_set(BSP_USART,USART_WL_8BIT); // 8位数据位
usart_stop_bit_set(BSP_USART,USART_STB_1BIT); // 1位停止位
2
3
4
5
6
这里要注意一下BSP_USART是一个宏定义
#define BSP_USART USART0
如还需要配置其它的参数请查找对应的函数调用即可。
1.6.使能串口
串口配置好之后并不能开始工作,还需要去使能,就是相当于有一个开关可以打开关闭。值得注意的是发送和接收也需要分别去使能,串口使能相当于一个总开关,发送和接收相当于分别控制的开关。要使用串口,首先要打开总开关,
void usart_enable(uint32_t usart_periph);
这个函数使能串口,有一个参数,就是要使能的串口。 然后如果要发送数据的话还需要使能发送功能。
void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig);
这个函数配置串口发送。有两个参数,第二个参数对应的选项如图1-6-1所示。
从图1-6-1中可以看到,如果使能串口发送就配置为USART_TRANSMIT_ENABLE。 如果要接收数据的话还需要使能接收功能。
void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig);
这个函数配置串口接收。有一个参数,对应的选项如图1-6-2所示。
从图1-6-2中可以看到,如果使能串口接收就配置为USART_RECEIVE_ENABLE。 我们只使用串口发送功能,所以只需要配置串口使能和串口发送使能即可。 转化为代码为
usart_transmit_config(BSP_USART,USART_TRANSMIT_ENABLE); // 使能串口发送
usart_enable(BSP_USART); // 使能串口
2
到此,串口使能就完成了。
2.串口发送数据
配置好串口之后,下一步的操作就是要发送数据。
void usart_data_transmit(uint32_t usart_periph, uint32_t data);
这个函数可以发送数据,有两个参数,第一个参数是要使用的串口,第二个参数是要发送的数据,不过需要注意的是这个函数一次只能发送一个字节。要保证串口稳定的传输,就需要在发送完一个字节之后再发送下一个字节。需要去检测数据发送完成。
FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag);
这个函数是获取状态寄存器的标志。有两个参数,第一个参数是要使用的串口,第二个参数是要获取的状态位,状态位选项为图2-1-1所示(只截取一部分)。
可以获取发送缓冲区的标志位,看当前是否还有数据,如果有数据,将等待,如果没有数据就可以继续发送。 关于TBE的介绍如图2-1-2所示。
从图2-1-2可以了解到当将要发送的数据写入USART_DATA时,此位被清0,当数据发送完成之后,此位置1。所以当检测到此位为1时就表明当前数据缓冲区为空,可以继续发送数据。 关于串口发送数据可以封装为一个函数如下。
void usart_send_data(uint8_t ucch)
{
usart_data_transmit(BSP_USART, (uint8_t)ucch);
while(RESET == usart_flag_get(BSP_USART, USART_FLAG_TBE)); // 等待发送数据缓冲区标志置位
}
2
3
4
5
当我们调用
usart_send_data('h');
usart_send_data('e');
usart_send_data('l');
usart_send_data('l');
usart_send_data('o');
2
3
4
5
将会在串口助手打印hello,如图2-1-3所示。
这样一个字符一个字符的打印输出是不是很麻烦,没关系,我们可以再进行封装一层,一次发送一个字符串。
void usart_send_String(uint8_t *ucstr)
{
while(ucstr && *ucstr) // 地址为空或者值为空跳出
{
usart_send_data(*ucstr++);
}
}
2
3
4
5
6
7
通过调用
usart_send_String("hello\r\n");
这一句就可以打印出hello。
3.串口重定向
上面封装的发送字符串的方式打印信息看似方便,但如果我们想要打印数字,小数,该怎么打印呢?大家是否习惯使用了printf这个函数,可以通过%d,%f打印整形和小数,这一小节就教大家怎么把串口重定向到printf函数。
3.1.串口重定向介绍
C语言中的printf函数默认输出设备是显示器,如果要在串口显示,必须重新定义标准库函数里调用的与输出设备相关的函数。需要注意的是,在keil中使用printf一定要勾选“微库”选项。
3.2.printf重定向
首先c语言的printf函数中不断循环调用fputc函数,所以需要重写fputc函数,这个函数的功能就是打印输出一个字符,这不正和我们编写的usart_send_data函数功能一样。fputc函数可写为
int fputc(int ch, FILE *f)
{
usart_data_transmit(USART0, (uint8_t) ch);
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
return ch;
}
2
3
4
5
6
编写好fputc函数之后就可以使用printf函数输出信息了。
4.实验现象
关于这一章节的代码,在资源包的软件资料目录中。 烧写我们的代码之后,打开串口调试助手,每隔2s会打印一次信息,信息为小数每隔2s+0.11,打印信息如图5-1-1所示。