SDCC编译STC15虚拟串口输出打印示例程序
前言
STC15W408AS(51系列) 单片机从 Keil 迁移到 SDCC上进行开发时,直接使用 Keil上使用的 Soft_UART.c 中的软件模拟串口,通信出现错误,但是同样的代码在 Keil 上编译后下载到单片机执行没有问题。
这也侧面说明了 SDCC和Keil之间编译时存在一些差异的。
原因定位
库的说明是模拟串口,9600波特率,其中延时函数如下所示,其中MAIN_Fosc 是MCU的时钟频率。
void BitTime(void)
{
u16 i;
i = ((MAIN_Fosc / 100) * 104) / 130000L - 1; //根据主时钟来计算位时间
while(--i);
}
这里移植后串口通信错误,是这个BitTime 延时不对无误了,因为其他部分肯定没有问题,在Keil上执行的好好的。
我的工程配置的主时钟为33MHz 。
[env:STC15W408AS]
platform = intel_mcs51
board = STC15W408AS
board_build.f_cpu = 33000000L ; 设置时钟频率 5.5296MHz, 6M, 11.0592M, 12M, 18.432M, 20M, 22.1184M, 24M, 27M , 30M, 33M, 33.1776M
; build_flags =
; --model-small
[platformio]
src_dir = ./
; include_dir = ./
9600波特率换算为时间为:
Δ
t
=
1
s
9600
=
1000000
9600
=
104.167
u
s
\varDelta t=\frac{1s}{9600}=\frac{1000000}{9600}=104.167us
Δt=96001s=96001000000=104.167us
于是我们知道 i = ((MAIN_Fosc / 100) * 104) / 130000L - 1;
这个是用来延时 104us的,在 delay.c
中给出了ms级别延时的函数如下所示:
void delay_ms(unsigned int ms)
{
unsigned int i;
do{
i = MAIN_Fosc / 13000;
while(--i) ; //14T per loop
}while(--ms);
}
所以实际上i = ((MAIN_Fosc / 100) * 104) / 130000L - 1;
应该是 i = (MAIN_Fosc / 13000000L)*104 - 1;
,就是延时104us ,大概是考虑到一一般时钟频率最后两位都为0,以及防止溢出进行了变形处理。
这么看来串口通信错误实际上就是延时出错了,我们只需要就纠正这个延时函数中的 13000 ,就可以推出正确的公示了。
首先看一下再没有纠正之前实际的延时时间:
测试的代码如下所示,理论上高低昂坪时间会持续1s,通过测试,发现高电平的时间为 921.745ms,比预期的小了,说明延时中除数为 13000 大了。
void main()
{
while(1)
{
//循环交替让红灯、蓝灯闪烁
Led_Red = 1;
P1_2 = 1;
delay_ms(1000);
Led_Red = 0;
P1_2=0;
delay_ms(500);
}
}
修正延时公式,原来的延时公式为
void delay_ms(unsigned int ms)
{
unsigned int i;
do{
i = MAIN_Fosc / 13000;
while(--i) ; //14T per loop
}while(--ms);
}
现在除数值修正为 13000 * 0.921591 = 11980.683 ,考虑到此处时钟频率较高,我们可以微调一下改制,让其适配多个时钟频率,不妨取12000试一试 。
改为
void delay_ms(unsigned int ms)
{
unsigned int i;
do{
i = MAIN_Fosc / 12000;
while(--i) ; //14T per loop
}while(--ms);
}
此时高电平持续时间为998.360333ms,误差很小,不影响9600波特率的串口!
代码修改
根据前面的定位分析,修改延时代码如下:
void delay_ms(unsigned int ms)
{
unsigned int i;
do{
#ifdef __SDCC
i = MAIN_Fosc / 12000 ; // 12018;
#else
i = MAIN_Fosc / 13000;
#endif
while(--i) ; //14T per loop
}while(--ms);
}
串口中的延时代码更改为:
void BitTime(void)
{
u16 i;
#ifdef __SDCC
i = ((MAIN_Fosc / 100) * 104) / 120000L - 1;
#else
i = ((MAIN_Fosc / 100) * 104) / 130000L - 1; //根据主时钟来计算位时间
#endif
while(--i);
}
测试工程的主函数代码为:
void main()
{
while(1)
{
//循环交替让红灯、蓝灯闪烁
Led_Red = 1;
P1_2 = 1;
delay_ms(1000);
Led_Red = 0;
P1_2=0;
delay_ms(500);
// TxSend('0');
PrintString("Hello World!\r\n");
}
}
串口接收的数据结果如下所示: