第三章 Printf函数的实现
前言
单片机开发过程总会遇到各式各样的问题,不像桌面系统,我们看不见摸不着,很难去定位问题,那么就需要一个工具来监控单片机的运行状态,一种可以使用我们的调试工具,另一种就是使用Printf函数通过串口打印调试信息。
一、实现逻辑
- Printf函数是一个标准的库函数,默认是输出到显示器上的,如果想把他用到单片机上,就需要告诉它我们的输出接口在哪,而一般单片机上输出接口用的最多的就是USART通讯接口了,将Printf输出改到串口上,我们称为Printf的重定向。
二、USART通讯
- 在做Printf重定向之前,我们需要先将USART驱动写好,我们这里使用的是GD32F330单片机的USART0来实现。
- 这里我们直接在上一章的工程中进行修改,新建两个文件debug.c和debug.h保存到我们的SYSTEM文件夹中,并添加到工程中。
- 要使用GD32的串口首先要对其进行初始化,单片机串口初始化离不开这几步:时钟使能 ——> IO配置 ——> 串口配置 ——> 串口使能
- 这里推荐大家看一下GD32F3x0固件库使用指南,首先我们找到时钟单元RCU,可以找到
rcu_periph_clock_enable
使能外设时钟函数,这正是我们想要的。再看看参数,我们用的串口和GPIO所以,我们的参数需要选择RCU_USARTx和RCU_GPIOx。
- IO配置,需要找到GPIO函数部分,看一下函数配置表,一下功能是我们可能用到的,然后再去看看函数的具体说明,就知道怎么用了。
- 同样串口配置,需要找到USART函数部分,再看函数表,找到我们常用的函数,这里发现GD32支持USART反转功能,这个在硬件设计中可以用的上,有时候可能不小心将RX和TX管脚接反了,就可以用这个函数来配置管脚功能交换。
- 了解完库函数以后,就可以开始写我们的USART初始化函数了。
void USART0_Init(uint32_t Bound)
{
//时钟使能
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_USART0);
//IO复用
gpio_af_set(GPIOA,GPIO_AF_1,GPIO_PIN_9);
gpio_af_set(GPIOA,GPIO_AF_1,GPIO_PIN_10);
//IO配置
gpio_mode_set(GPIOA,GPIO_MODE_AF,GPIO_PUPD_NONE,GPIO_PIN_9);//TX
gpio_output_options_set(GPIOA,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_9);
gpio_mode_set(GPIOA,GPIO_MODE_AF,GPIO_PUPD_NONE,GPIO_PIN_10);//RX
gpio_output_options_set(GPIOA,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_10);
//串口配置
usart_deinit(USART0);
usart_baudrate_set(USART0,Bound);//波特率
usart_parity_config(USART0,USART_PM_NONE);//校验
usart_word_length_set(USART0,USART_WL_8BIT);//数据位
usart_stop_bit_set(USART0,USART_STB_1BIT);//停止位
//串口使能
usart_transmit_config(USART0,USART_TRANSMIT_ENABLE);//使能发送
usart_enable(USART0);
}
- 写完我们先来测试一下,串口输出功能是否正常,在main.c中加入如下代码:
#include "main.h"
int main(void)
{
//延时函数初始化
Delay_Init();
//LED GPIO初始化
LED_Init();
//串口初始化
USART0_Init(115200);
while(1)
{
//点亮LED
gpio_bit_reset(GPIOB,GPIO_PIN_0);
Delay_ms(1000);
//熄灭LED
gpio_bit_set(GPIOB,GPIO_PIN_0);
Delay_ms(1000);
//串口打印
usart_data_transmit(USART0,0xAA);
}
}
- 重新编译后烧录到单片机中,使用串口工具连接我们的电能,使用串口工具观察,可以看到有数据输出,说明我们已经能成功使用单片机的USART功能了。
三、Printf重定向
- printf重定向只需要去重写fputc函数即可,当使用printf时候,会自动去调用fputc函数,我们在debug.c中加入如下代码:
int fputc(int ch, FILE *f)
{
uint16_t cnt = 0;
//发送缓冲区空
while(usart_flag_get(USART0,USART_FLAG_TBE) == RESET)
{
if(cnt++ >= 60000)
return ch;
}
//发送数据
usart_data_transmit(USART0,(uint8_t)ch);
return ch;
}
- 直接编译会报
error: #20: identifier "FILE" is undefined
,需要增加头文件#include <stdio.h>,使用Printf会增加程序的体积,我们要勾选一下MicroLIB,使用微库功能,这样可以大大减小程序的体积。
- 到这里,我们就可以使用printf函数了,修改main.c文件,测试一下我们的printf函数
#include "main.h"
int main(void)
{
//延时函数初始化
Delay_Init();
//LED GPIO初始化
LED_Init();
//串口初始化
USART0_Init(115200);
while(1)
{
//点亮LED
gpio_bit_reset(GPIOB,GPIO_PIN_0);
Delay_ms(1000);
//熄灭LED
gpio_bit_set(GPIOB,GPIO_PIN_0);
Delay_ms(1000);
//串口打印
printf("Hello GD32\r\n");
}
}
- 编译后烧录到单片机中,再看看我的串口工具打印信息,完美收工。
四、总结
本章主要介绍了如何使用GD32的USART功能来做Print的重定向,方便我们日后调试代码。写作不易,如果觉得对你有用,帮忙点个赞,你的支持会让我有更多动力写下去,感谢!
Printf重定向