嵌入式系统中串口是非常重要的接口,除了用于与外设通信外,用它打印log和调试是最常用的手段之一。本文介绍gd32vf103芯片串口的最小配置,并实现printf函数,以方便log打印。串口的配置如下所示:
1、使能串口时钟;
2、配置串口使用的引脚为AFIO模式;
3、串口初始化;
4、通过发送接收寄存器收发数据;
我这里以串口0为例,其他的串口都是类似的操作。longan nano的串口0位于板子正面的左边。
1、使能串口时钟
查阅《GD32VF103_User_Manual_CN_V1.2》可以找到gd32vf103器件的系统架构示意图,从图中我们可以看到串口0挂在APB2上。
与前面讲GPIO类似,在手册中找到RCU寄存器一章,然后找到APB2使能寄存器(RCU_APB2EN),它的第14位就是串口0的时钟使能。
代码如下所示:
2、配置串口使用的引脚为AFIO模式
串口0默认用到了GPIOA的9和10引脚(关于gpio的基地址和配置等内容可以查看我的另一篇文章《Longan nano(GD32VF103)之GPIO最小配置》)。也可以使用重映射功能映射到GPIOB6和7上,关于重映射的内容我会专门写文章介绍。
用作串口通信的Rx和Tx引脚要配置正确,Rx是接收,所以要配置成输入,具体模式我都试过,发现三种(模拟、浮空、上下拉)都可以,一般是浮空输入。Tx是发送,所以配置成输出,输出模式必须为AFIO推挽输出或者AFIO开漏输出。
具体代码如下所示:
3、串口初始化
我们平时使用串口感觉它很简单,其实不然,串口完整的内容体系还是有一点复杂的。当然我们先不管那么多,让它能够正常收发就可以了。
串口有几个常用参数:波特率、数据位、校验位、停止位。使用过串口终端或者串口助手的应该很熟悉。这些都在串口相关寄存器中配置。在《GD32VF103 用户手册》中可以找到串口的所有内容,usart寄存器的基地址如下所示:
波特率寄存器设置串口通信的波特率,它的地址偏移0x08,寄存器分成了两部分,低四位设置波特率分频器的小数部分,高12位设置波特率分频器的整数部分。
这两个值的计算方法在手册中有说明,具体如下:
实际代码中不这么计算,而是用下面的公式:
这个公式算出的结果可以直接放到波特率寄存器(USART_BAUD)中,它已经包含了移位、小数转换、进位和四舍五入。大家可以自己研究。
数据位和校验位在控制寄存器0(USART_CTL0)中设置,数据位通过bit12设置,校验位通过bit9设置。
停止位在控制寄存器1(USART_CTL1)中设置,通过bit[13:12]设置。
除了波特率我其他都使用默认值,所以没有设置它们。大家可以参考波特率的设置方法设置其他寄存器。
初始化的最后一步就是使能串口和收发了,它们都在控制寄存器 0 (USART_CTL0) 中设置,bit13使能串口,bit3使能发送,bit2使能接收。这里要注意,使能要放到最后,因为使能后波特率寄存器、停止位和其他很多寄存器都不能写了。
4、通过发送接收寄存器收发数据
初始化完成后就可以通过数据寄存器 (USART_DATA) 读写数据了,它只有低8位有效。
发送时系统会将数据寄存器中的值移到发送移位寄存器中发送,接收时数据会通过接收移位寄存器将数据放到数据寄存器中读取。
数据的发送需要一定的时间,连续发送的太快会导致数据丢失。为了准确判断发送完成没有,就需要用到状态寄存器 0 (USART_STAT) ,它提供了各种状态和错误标志,通过这些标志就可以知道串口当前的工作状态。bit6是发送完成标志,bit5是读数据缓冲区非空标志。通过它们就可以准确控制数据的收发了。
我这里使用了轮询的方法,一直while读取标志,实际中发送可以这样做,读取是不行的,否则干不了其他事了。接收由于时间的不确定性一般都使用中断的方式,我们以后介绍完中断再将这里改了。
实现printf函数
学过c语言的都知道用printf函数打印信息非常方便,在嵌入式系统中尤其如此,所以下面说说怎么在我们上面串口0的基础上实现printf。
很多开发平台只需要重定位printf的输出就可以了,但是vscode+platformio这个我不知道怎么重定位,所以就找了一个printf的实现源码,这个源码实现了基本的功能,而且非常小,很适合嵌入式使用。
配置非常简单:
1、把两个文件添加到工程中
2、然后include头文件
3、实现发送字符的函数
4、最后初始化一下就可以使用了
我还没有尝试过它的打印极限,也不知道它有没有bug,以后用着慢慢看吧。
目前我们平台的基础已经搭好了,可以控制gpio、可以延时、可以打印log,以后学习其他模块就要方便很多了。