蓝桥杯第十届省赛代码错误总结

蓝桥杯单片机第十届省赛代码错误总结

错误列表

1.:cross_mark: 电压显示计算精度丢失

错误代码:

Seg_Buf[5]=(unsigned char)Voltage*100/100%10+',';  // ❌ 类型转换顺序错误
Seg_Buf[6]=(unsigned char)Voltage*100/10%10;
Seg_Buf[7]=(unsigned char)Voltage*100%10;

错误原因:

  • C语言运算符优先级:类型转换 > 乘法 > 除法 > 取模

  • (unsigned char)Voltage*100 的执行顺序:

    1. 先执行 (unsigned char)Voltage → 3.41V 转换为 3

    2. 再执行 3 * 100 = 300

  • 导致小数部分全部丢失,3.41V显示成3.00V

数值验证(假设Voltage=3.41V):

❌ 错误计算过程:
Seg_Buf[5] = (unsigned char)3.41 * 100 / 100 % 10 + 44
           = 3 * 100 / 100 % 10 + 44
           = 300 / 100 % 10 + 44
           = 3 % 10 + 44 = 47(显示3.)
​
Seg_Buf[6] = 3 * 100 / 10 % 10
           = 300 / 10 % 10
           = 30 % 10 = 0(显示0)
​
Seg_Buf[7] = 3 * 100 % 10
           = 300 % 10 = 0(显示0)
​
最终显示:U _ _ _ _ 3.00  ❌ 应该是3.41

正确代码:

case 1:
    Seg_Buf[0]=12;
    Seg_Buf[1]=10;
    Seg_Buf[2]=10;
    Seg_Buf[3]=10;
    Seg_Buf[4]=10;
    Seg_Buf[5]=(unsigned int)(Voltage*100)/100%10+','; // 个位 + 小数点标志
    Seg_Buf[6]=(unsigned int)(Voltage*100)/10%10;      // 小数第一位
    Seg_Buf[7]=(unsigned int)(Voltage*100)%10;         // 小数第二位
break;

正确计算过程(假设Voltage=3.41V):

✓ 正确计算过程:
​
Seg_Buf[5] = 341/100%10 + 44 = 3 + 44 = 47(显示3.)
Seg_Buf[6] = 341/10%10 = 34%10 = 4(显示4)
Seg_Buf[7] = 341%10 = 1(显示1)
​
最终显示:U _ _ _ _ 3.41  ✓ 正确!

关键点:

  • :sparkles: 括号位置决定计算顺序:(unsigned int)(Voltage * 100) vs (unsigned int)Voltage * 100

  • :sparkles: 浮点数运算完成后再转换为整数,避免精度丢失

  • :sparkles: 使用临时变量避免重复计算,提高效率和可读性


2. :cross_mark: 缺少DAC输出调用

错误代码:

void AD_DA()
{
    RB2=Ad_Read(0x43);  // 只读取了ADC
    Voltage=(Output_Mode==0)?2:RB2;  // 只设置了变量
    // ❌ 完全没有调用 Da_Write() 输出到DAC!
}

错误原因:

  • 题目明确要求: “使用 PCF8591 测量电位器 RB2 的输出电压,并根据试题要求通过其 DAC 功能输出该电压值

  • PCF8591有两个功能:

    • ADC(模数转换):读取模拟电压 → Ad_Read()

    • DAC(数模转换):输出模拟电压 → Da_Write()

  • 只读取了电压,但没有输出到DAC的AOUT引脚

  • 导致DAC完全不工作,不符合题目要求

  • 评分时可能直接测量AOUT引脚电压,没有输出会直接扣分或零分!

题目要求对照:

要求 对应操作 是否完成
测量RB2电压 Ad_Read(0x43) :white_check_mark:
通过DAC输出电压 Da_Write(...) :cross_mark: 缺失!
S5切换固定2V/跟随RB2 Output_Mode :white_check_mark:
LED L5指示输出模式 ucLed[4] :white_check_mark:

正确代码(完整版):

void AD_DA()
{
    RB2=Ad_Read(0x43)/51.0; //  读取ADC原始值,ADC值转换为实际电压
    Voltage=(Output_Mode==0)?2:RB2;  //根据输出模式确定要输出的电压
    Da_Write((unsigned char)(Voltage * 51.0));    //通过DAC输出电压(关键!)
}

3. :cross_mark: ADC转电压公式错误

初次错误代码:

RB2=Ad_Read(0x43);  // ❌ 直接赋值,没有转换
Voltage=(Output_Mode==0)?2:RB2;  // ❌ RB2是0-255,不是实际电压!

错误原因:

  • Ad_Read() 返回的是8位ADC的数字量,范围:0-255

  • PCF8591的参考电压是5V

  • 转换公式:实际电压 = ADC值 × 5.0 ÷ 255

  • 直接赋值会导致:

    • RB2的值是0-255,而不是0-5V的实际电压

    • 数码管显示错误(显示超大的数字)

    • DAC输出错误

数值验证(假设电位器输出3.41V):

❌ 错误:
Ad_Read(0x43) = 174 (ADC采样值)
RB2 = 174 (直接赋值)
Voltage = 174 (Output_Mode=1时)
数码管显示:U _ _ _ _ 174.00  ❌ 完全错误!
​
✓ 正确:
Ad_Read(0x43) = 174
RB2 = 174 × 5.0 ÷ 255 = 3.41V
Voltage = 3.41V
数码管显示:U _ _ _ _ 3.41  ✓ 正确!

正确代码(两种等价写法):

// 方法1: 标准公式(推荐,易理解)
RB2 = Ad_Read(0x43) * 5.0 / 255.0;

// 方法2: 简化公式(更高效)
RB2 = Ad_Read(0x43) / 51.0;  // 255÷5 = 51

转换对照表:

ADC值 标准公式 简化公式 实际电压
0 0×5/255 0/51 0.00V
51 51×5/255 51/51 1.00V
102 102×5/255 102/51 2.00V
174 174×5/255 174/51 3.41V
255 255×5/255 255/51 5.00V

4. :cross_mark: DAC输出转换公式错误

初次错误代码:

Da_Write((unsigned char)Voltage);  // ❌ 直接转换,范围错误

错误原因:

  • Voltage 的范围是 0-5V (float类型)

  • Da_Write() 需要的参数是 0-255的数字量

  • 直接转换 (unsigned char)Voltage

    • Voltage=2.0V → (unsigned char)2.0 = 2

    • Voltage=3.41V → (unsigned char)3.41 = 3

  • 转换后的值只有0-5,而不是0-255

  • 导致DAC输出电压只有实际电压的1/51

数值验证(假设Voltage=2.0V):

❌ 错误转换:
Voltage = 2.0V
(unsigned char)Voltage = 2
Da_Write(2)
实际输出电压 = 2 × 5.0 ÷ 255 = 0.039V  ❌ 应该输出2.0V!

✓ 正确转换:
Voltage = 2.0V
(unsigned char)(Voltage × 255 / 5.0) = (unsigned char)(2.0 × 51) = 102
Da_Write(102)
实际输出电压 = 102 × 5.0 ÷ 255 = 2.0V  ✓ 正确!

正确代码(两种等价写法):

// 方法1: 标准公式(推荐,易理解)
Da_Write((unsigned char)(Voltage * 255.0 / 5.0));

// 方法2: 简化公式(更高效)
Da_Write((unsigned char)(Voltage * 51.0));  // 255÷5 = 51

关键点:

  • :warning: 括号位置非常重要:

    • :cross_mark: (unsigned char)Voltage * 51.0 → 先转换再乘,错误!

    • (unsigned char)(Voltage * 51.0) → 先乘再转换,正确!

  • :warning: ADC和DAC的转换是互逆的:

    • ADC: 数字量 → 电压:V = ADC ÷ 51

    • DAC: 电压 → 数字量:DAC = V × 51

DAC转换对照表:

电压 标准公式 简化公式 DAC值
0.00V 0×255/5 0×51 0
1.00V 1×255/5 1×51 51
2.00V 2×255/5 2×51 102
3.41V 3.41×255/5 3.41×51 174
5.00V 5×255/5 5×51 255

5. :warning: 电压显示重复计算问题(可优化)

当前代码:

Seg_Buf[5]=(unsigned int)(Voltage*100)/100%10+',';
Seg_Buf[6]=(unsigned int)(Voltage*100)/10%10;
Seg_Buf[7]=(unsigned int)(Voltage*100)%10;

潜在问题:

  • 每行都重复计算 (unsigned int)(Voltage*100),共计算3次

  • 如果在计算过程中发生中断,Voltage被AD_DA()修改,可能导致三位数字不一致

  • 代码可读性差,不符合DRY原则(Don’t Repeat Yourself)

改进建议(使用临时变量):

case 1:
{
    unsigned int temp;  // 临时变量,只计算一次

    Seg_Buf[0]=12;
    Seg_Buf[1]=10;
    Seg_Buf[2]=10;
    Seg_Buf[3]=10;
    Seg_Buf[4]=10;

    temp = (unsigned int)(Voltage * 100);  // ✓ 只计算一次
    Seg_Buf[5] = temp/100%10 + ',';
    Seg_Buf[6] = temp/10%10;
    Seg_Buf[7] = temp%10;
}
break;

优势:

  • ✓ 避免重复计算,提高效率

  • ✓ 确保三位数字来自同一次计算,显示一致

  • ✓ 代码可读性更好


总结

最严重的错误(会导致功能完全无法工作或不符合题目要求):

  1. 缺少DAC输出调用 - 题目核心功能缺失,评分时可能直接零分

  2. ADC转电压公式错误 - 显示数值完全错误,超出0-5V范围

  3. 电压界面提示符错误 - 界面标识错误,不符合题目要求

中等错误(会导致功能异常):

  1. 电压显示计算精度丢失 - 小数部分全部丢失,显示错误

  2. DAC输出转换公式错误 - 输出电压只有应该值的1/51

轻微问题(可优化,不影响基本功能):

  1. 电压显示重复计算 - 效率低,可能有极小概率的显示不一致问题

学到的经验

1. 类型转换的顺序很重要

  • :cross_mark: (unsigned char)Voltage * 100 - 先转换再乘,精度丢失

  • (unsigned char)(Voltage * 100) - 先乘再转换,保留精度

  • 口诀: 浮点运算做完再转整数

2. ADC/DAC芯片的双重功能要理解

  • PCF8591既有ADC又有DAC,是两个独立的功能

  • ADC(读取)≠ DAC(输出),不要混淆

  • 题目要求"通过DAC输出",必须调用Da_Write()

3. 数字量和实际物理量的转换公式

  • ADC/DAC都是数字量(0-255),需要转换为实际电压(0-5V)

  • ADC转电压: V = ADC ÷ 51V = ADC × 5 ÷ 255

  • 电压转DAC: DAC = V × 51DAC = V × 255 ÷ 5

  • 记忆技巧: 255÷5=51,记住51这个转换系数

4. 仔细阅读题目要求

  • 题目明确说"通过其 DAC 功能输出该电压值"

  • 不要以为只读取ADC就够了,输出DAC也是必须的

5. 代码优化的基本原则

  • DRY原则: 不要重复计算,使用临时变量

  • 括号原则: 复杂表达式加括号,明确优先级

  • 原子性原则: 相关的多位数据应该来自同一次计算,避免中断导致的不一致


生成时间: 2026-02-06
蓝桥杯第十届省赛代码调试总结