STC8G1K08A 仅 349 字节的 UART hello world 示例
另请参见:
- 只想要一个不进行体积优化的简单示例?请查看 STC8G1K08A 最小 UART hello world 示例
在上一篇文章 STC8G1K08A 最小 UART hello world 示例 中,我们演示了如何使用 FwLib_STC8 为 STC8G1K08A 编写一个简单的 UART hello world 应用,其体积略大于 2.2 KB。
虽然该示例非常简单,但它存在一个与 SDCC 功能集相关的严重问题:截至目前,SDCC 尚不支持从链接输出中裁剪未使用的函数。 这意味着,只要你使用了 UART 库中的任意一个函数,最终就会链接整个库,从而带来大量额外的代码体积开销。
为了解决这一问题,我们可以将代码内联并进行一定程度的优化。
main.c
#include "fw_uart.h"
/* 将波特率集中定义在一处,使 Timer1 重载公式保持直观。 */
#define UART1_BAUD_RATE 115200UL
/* 不调用任何运行时代码,复现库的 SYSCLK 计算。 */
#define UART1_SYSCLK_HZ (__CONF_FOSC / ((__CONF_CLKDIV == 0) ? 1UL : (unsigned long)__CONF_CLKDIV))
/* UART1 模式 1 使用 Timer1 溢出频率除以 4 作为波特率时钟。 */
#define UART1_TIMER1_RELOAD (65536UL - (UART1_SYSCLK_HZ / (4UL * UART1_BAUD_RATE)))
/* 将整条消息存放在同一块内存区域,便于库将其逐字节输出。 */
static unsigned char uart_message[] = "Hello, world from UART1!\r\n";
static void UART1_SendString(unsigned char *str)
{
while (*str)
{
SBUF = *str++;
TI = 0;
while (!TI)
{
}
}
}
#define TICKS_MS (__CONF_FOSC / ((__CONF_CLKDIV == 0) ? 1UL : (unsigned long)__CONF_CLKDIV) / 9000UL)
static void delay_ms(unsigned int ms)
{
unsigned int i;
do
{
i = TICKS_MS;
while (--i)
{
}
} while (--ms);
}
static void UART1_Init(void)
{
/*
* 打开扩展 SFR 区,以便在启用 UART1 之前写入 CLKDIV。
* B7 = 1:允许访问扩展 SFR 区域。
* B6..B0 = 0:保持其他区切换与外设路由控制不变。
*/
P_SW2 = 0x80;
/* 通过整寄存器写入应用所配置的时钟分频系数。 */
CLKDIV = __CONF_CLKDIV;
/*
* 时钟分频已编程完成,现在返回普通 SFR 区。
* B7 = 0:再次禁用扩展 SFR 访问。
* B6..B0 = 0:在此最小示例中保持 P_SW2 其余控制位无效。
*/
P_SW2 = 0x00;
/*
* 选择与已校准微调值匹配的高速内部 RC 振荡器频段。
* 0x03 仅保留 IRCBAND[1:0],即频段选择位。
* 配置值中的高位将被丢弃,确保只修改文档所述的频段字段。
*/
IRCBAND = (__CONF_IRCBAND & 0x03);
/* 加载与该 RC 频段对应的电压微调字节。 */
VRTRIM = __CONF_VRTRIM;
/* 加载高速 RC 微调字节。 */
IRTRIM = __CONF_IRTRIM;
/*
* 即使本示例运行在快速内部时钟下,也加载低速 RC 微调字段。
* 0x03 仅保留 LIRTRIM[1:0],即有效的低速微调位。
* 高位强制为低,避免向该寄存器写入未定义值。
*/
LIRTRIM = (__CONF_LIRTRIM & 0x03);
/*
* 将 UART1 置于标准 8 位异步模式,并允许接收器运行。
* 0x50 = 0101 0000b。
* SM0 = 0,SM1 = 1:选择 UART 模式 1,即常见的可变波特率 8 位 UART。
* SM2 = 0:禁用多机地址过滤,因此接收到的每个字节都会被接受。
* REN = 1:启用 UART 接收器。
* TB8 = 0,RB8 = 0:第九位发送/接收状态初始清零,因为模式 1 不使用该位。
* TI = 0,RI = 0:在发送第一个字符前清除发送与接收状态标志。
*/
SCON = 0x50;
/*
* 将 Timer1 配置为 UART1 计数所用的波特率发生器。
* 0x40 = 0100 0000b。
* B6 = 1:Timer1 运行于 1T 模式,每个系统时钟周期递增一次,而非每 12 个时钟一次。
* B0 = 0:保持 UART1 的波特率源为 Timer1,不切换到 Timer2。
* B5..B1 = 0:在本示例中保持 AUXR 其余无关功能禁用。
*/
AUXR = 0x40;
/*
* 将 Timer1 置于 FwLib UART 代码路径所期望的重载模式。
* 0x00 = 0000 0000b。
* GATE1 = 0:Timer1 仅由 TR1 启停,不受外部 INT1 引脚控制。
* C/T1 = 0:Timer1 计数时钟节拍,而非外部事件。
* M1_1 = 0,M0_1 = 0:选择本 STC8 库用于 UART 波特率生成的 Timer1 模式。
* TMOD 中 Timer0 部分也被清零,因为本示例不使用 Timer0。
*/
TMOD = 0x00;
/* 加载编译期 Timer1 重载值的高字节。 */
TH1 = (unsigned char)(UART1_TIMER1_RELOAD >> 8);
/* 加载低字节,使首次溢出即采用正确的波特率。 */
TL1 = (unsigned char)(UART1_TIMER1_RELOAD & 0xFF);
/* 启动 Timer1,使 UART1 能够据此推导位定时。 */
TR1 = 1;
}
void main(void)
{
/* 以零初始化的无符号循环变量实现紧凑的全范围延时。 */
unsigned int settle = 0;
UART1_Init();
/* 将整个以 NUL 结尾的消息缓冲区交给本地字符串发送辅助函数。 */
while (1)
{
UART1_SendString(uart_message);
delay_ms(1000);
}
}如何烧录
参见我们的文章 如何使用 stcgal 烧录 STC8G1K08A
如何查看串口输出
使用以下命令查看输出:
monitor.sh
picocom -b 115200 /dev/ttyUSB0内存占用
memory_usage.txt
===== Memory usage summary for uart_minimal_size_demo =====
Internal RAM layout:
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00:|0|0|0|0|0|0|0|0|S|S|S|S|S|S|S|S|
0x10:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x20:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x30:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x40:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x50:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x60:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x70:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x80:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x90:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xa0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xb0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xc0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xd0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xe0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xf0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0-3:Reg Banks, T:Bit regs, a-z:Data, B:Bits, Q:Overlay, I:iData, S:Stack, A:Absolute
Stack starts at: 0x08 (sp set to 0x07) with 248 bytes available.
No spare internal RAM space left.
Other memory:
Name Start End Size Max
---------------- -------- -------- -------- --------
PAGED EXT. RAM 0 256
EXTERNAL RAM 0x0001 0x0020 32 1024
ROM/EPROM/FLASH 0x0000 0x015c 349 8192
==========================================================使用 SDCC 4.2.0 #13081 时,349 字节的 flash 相比上一示例的 2.2 KB 是显著改进。
你可以选择只将 FwLib 的部分函数复制到自己的项目中(FwLib_STC8 宽松的 Apache 许可条款允许这样做,但可能需要保留版权声明),或者将它们整体“内联”以进一步压缩代码。在上面的代码中,我们采用了两者结合的方式。
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow