大同市网站建设_网站建设公司_后端工程师_seo优化
2026/1/3 2:46:21 网站建设 项目流程

**

蓝桥杯单片机备赛指南第十三讲:IIC 总线与PCF8591 AD DA 转换

**

1. IIC 总线与PCF8591 硬件原理

1.1 IIC 通信协议(软件模拟)

IIC (Inter-Integrated Circuit) 是一种双线串行总线。

  • SCL (P2.0):时钟线。

  • SDA (P2.1):数据线。

时序核心(死记硬背)

  • 起始(Start):SCL 高期间,SDA 下降沿。

  • 停止(Stop):SCL 高期间,SDA 上升沿。

  • 应答(ACK):发送8 位后,第9 个时钟周期SDA 被拉低。

1.2 PCF8591 芯片详解

  • 设备地址:写地址0x90,读地址0x91

  • 控制字(Control Byte)

    • 通道选择:Bit 0-1 (00=AIN0, 01=AIN1, 10=AIN2, 11=AIN3)。

    • DA 使能:Bit 6 (1=开启模拟输出)。

    • 常用控制字

      • 0x01: 读光敏电阻(AIN1)。

      • 0x03: 读电位器Rb2 (AIN3)。

      • 0x40: 仅启用DAC 输出。


2. 进阶底层驱动模块

2.1 基础外设驱动(LED.c更新版)

重点:增加了BeepRelay,且所有函数均采用static变量+ 位运算,确保互不干扰。

#include"LED.h"// LED 单灯控制 (非破坏性)voidLED_Disp(unsignedcharaddr,bit enable){staticunsignedcharTemp=0x00;staticunsignedcharTemp_Old=0xFF;if(enable)Temp|=(0x01<<addr);elseTemp&=~(0x01<<addr);if(Temp!=Temp_Old){P0=~Temp;// 共阳极,取反输出P2=P2&0x1F|0x80;// 打开 Y4 (LED)P2&=0x1F;// 锁存Temp_Old=Temp;}}// 蜂鸣器控制 (非破坏性)voidBeep(bit enable){staticunsignedcharTemp=0x00;// 0x00 表示初始关闭状态 (假设 ULN2003 输入0为关)staticunsignedcharTemp_Old=0xFF;// 蜂鸣器通常接在 P0.6 (0x40)if(enable)Temp|=0x40;// 置 1 开启elseTemp&=~0x40;// 置 0 关闭if(Temp!=Temp_Old){P0=Temp;// 直接输出 (ULN2003 驱动)P2=P2&0x1F|0xA0;// 打开 Y5 (蜂鸣器/继电器)P2&=0x1F;Temp_Old=Temp;}}// 继电器控制 (非破坏性)voidRelay(bit flag){staticunsignedchartemp=0x00;staticunsignedchartemp_old=0xff;// 继电器通常接在 P0.4 (0x10)if(flag)temp|=0x10;elsetemp&=~0x10;if(temp!=temp_old){P0=temp;P2=P2&0x1F|0xA0;// 打开 Y5P2&=0x1F;temp_old=temp;}}

2.2 IIC 与PCF8591 读写(iic.c补全)

官方提供的代码包通常只有基础时序,必须熟练默写以下两个函数。

#include"iic.h"#include"intrins.h"// ... (I2CStart, I2CStop, I2CSendByte 等基础时序略,参考官方文件) ...// === 1. PCF8591 A/D 读取函数 ===// addr: 通道号 (0x01 光敏, 0x03 电位器)unsignedcharAd_Read(unsignedcharaddr){unsignedchartemp;I2CStart();I2CSendByte(0x90);// 写设备地址I2CWaitAck();I2CSendByte(addr);// 写控制字 (选择通道)I2CWaitAck();I2CStart();// 重新起始I2CSendByte(0x91);// 读设备地址I2CWaitAck();temp=I2CReceiveByte();// 读取数据I2CSendAck(1);// 发送非应答 (1),结束读取I2CStop();returntemp;}// === 2. PCF8591 D/A 写入函数 ===// dat: 输出电压对应的数字量 (0~255)voidDa_Write(unsignedchardat){I2CStart();I2CSendByte(0x90);// 写设备地址I2CWaitAck();I2CSendByte(0x40);// 写控制字 (0x40 = 启用 DAC)I2CWaitAck();I2CSendByte(dat);// 发送 DAC 数据I2CWaitAck();I2CStop();}

3. 功能设计要求(电压采集装置)

基于**《蓝桥杯模块训练- PCF8591.pdf》**原题整理。

3.1 数码管显示

系统包含两个界面,通过S4 切换。

  1. 电压显示界面(界面U)

    • 内容:显示电位器RB2 (AIN3) 的实时电压。

    • 格式U 3.41(保留两位小数)。

    • 细节:提示符’U’ 在第1 位,数据在6-8 位,中间熄灭。

  2. 电压输出界面(界面F)

    • 内容:显示当前DAC 输出端的电压值。

    • 格式F 2.50

    • 细节:提示符’F’ 在第1 位。

3.2 按键逻辑

  • S4 (界面切换):在U(显示) 和F(输出) 界面间循环。

  • S5 (模式切换):切换DAC 的输出模式。

    • 模式1 (跟随):输出电压= RB2 输入电压。

    • 模式2 (固定):输出电压固定为2.0V。

  • S6 (LED 开关):开启或关闭LED 指示功能。关闭时所有LED 熄灭。

3.3 LED 指示逻辑(修正版)

根据PDF 任务书要求:

  1. L1:指示当前在电压显示界面

  2. L2:指示当前在电压输出界面

  3. L3:当测量电压V>=3.5VV >= 3.5VV>=3.5V2.5V>V>=1.5V2.5V>V >= 1.5V2.5V>V>=1.5V时点亮。

  4. L4:DAC 输出固定电压(2.0V)时,L4 熄灭,DAC 输出电压跟 随 RB2 电位器输出电压变化时,L4 点亮。

  • S6 限制:若S6 关闭了指示功能,L1-L4 必须全部熄灭。

4. 核心代码解答(main.c)

#include<STC15F2K60S2.H>#include"Init.h"#include"Key.h"#include"LED.h"#include"Seg.h"#include"iic.h"// === 变量声明 ===typedefunsignedcharu8;typedefunsignedintu16;u8 Key_Slow_Down=0;u16 Seg_Slow_Down=0;u8 LED_Seg_Pos=0;// 数码管/LED 扫描指针// 显存与状态u8 Seg_Buf[8]={10,10,10,10,10,10,10,10};u8 Seg_Point[8]={0,0,0,0,0,0,0,0};u8 LED_Buf[8]={0,0,0,0,0,0,0,0};// LED 状态缓存 (1亮 0灭)u8 Key_Val,Key_Old,Key_Down,Key_Up;// 业务标志位bit Seg_Mode=0;// 0:电压显示界面(U), 1:电压输出界面(F)bit Mode1_Flag=0;// DAC模式: 0=固定2.0V, 1=跟随RB2bit LED_Flag=1;// LED总开关: 1=开, 0=关 (默认开)bit Seg_Flag=1;// 数码管总开关 (题目未要求按键控制,保持常开)floatVoltage=0;// 测量电压 (RB2)floatOutput_Voltage=2.0;// DAC 输出电压// === 按键逻辑 ===voidKey_Proc(){if(Key_Slow_Down)return;Key_Slow_Down=1;Key_Val=Key_Read();Key_Down=Key_Val&(Key_Val^Key_Old);Key_Old=Key_Val;switch(Key_Down){case4:// S4: 界面切换Seg_Mode^=1;break;case5:// S5: DAC 输出模式切换Mode1_Flag^=1;break;case6:// S6: LED 功能开关LED_Flag^=1;break;}}// === 数据处理与显示 ===voidSeg_Proc(){u8 i;if(Seg_Slow_Down)return;Seg_Slow_Down=1;// 1. 读取 A/D (RB2 -> AIN3)// 算法: Val / 255.0 * 5.0 => Val / 51.0Voltage=Ad_Read(0x03)/51.0;// 2. 处理 D/A 输出if(Mode1_Flag)Output_Voltage=Voltage;// 跟随模式elseOutput_Voltage=2.0;// 固定模式Da_Write((u8)(Output_Voltage*51.0));// 写入 DAC// 3. 数码管显示更新if(Seg_Flag){// 熄灭无关位Seg_Buf[1]=Seg_Buf[2]=Seg_Buf[3]=Seg_Buf[4]=10;// 设置小数点 (第5位点亮,对应 xxx.xx)for(i=0;i<8;i++)Seg_Point[i]=0;Seg_Point[5]=1;if(Seg_Mode==0)// 界面 U: 显示测量电压{Seg_Buf[0]=17;// 'U' (假设字模 17)Seg_Buf[5]=(u8)Voltage;// 个位Seg_Buf[6]=(u8)(Voltage*10)%10;// 十分位Seg_Buf[7]=(u16)(Voltage*100)%10;// 百分位}else// 界面 F: 显示输出电压{Seg_Buf[0]=16;// 'F' (假设字模 16)Seg_Buf[5]=(u8)Output_Voltage;Seg_Buf[6]=(u8)(Output_Voltage*10)%10;Seg_Buf[7]=(u16)(Output_Voltage*100)%10;}}else{for(i=0;i<8;i++)Seg_Buf[i]=10;}}// === LED 逻辑控制 ===voidLED_Proc(){u8 i;if(LED_Flag)// 总开关打开{// L1/L2: 界面指示 (互斥)LED_Buf[0]=!Seg_Mode;// Mode=0(U) -> L1亮LED_Buf[1]=Seg_Mode;// Mode=1(F) -> L2亮// L3/L4/L5: 电压范围指示 (基于测量电压 Voltage)LED_Buf[2]=(Voltage<1.5);LED_Buf[3]=(Voltage>=1.5&&Voltage<2.5);LED_Buf[4]=(Voltage>=2.5);// L6-L8: 熄灭LED_Buf[5]=LED_Buf[6]=LED_Buf[7]=0;}else// 总开关关闭{for(i=0;i<8;i++)LED_Buf[i]=0;}}// === 定时器中断 (驱动层) ===voidTimer0_Server()interrupt1{// ... 重装载代码 (1ms) ...if(++Key_Slow_Down==10)Key_Slow_Down=0;if(++Seg_Slow_Down==200)Seg_Slow_Down=0;// 硬件扫描if(++LED_Seg_Pos==8)LED_Seg_Pos=0;// 驱动数码管Seg_Disp(LED_Seg_Pos,Seg_Buf[LED_Seg_Pos],Seg_Point[LED_Seg_Pos]);// 驱动 LED (调用更新后的非破坏性驱动)LED_Disp(LED_Seg_Pos,LED_Buf[LED_Seg_Pos]);}voidmain(){// System_Init(); // 需包含初始化// Timer0_Init(); // 需包含定时器初始化while(1){Key_Proc();Seg_Proc();LED_Proc();}}

5. 总结速查表(Cheat Sheet)

模块关键代码/公式备注
电压计算V = Val / 51.0255对应5V,系数为51
DAC 输出Val = V * 51.0逆运算写入PCF8591
通道地址0x03(电位器),0x01(光敏)一定要看电路图确认
DAC 控制字0x40写数据前必须发送此控制字
LED 逻辑LED_Buf[0] = !Seg_Mode利用逻辑非运算实现互斥指示
非破坏驱动`P2 & 0x1F0x80`

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询