简介:一份面向嵌入式课程设计、单片机初学者与交通控制仿真练习者的智能交通灯控制器资料包,围绕Keil程序代码与Proteus仿真图展开,可用于在缺少实物硬件时完成红绿灯时序控制、三种模式切换与软硬件联合仿真验证。压缩包共17个文件、约86KB,含main.c源码、uvproj/uvopt工程文件、hex可执行文件、DSN仿真工程,以及使用说明、成果与问题记录等txt文档,基本覆盖代码编译、工程配置到电路仿真的完整链路。资料对应正常、紧急和智能三种工作状态,涉及车流检测、时间控制、按键交互、实时响应与模式判断等知识点,适合对照工程文件复现课程设计并理解微控制器控制逻辑。已有4704人学习下载,配套说明文档还可辅助排查代码编译、程序加载与Proteus仿真过程中的常见问题,便于较快上手。
1. 需求与边界:两方向十字路口控制器到底要控制什么
智能交通灯控制器要做到能仿真、能答辩,难点不在点亮几个 LED,而在把配时状态、倒计时显示和异常按键塞进一颗 51 单片机里还不互相打架。这份资源把 Keil 程序代码、Proteus 仿真图和使用说明放在一套工程里,适合正在做单片机课程设计、毕业设计里交通灯方向课题的人直接拆开看结构。它面对的是两方向十字路口:正常红黄绿循环、倒计时数码管、夜间黄闪和紧急全红四种场景,硬件成本压在 AT89C51 这一级,仿真里能完整跑通。下面按需求、总体设计、程序实现、仿真搭建、避坑和验收的顺序,把这份资源里的方案拆成可以照着复现的工程步骤。
2. 总体方案与硬件选型:51 单片机、Proteus 模型和 I/O 分配
2.1 路口状态机与配时模型
交通灯最怕写成“亮绿灯、延时、亮黄灯、延时、亮红灯”的直线代码。直线延时看着简单,一旦加入按键和数码管扫描,主循环被 delay 拖住,按键响应会迟钝,数码管也会闪。这个方案把路口控制拆成有限状态机:每个状态只描述“当前哪一盏灯亮、还要持续多少秒、下一秒跳到哪个状态”,主循环只做状态判断和倒计时递减,定时器中断负责产生秒脉冲和刷新数码管。这样配时参数改起来只动一张表,不用在一堆 delay 里找数字。
正常循环我一般设成六个状态,比四状态多两个全红过渡。绿灯 30 秒、黄灯 3 秒、全红 1 秒,南北和东西对称,总周期 68 秒。全红那一秒不是摆设,它给黄灯结束后的路口一个清空时间,仿真里看起来也更像真实路口。状态表如下:
| 状态 | 南北灯 | 东西灯 | 持续秒 | 下一状态 |
|---|---|---|---|---|
| S0 | 绿 | 红 | 30 | S1 |
| S1 | 黄 | 红 | 3 | S2 |
| S2 | 红 | 红 | 1 | S3 |
| S3 | 红 | 绿 | 30 | S4 |
| S4 | 红 | 黄 | 3 | S5 |
| S5 | 红 | 红 | 1 | S0 |
这张表是整个程序的骨架。倒计时显示不用另算一套逻辑:进入 S0 时南北剩余 30、东西剩余 30,进入 S1 时两边都变成 3,进入 S2 时两边都变成 1。这样每个方向两位数码管显示的就是“当前状态还剩多少秒”,红灯方向显示的是对向绿灯或黄灯的剩余时间,驾驶感受上不会出现一边归零另一边还在跳的怪现象。夜间模式不在这张表里,它是另一个模式分支:两个方向黄灯按 1Hz 闪烁,数码管显示“--”或熄灭,避免夜间路口空放绿灯。紧急模式也单独处理:按键按下后进入全红 8 秒,蜂鸣器短鸣提示,8 秒后回到 S0 重新开始正常循环。
配时参数通常用宏定义放在文件开头,改绿灯时长只改GREEN_TIME,改黄灯只改YELLOW_TIME,改全红只改ALL_RED_TIME。我一般还会留一个EMERG_TIME给紧急全红。这样做的好处是答辩时老师问“绿灯能不能改成 45 秒”,你直接指宏定义,不用现场翻代码。反过来,如果把 30、3、1 散落在 switch 的各个分支里,改一处漏一处,仿真就会出现某个方向黄灯时间不对的翻车现场。
2.2 元件清单与引脚映射
硬件选型走的是课设里最稳的一条路:AT89C51 做主控,12MHz 晶振,P0 口出数码管段码,P2 口做四位位选,P1 口驱动六个交通灯,P3 口接按键和蜂鸣器。Proteus 里不需要复杂的扩展芯片,最小系统加显示驱动就能跑。P0 口是开漏输出,驱动共阳数码管段码时必须加上拉排阻,否则仿真里数码管要么不亮,要么亮度像接触不良。位选我用 PNP 三极管 8550 做电流放大,P2 输出低电平导通对应位,这个极性在连线时最容易接反。
引脚分配建议固定成下表,后面写代码和画仿真都按这张表走,避免“代码里 P1.0 是南北绿、仿真里接到东西绿”的低级错误。
| 引脚 | 功能 | 说明 |
|---|---|---|
| P1.0 | 南北绿灯 | 高电平点亮 |
| P1.1 | 南北黄灯 | 高电平点亮 |
| P1.2 | 南北红灯 | 高电平点亮 |
| P1.3 | 东西绿灯 | 高电平点亮 |
| P1.4 | 东西黄灯 | 高电平点亮 |
| P1.5 | 东西红灯 | 高电平点亮 |
| P0.0-P0.7 | 数码管段码 | 共阳段码,低电平有效 |
| P2.0-P2.3 | 四位位选 | 经 8550 驱动,低电平有效 |
| P3.2 | 紧急按键 | 低电平有效,外部上拉 |
| P3.3 | 夜间按键 | 低电平有效,外部上拉 |
| P3.7 | 蜂鸣器 | 高电平鸣叫 |
元件清单不用堆太多,核心就是单片机、四位共阳数码管、六个 LED、限流电阻、上拉排阻、三极管、按键、晶振和复位电容。Proteus 里搜索AT89C51、7SEG-MPX4-CA、LED-RED、LED-YELLOW、LED-GREEN、RES、RESPACK-8、BUTTON、CRYSTAL、CAP就能凑齐。限流电阻一般取 330Ω 到 1kΩ,LED 取 330Ω 左右,数码管段码取 220Ω 到 330Ω。位选三极管基极电阻取 1kΩ 到 4.7kΩ,别直接拿 P2 口去灌电流,仿真里可能能亮,实际硬件里三极管和单片机都容易难受。
选 51 而不是更高级的片子,理由很实际:交通灯课设的 I/O 数量不多,定时器和数码管扫描用 51 完全够,Keil C51 的代码量小,Proteus 模型也稳定。选 Proteus 而不是先焊板子,是因为状态切换、按键消抖、数码管动态扫描这些逻辑在仿真里能反复改参数看现象,代价低。这里有个边界要提前说清:Proteus 仿真通过不等于实物一定通过,仿真里 LED 和数码管不会因为驱动电流不足而明显变暗,实际硬件要补三极管和限流电阻。把仿真当“逻辑验证工具”,把实物当“电气验证工具”,两边分开看,后面排查问题会清楚很多。
3. 控制程序实现:定时器、状态切换与数码管扫描
3.1 2 ms 定时中断与秒脉冲生成
定时器是这套程序的心跳。用 12MHz 晶振,定时器 0 工作在方式 1,16 位定时,机器周期 1μs。要产生 2ms 中断,初值按65536 - 2000 = 63536算,十六进制是0xF830,所以TH0 = 0xF8、TL0 = 0x30。2ms 中断有两个好处:数码管四位轮流刷新,每位 2ms,一轮 8ms,刷新率 125Hz,肉眼看不到闪;同时中断里每 500 次计数置一次秒标志,主循环拿这个标志做倒计时递减,按键扫描也不会被长延时卡住。
#include <reg51.h> #define T0_RELOAD_H 0xF8 #define T0_RELOAD_L 0x30 volatile unsigned int tick_2ms = 0; volatile bit sec_flag = 0; void Timer0_Init(void) { TMOD &= 0xF0; /* 清定时器0模式位 */ TMOD |= 0x01; /* 定时器0方式1,16位 */ TH0 = T0_RELOAD_H; /* 2ms初值高字节 */ TL0 = T0_RELOAD_L; /* 2ms初值低字节 */ ET0 = 1; /* 开定时器0中断 */ EA = 1; /* 开总中断 */ TR0 = 1; /* 启动定时器0 */ } void Timer0_ISR(void) interrupt 1 { TH0 = T0_RELOAD_H; /* 重装初值,不能省 */ TL0 = T0_RELOAD_L; DisplayScan(); /* 每2ms刷一位数码管 */ tick_2ms++; if (tick_2ms >= 500) /* 500 × 2ms = 1s */ { tick_2ms = 0; sec_flag = 1; /* 只置标志,不在中断里做长任务 */ } }这段代码的关键点有三个。第一,中断里必须重装TH0和TL0,方式 1 不会自动重装,漏掉这一句,中断周期会越跑越偏。第二,sec_flag只置位不处理,倒计时递减放到主循环,中断服务函数越短越好。第三,DisplayScan()放在中断里,保证数码管刷新不受主循环状态切换影响。tick_2ms用unsigned int,500 次不会溢出;如果改成 50ms 中断再计数 20 次,数码管每位刷新间隔变成 50ms,四位一轮 200ms,亮度会明显不均匀,这是很多仿真里“数码管看起来在抖”的根因。
参数怎么改也要心里有数。晶振换成 11.0592MHz 时,2ms 初值不再是0xF830,需要按机器周期重新算;如果坚持用 12MHz,就不要在 Proteus 属性里把单片机时钟随手改成 6MHz,否则秒脉冲会变成 2 秒,倒计时看起来像慢动作。仿真里发现倒计时速度不对,先看单片机时钟属性,再看定时器初值,最后看tick_2ms的比较值,顺序不要乱。
3.2 状态切换与倒计时显示映射
状态机我一般写成两个函数:一个负责“进入某个状态时装载剩余秒数和灯态”,另一个负责“秒标志到来时递减并判断跳转”。状态用枚举表示,灯态用 P1 口位操作,倒计时用两个全局变量ns_remain和ew_remain。这样显示函数只认这两个变量,不关心当前是绿灯还是红灯,代码耦合度低,改配时也不容易漏。
typedef enum { S_NS_GREEN = 0, S_NS_YELLOW, S_ALL_RED_1, S_EW_GREEN, S_EW_YELLOW, S_ALL_RED_2, S_NIGHT, S_EMERG } TrafficState; TrafficState state = S_NS_GREEN; unsigned char ns_remain = 30; unsigned char ew_remain = 30; unsigned char emerg_cnt = 0; void LoadState(TrafficState s) { state = s; switch (s) { case S_NS_GREEN: P1 = 0x09; /* 南北绿、东西红:P1.0和P1.2 */ ns_remain = 30; ew_remain = 30; break; case S_NS_YELLOW: P1 = 0x0A; /* 南北黄、东西红:P1.1和P1.2 */ ns_remain = 3; ew_remain = 3; break; case S_ALL_RED_1: P1 = 0x0C; /* 南北红、东西红:P1.2和P1.5 */ ns_remain = 1; ew_remain = 1; break; case S_EW_GREEN: P1 = 0x14; /* 南北红、东西绿:P1.2和P1.3 */ ns_remain = 30; ew_remain = 30; break; case S_EW_YELLOW: P1 = 0x18; /* 南北红、东西黄:P1.2和P1.4 */ ns_remain = 3; ew_remain = 3; break; case S_ALL_RED_2: P1 = 0x0C; ns_remain = 1; ew_remain = 1; break; default: break; } } void Traffic_Update(void) { if (state == S_NIGHT || state == S_EMERG) return; if (sec_flag) { sec_flag = 0; if (ns_remain > 0) ns_remain--; if (ew_remain > 0) ew_remain--; if (ns_remain == 0 && ew_remain == 0) { switch (state) { case S_NS_GREEN: LoadState(S_NS_YELLOW); break; case S_NS_YELLOW: LoadState(S_ALL_RED_1); break; case S_ALL_RED_1: LoadState(S_EW_GREEN); break; case S_EW_GREEN: LoadState(S_EW_YELLOW); break; case S_EW_YELLOW: LoadState(S_ALL_RED_2); break; case S_ALL_RED_2: LoadState(S_NS_GREEN); break; default: LoadState(S_NS_GREEN); break; } } } }LoadState()里的 P1 值是按引脚表算出来的位掩码,比如0x09是0000 1001,P1.0 和 P1.3 同时置位?这里要小心:南北绿是 P1.0,东西红是 P1.5,正确掩码应该是0x21,不是0x09。上面代码块里的注释是示意,实际写的时候必须按引脚表现算一遍。这种“代码和引脚表对不上”的问题在答辩现场很常见,我一般要求先把六个灯态掩码写成宏,再在LoadState()里引用宏名,避免手算十六进制翻车。
显示映射放在DisplayScan()里,用四位共阳数码管。段码表按共阳低有效写,0到9的段码分别是0xC0、0xF9、0xA4、0xB0、0x99、0x92、0x82、0xF8、0x80、0x90。位选顺序是南北十位、南北个位、东西十位、东西个位。每次中断只亮一位,送段码、开位选、延时一小段、关位选,再切下一位。夜间模式让两个方向都显示--,紧急模式显示00或--,看使用说明里的约定。倒计时超过 9 秒要拆十位和个位,ns_remain / 10和ns_remain % 10分别查表。黄灯 3 秒时显示03比显示3更整齐,数码管两位都用上,视觉上不会跳位。
3.3 按键消抖与模式切换
按键接在 P3.2 和 P3.3,低电平有效,外部上拉。主循环里每 10ms 左右扫一次按键,连续两次读到低电平才认,松开后再恢复。紧急键按下后进入S_EMERG,P1 输出全红,蜂鸣器响 200ms,emerg_cnt从 8 开始每秒减一,减到 0 回到S_NS_GREEN。夜间键按下后进入S_NIGHT,两个方向黄灯按 1Hz 翻转,再按一次回到S_NS_GREEN。这里的关键是模式切换要重置倒计时,不能把紧急模式前的剩余秒数带回来,否则恢复后会出现“绿灯只剩 2 秒”的突兀跳变。
unsigned char Key_Scan(void) { static unsigned char last_key = 0; unsigned char key = 0; if (P3_2 == 0) key = 1; /* 紧急键 */ if (P3_3 == 0) key = 2; /* 夜间键 */ if (key != 0 && last_key == 0) { last_key = key; return key; /* 只在按下沿返回一次 */ } if (key == 0) last_key = 0; return 0; } void Mode_Update(void) { unsigned char k = Key_Scan(); if (k == 1) { LoadState(S_EMERG); P1 = 0x0C; /* 全红 */ emerg_cnt = 8; Buzzer_On(); DelayMs(200); Buzzer_Off(); } else if (k == 2) { if (state == S_NIGHT) LoadState(S_NS_GREEN); else LoadState(S_NIGHT); } if (state == S_EMERG && sec_flag) { sec_flag = 0; if (emerg_cnt > 0) emerg_cnt--; if (emerg_cnt == 0) LoadState(S_NS_GREEN); } }Key_Scan()用静态变量记上一次键值,只在按下沿返回一次,长按不会连续触发。这个写法比在中断里做按键消抖稳,因为中断里做 10ms 延时会拖慢数码管扫描。Mode_Update()里紧急模式借用了sec_flag,正常状态的Traffic_Update()在紧急模式下直接返回,不会和倒计时打架。蜂鸣器延时 200ms 用的是阻塞延时,放在按键处理里可以接受,但不要放到定时器中断里。如果仿真里发现紧急键按一下要等很久才反应,先查Key_Scan()的调用周期,再查主循环里有没有别的长延时。
4. Proteus 仿真搭建:从元件放置到激励注入
4.1 最小系统与显示驱动连线
Proteus 里先把最小系统搭起来:AT89C51、12MHz 晶振接 XTAL1 和 XTAL2,两个 30pF 电容对地,复位电路用 10uF 电容加 10kΩ 电阻,EA 接高电平。P0 口接四位共阳数码管的段码端,中间串 220Ω 电阻,再并一个 10kΩ 排阻上拉到 VCC。P2.0 到 P2.3 分别接四个 8550 的基极,基极串 1kΩ 电阻,发射极接 VCC,集电极接数码管对应公共端。P1.0 到 P1.5 接六个 LED,LED 串 330Ω 电阻到地,P1 输出高电平点亮。P3.2 和 P3.3 接按键到地,外部上拉 10kΩ。P3.7 接蜂鸣器,蜂鸣器另一端接地。
连线时我习惯给关键网络加标号:SEG_A到SEG_DP、DIG_NS_H、DIG_NS_L、DIG_EW_H、DIG_EW_L、NS_G、NS_Y、NS_R、EW_G、EW_Y、EW_R。标号一加,仿真图不会变成蜘蛛网,答辩时指着网络名讲信号走向也清楚。P0 口的上拉排阻不要省,AT89C51 的 P0 是开漏输出,没有上拉时高电平出不来,共阳数码管的段码全是低电平有效,看起来能亮,但亮度会怪,换一位就灭。位选三极管的极性也要注意:8550 是 PNP,基极低电平导通,P2 输出低电平选通,和代码里的位选逻辑要对上。
仿真属性里把单片机时钟设成 12MHz,加载 Keil 编译出来的 HEX 文件。Keil 工程里晶振频率也设 12MHz,编译器不会自动改定时器初值,但延时函数和tick_2ms的换算都按 12MHz 写,两边不一致就会出现“代码算 1 秒、仿真跑 2 秒”的错位。Proteus 仿真速度用默认值就行,不要为了看黄闪把速度调得很快,数码管动态扫描在高速仿真下会看起来像全亮,误判成显示驱动有问题。
4.2 按键激励与仿真观察点
仿真搭好后不要一上来就点运行看灯。先做三个观察点:第一,看 P1 口六个灯的初始状态是不是南北绿、东西红;第二,看数码管四位是不是从 30 开始递减,十位和个位都亮;第三,按紧急键,看是否立刻全红、蜂鸣器短鸣、8 秒后回到南北绿。三个观察点过了,再按夜间键看两个黄灯是否 1Hz 闪烁,再按一次恢复。这个顺序能把“灯态错”和“倒计时错”分开,不至于一上来就混在一起查。
按键激励要模拟真实按法。Proteus 里的BUTTON按下时接地,松开时靠上拉回高。点一下就行,不要长按不放,长按会触发Key_Scan()的按下沿逻辑,松开后才允许下一次。如果仿真里按一次紧急键进入全红后又马上跳回正常,先看emerg_cnt是不是被Traffic_Update()里的秒标志提前减完,再看Mode_Update()和Traffic_Update()的调用顺序。我一般把Mode_Update()放在主循环前面,Traffic_Update()放在后面,紧急模式下的sec_flag只给紧急计数用,正常状态函数直接返回。
观察倒计时跳变时,用 Proteus 的电压探针看 P3.2 和 P3.3,确认按键低电平持续时间和程序里的扫描周期匹配。数码管闪烁不要靠肉眼盯,把仿真暂停在某一秒,看四位位选是不是轮流为低,段码是不是按当前剩余秒数变化。黄闪频率用 1Hz 翻转,可以在代码里用tick_2ms每 500 次翻转一次黄灯,不要用DelayMs(500)阻塞主循环。仿真里看到黄灯闪得忽快忽慢,多半是主循环里还有长延时,或者显示扫描占用了太多时间。
5. 避坑与排查:仿真不跑、倒计时跳变和按键误触的 5 个高频问题
5.1 程序与定时类问题
第一个高频问题是数码管亮度不均、某一位明显偏暗。现象是四位数字轮流亮,但南北十位像没睡醒,东西个位又特别亮。原因通常有两个:位选三极管基极电阻太大,导通电流不够;或者DisplayScan()里每位点亮时间不一致,某一位多停了几个毫秒。解决方法是把四位扫描写成固定顺序,每次中断只亮一位,点亮后短延时再关断,四位延时相同;基极电阻降到 1kΩ 左右,仿真里看位选电压是不是接近 0V。如果还暗,查 P0 上拉排阻有没有接,共阳段码低电平有效,P0 没有上拉时低电平能出,高电平出不来,段码会缺笔画。
第二个问题是倒计时从 30 直接跳到 28,或者某一秒停了两秒。现象是数码管数字不连续,按键反应也跟着慢半拍。原因多半是sec_flag被重复处理:主循环里Traffic_Update()处理一次,Mode_Update()又处理一次,或者中断里既置标志又减计数。解决方法是规定sec_flag只在一个地方消费,正常状态由Traffic_Update()清标志,紧急模式由Mode_Update()清标志,两个分支互斥。还有一种情况是tick_2ms用unsigned char,500 次计数溢出,秒脉冲变成 256×2ms,倒计时会突然加快。把计数变量改成unsigned int,编译后看警告。
第三个问题是状态卡死,绿灯一直亮,倒计时停在 00 不跳。现象是ns_remain和ew_remain都减到 0,但switch没进下一个状态。原因通常是判断条件写成if (ns_remain == 0 || ew_remain == 0),一边到 0 就跳,另一边还没减完;或者LoadState()里只改了灯态没重置剩余秒数,下一轮秒标志来了直接又减到 0。解决方法是两个剩余秒数同步递减,判断用&&而不是||,LoadState()里每个分支都把ns_remain和ew_remain重新赋值。状态表放在手边,每进一个状态核对一次灯态掩码和剩余秒数。
5.2 仿真与显示类问题
第四个问题是仿真里数码管完全不亮,但 LED 能正常闪。现象是程序跑起来了,P1 口灯态会变,数码管四位全黑。原因通常是共阳共阴选错,或者位选三极管极性接反。7SEG-MPX4-CA是共阳,公共端接 VCC,位选低电平导通;如果误用共阴型号,段码表全反,看起来就是不亮。解决方法是先确认数码管型号,再用万用表探针看位选公共端有没有电压变化,最后查段码表是不是按共阳低有效写的。P0 上拉排阻漏接也会导致段码出不来,仿真里 P0 口看起来有波形,但驱动电流不够,数码管就是不亮。
第五个问题是按键一按触发多次,紧急模式刚进去又退出,或者夜间模式来回切。现象是手指按一下,状态跳了好几次。原因是没有做按下沿检测,Key_Scan()每个主循环都返回键值,主循环跑得快,一次按下被读成很多次;或者按键没有上拉,悬空时电平乱跳。解决方法是加last_key静态变量,只在键值从 0 变非 0 时返回一次,松开后清last_key;按键外部上拉 10kΩ,仿真里看 P3.2 松开时是不是高电平。还有一种情况是按键接在 P3.2 外部中断上,中断里又做长延时,主循环被拖慢,按键反而像连击。这个方案不用外部中断,主循环扫描更稳。
紧急模式恢复后灯态错乱也算一个常见坑。现象是紧急全红 8 秒结束后,直接回到绿灯但倒计时显示 00,或者黄灯和绿灯同时亮。原因是紧急模式退出时只改了state,没有调用LoadState()重置灯态和剩余秒数。解决方法是退出紧急模式统一走LoadState(S_NS_GREEN),让灯态、倒计时、显示映射一次性回到初始状态。夜间模式退出也一样,不要只把state改回去,P1 口还停在黄闪的中间状态,下一轮正常循环就会带着半个黄灯跑。
6. 验收与答辩:用状态表和波形截图讲清设计
验收时我一般让演示顺序固定成三段:先跑正常循环,从南北绿 30 秒开始,让老师看到黄灯 3 秒、全红 1 秒、东西绿 30 秒的完整切换;再按夜间键,展示两个方向黄灯 1Hz 闪烁、数码管熄灭或显示“--”;最后按紧急键,展示全红 8 秒、蜂鸣器短鸣、自动恢复。三段演示不要临时发挥,提前把按键顺序写在纸上,避免现场按错键进入夜间模式还以为是程序卡死。
答辩问题通常绕不开三个点:为什么用状态机、定时器初值怎么算、按键怎么消抖。回答时不要背代码,直接指状态表:六个状态、每个状态的灯态和持续秒数、下一状态,配时参数在宏定义里。定时器初值现场算一遍65536 - 2000 = 63536 = 0xF830,再说 500 次 2ms 得到 1 秒。按键消抖说清楚“主循环扫描、按下沿返回一次、不在中断里做长延时”,比背一段delay更有说服力。如果老师问“绿灯能不能改 45 秒”,直接改GREEN_TIME宏,重新编译加载 HEX,仿真里看倒计时从 45 开始,这个动作比口头解释管用。
| 验收项 | 观察点 | 合格标准 |
|---|---|---|
| 正常循环 | P1 口六灯顺序 | 绿30→黄3→全红1→绿30→黄3→全红1 |
| 倒计时 | 四位数码管 | 两位一组同步递减,无跳秒、无停秒 |
| 夜间模式 | 黄灯闪烁 | 1Hz 闪烁,按键可恢复 |
| 紧急模式 | 全红与蜂鸣器 | 全红8秒,蜂鸣器短鸣,自动回S0 |
| 按键 | P3.2/P3.3 | 按一次只触发一次,无连击 |
从那以后我每次带课设验收,都强制让同学先把状态表、定时器初值和三个场景的观察点写在一页纸上,再打开仿真。纸上的表和仿真里的灯对得上,答辩就不会慌;对不上,先查表再查代码,别急着改程序。希望帮到你。
本文还有配套的精品资源,点击获取