在单片机开发中,写出能跑的代码只是起点,而程序架构的好坏才是区分菜鸟和高手的真正标尺。
程序架构是代码的“骨架”,它决定了代码的组织方式、运行效率和维护难度。
菜鸟往往只求功能实现,代码杂乱无章;而高手则注重架构设计,追求清晰、高效、可扩展的代码结构。
今天,我们就通过一个简单的代码模型,来看看菜鸟和高手的程序架构有何不同。
我们设想需要实现以下功能:
-
LED灯控制:通过PWM调节LED亮度,支持开关。
-
温度传感器读取:每秒读取一次温度值并通过串口输出。
-
按键输入:短按切换LED开关,长按调节亮度。
-
定时任务:每5秒自动根据温度调整LED亮度。
这个系统功能不算复杂,但足以暴露菜鸟和高手在架构设计上的差距。接下来,我们分别看看两者的实现方式。
菜鸟的架构:一锅乱炖
菜鸟通常会把所有功能塞进一个大循环,代码逻辑混杂在一起,乍一看能跑,但细看问题多多。
菜鸟代码示例
#include <reg52.h>
#include <stdio.h>
void delay_ms(unsigned int ms)
{
unsigned int i, j;
for (i = ms; i > 0; i--)
{
for (j = 110; j > 0; j--);
}
}
unsigned char read_temperature()
{
return 25; // 模拟温度读取,返回固定值
}
void main()
{
unsigned char led_state = 0;
unsigned char key_state = 0;
unsigned int timer_count = 0;
unsigned char temp = 0;
P1 = 0x00; // LED初始关闭
while (1)
{
// 按键检测
if (P3_0 == 0) // 假设P3.0是按键
{
delay_ms(20); // 消抖
if (P3_0 == 0)
{
key_state++;
if (key_state < 50) // 短按
{
led_state = !led_state;
P1 = led_state ? 0xFF : 0x00;
}
else // 长按
{
P1 += 10; // 增加亮度
}
}
}
else
{
key_state = 0;
}
// 温度读取
timer_count++;
if (timer_count % 1000 == 0) // 粗略模拟1秒
{
temp = read_temperature();
printf("Temp: %d\n", temp);
}
// 定时调整亮度
if (timer_count >= 5000) // 粗略模拟5秒
{
if (temp > 30)
{
P1 = 0xFF;
}
else
{
P1 = 0x80;
}
timer_count = 0;
}
delay_ms(1); // 循环延时
}
}
-
菜鸟架构的缺陷
-
逻辑混杂:按键检测、温度读取、定时任务都挤在
main
函数里,代码臃肿,阅读困难。 -
延时阻塞:使用
delay_ms
实现消抖和定时,导致CPU大部分时间空转,效率低下。 -
无模块化:功能未拆分,复用性差,想改一个功能得翻遍整个代码。
-
扩展性差:新增功能(如湿度传感器)只能继续堆砌代码,复杂度激增。
-
时间管理粗糙:靠循环计数模拟定时,精度低且不可靠。
这种架构就像一个杂物堆,想找东西得翻半天,稍微一动就可能全乱。短期能用,但长期维护和扩展简直是噩梦。
高手的架构:层次分明
高手会采用分层设计、模块化和中断驱动,将系统分解为清晰的模块,既高效又易于维护。
高手代码示例
#include <reg52.h>
#include <stdio.h>
// 硬件抽象层
void hal_timer_init()
{
TMOD = 0x01; // 定时器0,模式1
TH0 = 0xFC; // 1ms中断
TL0 = 0x18;
TR0 = 1; // 启动定时器
}
void hal_gpio_init()
{
P1 = 0x00; // LED初始关闭
IE = 0x82; // 使能外部中断0和定时器中断
}
// 驱动层
bit drv_key_pressed()
{
return (P3_0 == 0); // 假设P3.0是按键
}
void drv_led_set(unsigned char brightness)
{
P1 = brightness;
}
unsigned char drv_temp_read()
{
return 25; // 模拟温度读取
}
// 应用层
void app_key_handler(bit pressed, unsigned int duration)
{
static unsigned char brightness = 0;
if (pressed && duration < 50) // 短按
{
brightness = (brightness == 0) ? 0xFF : 0;
drv_led_set(brightness);
}
else if (pressed && duration >= 50) // 长按
{
brightness += 10;
drv_led_set(brightness);
}
}
void app_temp_monitor()
{
unsigned char temp = drv_temp_read();
printf("Temp: %d\n", temp);
}
void app_auto_adjust(unsigned char temp)
{
if (temp > 30)
{
drv_led_set(0xFF);
}
else
{
drv_led_set(0x80);
}
}
// 任务调度
volatile unsigned int tick_count = 0;
void task_scheduler()
{
static unsigned int temp_timer = 0;
static unsigned int adjust_timer = 0;
if (++temp_timer >= 1000) // 1秒
{
app_temp_monitor();
temp_timer = 0;
}
if (++adjust_timer >= 5000) // 5秒
{
app_auto_adjust(drv_temp_read());
adjust_timer = 0;
}
}
void main()
{
hal_gpio_init();
hal_timer_init();
EA = 1; // 使能总中断
while (1)
{
task_scheduler();
}
}
// 中断服务
void timer0_isr() interrupt 1
{
TH0 = 0xFC; // 重载1ms
TL0 = 0x18;
tick_count++;
task_scheduler();
}
void exint0_isr() interrupt 0
{
static unsigned int press_time = 0;
if (drv_key_pressed())
{
press_time++;
app_key_handler(1, press_time);
}
else
{
press_time = 0;
}
}
-
高手架构的优势
-
分层清晰:硬件操作、驱动实现和业务逻辑分层,职责明确。
-
模块独立:按键、LED、温度等功能独立封装,复用性强。
-
中断驱动:用定时器和外部中断替代延时,CPU效率高。
-
任务调度:通过tick计数管理定时任务,时间精确。
-
扩展性强:新增功能只需添加模块和任务,不影响现有代码。
这种架构像一个精心设计的图书馆,分类明确,查找方便,扩展时只需加个书架即可。
菜鸟 vs 高手:架构设计的本质差异
通过对比,我们可以总结出以下关键区别:
设计理念
-
菜鸟:功能导向,只求能跑,缺乏规划。
-
高手:系统导向,注重整体设计和未来扩展。
代码组织
-
菜鸟:大杂烩式,所有逻辑塞在
main
里。 -
高手:分层模块化,结构清晰。
资源利用
-
菜鸟:延时阻塞,浪费CPU。
-
高手:中断驱动,高效利用资源。
可维护性
-
菜鸟:代码混乱,改动困难。
-
高手:模块独立,维护简单。
可扩展性
-
菜鸟:新增功能如滚雪球,越滚越乱。
-
高手:新增功能如搭积木,轻松组合。
如何从菜鸟进阶到高手?
想提升程序架构能力,可以尝试以下方法:
-
学习分层与模块化:将代码分解为硬件层、驱动层和应用层。
-
掌握中断与状态机:用中断替代轮询,用状态机管理复杂逻辑。
-
重构练习:拿自己的旧代码重构,优化结构。
-
阅读优秀代码:研究开源项目,学习高手的设计思路。
-
项目实践:从小项目开始,逐步挑战复杂系统。
我在2018年也录了一套程序架构进阶的系统教程,可以站在我们的肩膀上起飞,找无际单片机直接安排无套路,目前已让至少2000人受益。
程序架构是单片机开发的灵魂,也是菜鸟和高手的分水岭。
菜鸟的代码能跑却乱七八糟,高手的代码不仅高效,还优雅易维护。想成为高手,就从架构设计入手,让你的代码既有“骨架”,又有灵魂。
除此以外,它还能给想入行嵌入式开发的一个王炸Buff,没有说服力,直接给他展示你写得代码,并解释实现思路。
为啥我对无际单片机的项目信心十足?
其实就是我们的架构设计经验,是实打实在一线积累了很多年的,有很多高阶的架构技巧融合,比如时间片轮询架构做任务管理,状态机+查表法融合做复杂功能,指针做队列算法,链表做多级子菜单等等。
之前很多小哥面试的时候,就问到了我们程序架构的问题,比如跟rtos有啥区别,有啥优势等等。
只要吸收并能把这些流畅说出来,背后能反映出你扎实的编程功底,无形装逼最致命啊,哈哈,个人觉得对于找份工作来说,还是相对轻松的。
下次写代码时,刻意去练习吧~
最近很多粉丝问我单片机怎么学,我根据自己从业十年经验,累积耗时一个月,精心整理一份「单
片机最佳学习路径+单片机入门到高级教程+工具包」,全部无偿分享给铁粉!!!
除此以外,再含泪分享我压箱底的22个热门开源项目,包含源码+原理图+PCB+说明文档,让你迅速进阶成高手!
教程资料包和详细的学习路径可以看我下面这篇文章的开头。