第七周 PCF8591

:memo: 蓝桥杯模块训练笔记:PCF8591 电压采集与输出

1. 核心功能概述

本系统主要实现两个功能:

  1. ADC 采集:读取板载电位器 (RB2) 的电压值。

  2. DAC 输出:根据模式,输出固定电压或跟随采集电压。

    同时配合数码管显示界面切换和 LED 状态指示。


2. 代码与题目需求对照解析

2.1 初始状态设置 (Initial State)

题目要求:

“初始状态上电默认处于电压测量状态… PCF8591 DAC 芯片输出固定电压值 2.0V。” 1

代码实现:

C

 // 1. 定义全局变量时直接初始化
 unsigned char seg_disp_mode; // 默认为0,即电压测量显示界面(U)
 bit voltage_mode = 1;        // 【关键】初始化为1,代表固定电压模式
 ​
 void seg_proc() {
     // ...
     // 2. 三目运算符决定输出值:如果 mode 是 1,输出 2.0,否则跟随 voltage
     voltage_out = voltage_mode ? 2 : voltage; 
     // ...
 }

解析:

通过将 voltage_mode 显式初始化为 1,保证了上电第一刻 voltage_out 就是 2.0V,完美符合题目对初始状态的要求。


2.2 AD/DA 核心驱动 (ADC & DAC)

题目要求:

“电位器 RB2 输出的电压值” 22"DAC 输出电压…" 3

代码实现:

C

 // 在 seg_proc 中:
 voltage = ad_read(0x43) / 51.0; 
 ​
 // 在 led_proc 中:
 da_write(voltage_out * 51);

解析与防坑:

  • ad_read(0x43) 的含义:

    • 0x03 代表读取通道 3 (AIN3,即电位器 RB2)。

    • 0x40 代表 允许 DAC 输出 (DA Output Enable)

    • 重点: 如果只写 0x03,在读取电压的同时会关闭 DA 输出。写成 0x43 是为了保证“一边读电压,一边还能正常输出电压”。

  • / 51.0\* 51 8位 AD/DA 的分辨率是 255 (5V)。即 1V = 51 (255/5)。


2.3 按键逻辑映射 (Key Logic)

S4:界面切换

题目要求:

“S4…切换选择电压显示界面和电压输出界面” 4

代码实现:

C

 case 4:
     if(++seg_disp_mode == 2) seg_disp_mode = 0;
 break;

解析: 利用 seg_disp_mode 在 0 和 1 之间循环,0 代表 U 界面,1 代表 F 界面。

S5:DAC 模式切换

题目要求:

“S5…DAC 输出电压跟随电位器…再次按下 S5,DAC 输出固定电压 2.0V” 5

代码实现:

C

 case 5:
     voltage_mode ^= 1; // 0和1之间取反
 break;

解析: voltage_mode 变量控制了数据源的选择逻辑。

S6 & S7:显示功能控制

题目要求:

“S6…关闭或打开 LED 指示灯” 6"S7…关闭或打开数码管显示" 7

代码实现:

C

 case 6: led_flag ^= 1; break; // 翻转 LED 标志位
 case 7: seg_flag ^= 1; break; // 翻转 数码管 标志位
 ​
 // 在中断函数 timer0serve 中生效:
 if(seg_flag) seg_disp(...); // 标志位为 1 才扫描显示
 if(led_flag) led_disp(...); 

解析: 使用标志位控制底层扫描函数的执行,是实现“整体熄灭”最干净的方法。


2.4 数码管显示逻辑 (Display)

题目要求:

“测量界面显示 U…输出界面显示 F” 8888"保留小数点后两位有效数字" 9999

代码实现:

C

 if(seg_disp_mode == 0) { // U 界面
     seg_buf[0] = 11; // 对应字模 'U'
     // 显示采集到的电压 voltage
     seg_buf[5] = (unsigned char)voltage % 10; 
     seg_buf[6] = (unsigned char)(voltage * 10) % 10;
     seg_buf[7] = (unsigned char)(voltage * 100) % 10;
 } else { // F 界面
     seg_buf[0] = 12; // 对应字模 'F'
     // 显示输出电压 voltage_out
     // ...同上处理 voltage_out ...
 }

解析:

  • 精度处理: 浮点数取小数位的经典算法。例如 2.34V:

    • 个位:2.34 % 10 (强转int) = 2

    • 十分位:23.4 % 10 = 3

    • 百分位:234 % 10 = 4


2.5 LED 逻辑 (Logic Operation)

L3:电压区间指示(重点逻辑)

题目要求:

1.5V ≤ V < 2.5V 点亮; V ≥ 3.5V 点亮;其余熄灭 10

代码实现:

C

 ucled[2] = ((voltage >= 1.5 && voltage < 2.5) || (voltage >= 3.5));

解析:

这段代码直接把数学区间翻译成了 C 语言逻辑表达式:

  • 区间1:[1.5, 2.5)voltage >= 1.5 && voltage < 2.5

  • 区间2:[3.5, +∞)voltage >= 3.5

  • 逻辑或 || 连接两个区间。

L4:DAC 模式指示

题目要求:

“DAC 输出固定电压(2.0V)时,L4 熄灭…跟随…时,L4 点亮” 11

代码实现:

C

 ucled[3] = (voltage_mode == 0);

解析:

  • voltage_mode 为 1 (固定模式) → (1==0) 为假 → L4 熄灭。

  • voltage_mode 为 0 (跟随模式) → (0==0) 为真 → L4 点亮。


3. 核心知识点总结

  1. 标志位初始化的重要性: 全局变量如果不初始化,默认为 0。对于要求“上电默认开启/特定模式”的题目,必须手动赋值(如 bit voltage_mode = 1)。

  2. PCF8591 控制字技巧: 记住 0x40 是 DA 使能位。读 AD 时如果想保持 DA 输出,一定要加上这个值(如 0x01 | 0x400x03 | 0x40)。

  3. 分离采集与显示:

    • 采集 (seg_proc): 处理数据,计算 voltage_out

    • 显示 (timer0serve): 负责刷新数码管和 LED。

    • 这样逻辑分层清晰,不会因为显示延时影响数据计算。

  4. 按键消抖与状态机: 代码使用了 switch(key_down) 结构,这是处理多按键功能最清晰的方式。

4. 你的代码评价

这份代码结构非常优秀,属于高分模板

  • 优点: 逻辑变量命名规范(voltage_mode, led_flag),代码可读性强,完美利用了 C 语言的逻辑运算符简化代码(如 LED 部分)。

  • 适用性: 直接可以作为蓝桥杯省赛、国赛的基础框架使用。

1 个赞