单片机第四周笔记第二题

电压采集器系统详解

一、键盘输入系统设计

1. 键盘使能条件与输入限制

if(key_down >= 1 && key_down <= 10)  // 键盘使能条件:只响应S1~S10按键
{
    if(seg_mode == 0 && seg_input_index < 4)  // 输入限制:模式0且输入位数小于4
    {
        seg_input[seg_input_index] = key_down - 1;  // 存储按键值(0-9)
        seg_input_index++;  // 输入位置指针后移
        key_error_count = 0;  // 错误计数清零
    }
    else 
        key_error_count++;  // 不符合条件时错误计数增加
}

通俗理解

  • 就像银行的ATM机:只有插入有效银行卡(按键S1-S10)才能操作
  • 每次只能输入4位数字(类似密码长度限制)
  • 连续输错会增加错误计数,防止恶意操作

二、数码管显示优化

1. 智能闪烁控制 - 避免程序"卡死"

// 电压采集界面下的智能闪烁
if(seg_buf[5] == 11)  // 只有当最后一位显示为"-"时
    seg_buf[2 + seg_input_index] = seg_flash ? seg_input[seg_input_index] : 10;

比喻说明

就像交通信号灯:红灯(熄灭)和绿灯(显示)交替闪烁,但不能同时亮红灯或同时亮绿灯,否则交通会混乱(程序卡死)。这里的"-"就像是"等待输入"的指示灯。

2. Float类型数据显示技巧

// 将浮点数转换为整数显示
seg_buf[3] = (unsigned char)voltage_parameter % 10;        // 整数部分个位
seg_buf[4] = (unsigned int)(voltage_parameter * 100) / 10 % 10;  // 小数第一位
seg_buf[5] = (unsigned int)(voltage_parameter * 100) % 10;       // 小数第二位

计算方法说明

假设电压参数为3.14V:

  1. 乘以100:3.14 × 100 = 314
  2. 分离数字:
    • 314 ÷ 10 = 31 余 4 → 个位是4
    • 314 ÷ 100 = 3 余 14 → 十位是1(实际上是小数第一位)
    • 314 ÷ 1000 = 0 余 314 → 百位是3(整数部分)

3. 计数界面显示 - “数位分解法”

// 显示5位计数值
seg_buf[1] = voltage_count / 10000 % 10;  // 万位
seg_buf[2] = voltage_count / 1000 % 10;   // 千位
seg_buf[3] = voltage_count / 100 % 10;    // 百位
seg_buf[4] = voltage_count / 10 % 10;     // 十位
seg_buf[5] = voltage_count % 10;          // 个位

通俗理解

就像拆解一个五位数12345:

  • 万位:12345 ÷ 10000 = 1 余 2345 → 显示1
  • 千位:12345 ÷ 1000 = 12 余 345 → 12 ÷ 10 余 2 → 显示2
  • 以此类推…

三、显示优化技巧

高位熄灭(Leading Zero Blanking)

// 高位熄灭:从第2位到第5位检查
for(j = 1; j < 5; j++)
{
    if(seg_buf[j] == 0)  // 如果该位是0
        seg_buf[j] = 10;  // 设置为熄灭状态(10表示熄灭)
}

实际效果

显示123:   0 0 1 2 3 → 熄灭熄灭1 2 3
显示45:    0 0 0 4 5 → 熄灭熄灭熄灭4 5

四、核心计数逻辑

计数值增加的智能判断

if(voltage > voltage_parameter)  // 电压高于设定参数
{
    voltage_flag = 1;  // 设置标志位(像举起小红旗)
}
else if(voltage_flag == 1)  // 电压下降且之前是高位状态
{
    voltage_flag = 0;  // 清除标志位(放下小红旗)
    voltage_count++;   // 计数增加
}

形象比喻

想象过山车通过最高点:

  1. 过山车上坡(电压上升)→ 到达顶点(超过参数)→ 举起红旗(flag=1)
  2. 过山车下坡(电压下降)→ 通过顶点后下坡 → 放下红旗(flag=0)并计数一次

重要原则:不能在上坡途中就放下红旗,否则会错误计数!

五、高级LED控制技术

1. 专业的LED驱动函数

#include "led.h"

void led_disp(unsigned char addr, enable)
{
    static unsigned char temp = 0x00;      // 当前LED状态
    static unsigned char temp_old = 0xff;  // 上一次LED状态
    
    if(enable)  // 如果要点亮
        temp |= 0x01 << addr;     // 设置对应位为1
    else        // 如果要熄灭
        temp &= ~(0x01 << addr);  // 设置对应位为0
    
    // 只有状态变化时才更新硬件,避免频繁操作
    if(temp != temp_old)
    {
        P1 = ~temp;      // 取反后输出(硬件可能是低电平点亮)
        temp_old = temp; // 保存当前状态
    }
}

2. 重要技术细节

易错点提醒

// ✅ 正确写法:
temp &= ~(0x01 << addr);  // 使用按位取反

// ❌ 错误写法:
temp &= (0xfe << addr);   // 错误!因为左移会在右边补0

// 示例说明(假设addr=2):
// 正确:~(0x01 << 2) = ~(0x04) = 0xFB = 1111 1011
// 错误:0xFE << 2 = 1111 1110 << 2 = 1111 1000(右边补了两个0!)

硬件原理

很多LED模块是"低电平有效":P1端口输出0时LED亮,输出1时LED灭。
所以需要取反操作:P1 = ~temp

六、实用技巧总结

1. 状态机思维

  • 使用标志位(flag)记录系统状态
  • 状态变化时执行相应操作
  • 避免在循环中直接操作硬件

2. 显示优化策略

  • 高位熄灭提升可读性
  • 智能闪烁避免视觉疲劳
  • 状态变化时才更新显示,减少功耗

3. 防错设计

  • 输入范围检查
  • 错误计数机制
  • 状态一致性验证

4. 性能优化

  • 避免不必要的硬件操作
  • 使用局部静态变量保存状态
  • 条件判断优化,减少计算量

七、学习建议

1. 动手实践步骤

  1. 先理解原理:电压比较、数码管显示、LED控制
  2. 分模块调试:先调通键盘输入,再调显示,最后整合
  3. 添加调试信息:通过串口或LED显示中间状态
  4. 边界测试:测试最大值、最小值、异常输入

2. 进阶思考

  • 如何添加电压报警功能?
  • 如何实现数据存储和回放?
  • 如何通过通信接口远程监控?
  • 如何增加自动量程切换?

学习心得:电压采集器项目涵盖了嵌入式系统的多个核心知识点,包括输入处理、数据处理、显示控制和状态管理。通过这个项目,你可以建立起完整的嵌入式开发思维框架,为更复杂的系统设计打下坚实基础。记住:好的代码不仅是能运行,还要易于理解、维护和扩展!