物联网工程中GPIO模拟串口通用构件研究

2020-02-22 01:35
昆明学院学报 2020年6期
关键词:波特率内核串口

石 栋

(昆明学院 信息工程学院,云南 昆明 650214)

串口是嵌入式系统开发中微控制器(MCU)间或微控制器与上位机之间最基本的通信手段.随着物联网技术的不断发展,MCU的功能越来越强大.但考虑到成本问题,MCU生产商在MCU上仅会配置1~3个UART,以至于在嵌入式系统的开发过程中如果需要较多的串口通信时,就必须采用扩展串口的方式来实现.

目前,相关文献报道的扩展串口方法主要有3种基本方案:1)利用硬件扩展多串口通信[1].该方式是利用数字控制的模拟数据选择/分配器,或利用串口的扩展芯片来扩展串口.2)利用MCU定时器的定时功能实现软件模拟串口.该类型的设计又分为两种,一种是利用MCU上的定时器进行延时,从而用模拟的方法实现串口功能[2-5],另一种是使用定时器的输出比较、输入捕获的功能实现UART功能[6].3)利用软件空循环的方式实现模拟串口[7].这种方法使用for空循环语句进行延时,以实现控制串口通信每一位的持续时间.

在现有方案中,方案1的扩展方法需增加硬件成本;方案2需占用MCU定时器资源;方案3无法实现多时钟频率、多波特率下正常工作,可移植性差.因此,本文通过分析串口波特率、MCU内核时钟频率和延时函数之间的数学关系,借此给出准确延时的计算方案,从而在不增加硬件资源、不占用MCU定时器资源的情况下,利用纯软件方法实现可以在多内核时钟频率、多波特率下正常工作,且具备可移植、可复用的GPIO模拟串口构件设计.同时鉴于目前文献中尚未解决用软件方法实现串口功能时代码时间开销的测算问题,或只能粗略估算代码时间开销、仅能实现少量个字符接收的问题[7],提出一种较为准确的调试方法,从而较好地解决模拟串口的测试问题.

1 通用异步串口的技术要点分析

1.1 异步串口通信的技术要点

常见串口的基本要素包含串行通信数据格式、波特率和奇偶校验.图1[8]给出了8位数据、无奇偶校验情况的传送格式.

波特率是串口每秒内传送的位数,其倒数就是发送一位的位长,也称为位的持续时间.而奇偶校验分为奇校验和偶校验,在使用时其仅能验证字符中含“1”的位数是奇数个还是偶数个,校验功能有限,实际应用中多不使用.因此本设计不考虑该设计方法.

1.2 GPIO模拟串口的技术要点

要实现通过GPIO模拟串口与上位机等其他设备的通信,模拟串口要具备与普通串口相同的异步串口通信格式、波特率等.从图1可以看出,每发送(接收)一位都要持续一定的时间长度,即位长.一方面位长是波特率fbaud的倒数,另一方面位长还等于位长的时钟周期数与MCU内核时钟周期(内核时钟频率fCPU的倒数)的乘积.如果使用软件延时的方法实现位长,则软件延时所需时钟周期数就应该等于位长的时钟周期数.那么软件延时数N就应该与波特率fbaud和MCU内核时钟频率fCPU间存在数学关系N=f(fbaud,fCPU),找到这一关系后,就可以用软件的方式实现串口位长的精确延时,同时利用该数学关系还可实现GPIO模拟串口的可复用与可移植.

2 delay()函数延时公式推导

本文采用delay()函数来实现位延时:

void delay(uint_32 N)

{

uint_32 i;

for(i= 0;i

_asm(“NOP”);

}

其中,“NOP”是汇编指令,称为空指令.该指令的功能是无操作,通常用作指令对齐或延时[9].如果能测算出当N=1,2,3,…时,delay函数执行所需要的时钟周期数,并找到其入口参数N与时钟周期数的关系,就可以利用delay函数实现模拟串口的位延时.

2.1 delay()执行周期数的测量

delay()函数执行的时钟周期数可借助定时器测量:将定时器的工作时钟频率设定为MCU的内核时钟频率,记录下delay()函数开始执行时和执行结束时定时器的计数值,就可以计算出delay()函数执行的时钟周期数.由于Cortex-M系列处理器包含一个简单时钟定时器SysTick[10].因此,本文利用ARM公司的Cortex-M0+系列MCU中的SysTick定时器进行说明,测试方法如下:

……//初始化并使能SysTick定时器.

start_ticks=SysTick->VAL;//保存delay()函数开始时定时器计数值.

delay(N);//delay()函数执行.

end_ticks=SysTick->VAL;//保存delay()函数结束时定时器计数值.

……//计算输出语句或函数执行的时钟周期数.

在NXP公司基于ARM Cortex-M0+内核的KL25上,测量出delay()函数入口参数N与函数执行所需时钟周期数(Ntick)的关系,如表1所示.

表1 delay()函数入口参数与时钟周期数的关系

从表1可以看出,当delay()函数入口参数为1时,执行delay()函数需要的时钟周期数是55次,随后delay()函数的入口参数每增加1,delay()函数执行需要的时钟周期数就增加13次.由此不难得出,delay()函数的入口参数(N)与执行时钟周期数(Ntick)的关系为:

Ntick=13(N-1)+55,(N>0),

(1)

(1)式中的常数13和55除决定于MCU的CPU系列外,还与使用的编译器环境有关.本文是在NXP的KDS环境下使用GNU编译器实现的.在不同系列的MCU和不同的编译环境下可将公式抽象为:

Ntick=A(N-1)+B,(N>0),

(2)

其中A和B为两个待定常数,可在具体的MCU和编译环境中测量确定.本文以KL25在GNU编译环境下进行,则A=13,B=55.

2.2 串口波特率与MCU内核时钟频率的关系

串口的波特率fbaud代表了串口 1 s 能够传输的位(Bit)数,其倒数就是串口传输一个二进制位(1 Bit)的位长.那么,串口传输一个二进制位(1 Bit)所需的时钟周期数就应该等于串口传输一个二进制位(1 Bit)所持续的时间与内核时钟周期的比值,也就是内核时钟频率fCPU与波特率fbaud的比值,即:

(3)

2.3 delay()函数延时次数的公式推导

用delay()函数来实现模拟串口的位延时,则delay()函数执行延时所需的时钟周期数应等于串口传输1 Bit的时钟周期数.即:

Nticks=Nbit.

(4)

将公式(1)和公式(3)带入公式(4),可得:

(5)

从公式(5)可以得出,只要确定MCU的内核时钟频率以及串口的波特率,就可以确定delay()函数的入口参数,因此就可用delay()函数在GPIO模拟串口实现位延时.即使内核时钟频率改变或波特率改变,也可用公式(5)计算出新入口参数N.

3 可移植与可复用GPIO模拟串口构件设计

为delay()函数找到准确的入口参数后,就可以编程实现模拟串口的功能.本文采用苏州大学嵌入式实验中心为KL25开发的底层驱动,并选用KL25开发板,在NXP公司提供的KDS软件环境中实现模拟串口功能.为实现可移植与可复用的目的,工程采用构件化的思想将模拟串口的功能进行封装,包含iouart.h和iouart.c文件.iouart.h是模拟串口构件的头文件,其内容包含声明保存延时函数入口参数的数组变量,以及模拟串口号的宏定义和函数声明.iouart.c提供模拟串口的具体实现.这里仅就模拟串口的初始化、发送和接收功能进行描述,将这3个功能封装成3个构件:初始化函数iouart_init();发送一字节函数iouart_send1();接收一字节函数iouart_re1().

3.1 GPIO模拟串口构件初始化设计

将GPIO模拟串口的初始化功能封装成构件:iouart_init(uint_8 uartNo,uint_32 baud),参数uartNo和baud分别代表模拟串口编号和待用的波特率.该构件需完成3个任务:1)在MCU上选择的2个空闲I/O引脚,将其复用为GPIO功能.2)将发送功能引脚的GPIO功能定义为输出,并令其输出“1”,即输出空闲电平;将用作接收功能引脚的GPIO功能定义为输入.3)将MCU的内核时钟频率(已知值)和选定的波特率代入公式(5)中计算delay()函数的入口参数,并将计算出来的入口参数结果放入到一个全局变量中供delay()函数使用.基本程序如下:

void iouart_init(uint_8 uartNo,uint_32 baud)

{

……//串口号解析及局部变量的声明.

gpio_init(Uport[ioN],1,1);//初始化TX引脚:GPIO功能、输出、高电平.

gpio_init(Uport[ioN+1],0,1);//初始化RX引脚:GPIO功能、输入.

//计算特定波特率下“NOP”指令执行的延时次数,保存到全局变量nop_ticks[]中供延时函数使用.

nop_ticks[nopN]= ((SystemCoreClock*1.0/baud)-55.0)/13.0+1;

}

代码中,nop_ticks[]是保存delay()函数入口参数的数组,SystemCoreClock用于保存系统时钟频率,由MCU在启动时配置好时钟频率后赋值.模拟串口引脚号放置于Uport[]数组中,在“iouart.c”文件中编写.

3.2 GPIO模拟串口构件发送功能设计

GPIO模拟串口构件发送功能封装为构件:iouart_send1(uint_8uartNo, uint_32 ch).设计只需严格按照图1所示的串行通信数据格式设计即可.构件函数基本程序如下:

void iouart_send1(uint_8 uartNo, uint_8 ch)

{

……//函数初始化.

gpio_set(Uport[ioN],0);//发送起始位.

delay(nop_ticks[nopN]);//延时一位位长.

//发送8位数据.

for(i=0;i<8;i++)

{

j= ((ch>>i)&0x01);//获取第i位状态.

gpio_set(Uport[ioN],j);//发送1位.

delay(nop_ticks[nopN]);//数据位延时.

}

gpio_set(Uport[ioN],1);//发送停止位.

delay(nop_ticks[nopN]);//停止位延时.

}

3.3 GPIO模拟串口构件接收功能设计

GPIO模拟串口构件接收功能封装成构件:iouart_re1(uint_8uartNo).设计按照图1所示的串行通信数据格式.但与发送功能比较,有3点不同:1)将开始位持续时间延长为原来的1.5倍,保证可以在每一个数据位的中间位置读取接收数据,避免在电平变换时刻错误读取接收引脚的数值.2)在接收数据位的每一位时采用连续接收3次的策略,实现滤波功能,避免因信号波动导致读数错误.3)接收到停止位后不做位延时而直接返回数据,保证模拟串口接收连续字符时有充裕的处理时间.uint_8 iouart_re1(uint_8 uartNo)函数的核心代码为:

uint_8 iouart_re1(uint_8 uartNo)

{

……//利用模拟串口号解析出RX引脚号及delay入口参数.

k=gpio_get(Uport[ioN]);//获取起始位.

delay(nop_ticks[nopN]);

for(i=0;i<8;i++)//读8位数据.

{

//连读3次滤波,同时可减少由于延时不准确的影响.

k1=gpio_get(Uport[ioN]);

_asm(“NOP”);

k2=gpio_get(Uport[ioN]);

_asm(“NOP”);

k3=gpio_get(Uport[ioN]);

delay(nop_ticks[nopN]);

j=0;

//挑选3次读中2次相同的值为最终值.

k1+=(k2+k3);

if(k1>=2) j=1;

dat|=((j)<

}

k=gpio_get(Uport[ioN]);//读停止位.

gpio_clear_int(Uport[ioN]);//清除ISF中断标志.

return dat

}

3.4 GPIO模拟串口构件的调试方法

实现了发送、接收功能的基本编程后,由于程序语句执行、子函数调用都需要时间,就会导致模拟串口在发送接收数据时的时序出现问题.因此还需要对构件进行调试方能正常使用.为使GPIO模拟串口能够正常工作在多种内核时钟频率下,且能够使用多种波特率,调试最好选在低内核时钟频率、高波特率下进行.

3.4.1 发送构件的调试

由于本构件的位延时可以做到非常准确,因此发送时序的开始位、停止位可以少调试甚至不调试,仅需调试数据位的发送时序.下面以数据位的调试简要说明其调试方法:1)根据使用的内核时钟频率和波特率,计算出位长的理论时钟周期数Ntick(公式(3)).2)利用SysTick定时器测量发送构件iouart_send1()发送1个字节所用的时钟周期数10Nreal(Nreal是发送1位所需的周期数,发送1个字节包含1个开始位、1个结束位、8个数据位),这样可以得到实际位长的时钟周期数Nreal.然后比较Ndata与Ntick,其差值ΔN=Nreal-Ntick就是由于数据位语句执行消耗的时钟周期数,这样数据位的位延时就应该减少△N/13次,则数据位delay()函数的入口参数为N-△N/13,至此完成数据位的调试.

3.4.2 接收构件的调试

本文模拟串口采用GPIO中断实现接收功能,则接收功能的调试有两个位置:开始位和数据位.调试开始位时,定时器SysTick应从进入GPIO中断服务例程时开始计时,到iouart_re1()函数开始位延时后计时结束,此时定时器测得的计数值就是开始位的实际位长Nreal,之后处理方法与发送功能的数据位调试方法基本一致.而数据位的调试与发送功能的数据位调试基本一致,此处不再详述.

4 测试与分析

为实现GPIO模拟串口的通信功能,本文使用苏州大学NXP嵌入式实验中心的KL25开发板,在KDS3.0环境下利用KL25工程框架进行测试.测试中本文设计的场景是,从上位机发送“0x01~0xFF”共255个字符,GPIO模拟串口接收到字符后再回发到上位机.接收回发功能在中断中实现.

测试结果如图2所示.该图是在KL25内核时钟频率为48 MHz、波特率为9 600 bps情况下的测试结果.经过改变系统内核时钟频率和波特率进行反复测试的结果表明,本构件在内核时钟频率分别为24,48 MHz,波特率可以在300,600,1 200,2 400,4 800,9 600,1 440,19 200,38 400 bps下正常工作.若将其移植到NXP基于Cortex-M4F的K64MCU上,只需重新确定公式(2)的待定参数A和B,模拟串口构件就可在K64上正常工作,从而实现了基于GPIO模拟串口的通用性和可复用性.

5 结语

本文从计算位长的时钟周期数出发,测算了delay函数的入口参数N及MCU内核时钟频率、串口波特率三者间的关系,提出位延时的数学模型N=f(fbaud,fCPU), 并根据该模型设计并实现了GPIO模拟串口构件.利用此方法设计的GPIO模拟串口构件可在不同的内核时钟频率、不同的波特率下,实现GPIO模拟串口的可移植和可复用.同时针对目前文献尚未报道如何很好地解决模拟串口调试方法的问题,提出利用定时器实现GPIO模拟串口发送、接收功能的调试.此外,本设计方法可以使GPIO模拟串口具备较好的工作性能.

猜你喜欢
波特率内核串口
UART 波特率检测电路的FPGA 设计算法与实现
多内核操作系统综述①
基于NPORT的地面综合气象观测系统通信测试方法及故障处理
强化『高新』内核 打造农业『硅谷』
活化非遗文化 承启设计内核
CAN 总线波特率自适应程序设计
基于API函数库实现串口数据通信的分析与设计
基于EM9000工控板高性能双串口通信模型设计与实现
微软发布新Edge浏览器预览版下载换装Chrome内核
浅谈西门子S7—400与S7—200的通讯实现方法