代码简化 —— 逻辑运算
if→找规律→简化代码
对于参数切换时部分代码:数组→索引→代码简化
P_dat、P_ctrol和P_dat_index这三个变量协同工作,实现了一个“编辑-确认”的参数设置流程。可以将它们理解为电视遥控器上的“频道预设”功能。
问题:
一、核心比喻:电视遥控器的频道预设
想象你有一个老式电视遥控器,上面有“频道+”、“频道-”、“确认”和“切换预设”几个按钮,用来设置你最喜欢的电视频道:
-
P_ctrol数组:相当于电视当前正在播放的频道。它存储了系统当前实际生效的温度参数,P_ctrol[0]是温度上限,P_ctrol[1]是温度下限。 -
P_dat数组:相当于你在遥控器上正在编辑的频道号码。它存储了你通过按键修改但尚未确认的临时参数。 -
P_dat_index:相当于遥控器上的**“切换预设”按钮**,用来在“上限”和“下限”两个参数之间切换。当它为0时,表示你正在调整上限;当它为1时,表示你正在调整下限。
二、参数设置的完整流程
1. 进入设置模式(按下“菜单”键)
c
1case 12://界面切换
2 if(++seg_disp_mode == 2) seg_disp_mode = 0;
3 if(seg_disp_mode == 0) { // 从参数设置跳转到温度显示
4 P_dat_index = 0;
5 if(P_dat[0] > P_dat[1]) { // 检查合理性
6 P_ctrol[0] = P_dat[0]; // 确认上限
7 P_ctrol[1] = P_dat[1]; // 确认下限
8 }
9 }
10 if(seg_disp_mode == 1) { // 从温度显示跳转到参数设置
11 P_dat[0] = P_ctrol[0]; // 读取当前生效的上限
12 P_dat[1] = P_ctrol[1]; // 读取当前生效的下限
13 }
14break;
-
当你按下“菜单”键(
case 12),系统首先切换显示模式(seg_disp_mode)。 -
进入设置界面时(
seg_disp_mode变为1):系统会先把当前正在生效的温度上下限(P_ctrol[0]和P_ctrol[1])复制到编辑区(P_dat[0]和P_dat[1])。这就像遥控器在进入设置菜单时,先显示当前的频道号码。 -
退出设置界面时(
seg_disp_mode变为0):系统会检查你设置的上限是否大于下限。如果合理,就把编辑区的值(P_dat)正式应用到系统设置中(P_ctrol),让新的参数生效。
2. 切换参数项(按下“切换预设”键)
c
1case 13://参数切换
2 if(seg_disp_mode == 1)//在参数设置界面
3 P_dat_index ^= 1; // 在0和1之间切换
4break;
-
在设置界面中,按下“切换预设”键(
case 13),会切换P_dat_index的值。 -
这就像在遥控器上切换“频道1”和“频道2”,让你可以分别设置不同的参数。
3. 修改参数值(按下“+”或“-”键)
c
1if(key_up == 14) { // 短按+
2 key_flag = key_time = 0;
3 if(++P_dat[P_dat_index] == 71)
4 P_dat[P_dat_index] = 10;
5}
6if(key_up == 15) { // 短按-
7 key_flag = key_time = 0;
8 if(--P_dat[P_dat_index] == 9)
9 P_dat[P_dat_index] = 71;
10}
-
按下“+”键(
key_up == 14)或“-”键(key_up == 15)时,会根据P_dat_index的值,对P_dat数组中当前选中的参数进行增减。 -
这就像在遥控器上按“+”或“-”来修改当前选中的频道号码。
三、为什么需要两个数组?
你可能会问,为什么不直接修改P_ctrol,而要多此一举地用P_dat来中转呢?
这正是程序设计的精妙之处!使用P_dat作为“编辑区”有三大好处:
-
安全检查:在将新参数应用到系统前,可以进行合理性检查(如
P_dat[0] > P_dat[1])。就像遥控器在保存新频道前,会检查号码是否在有效范围内。 -
数据回显:进入设置界面时,将
P_ctrol的值赋给P_dat,是为了在屏幕上显示出当前的参数值,让你知道从哪里开始修改。 -
事务性操作:参数的修改是“原子”的,即要么全部生效,要么全部不生效,避免了在修改过程中系统使用到不完整或错误的参数。
四、生动的遥控器操作示例
-
初始状态:电视正在播放频道5(相当于
P_ctrol[0] = 30, P_ctrol[1] = 20)。 -
进入设置:你按下“菜单”键,遥控器显示“频道预设1:5”(相当于
P_dat[0] = P_ctrol[0] = 30)。 -
修改参数:你按“+”键,频道号变成6(相当于
P_dat[0] = 31);再按“切换预设”,准备设置频道2。 -
确认修改:你再次按下“菜单”键,遥控器将预设频道1从5更新为6(相当于
P_ctrol[0] = P_dat[0]),电视现在开始播放频道6。
五、关键Tips
-
数组索引:
P_dat_index作为索引,让程序能用统一的代码处理不同的参数,避免了为上限和下限分别编写一套几乎一样的代码,使程序更简洁、更易于维护。 -
边界检查:代码中的
if(++P_dat[P_dat_index] == 71)和if(--P_dat[P_dat_index] == 9)是边界检查,防止参数值超出有效范围(10-70),确保了程序的健壮性。 -
数据一致性:只有在参数设置界面(
seg_disp_mode == 1)才会响应“+”、“-”和“切换预设”键,而在温度显示界面则不会,这保证了数据修改的上下文一致性。
通过这种“编辑-确认”的模式,程序实现了对系统参数的安全、可靠修改,这在嵌入式系统开发中是一种非常常见且有效的设计模式。
这两行代码看起来像是“把A给B”和“把B给A”,确实非常绕。但在嵌入式开发中,这是一种非常高明且标准的**“双缓冲”**设计模式。
为了让你彻底明白,我们把这两个数组想象成两个不同的人,他们手里各有一张纸。
角色设定
-
P_ctrol(控制者):这是老板。他手里拿着的纸(数值)是最终决定,直接决定了机器怎么运行(比如加热还是制冷)。老板的决定一旦做出,机器就立刻执行。 -
P_dat(数据者):这是秘书。他手里拿着的纸是草稿,是用来给老板修改用的。秘书在草稿纸上随便涂改,老板和机器是完全不知道的,机器也不会受影响。 -
P_dat_index:这是秘书手中的笔,指着草稿纸上的哪一个格子(上限还是下限)正在被修改。
逻辑详解
1. 进入设置界面时:秘书去抄老板的作业
代码逻辑:
c
1// 秘书把老板现在的数值抄过来
2P_dat[0] = P_ctrol[0];
3P_dat[1] = P_ctrol[1];
场景模拟:
当你按下“设置”按钮进入界面时,你想修改参数。
-
问题:秘书的草稿纸上本来可能是乱码,或者是上次没改完的废稿。
-
动作:为了让秘书知道现在的参数是多少,他必须先看一眼老板手里的纸,把老板现在的数值(比如上限30,下限20)抄到自己的草稿纸上。
-
结果:现在秘书和老板的纸一模一样。这是修改的起点。
2. 调节按键时:秘书在草稿纸上涂改
代码逻辑:
你在按键时,代码修改的是 P_dat[P_dat_index](秘书的草稿纸)。
场景模拟:
你在键盘上按“+”号,想把温度上限调高。
-
动作:秘书在自己的草稿纸上把“30”划掉,改成“35”。(此时老板完全没动,还在看他的“30”)。
-
好处:如果你按住不放,数字会一直变(31, 32, 33…)。如果这时候机器就直接执行,机器会疯掉,一会儿加热一会儿停。所以,只改秘书的草稿,不打扰老板。
3. 退出设置界面时:秘书把草稿交给老板(关键!)
代码逻辑:
c
1// 老板采纳秘书的草稿
2P_ctrol[0] = P_dat[0];
3P_ctrol[1] = P_dat[1];
场景模拟:
你调好数字(比如上限35,下限25)了,按下“退出”或“确认”。
-
动作:这时候,秘书拿着改好的草稿纸跑到老板面前:“老板,按这个执行!” 老板这才把秘书手里的新数值抄到自己的纸上。
-
结果:此时此刻,机器才真正接收到新的指令(35度),开始改变行为。
为什么不能直接改老板的纸(P_ctrol)?
如果你不用 P_dat,直接在按键时修改 P_ctrol,会发生以下可怕的事情:
-
机器抽搐:你按一下“+”,机器觉得上限变成31,开始调整;你再按一下变成32,机器又调整……在你确认之前,机器就已经开始乱动了。
-
逻辑冲突:假设你在设置上限,还没设置下限。如果你直接改了
P_ctrol[0],而P_ctrol[1]还是旧的,这时候可能会出现“上限比下限还低”的逻辑错误,机器直接报错或短路。 -
没得后悔:一旦按错,机器立刻执行。而用“双缓冲”,你按错了没关系,只要不退出界面,老板就不知道,机器也不会动。
总结
-
P_dat = P_ctrol:“初始化”。进入设置时,让草稿纸显示当前值。 -
P_ctrol = P_dat:“生效”。退出设置时,让修改后的值真正执行。
这就像是**“排练”(改P_dat)和“演出”**(P_ctrol控制机器)。排练的时候怎么出错都没事,只有等导演喊“通过”(退出界面)的那一刻,排练的内容才变成正式的演出。
PCF8951遇到问题
1. 最大的坑:PCF8591 的读写逻辑缺失
文档要求根据按键 S5 来决定 DAC 输出是“跟随电位器”还是“固定 2.0V”。
-
你的代码现状: 你只读取了
dat = Ad_Read(0x43);(读电位器),然后直接把读到的值赋给了dat2。你完全没有调用Da_Write(写 DAC)的函数。 -
后果: 无论你怎么按 S5,PCF8591 的 DAC 都不会输出电压,或者一直保持上一次的状态。
2. 第二个坑:LED 指示灯逻辑没写
文档对 L1-L4 的灯有非常明确的要求:
-
L1/L2:根据显示界面(U 或 F)切换。
-
L3:根据电位器电压范围闪烁(1.5V-2.5V亮,2.5V-3.5V灭…)。
-
L4:根据 S5 模式(跟随模式亮,固定模式灭)。
-
你的代码现状:
Led_Proc()函数里是空的。 -
后果: 灯根本不亮,或者不按规则亮。
3. 第三个坑:变量定义混乱
-
你的代码现状: 你定义了
unsigned char dat, dat2;,然后在Seg_Proc里又定义了一次dat, dat2。 -
后果: 这会导致编译错误(重复定义),或者逻辑混乱。
4. 第四个坑:数码管显示逻辑错误
-
你的代码现状: 在
Seg_Proc里,你用dat/100%10这种方式处理电压。这是把电压当成“整数”处理了。 -
后果: 假设 ADC 读到的值是 128(代表 2.5V 左右),你这样除会直接变成 1、2、8,显示出来是乱码,而不是
2.50。 -
正确做法: 需要将 ADC 的 0-255 映射到 0-5V,然后通过乘除法把小数点提出来。