今天来看看UART的使用。
串口是嵌入式领域最常用的通讯方式之一了。
STC32G12K128有俩异步串口,串口三和串口四。
STC8G1K08A是只有一个串口,虽然表格没写是哪个,但是我查了一下,是串口一。
关于串口大家应该都比较熟悉了,所以我们直接讲如何使用STC库函数来使用串口。
以下都是以STC32为例,STC8需要自行去对应手册查,不过流程大差不差。
我们一般就使用8位数据位,1位停止位,无校验位。
STC的UART有两种工作模式。
因为我们使用8位数据位,所以一般采用模式0。
剩下我们要确定的就是波特率了。
有俩定时器可以用,不过我们优先使用定时器3,因为串口2(同步串口)只能使用定时器2,为了避免冲突,我们使用定时器3,不过如果你确定用不到串口2的话使用定时器2也是可以的。
波特率计算公式就如同上表,虽然不复杂,但我也懒得计算,好在我们使用库函数的话不用我们自己去算。
我们需要把STC32G_UART.c和STC32G_UART_Isr.c两个文件加入到我们的工程里。
接着使用下面这个函数UART_Configuration来配置串口。
参数一指定串口,参数二是结构体变量的指针,用来进行具体配置。
从上往下依次是:
串口模式,刚刚有提了,我们就用模式0,可变波特率8位数据位,UART_8bit_BRTx
波特率发生器,也就是指定使用的时钟,我们使用定时器3,BRT_Timer3,我这边假设使用的是串口3,不过如果我们使用的是串口4,那么我们就只能用定时器4。或者使用定时器2,这是所有串口都能用的,不过不使用的理由刚刚也说了,串口2只能用定时器2,只有在确定用不到串口2的时候我们才能用定时器2。
波特率,我们直接填写数值就行,范围是110 ~ 115200,可以超过范围,但是没必要。
是否多机通讯,一般不用,我们选择DISABLE
是否接收,如果要接收的话选择ENABLE,如果只需要串口发送数据,那么可以DISABLE
是否波特率加倍,这个直接DISABLE,用不着,并且只有串口1能用。
配置完之后有俩函数可以发送数据,分别是发送字节和发送字符串的。
懂了发送,那我们还需要懂的接收。
关于接收,我们直接搬官方的的示例代码即可,因为在串口的中断函数里已经帮我们把收到的数据收好了,我们只需要学着示例代码把东西取出来即可。
while (1)
{
delay_ms(1);
if(COM3.RX_TimeOut > 0) //超时计数
{
if(--COM3.RX_TimeOut == 0)
{
if(COM3.RX_Cnt > 0)
{
for(i=0; i<COM3.RX_Cnt; i++) TX3_write2buff(RX3_Buffer[i]); //收到的数据原样返回
}
COM3.RX_Cnt = 0;
}
}
}
上面是示例代码中main函数里的主循环,接下来我就简单分析一下代码。
首先是COM3,就是代表串口3的结构体,库函数里定义好的。
RX_TimeOut就是等待时间,在中断函数里(详情可以自行去库函数里看),接收到数据的时候就会把它设为5,然后在上面代码里是当这个等待时间大于5,我们进入一次第一层的if语句,接着如果减一之后它等于0了,我们进入第二层if语句。
RX_Cnt就是接收缓冲区里收到数据的大小,TX3_write2buff就是串口3的发送缓冲区,RX3_Buffer就是串口3的接收缓冲区,上面就是把收到的数据再发送回去。
由于我们的主循环是1ms执行一下这一坨代码,所以每个等待时间就是1ms,上面代码的意思就是如果串口收到数据了,那么我等它5ms,每收到一次数据,就会在中断函数里把等待时间刷新为5,我在main函数里每隔1ms把等待时间减1,如果我能够把它减到0,那么表示至少是5ms没有收到数据了,那我就把数据取出来发送回去。
上面听不明白没关系,我们只需要把这段代码复制下来,把最里面的一层if语句里的逻辑改改就能够自行处理接收到的数据了。
接下里就是最后一个问题。
串口的引脚。
每个串口至少有两组引脚可以供我们选择。
所以我们需要设置我们使用的是哪一组引脚。
我们需要包含一个头文件STC32G_Switch.h,坑人的是库函数手册里没有介绍这个头文件,我还是去翻官方示例代码的时候看到的。
我们使用这个
UART3_SW(UART3_SW_P00_P01);
或者这个
UART3_SW(UART3_SW_P50_P51);
就可以设置串口3的引脚了,其他串口只需要把上面函数名(实际上是宏定义)里的数字改掉就行,包括GPIO也是一样的。
然后记得给使用的GPIO配置一下,TXD就给推挽输出,RXD就给高阻输入,或者直接两个都设置为准双向口也可以。
最后的最后,串口输出的可能是乱码,这不一定是代码的问题,我们需要在Keil里改个设置。
其实官方手册里有建议这样设置,但是我在这个系列的第一篇环境配置的时候没有提这个,因为我当时没发觉有什么差别。
然而如果不把这个配置好的话,就算代码没问题也还是会出问题。
#include <STC32G.H>
#include "STC32G_GPIO.h"
#include "STC32G_Delay.h"
#include "STC32G_UART.h"
#include "STC32G_NVIC.h"
#include "STC32G_Switch.h"
void GPIO_Init(void){
P0_MODE_OUT_PP(GPIO_Pin_1);
P0_MODE_IN_HIZ(GPIO_Pin_0);
}
void UART_Init(void){
COMx_InitDefine initer;
initer.BaudRateDouble = DISABLE;
initer.Morecommunicate = DISABLE;
initer.UART_BaudRate = 115200;
initer.UART_BRT_Use = BRT_Timer3;
initer.UART_Mode = UART_8bit_BRTx;
initer.UART_RxEnable = ENABLE;
UART_Configuration(UART3, &initer);
NVIC_UART3_Init(ENABLE, Priority_1);
UART3_SW(UART3_SW_P00_P01);
}
void main(void){
uint8 i = 0;
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXSFR(); //扩展SFR(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
GPIO_Init();
UART_Init();
EA = 1;
PrintString3("Hello World\r\n");
while(1){
delay_ms(1);
if(COM3.RX_TimeOut > 0){ //超时计数
if(--COM3.RX_TimeOut == 0){
if(COM3.RX_Cnt > 0){
for(i=0; i<COM3.RX_Cnt; i++) TX3_write2buff(RX3_Buffer[i]); //收到的数据原样返回
}
COM3.RX_Cnt = 0;
}
}
}
return ;
}
顺带一提,STC的烧录软件里自带串口助手,我更爱STC了。
然后更屌的是我们可以直接使用printf,只需要把stdio.h的头文件加上就行了。
唯一需要改的地方在这里。
把PRINTF_SELECT的宏定义改成我们要用的串口就行。
因为在库函数里帮我们重写了putchar函数,而printf底层用的就是putchar,所以等于变相改写了printf。