第四周两套题笔记
LED控制部分
四种炫酷的流水灯效果
switch (led_mode)
{
// 模式1:从左向右流动(像跑马灯)
case 0:
if(ucled == 0x7e) // 灯流到最右边了?
{
ucled = 0xfe; // 回到最左边重新开始
}
else
{
ucled = _crol_(ucled, 1); // 灯往右移一位
if(ucled == 0x7e) // 又流到最右边了?
{
led_mode = 1; // 切换到下一个模式
}
}
break;
// 模式2:从右向左流动(倒着流)
case 1:
ucled = _cror_(ucled, 1); // 灯往左移一位
if(ucled == 0xfe) // 回到最左边了?
{
led_mode = 2; // 再换模式
}
break;
// 模式3:两边向中间合拢(像关门)
case 2:
ucled = ucled_data[ucled_data_index]; // 从预设的"关门"图案中取一个
ucled_data_index++; // 准备显示下一幅图案
if(ucled_data_index == 4) // 4幅图都显示完了?
{
led_mode = 3; // 切换到"开门"模式
ucled_data_index = 3; // 从最后一幅图开始往回放
}
break;
// 模式4:中间向两边展开(像开门)
case 3:
ucled = ucled_data[ucled_data_index]; // 从预设的"开门"图案中取一个
ucled_data_index--; // 往回取图案
if(ucled_data_index == 255) // 已经取到第一个了?
{
led_mode = 0; // 回到模式1重新开始
ucled_data_index = 0; // 从头开始
}
break;
}
通俗理解:LED有4种花样玩法,自动轮流切换,就像灯光秀一样!
数字处理技巧
四舍五入的小窍门
// 想把3.141变成3.14(四舍五入到两位小数)
电压 = (3141 + 5) / 1000.0; // 先加5毫伏,再除以1000
// 结果是3.146,但显示时会截成3.14
为什么加5? 因为我们是四舍五入,加5等于在千分位上加0.005,这样刚好实现四舍五入的效果。
保留两位小数的方法
// 方法1:直接砍掉多余的小数位
int 临时变量 = (int)(3.141 * 100); // 3.141×100=314.1,取整→314
float 结果 = 临时变量 / 100.0; // 314÷100=3.14
// 方法2:真正的四舍五入
int 临时变量 = (int)(3.141 * 100 + 0.5); // 3.141×100+0.5=314.6,取整→315
float 结果 = 临时变量 / 100.0; // 315÷100=3.15
三目运算符(问号冒号运算符)
// 问号运算符:如果...就...否则...
Seg_Buf[位置] = Seg_Flag ? 显示数字 : 显示空白;
// 相当于:
if(Seg_Flag == 1) {
Seg_Buf[位置] = 显示数字;
} else {
Seg_Buf[位置] = 显示空白;
}
使用场景:在输入电压时,让当前输入位置一闪一闪的,提示用户该输入哪里了。
常用编程符号
不等于符号(!=)
if(a != b) // 如果a不等于b
{
// 执行这里
}
if-else 家族的区别
| 类型 | 用途 | 例子 |
|---|---|---|
if-else |
二选一 | 如果是白天就开灯,否则关灯 |
if-else if |
多选一 | 成绩≥90为A,≥80为B,≥70为C,否则D |
数码管显示秘籍
常用字符编码表
0-9 :数字0到9
10 :什么都不显示(空白)
11 :减号(-)
12 :字母U(表示电压)
13 :字母P(表示参数)
14 :字母n(表示数量)
计数显示优化
去掉多余的零
原始显示:n 0 0 1 2 3
优化后: n 1 2 3
代码实现:
// 从左边开始检查,遇到0就换成空白
while(显示数组[位置] == 0)
{
显示数组[位置] = 10; // 10表示空白
位置++;
if(位置 == 5) break; // 检查到十位就停止
}
// 注意:个位的0要保留,因为0也是一个有效的数字
LED状态指示灯
三个LED的不同含义
void Led_Proc()
{
// LED0:电压过低警报灯
if(电压 < 设定电压) {
if(计时 >= 5000) // 等了5秒钟
LED0 = 1; // 点亮报警灯
} else {
计时 = LED0 = 0; // 电压正常,关灯
}
// LED1:计数奇偶指示灯
LED1 = 计数 % 2; // 计数是奇数时亮,偶数时灭
// LED2:操作错误提示灯
LED2 = 错误次数 / 3; // 连错3次以上就亮
}
系统心跳——定时器中断
1毫秒定时器做什么?
void Timer0Server() interrupt 1
{
// 1. 按键防抖:每10ms才检查一次按键
if(++按键计时 == 10) 按键计时 = 0;
// 2. 显示更新:每500ms才更新一次显示内容
if(++显示计时 == 500) 显示计时 = 0;
// 3. 数码管扫描:轮流点亮6个数码管
if(++数码管位置 == 6) 数码管位置 = 0;
点亮数码管(数码管位置, 要显示的数字, 小数点);
// 4. LED扫描:轮流控制8个LED
if(++LED位置 == 8) LED位置 = 0;
控制LED(LED位置, 亮灭状态);
// 5. 低电压计时
if(电压 < 设定电压) 计时器++;
// 6. 闪烁控制:每500ms切换一次
if(++闪烁计时 == 500) {
闪烁计时 = 0;
闪烁标志 = !闪烁标志; // 0变1,1变0
}
}
通俗理解:定时器就像乐队的指挥,每1毫秒挥一下指挥棒,让不同的乐器(按键、显示、LED)按节奏演奏。
显示驱动函数
数码管显示函数
// 点亮指定的数码管,显示指定的内容
点亮数码管(第几个管, 显示什么, 要不要小数点);
// 例子:点亮第3个数码管,显示数字5,带小数点
点亮数码管(3, 5, 1);
LED显示函数
// 控制指定的LED亮或灭
控制LED(第几个灯, 亮还是灭);
// 例子:点亮第2个LED
控制LED(2, 1);
总结表格
| 功能 | 实现方法 | 通俗理解 |
|---|---|---|
| 按键处理 | 10ms检查一次 | 防止手抖按太快 |
| 数码管显示 | 6个数码管轮流点亮 | 6个演员轮流上台 |
| LED控制 | 8个LED轮流控制 | 8个灯泡轮流闪 |
| 输入提示 | 500ms闪烁一次 | 一闪一闪提示你 |
| 错误提示 | 连错3次亮灯 | 老师提醒你错了 |
| 电压报警 | 低电压5秒后亮灯 | 电压太低会报警 |
学习要点
- 分层设计:底层驱动和上层逻辑分开
- 定时扫描:用定时器统一管理时间
- 状态机:用状态变量控制不同模式
- 用户友好:有提示、有反馈、防止误操作
- 代码复用:同样的功能写成函数,多处调用
终极秘诀:把复杂的系统拆分成简单的小模块,每个模块做好一件事,然后让定时器像心脏一样有节奏地推动整个系统运行!