首先,本代码是基于小蜜蜂老师的模板进行编写的,希望对大家有所帮助。

4T满分截图

部分题目展示:

下面直接进行代码解析

// ================= 1. 头文件引用 =================
#include <STC15F2K60S2.H>   // STC15系列单片机的核心寄存器定义文件
#include <onewire.H>        // 单总线协议驱动 (通常用于 DS18B20 温度传感器)
#include <intrins.H>        // 内部函数库,包含 _nop_() 等空操作指令,用于精确延时
#include <iic.H>            // I2C 总线协议驱动 (通常用于 PCF8591 DAC 或 AT24C02)

// ================= 2. 数码管段码表 (共阳极) =================
// 0-9, A, b, C, d, E, F (共16个字符)
// 0xC0 对应 '0', 0xF9 对应 '1' ...
unsigned char code SMG_NODOT[] = { 
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E 
};

// 带小数点的段码表 (0-9)
// 原理:将 SMG_NODOT 的值与 0x80 (小数点位) 进行“或”运算
// 例如:0x40 = 0xC0 | 0x80 (这里数据看起来像是单独定义的,或者是特定硬件接法)
// 注意:通常共阳极小数点是 0x80,这里 0x40 可能是硬件接在 dp 位不同,或者是代码特有逻辑
unsigned char code SMG_DOT[] = { 
    0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00, 0x10 
};

// ================= 3. 全局变量定义 (系统状态核心) =================

// --- 模式控制变量 ---
unsigned char mode = 1;      // 主显示模式:1=温度界面, 2=DAC界面, 3=误差界面
unsigned char mode_temp = 0; // 温度子模式:0=自动模式, 1=手动模式

// --- 数据处理变量 ---
float volt;                  // 存储计算出的电压值 (用于 DAC 输出)
float temp;                  // 存储 DS18B20 读取的原始温度值

// --- LED 闪烁与计时控制 (用于定时器中断) ---
char js = 2;                 // 通用计时/闪烁控制变量 (控制闪烁频率)
char jo = 0;                 // 奇偶计数器 (用于交替闪烁逻辑)

// --- 温度相关状态机变量 ---
char temp_smg;               // 最终显示在数码管上的温度值 (可能包含修正)
char temp_last;              // 上一次采样的温度值 (用于计算温差)
char temp_err_led;           // 温度变化差值 (用于控制 LED 报警闪烁)
char temp_count;             // 闪烁持续时间计数器
char temp_flag = 0;          // 温度状态标志 (0=无变化, 1=升温, 2=降温)
char temp_flag_lx = 0;       // 温度连续变化标志 (用于秒计数逻辑)

// --- 误差与修正 ---
char temp_err = 0;           // 用户设定的误差修正值 (通过按键调整)
char temp_err_real;          // 实际应用的误差值

// --- DAC 与 继电器控制 ---
unsigned char dac_dat = 100; // DAC 输出数值 (0-255),默认初始值 100
unsigned char jdq_mode = 0;  // 继电器锁状态:0=解锁(可操作), 1=上锁(不可操作)

// --- 硬件状态输出缓存 ---
unsigned char led_state = 0xff; // LED 状态缓存 (0xff 表示全灭,共阳极)
unsigned char jdq_state = 0x00; // 继电器控制缓存 (0x00 可能表示吸合或断开,视电路而定)

// --- 时间控制 ---
char second_r = 0;           // 秒计数器 (右/临时)
char second_l = 0;           // 秒计数器 (左/超时控制)

// --- 按键处理 ---
unsigned char key_flag;      // 按键按下标志位 (用于中断计数)
unsigned char key_js;        // 按键长按计时器 (用于区分短按/长按)

一.底层驱动

1.延时函数

// 1ms 延时函数 (基于12MHz晶振)
void Delay1ms(void) {
    unsigned char data i, j;
    i = 12; j = 169;
    do { while (--j); } while (--i);
}

// 200us 延时函数
void Delay200us(void) {
    unsigned char data i, j;
    i = 3; j = 82;
    do { while (--j); } while (--i);
}

// 50us 延时函数 (用于按键消抖)
void Delay50us(void) {
    unsigned char data i;
    _nop_(); _nop_(); // 空操作指令,微调时间
    i = 147;
    while (--i);
}

注释解释:蓝桥杯开发板通常使用12MHz晶振。这些函数通过嵌套循环消耗CPU时间来实现精确延时。Delay1ms用于数码管动态扫描消隐;Delay50us专门用于按键按下时的硬件消抖。

2.  74HC573 锁存器控制

// 选择锁存器通道
void HC573(unsigned char channel) {
    switch(channel) {
        case 4: P2=(P2&0X1F)|0X80; break; // L1-L8 LED & 继电器
        case 5: P2=(P2&0X1F)|0Xa0; break; // BUZZ & RELAY (蜂鸣器/继电器使能)
        case 6: P2=(P2&0X1F)|0Xc0; break; // SMG_BIT (数码管位选)
        case 7: P2=(P2&0X1F)|0Xe0; break; // SMG_IO (数码管段选)
    }
}
  • 注释解释:这是蓝桥杯开发板最核心的逻辑。P2 口的高3位(P2.7-P2.5)用来选择控制哪个外设。
  • 关键技巧P2 & 0x1F (00011111) 是为了保留 P2 低5位的值不变,只修改高3位。如果不这样做,可能会意外改变正在运行的外设状态。

3.数码管显示操作

// 初始化系统状态
void init_system() {
    HC573(4); P0=0Xff; // 关闭所有LED
    HC573(5); P0=0x00; // 关闭蜂鸣器和继电器
}

// 设置数码管某一位显示什么
void set_smg(unsigned char pos, unsigned char value) {
    HC573(6); P0=0X01<<pos; // 选择位选 (pos位)
    HC573(7); P0=value;      // 输出段选码 (显示内容)
    Delay1ms();              // 保持1ms (动态扫描)
    HC573(6); P0=0X01<<pos;  // 消隐:再次选中该位
    HC573(7); P0=0xff;       // 段选全灭 (防止拖影)
}

// 全部数码管熄灭
void smg_xy() {
    HC573(6); P0=0xff; // 位选全0 (不选中任何位)
    HC573(7); P0=0xff; // 段选全1 (共阳极,1为灭)
}
  • 注释解释set_smg 函数实现了动态扫描的核心。先选中某一位(位选),输出数据(段选),延时保持,最后必须进行“消隐”操作(先关闭段选再关闭位选,或像代码中那样在关闭位选前将段选全灭),否则会出现串位显示。

二.外设

1.DS18B20 温度读取

void ds18b20()
{
    unsigned char MSB, LSB; // 定义高8位和低8位变量
    unsigned int dat;       // 定义合并后的16位数据变量

    // --- 步骤 1: 启动温度转换 ---
    
    init_ds18b20();         // 1. 复位 DS18B20 (发送复位脉冲)
    SMG_SHOW();             // 【关键技巧】: 在等待总线稳定或转换开始时,刷新数码管,防止熄灭
    
    Write_DS18B20(0xcc);    // 2. 跳过 ROM 匹配指令 (因为总线上只有一个传感器)
    Write_DS18B20(0x44);    // 3. 发送温度转换指令 (开始进行 A/D 转换)
    
    // --- 步骤 2: 模拟延时等待 ---
    // 官方驱动通常需要延时 750ms 等待转换完成。
    // 这里没有使用死板的 Delay(750ms),而是通过多次调用 SMG_SHOW() 来消耗时间。
    // 这样既满足了时序要求,又维持了人机交互界面的刷新。
    SMG_SHOW();             // 消耗时间片 1
    init_ds18b20();         // 4. 再次复位 DS18B20,准备读取
    SMG_SHOW();             // 消耗时间片 2
    SMG_SHOW();             // 消耗时间片 3 (此时 DS18B20 内部转换已完成)

    // --- 步骤 3: 读取温度数据 ---
    
    Write_DS18B20(0xcc);    // 5. 再次跳过 ROM
    Write_DS18B20(0xbe);    // 6. 发送读取暂存器指令 (Read Scratchpad)
    
    LSB = Read_DS18B20();   // 7. 读取低 8 位 (小数部分在低4位)
    MSB = Read_DS18B20();   // 8. 读取高 8 位 (符号位在高5位)

    // --- 步骤 4: 数据合并与处理 ---
    
    dat = MSB;
    dat = (MSB << 8) | LSB; // 将高8位左移8位,与低8位进行“或”运算,合并成16位数据

    // 判断温度正负 (检查高5位是否全为0)
    // 0xf800 = 1111 1000 0000 0000
    // 如果高5位是0,说明是正温度
    if ((dat & (0xf800)) == 0x0000)
    {
        temp = dat * 0.0625; // 正温度直接乘以分辨率 0.0625
    }
    // 注意:代码中省略了负温度的处理逻辑,可能默认只处理正温或通过其他方式处理

    // --- 步骤 5: 更新全局状态 ---
    
    temp_last = temp_smg;            // 保存上一次的显示温度 (用于计算温差)
    temp_smg = temp + temp_err_real; // 计算最终显示温度 (原始温度 + 用户修正值)
    temp_err_led = temp_smg - temp_last; // 计算本次温度变化量 (用于驱动 LED 闪烁逻辑)
}
  • 注释解释
    • 代码中穿插了 SMG_SHOW(),这是为了在等待DS18B20转换时(转换需要几百毫秒)刷新数码管,防止数码管熄灭。
    • temp_err_led 记录了温度的变化量,这是为了在中断里判断温度是升高了还是降低了,从而控制LED闪烁。
2.DAC 输出控制 (PCF8591)
// ================= 底层驱动:I2C 通信协议 =================
// 功能:向 PCF8591 发送一个字节的数据,控制 DAC 输出电压
void dac_output(unsigned char dat)
{
    I2CStart();           // 1. 发送 I2C 起始信号
    I2CSendByte(0x90);    // 2. 发送设备地址 (PCF8591 写地址)
                          // 0x90 = 1001 0000 (A2-A0接地,LSB=0表示写)
    I2CWaitAck();         // 3. 等待从机应答
    
    I2CSendByte(0x43);    // 4. 发送控制字节
                          // 0x43 = 0100 0011
                          // Bit6=1 (使能DAC), Bit0-1=11 (选择通道3)
    I2CWaitAck();         // 5. 等待从机应答
    
    I2CSendByte(dat);     // 6. 发送数据 (0-255)
                          // 这个值决定了输出电压的大小
    I2CWaitAck();         // 7. 等待从机应答
    
    I2CStop();            // 8. 发送 I2C 停止信号,释放总线
}

// ================= 业务逻辑:DAC 策略控制 =================
// 功能:根据模式决定输出什么电压
void dac_chuli()
{
    unsigned char dat; // 定义 DAC 的数字量 (0-255)
    
    // 模式 0:自动模式 (温度控制电压)
    if(mode_temp == 0)
    {
        // 分段函数逻辑
        if(temp_smg <= 10)
        {
            volt = 2; // 温度 <= 10°C,固定输出 2V
        }
        else if(temp_smg > 10 && temp_smg < 40)
        {
            // 10°C < 温度 < 40°C,线性增长
            // 斜率 k = (5V - 2V) / (40 - 10) = 3 / 30 = 0.1 V/°C
            volt = 2 + 0.1 * (temp_smg - 10);
        }
        else if(temp_smg >= 40)
        {
            volt = 5; // 温度 >= 40°C,固定输出 5V (饱和)
        }
        
        // 将电压转换为 DAC 数字量
        // 公式:D = (V_out / V_ref) * 255
        // 这里 V_ref = 5V,所以 dat = volt * 51
        dat = volt * (255 / 5.0);
        dac_output(dat); // 调用底层驱动输出
    }
    // 模式 1:手动模式 (用户控制电压)
    else if(mode_temp == 1)
    {  
        dac_output(dac_dat); // 直接输出用户设定的值 (dac_dat 由按键控制)
    }
}

注释解释dac_chuli 是一个典型的“策略模式”函数。它根据 mode_temp 的值决定是走自动算法(温度查表)还是直接输出用户设定的手动值。

3.数码管显示逻辑

void SMG_SHOW()
{
    // 根据全局变量 mode 的值,决定显示哪个界面
    switch(mode)
    {
        // ================= 模式 1:温度显示界面 =================
        case 1:
            // 1. 显示单位/标识符
            // 在第0位数码管显示 'C' (假设 SMG_NODOT[12] 是 'C' 的段码)
            set_smg(0, SMG_NODOT[12]); 
            
            // 2. 温度数值显示 (动态位数处理)
            // 情况A: 温度 < 10 (个位数)
            if(temp_smg <= 9)
            {
                // 只显示个位 (第7位),高位自动熄灭 (消隐)
                set_smg(7, SMG_NODOT[temp_smg % 10]);
            }
            // 情况B: 10 <= 温度 <= 999 (十位或百位)
            else if(temp_smg <= 999)
            {
                // 显示十位 (第6位) 和 个位 (第7位)
                // 注意:这里代码似乎省略了百位的显示逻辑,或者题目要求只显示后两位
                set_smg(6, SMG_NODOT[temp_smg / 10]);     // 取十位
                set_smg(7, SMG_NODOT[temp_smg % 10]);     // 取个位
            }
            break;

        // ================= 模式 2:DAC 数值显示界面 =================
        case 2:
            // 1. 显示单位/标识符
            // 在第0位数码管显示 'H' (假设 SMG_NODOT[10] 是 'H' 的段码,代表Hex或High)
            set_smg(0, SMG_NODOT[10]);
            
            // 2. DAC数值显示 (0-255)
            // 情况A: 个位数 (0-9)
            if(dac_dat <= 9)
            {
                set_smg(7, SMG_NODOT[dac_dat % 10]);
            }
            // 情况B: 十位数 (10-99)
            else if(dac_dat <= 99)
            {
                set_smg(6, SMG_NODOT[dac_dat / 10]);      // 十位
                set_smg(7, SMG_NODOT[dac_dat % 10]);      // 个位
            }
            // 情况C: 百位数 (100-255)
            else if(dac_dat <= 999)
            {
                set_smg(5, SMG_NODOT[dac_dat / 100]);           // 百位
                set_smg(6, SMG_NODOT[(dac_dat / 10) % 10]);     // 十位
                set_smg(7, SMG_NODOT[dac_dat % 10]);            // 个位
            }
            break;

        // ================= 模式 3:误差/偏差显示界面 =================
        case 3:
            // 1. 显示单位/标识符
            // 在第0位显示自定义符号 (0x8c),可能是 '-' 或 'E' (Error)
            set_smg(0, 0x8c);
            
            // 2. 误差数值显示 (带符号显示)
            // 情况A: 正误差或零 (>= 0)
            if(temp_err >= 0)
            {
                // 直接显示数值
                set_smg(7, SMG_NODOT[temp_err]);
            }
            // 情况B: 负误差 (< 0)
            else if(temp_err < 0)
            {
                // 先显示负号 '-' (0xbf 是共阳极 '-' 的段码)
                set_smg(6, 0xbf);
                // 再显示数值的绝对值 (-temp_err)
                set_smg(7, SMG_NODOT[-temp_err]);
            }
            break;
    }
    
    // 3. 消隐处理
    // 关闭所有位选,防止上一轮显示的残影(鬼影)
    smg_xy();
}
  • 注释解释:这个函数非常繁琐但重要。它根据全局变量 mode(模式)的不同,调用 set_smg 在数码管的不同位置显示不同的数组内容。注意处理了多位数的拆分(/10%10)。
4.按键扫描 
void key_scan()
{
    // ================= 检测 S12 (模式切换键) =================
    // 技巧:行反转法扫描矩阵按键
    // 0x08 = 0000 1000, ~0x08 = 1111 0111
    // 将 P3.3 拉低,如果按键按下,P3.5 也会被拉低
    P3 = ~0X08; 
    if(P35 == 0) // 检测 P3.5 是否为低电平
    {
        Delay50us(); // 软件消抖
        if(P35 == 0)
        {
            // 功能:切换主显示模式 (1->2->3->1)
            mode++;
            if(mode == 4)
            {
                mode = 1;
                // 当切回模式1时,将用户设定的误差值写入实际误差变量
                temp_err_real = temp_err; 
            }
            // 等待松手 (死循环),期间保持系统刷新,防止死机
            while(P35 == 0)
            {
                dac_chuli();  // 保持 DAC 输出稳定
                SMG_SHOW();   // 保持数码管显示
            }
        }
    }

    // ================= 检测 S16 (加/开关键) =================
    // 注意:这里直接检测 P34,说明 S16 和 S17 可能是独立按键,或者 P3 口已配置好
    if(P34 == 0)
    {
        Delay50us();
        if(P34 == 0)
        {
            // 功能复用:根据 mode 决定按键功能
            if(mode == 1) // 模式1:控制继电器
            {
                if(jdq_mode == 0) // 只有在解锁状态下才能操作
                    jdq_state |= 0x10; // 继电器吸合 (P0.4置0)
            }
            else if(mode == 2) // 模式2:DAC 数值增加
            {
                dac_dat += 5; // 步进为 5
                if(dac_dat >= 255) dac_dat = 255; // 限幅
            }
            else if(mode == 3) // 模式3:误差值增加
            {
                temp_err++;
                if(temp_err >= 9) temp_err = 9; // 限幅
            }
            
            while(P34 == 0)
            {
                dac_chuli();
                SMG_SHOW();
            }
        }
    }
    
    // ================= 检测 S13 (功能/长按键) =================
    P3 = ~0X04; // 0000 0100 -> 1111 1011 (将 P3.2 拉低)
    if(P35 == 0) // 检测 P3.5
    {
        Delay50us();
        if(P35 == 0)
        {
            // 核心逻辑:长按检测
            // 在按住按键期间,不断置位 key_flag,并刷新显示
            while(P35 == 0)
            {
                dac_chuli();
                SMG_SHOW();
                key_flag = 1; // 通知定时器中断:按键被按住了
            }
            
            // 松手后进行判断
            // key_js 是在定时器中断中累加的 (每50ms加1)
            // 30 * 50ms = 1500ms = 1.5秒
            if(key_js <= 30) // 短按 (小于1.5秒)
            {
                // 切换 自动/手动 模式
                if(mode_temp == 0) mode_temp = 1;
                else if(mode_temp == 1) mode_temp = 0;
                
                key_js = 0; // 清零计数器
                key_flag = 0;
            }
            else // 长按 (大于1.5秒)
            {
                // 切换 继电器锁 状态 (解锁 <-> 上锁)
                if(jdq_mode == 0) jdq_mode = 1;
                else if(jdq_mode == 1) jdq_mode = 0;
                
                key_flag = 0;
                key_js = 0;
            }
        }
    }

    // ================= 检测 S17 (减/关关键) =================
    if(P34 == 0)
    {
        Delay50us();
        if(P34 == 0)
        {
            // 功能复用:与 S16 逻辑相反
            if(mode == 1) // 模式1:控制继电器
            {
                if(jdq_mode == 0)
                    jdq_state &= ~(0x10); // 继电器断开 (P0.4置1)
            }
            else if(mode == 2) // 模式2:DAC 数值减小
            {
                dac_dat -= 5;
                if(dac_dat <= 0) dac_dat = 0; // 限幅
            }
            else if(mode == 3) // 模式3:误差值减小
            {
                temp_err--;
                if(temp_err <= -9) temp_err = -9; // 限幅
            }
            
            while(P34 == 0)
            {
                dac_chuli();
                SMG_SHOW();
            }
        }
    }
}
  • 注释解释:按键逻辑是代码中最复杂的部分。
    • 短按/长按检测S13 按键通过 key_flag 和 key_js(在中断中计数)来判断用户是短按(切换自动/手动)还是长按(切换上锁/解锁)。这是通过在 while(按键按下) 循环中不重置计数器实现的。
    • 功能复用:同一个按键(S16/S17)在不同 mode 下有不同的功能(控制继电器、调节DAC、调节误差)。

三.主循环与中断

1.定时器0 中断

void init_timer0()
{
    // 1. 设置工作模式
    // TMOD = 0x01 -> 0000 0001
    // 低4位控制 T0: GATE=0, C/T=0 (定时), M1=0, M0=1 (模式1: 16位定时器)
    TMOD = 0X01;

    // 2. 装载初值 (50ms @ 12MHz)
    // 65536 - 50000 = 15536
    TH0 = (65536 - 50000) / 256; // 高8位
    TL0 = (65536 - 50000) % 256; // 低8位

    // 3. 开启中断
    ET0 = 1; // 允许定时器0中断
    EA = 1;  // 允许总中断

    // 4. 启动定时器
    TR0 = 1; // 开始计数
}

void timer0() interrupt 1
{
    // 1. 定时器重载 (产生50ms基准时钟)
    // 晶振12MHz,机器周期1us。65536-50000 = 15536,即50ms溢出一次
    TH0 = (65536 - 50000) / 256; 
    TL0 = (65536 - 50000) % 256;

    // 2. 按键长按计数器 (配合S13功能)
    // 如果按键扫描中检测到按键按下(key_flag=1),这里每50ms加1
    // 约计数30次(1.5秒)判定为长按,用于区分S13的短按(切模式)和长按(上锁)
    if (key_flag == 1)
    {
        key_js++;
    }

    // 3. 自动/手动模式指示 (LED1)
    // mode_temp: 0=自动, 1=手动
    // 逻辑:自动时LED1灭(0),手动时LED1亮(1)
    if (mode_temp == 0)
    {
        led_state &= (~0x01); // LED1 灭
    }
    else
    {
        led_state |= 0x01;    // LED1 亮
    }

    // 4. 温度变化报警闪烁逻辑 (LED2, LED3)
    // js 是一个全局计数器,用于控制闪烁频率
    if (js > 0) js--;
    else if (js == 0)
    {
        // LED2 闪烁逻辑 (温度升高/维持)
        // temp_err_led > 0 表示温度在升高
        if ((temp_err_led >= 1 || temp_flag == 1) && temp_flag != 2)
        {
            temp_flag = 1;       // 标记状态为“升高”
            temp_count++;        // 计数器累加
            led_state &= (~0x02); // LED2 亮 (共阳极,0为亮)
            
            // 闪烁40次 (约2秒) 后熄灭并重置
            if (temp_count >= 40)
            {
                led_state |= 0x02; // LED2 灭
                temp_count = 0;
                temp_flag = 0;     // 重置状态
            }
        }
        // LED3 闪烁逻辑 (温度降低)
        // temp_err_led < 0 表示温度在降低
        else if (temp_err_led <= -1 || temp_flag == 2)
        {
            temp_flag = 2;       // 标记状态为“降低”
            temp_count++;
            led_state &= (~0x04); // LED3 亮
            
            if (temp_count >= 40)
            {
                led_state |= 0x04; // LED3 灭
                temp_count = 0;
                temp_flag = 0;
            }
        }

        // 5. 秒计数与特殊闪烁 (LED4)
        // 只要温度在变化(temp_err_led非0),就启动秒计数
        if ((temp_err_led >= 1 || temp_err_led <= -1) || temp_flag_lx == 1)
        {
            temp_flag_lx = 1;
            second_r++; // 计数器r
            second_l++; // 计数器l
            
            // 这里实现了一个“慢速闪烁”逻辑
            // 每4次中断(约200ms)触发一次状态翻转
            if ((second_r - 1) % 4 == 0)
            {
                jo++; // 翻转计数器
                if (jo % 2 != 0) led_state &= (~0x08); // 奇数次:亮
                if (jo % 2 == 0) led_state |= 0x08;   // 偶数次:灭
            }
            
            // 60秒超时保护
            if (second_l >= 60)
            {
                led_state |= 0x08;  // 强制熄灭LED4
                temp_flag_lx = 0;
                second_l = 0;
                second_r = 0;
                jo = 0;
            }
        }
    }

    // 6. 锁状态指示 (LED8)
    // jdq_mode: 0=解锁, 1=上锁
    // 逻辑:解锁时灭,上锁时亮
    if (jdq_mode == 0)
    {
        led_state &= (~0x80); // LED8 灭
    }
    else if (jdq_mode == 1)
    {
        led_state |= 0x80;    // LED8 亮
    }
}
  • 注释解释:中断服务程序(ISR)不应该做太耗时的操作。这里的逻辑主要是更新变量(如 key_js 计数,second_r 计时),具体的硬件操作(P0 赋值)是在 main 循环中完成的。

2.main主函数

void main() {
    init_system();      // 初始化硬件
    init_timer0();      // 开启定时器
    
    while(1) {
        dac_chuli();    // 1. 处理DAC输出 (实时性要求高)
        ds18b20();      // 2. 读取温度 (包含延时,放在循环中)
        key_scan();     // 3. 扫描按键
        SMG_SHOW();     // 4. 刷新数码管
        
        // 5. 输出 LED 和 继电器 状态
        HC573(4); P0=led_state;   // LED状态由中断计算得出
        HC573(5); P0=jdq_state;   // 继电器状态由按键设置
    }
}
  • 注释解释:主循环采用了前后台系统(Super Loop)架构。
    • 顺序很重要:先处理数据(DAC、温度),再处理交互(按键、显示),最后输出硬件状态。
    • 分工明确:中断负责“感知”时间流逝和按键长短,主循环负责“执行”具体的业务逻辑。

四.总结

我这篇代码虽然有些地方(写法比较“野”,但它完美展示了蓝桥杯考察的核心,拿到比赛上是完全没问题的:

  1. 硬件基础:锁存器、I2C、单总线时序。
  2. 算法逻辑:浮点数转整型显示、电压映射、温度区间判断。
  3. 状态机:通过 mode 和 mode_temp 控制程序流程。
  4. 抗干扰:按键消抖、数码管消隐。
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐