第六周蓝桥杯总结DS18B20&PCF8591

第六周 DS18B20模块笔记

  • 关闭外设函数编写

    • 基本流程:先关闭 LED,点亮 LED 是 0 亮,之后打开 P2,P2 先与上 0X7F 再或上 0X80 选中 Y4C 即 LED 通道,之后 P2 与等于 0XEF 关闭通道。

    • 关闭继电器和蜂鸣器:与关闭 LED 同理,将相关参数改为 0X00 和 0XA0,选中外设通道后关闭通道。

    • 函数声明:将该函数在.h 文件里声明,注意使用英文分号。

  • LED 底层代码编写

    • 引用头文件:引用相关头文件,在.c 文件中先引用自身的.h 文件。

    • 函数参数与变量:采用两个入口参数的底层,一个是地址,一个是使能标志位,定义两个局部静态变量。

    • 点亮与熄灭操作:使能时,temp 或等于 0X01 左移相应位数点亮 LED;否则与等于取反熄灭 LED。

    • 数据刷新处理:比较 time 值和 time old 值,若不相等则将 time 值传给 old 值,且对 time 取反,避免出现电流声。

    • 函数声明:将该函数声明到.h 文件中。

  • 按键底层代码编写

    • 引用头文件:引用相关头文件和自身的.h 文件。

    • 返回值与变量初始化:函数需要返回值,定义变量 time 并初始化为 0,否则按键可能出现问题。

    • 按键判断:以 P44(仿真中为 P37)选中第一行,逐行判断按键,如 P33 等于 0 时 time 值等于 4 等,复制粘贴修改选中行和对应数字完成其他行判断。

    • 返回值与声明:将 time 值返回,在.h 文件中声明该函数。

  • 数码管底层代码编写

    • 引用头文件与声明数组:引用相关头文件和自身的.h 文件,在.h 文件中声明段选和位选两个数组,段选复制官方资料包中的共阳极数码管段选表,位选按 8421 规律编写。

    • 函数参数与消隐:函数有断码、位码、是否需要小数点三个入口参数,先进行消隐操作,P2 与上 0XEF 或上 0X1 选中后再 P2 与等于 0XEF 关闭。

    • 位选与段选:给位选数组中相应位赋值,选中位选(0XC0),再给段选,若需要小数点,P0 等于数码管段选后与上 0X7F

模块程序设计训练

新建文件夹

添加user driver

修改设置

添加底层

编写init.c

void 初始化

关闭所有的外设

然后

然后可以开始写我们的led模块了

同样的新建.c .h文件,引用头文件

seg底层也是一样

大模板最后就是这样的

e <STC15F2K60S2.H>
#include <key.h>
#include <led.h>
#include <seg.h>
#include <init.h>
​
​
/*变量声明*/
unsigned char Key_Slow_Down;//按键减速变量
unsigned char Key_Val,Key_Old,Key_Down,Key_Up;//按键扫描变量
unsigned int Seg_Slow_Down;//数码管减速专用变量
unsigned char Seg_Pos;//数码管扫描专用变量
unsigned char Seg_Buf[8] = {1,2,3,4,5,6,7,8};//数码管数据存放数组,默认全部熄灭
unsigned char Seg_Point[8] = {0,0,0,0,0,0,0,0};//数码管小数点数组,默认熄灭
unsigned char ucLed[8]={0,0,0,0,0,0,0,0};//led数据存放数组
​
​
/* 按键处理函数*/
void Key_Proc()
{
    if(Key_Slow_Down) return;
    Key_Slow_Down =1;
    
    Key_Val = Key_Read();//读取键码
    Key_Down = Key_Val &(Key_Old ^ Key_Val);//下降沿
    Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//上升沿
    Key_Old = Key_Val;//辅助扫描专属变量
    
    
    
    
}
​
/* 信息处理函数 */
void Seg_Proc()
{
    if(Seg_Slow_Down) return;
    Seg_Slow_Down =1;
​
}
​
/* 其他显示函数 */
void Led_Proc()
{
    
    
}
​
/*定时器0初始化函数*/
void Timer0_Init(void)      //1毫秒@12.000MHz
{
    AUXR &= 0x7F;           //定时器时钟12T模式
    TMOD &= 0xF0;           //设置定时器模式
    TL0 = 0x18;             //设置定时初始值
    TH0 = 0xFC;             //设置定时初始值
    TF0 = 0;                //清除TF0标志
    TR0 = 1;                //定时器0开始计时
    ET0 = 1;
    EA = 1;
}
​
/* 定时器0中断服务函数*/
void Timer0Server() interrupt 1
{   
  if(++Key_Slow_Down == 10)  Key_Slow_Down = 0;
    if(++Seg_Slow_Down == 500)  Seg_Slow_Down = 0;
    if(++Seg_Pos == 8 )  Seg_Pos = 0;
    Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
    Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
}
​
/*main函数 */
void main()
{
    Sys_Init();
    Timer0_Init();
    while(1)
    {
        Key_Proc();
        Seg_Proc();
        Led_Proc();
    }
}

搭配上init,led,key,seg四个底层的.c.h文件

出现的问题

1 为什么编译后仿真的显示有问题?

按道理来说应该显示12345678,因为在显示的数组seg_buf是12345678,但是是可以编译进去的

查找和显示有关的代码,底层

问题就出在底层这个段选的数组里面

写了两个0x92,删去一个

但是12的显示还是有问题的,继续查错

image-20260129155848548

对数组的前两位分别改成10,2和1,10

发现都显示在了第一位

考虑是点亮的数码管的位置有错误,找到位选的数组

发现第一和第二的位置的位码是一样的,改成 0x01 0x02后,编译成功

强制转换,让ucled等于 tem/ctrol 如果结果大于1, 强制转换后1就会点亮,如果小于1,强制转换之后就是0,就是灭

第89 10 讲笔记

蓝桥杯PCF8591芯片及应用技术总结

一、会议核心主题与目的

本次会议聚焦蓝桥杯竞赛中对PCF8591芯片的考核要求,系统讲解了该芯片的工作原理I2C通信协议以及AD/DA转换的实现方法。会议旨在帮助参赛者掌握模拟信号采集与数字控制的核心技术,为应对竞赛中相关的硬件编程题目打下坚实基础。

二、PCF8591芯片核心知识详解

2.1 芯片基础特性

PCF8591是一款集成了模数转换器数模转换器的单芯片解决方案,通过I2C总线进行通信。

  • 接口特性:采用双线制串行通信(SDA数据线、SCL时钟线)

  • 转换精度:8位分辨率,提供4路模拟输入和1路模拟输出

  • 供电电压:典型工作电压为5V,范围2.5V–6V

2.2 硬件地址设计

PCF8591通过硬件地址引脚(A0、A1、A2)进行设备寻址:

  • 地址格式:固定部分(1001) + 可编程部分(A2,A1,A0)

  • 读写控制:地址字节最后一位为读写位(0-写操作,1-读操作)

  • 典型地址:写地址0x90,读地址0x91

2.3 控制字节结构

控制字节用于配置芯片工作模式,具体结构如下:

功能 说明
D7 固定为0 必须设置为0
D6 模拟输出使能 0-允许DA输出,1-禁止输出
D5-D4 输入模式选择 00-4路单端输入
D3 自动增量标志 1-启用通道自动递增
D2-D1 通道选择 00-通道0,01-通道1,10-通道2,11-通道3
D0 保留位 通常设为0

常用配置

  • 光敏电阻(AIN1):控制字0x01

  • 滑动变阻器(AIN3):控制字0x03

  • DA输出使能:控制字0x40

三、I2C协议核心原理

3.1 协议基础

I2C是一种同步串行通信协议,由飞利浦公司开发,采用两线制设计:

  • SDA:串行数据线,用于数据传输

  • SCL:串行时钟线,提供同步时钟信号

  • 通信特性:支持多主从设备连接,总线空闲时保持高电平

3.2 关键信号时序

I2C协议包含三种基本信号:

  1. 起始信号:SCL为高电平时,SDA由高变低

  2. 停止信号:SCL为高电平时,SDA由低变高

  3. 应答信号:每传输完一个字节,接收方产生低电平应答

四、AD/DA转换原理与实现

4.1 转换基本原理

  • AD转换:将连续模拟信号转换为离散数字量(0-255)

  • DA转换:将数字量转换为模拟电压输出

  • 分辨率计算:8位AD的分辨率 = 参考电压/255

4.2 代码实现框架

以下是PCF8591的基本操作函数框架:

AD转换函数

unsigned char AD_Read(unsigned char channel)
{
    I2C_Start();
    I2C_SendByte(0x90);       // 发送写地址
    I2C_WaitAck();
    I2C_SendByte(channel);    // 发送控制字选择通道
    I2C_WaitAck();
    I2C_Stop();
    
    I2C_Start();
    I2C_SendByte(0x91);       // 发送读地址
    I2C_WaitAck();
    temp = I2C_ReceiveByte(); // 读取转换结果
    I2C_SendAck(1);
    I2C_Stop();
    
    return temp;
}

DA转换函数

void DA_Write(unsigned char data)
{
    I2C_Start();
    I2C_SendByte(0x90);
    I2C_WaitAck();
    I2C_SendByte(0x40);       // DA输出使能
    I2C_WaitAck();
    I2C_SendByte(data);       // 发送数字量
    I2C_WaitAck();
    I2C_Stop();
}

五、蓝桥杯竞赛实操要点

5.1 电压换算公式

竞赛中常用的电压换算关系:

  • 数字量转电压:电压值 = (数字量 × 参考电压) / 255

  • 电压转数字量:数字量 = (目标电压 × 255) / 参考电压

5.2 常见问题与解决方案

  1. 通信失败:检查I2C总线初始化、地址设置是否正确

  2. 数据错误:验证控制字配置、时序延时是否合适

  3. 通道选择错误:确认AIN1和AIN3对应的控制字

六、总结与备赛建议

本次会议系统梳理了PCF8591在蓝桥杯竞赛中的核心应用技术。建议参赛者:

  1. 深入理解I2C协议的工作原理和时序要求

  2. 熟练掌握AD/DA转换的编程实现方法

  3. 注重实践操作,通过实际调试加深理解

  4. 参考往届真题进行针对性训练

实际编写问题

行线引脚不同!实物用P44/P42,仿真用P37/P36!

Voltage_Read = AD_Read(0x43) / 51.0;//读取实时电压值
      Voltage_Output = Voltage_Mode ? Voltage_Read : 2;

这串代码实现了一个电压读取与可配置输出的逻辑,核心是使用ADC读取电压,并根据模式选择决定最终的输出电压值。下面这个表格能帮你快速理解两行代码各自的分工

代码行 核心功能 逻辑分解 关键点
Voltage_Read = AD_Read(0x43) / 51.0; 电压采样与转换 1. AD_Read(0x43):从PCF8591芯片的特定通道(0x43对应旋转电位器)读取ADC原始值(范围0-255)。 2. / 51.0:将原始值转换为实际电压值。因为255对应5V,所以每个数字量代表的电压为 5V / 255 ≈ 0.0196V。换算一下,255 / 5 = 51,因此除以51即可得到电压值(Voltage_Read = 原始值 / 51)。使用51.0是为了避免整数除法丢失小数精度。 实现模拟量到数字量的转换和标度变换。
Voltage_Output = Voltage_Mode ? Voltage_Read : 2; 输出模式选择 1. Voltage_Mode:这是一个标志位(Flag),它的值由按键操作设置(如代码中的case 5)。 2. ? :(三目运算符):这是一个条件判断。 - 如果 Voltage_Mode1(真),则 Voltage_Output等于 Voltage_Read(即输出跟随输入)。 - 如果 Voltage_Mode0(假),则 Voltage_Output等于固定值 2(即固定输出2V) 实现输出模式灵活切换

这串代码实现了一个电压读取与可配置输出的逻辑,核心是使用ADC读取电压,并根据模式选择决定最终的输出电压值。下面这个表格能帮你快速理解两行代码各自的分工:

代码行 核心功能 逻辑分解 关键点
Voltage_Read = AD_Read(0x43) / 51.0; 电压采样与转换 1. AD_Read(0x43):从PCF8591芯片的特定通道(0x43对应旋转电位器)读取ADC原始值(范围0-255)。 2. / 51.0:将原始值转换为实际电压值。因为255对应5V,所以每个数字量代表的电压为 5V / 255 ≈ 0.0196V。换算一下,255 / 5 = 51,因此除以51即可得到电压值(Voltage_Read = 原始值 / 51)。使用51.0是为了避免整数除法丢失小数精度。 实现模拟量到数字量的转换和标度变换。
Voltage_Output = Voltage_Mode ? Voltage_Read : 2; 输出模式选择 1. Voltage_Mode:这是一个标志位(Flag),它的值由按键操作设置(如代码中的case 5)。 2. ? :(三目运算符):这是一个条件判断。 - 如果 Voltage_Mode1(真),则 Voltage_Output等于 Voltage_Read(即输出跟随输入)。 - 如果 Voltage_Mode0(假),则 Voltage_Output等于固定值 2(即固定输出2V)。 实现输出模式灵活切换。

:light_bulb: 逻辑全景与应用场景

这个设计非常实用,常见于以下场景:

  • 设备调试与校准:固定输出一个已知电压(如2V),可以用来检验其他电路或测量工具是否正常工作。

  • 两种工作状态切换:例如,一个设备可以设置为“手动模式”(固定输出)和“自动模式”(跟随传感器输入),通过按键轻松切换。

总而言之,这段代码是一个简洁但功能完整的数据采集与输出控制模块,它负责将物理世界的模拟信号(电压)转换为单片机可以处理的数字信息,再根据用户指令,灵活地决定输出信号的特征。

需要除以51的原因:

这个操作是模数转换(ADC)后的标度变换,目的是将ADC读取的原始数字值转换为实际的电压值。它与温度和华氏度没有任何关系

其核心逻辑如下:

  1. 1.硬件前提:ADC的参考电压是 5V,且ADC是 8位精度(最大数值为255)。

  2. 2.计算原理

    • •每个数字量(1)代表的电压值 = 5V / 255 ≈ 0.0196V。

    • •因此,实际电压值 = 原始数值 × (5 / 255)

    • •将分数简化:5 / 255 = 1 / 51。所以,实际电压值 = 原始数值 / 51

简单来说,除以51是一个将数值范围从 0-255线性映射到电压范围 0V-5V的数学转换

总结

  1. AD 转换核心:读取 8 位数字量(0~255),通过/51.0换算为 0~5V 的实际电压值;

  2. DA 转换核心:将目标电压通过×51.0还原为 8 位数字量,写入芯片后输出对应模拟电压;

  3. 模式关联:DA 输出电压由Voltage_Output_Mode控制,可固定 2V 或跟随 AD 读取的电压值

在实时变化的电压场景下,AD/DA 转换的必要性,以及为什么先除以 51、又再乘以 51—— 本质上这是模拟量和数字量双向转换的必然过程,两个操作分别对应 “读模拟电压” 和 “输出模拟电压”,并不是无意义的重复

AD/DA 转换的必要性:单片机只能处理数字信号,而实时电压是模拟信号,必须通过 AD 把模拟电压转数字量(让单片机 “读得懂”),通过 DA 把数字量转模拟电压(让外部 “用得到”);

除以 51 的作用:把 AD 读取的数字量还原成实际电压值(数字→模拟,供单片机识别);

乘以 51 的作用:把目标电压值转换成 DA 能识别的数字量(模拟→数字,供 DA 输出)。

AD + 除以 51:让单片机看懂了外界的电压(数字量 → 实际电压值)。

乘以 51 + DA:让单片机表达出想要的电压(实际电压值 → 数字量,输出出去)