蓝桥杯单片机竞赛:PCF8591芯片I2C通信与ADC/DAC实战详解
2026/9/12 19:54:33 网站建设 项目流程

1. 项目概述:PCF8591在蓝桥杯中的核心地位

如果你正在准备蓝桥杯单片机或嵌入式的比赛,那么PCF8591这个芯片对你来说绝对不陌生。它几乎是历年真题和训练中绕不开的一个“常客”。这个小小的8位A/D和D/A转换器,看似简单,却串联起了模拟世界与数字世界的桥梁,是考察选手对I2C总线协议理解、数据采集与输出控制能力的绝佳载体。很多新手第一次接触它时,会被I2C的时序、地址、读写操作搞得一头雾水,调试半天发现ADC读数不对,或者DAC输出没反应。我自己带学生备赛时,发现大部分问题都出在对PCF8591的工作机制和I2C通信细节理解不透彻上。这个“Part_1”,我们就来彻底拆解PCF8591,从芯片手册的关键参数解读,到I2C通信的每一个字节含义,再到蓝桥杯竞赛板上的实战接线与基础代码框架,让你不仅会用,更能懂为什么这么用,为后续复杂的传感器融合应用打下坚实基础。

PCF8591是一个单电源、低功耗的8位CMOS数据采集器件,通过I2C总线与单片机通信。它集成了4路模拟输入(可配置为单端或差分)、1路模拟输出和一个振荡器。在蓝桥杯常见的CT107D等竞赛平台上,它通常被用来读取电位器的电压(ADC)或者控制LED的亮度(DAC),是完成“电压采集”、“灯光调光”、“信号发生器”等赛题的基础模块。理解它,就等于拿到了处理模拟信号的钥匙。

2. PCF8591芯片深度解析与I2C协议精讲

2.1 PCF8591内部结构与时序逻辑

要驾驭PCF8591,不能只停留在调用库函数的层面,必须深入其内部逻辑。芯片的核心是一个逐次逼近型(SAR)的ADC和一个R-2R梯形电阻网络的DAC。ADC的转换速率受内部振荡器控制,典型值约为11kHz,这意味着完成一次A/D转换大约需要90微秒,这个速度对于蓝桥杯大部分需要读取电位器、光敏电阻的赛题来说绰绰有余。

它的所有操作都通过I2C总线完成。I2C是一种两线制(串行数据线SDA,串行时钟线SCL)的同步串行总线,支持多主多从。PCF8591永远作为从设备。这里有一个关键点:PCF8591的I2C从地址是7位,固定为1001,加上三位由硬件引脚A2, A1, A0决定的地址位。在蓝桥杯官方板子上,这三个引脚通常都接地(逻辑0),所以完整的7位从地址是1001000,即0x48(十六进制)。但请注意,I2C协议在发送地址时,会将这7位地址左移一位,最低位表示读写方向(0写,1读)。因此,向PCF8591写入控制命令时,发送的器件地址字节是0x48 << 1 | 0 = 0x90;从PCF8591读取数据时,地址字节是0x48 << 1 | 1 = 0x91。很多同学代码出错,就是因为直接用了0x48去进行I2C操作,这是不对的。

控制PCF8591的核心是一个控制字节,它决定了芯片的工作模式。这个字节必须在每次启动转换或输出前,通过写操作发送给PCF8591。

名称功能描述蓝桥杯常见设置
7模拟输出使能1:启用模拟输出(DAC);0:关闭(高阻态)必须置1,否则DAC无输出
6模拟输入配置0:四路单端输入;1:三路差分输入通常为0(单端)
5,4通道选择00:通道0; 01:通道1; 10:通道2; 11:通道3根据你接的传感器选择
2自动增量标志1:每次读后通道号自动加1;0:固定通道多路巡检时设为1很方便
1,0保留必须设为00固定为00

例如,如果你想使能DAC输出,并读取连接在AIN0上的电位器电压(单端输入),那么控制字节就是:0x40(二进制0100 0000)。计算过程:第7位(DAC使能)=1,第6位(单端)=0,第5、4位(通道0)=00,第2位(自动增量)=0,第1、0位=00。合起来是0100 0000,即0x40

注意:控制字节的第7位(DAC使能)非常关键。即使你本次操作只想读取ADC,如果后续需要用到DAC,也建议在初始化时就将此位置1。否则,DAC输出引脚会保持高阻态,你无法控制电压输出。这是一个常见的坑点。

2.2 I2C通信时序的实战化理解

理解了地址和控制字节,我们来看一次完整的通信过程。I2C的时序由起始条件(S)、停止条件(P)、应答位(ACK)和数据传输组成。我们用蓝桥杯常用的STC15系列单片机(软件模拟I2C)来举例。

一次完整的“写入控制字节并启动ADC转换”的流程如下:

  1. 单片机(主)发送起始条件(S):在SCL高电平期间,SDA产生一个下降沿。
  2. 单片机发送从机地址字节(写):0x90。PCF8591收到后,会在第9个时钟周期将SDA拉低,发出应答(ACK)。
  3. 单片机发送控制字节:例如0x40。PCF8591再次应答。
  4. 单片机发送停止条件(P):在SCL高电平期间,SDA产生一个上升沿。此时,PCF8591才会根据收到的控制字节,开始对选定通道进行A/D转换。

一次完整的“读取ADC转换结果”的流程如下:

  1. 单片机发送起始条件(S)。
  2. 单片机发送从机地址字节(读):0x91。PCF8591应答。
  3. 单片机开始读取数据。这里有个重要细节:PCF8591在收到读地址并应答后,会立刻送出上一次A/D转换的结果。也就是说,你读到的数据是“旧的”。因此,标准的操作流程是:先发控制字节启动一次转换(写操作),稍作延时(等待转换完成,通常几十微秒到1毫秒足矣),然后再发起读操作,读取这次转换的新结果。
  4. 单片机读取一个字节数据后,不应答(发送NACK),然后发送停止条件。I2C协议规定,主设备在接收最后一个字节后,应发送NACK来通知从设备停止发送。

为什么读的时候要发NACK?因为PCF8591的ADC数据是8位的,一次只读一个字节。发送NACK告诉它:“我只要这一个,别发了。”然后紧跟停止条件结束通信。如果发了ACK,PCF8591会误以为主设备还想继续读,导致时序错乱。

3. 蓝桥杯平台实战:硬件连接与基础驱动编写

3.1 CT107D竞赛板硬件连接分析

在蓝桥杯官方指定的CT107D单片机制作训练平台上,PCF8591通常作为一个独立模块,通过排针与主板连接。你需要找到板上标有“PCF8591”或“ADC/DAC”的区域。关键的连接只有四根线:

  • VCC:接电源正极(通常是5V或3.3V,具体看板子说明,CT107D一般是5V)。
  • GND:接电源地。
  • SCL:接单片机的I2C时钟引脚。在STC15F2K60S2上,通常被映射到P2.0或P2.1,具体需查看板载原理图或赛事说明。
  • SDA:接单片机的I2C数据引脚。通常与SCL成对出现,例如P2.1。

此外,你还会看到:

  • AIN0~AIN3:4路模拟输入。通常AIN0连接一个可调电位器(Rb2),AIN1连接光敏电阻(LDR),AIN2连接热敏电阻(NTC),AIN3悬空或外接。这是赛题的经典配置。
  • AOUT:模拟输出。通常通过一个电阻连接到LED上,用于实现呼吸灯或调光效果。
  • A0, A1, A2:地址选择引脚。在官方板子上,它们通常被下拉到GND,所以地址就是000。

实操心得:上电前,务必用万用表蜂鸣档检查一下VCC和GND是否短路,SCL和SDA是否与单片机引脚正确连通。比赛时硬件故障虽少,但一旦出现,这种基础检查能帮你快速排除问题,节省大量时间。

3.2 软件模拟I2C底层驱动代码实现

蓝桥杯比赛中,为了代码的清晰度和可控性,通常不直接使用硬件I2C模块(况且有些型号可能没有),而是采用软件模拟的方式。下面给出一个针对STC15系列、经过实战检验的I2C底层驱动代码框架。

首先,定义引脚:

sbit I2C_SCL = P2^0; // 假设SCL接P2.0 sbit I2C_SDA = P2^1; // 假设SDA接P2.1

然后是核心的时序函数:

// I2C起始条件 void I2C_Start(void) { I2C_SDA = 1; I2C_SCL = 1; Delay5us(); // 微小延时,维持建立时间 I2C_SDA = 0; Delay5us(); I2C_SCL = 0; // 钳住总线,准备发送数据 } // I2C停止条件 void I2C_Stop(void) { I2C_SDA = 0; I2C_SCL = 1; Delay5us(); I2C_SDA = 1; Delay5us(); } // I2C发送一个字节(含应答检查) bit I2C_SendByte(unsigned char dat) { unsigned char i; bit ack; for (i=0; i<8; i++) { I2C_SDA = (dat & 0x80) ? 1 : 0; // 发送最高位 dat <<= 1; I2C_SCL = 1; Delay5us(); I2C_SCL = 0; Delay5us(); } // 读取应答 I2C_SDA = 1; // 释放SDA线,准备接收 I2C_SCL = 1; Delay5us(); ack = I2C_SDA; // 读取从机应答位,0为应答,1为非应答 I2C_SCL = 0; Delay5us(); return ack; // 返回应答状态,0表示成功 } // I2C接收一个字节 unsigned char I2C_RecvByte(void) { unsigned char i, dat = 0; I2C_SDA = 1; // 确保主设备释放SDA for (i=0; i<8; i++) { dat <<= 1; I2C_SCL = 1; Delay5us(); if (I2C_SDA) dat |= 0x01; // 读取数据位 I2C_SCL = 0; Delay5us(); } return dat; } // 主机发送非应答信号(用于结束读取) void I2C_SendNAck(void) { I2C_SDA = 1; // 非应答信号 I2C_SCL = 1; Delay5us(); I2C_SCL = 0; Delay5us(); I2C_SDA = 0; // 拉低SDA,为后续操作准备 }

Delay5us()是一个大约5微秒的延时函数,需要根据你的主频精确调整。可以使用STC-ISP软件的延时计算器生成。

有了底层驱动,我们就可以封装PCF8591的读写函数了。

#define PCF8591_WRITE_ADDR 0x90 // 写地址 #define PCF8591_READ_ADDR 0x91 // 读地址 // 向PCF8591写入控制字节 bit PCF8591_WriteCtrlByte(unsigned char ctrl) { I2C_Start(); if (I2C_SendByte(PCF8591_WRITE_ADDR)) { // 发送写地址失败 I2C_Stop(); return 1; // 返回错误 } if (I2C_SendByte(ctrl)) { // 发送控制字节失败 I2C_Stop(); return 1; } I2C_Stop(); return 0; // 成功 } // 从PCF8591读取ADC值 bit PCF8591_ReadADC(unsigned char ctrl, unsigned char *adc_val) { // 步骤1:写入控制字节,启动一次转换 if (PCF8591_WriteCtrlByte(ctrl)) return 1; // 步骤2:等待转换完成(非常重要!) Delay_ms(1); // 延时1ms,远大于转换所需的90us,确保转换完成 // 步骤3:发起读操作,读取转换结果 I2C_Start(); if (I2C_SendByte(PCF8591_READ_ADDR)) { I2C_Stop(); return 1; } *adc_val = I2C_RecvByte(); // 读取ADC数据 I2C_SendNAck(); // 发送非应答 I2C_Stop(); return 0; } // 向PCF8591的DAC写入数据 bit PCF8591_WriteDAC(unsigned char dac_val) { // 控制字节:使能DAC(1),单端输入(0),通道0(00),自动增量关闭(0),保留位(00) // 即 0x40。注意,即使不用ADC,也需要发送一个有效的控制字节来使能DAC。 unsigned char ctrl = 0x40; I2C_Start(); if (I2C_SendByte(PCF8591_WRITE_ADDR)) { I2C_Stop(); return 1; } if (I2C_SendByte(ctrl)) { I2C_Stop(); return 1; } if (I2C_SendByte(dac_val)) { // 发送要输出的DAC值 I2C_Stop(); return 1; } I2C_Stop(); return 0; }

4. 核心功能实现与调试技巧

4.1 单通道ADC读取与DAC输出实验

现在,我们将上面的驱动整合起来,完成一个经典实验:读取AIN0通道的电位器电压(ADC),并将这个电压值按比例通过DAC输出,实现一个“电压跟随器”。在CT107D板上,这会导致连接AOUT的LED亮度随电位器旋转而变化。

void main() { unsigned char adc_value = 0; unsigned char dac_value = 0; // 初始化...(包括定时器、数码管等,此处省略) while(1) { // 1. 读取AIN0的ADC值 if (PCF8591_ReadADC(0x40, &adc_value) == 0) { // 控制字0x40: 使能DAC,读AIN0 // 读取成功,adc_value范围是0-255 // 2. 将ADC值直接(或处理后)送给DAC输出 dac_value = adc_value; // 直接跟随 // 或者做一些处理,例如:dac_value = 255 - adc_value; (反向) PCF8591_WriteDAC(dac_value); // 3. 可以将adc_value显示在数码管上,方便观察 Display_ADC_Value(adc_value); } Delay_ms(50); // 控制采样率,避免过于频繁 } }

这个简单的循环清晰地展示了信号流:模拟输入(电位器)-> ADC数字值 -> 单片机处理 -> DAC数字值 -> 模拟输出(LED亮度)。通过旋转电位器,你应该能看到LED的平滑调光效果。

4.2 多通道ADC自动扫描读取

利用PCF8591的“自动增量”功能,我们可以高效地轮流读取多路模拟输入。将控制字节的第2位(自动增量标志)设置为1即可。

void Read_All_ADC_Channels(unsigned char *adc_buf) { // 控制字节: 使能DAC(1), 单端(0), 从通道0开始(00), 自动增量开启(1), 保留位(00) // 二进制: 1 0 00 1 00 = 0x44 unsigned char ctrl = 0x44; unsigned char i; // 启动转换(从通道0开始) PCF8591_WriteCtrlByte(ctrl); Delay_ms(1); // 等待第一次转换完成 // 连续读取4个通道的数据 I2C_Start(); I2C_SendByte(PCF8591_READ_ADDR); for (i=0; i<3; i++) { // 读取前3个字节,每个字节后发送ACK adc_buf[i] = I2C_RecvByte(); // 发送ACK,通知从机继续发送 I2C_SDA = 0; // ACK I2C_SCL = 1; Delay5us(); I2C_SCL = 0; Delay5us(); } // 读取第4个字节(最后一个),发送NACK adc_buf[3] = I2C_RecvByte(); I2C_SendNAck(); I2C_Stop(); }

调用这个函数后,adc_buf[0]adc_buf[3]就分别存储了AIN0到AIN3的转换结果。这里有一个极其重要的细节:当你启用自动增量模式并连续读取时,读到的第一个字节(adc_buf[0])实际上是上一次转换周期中最后一个通道的数据(这是一个“转换流水线”效应)。为了获得正确的、按通道顺序对应的数据,一个常见的做法是启动转换后,连续读取5个字节,然后丢弃第一个字节。后面的4个字节才是当前周期对通道0-3的转换结果。你可以修改上面的循环来验证这一点。

4.3 调试与问题排查实录

即使代码逻辑正确,在实际调试中也可能遇到各种问题。下面是我总结的常见问题排查清单:

现象可能原因排查步骤与解决方案
ADC读数始终为0或2551. I2C通信失败。
2. 控制字节错误,未使能DAC。
3. 模拟输入通道错误或未连接。
4. 参考电压问题。
1. 用逻辑分析仪或示波器抓取SCL/SDA波形,检查起始、停止、地址、应答信号是否正常。没有仪器的话,可以编写一个简单的I2C总线扫描程序,看能否检测到地址0x48的设备。
2.检查控制字节是否为0x4X(X为通道号),确保第7位是1。
3. 用万用表测量AIN0引脚电压是否随电位器变化(应在0-VCC之间)。
4. 检查PCF8591的VREF引脚(参考电压)。如果该引脚悬空,芯片会使用VCC作为参考。确保其连接稳定。
DAC输出无变化,LED常亮或常灭1. DAC未使能(控制字节第7位为0)。
2. DAC输出引脚(AOUT)未正确连接到负载(LED)。
3. 写入的DAC值没有变化。
1.确保PCF8591_WriteDAC函数中发送的控制字节是0x40或更高位为1
2. 检查板子原理图,确认AOUT是否通过限流电阻连接到LED阳极,LED阴极是否接地。
3. 在PCF8591_WriteDAC函数后,读取一下控制字节和DAC值,确认发送成功。
读取的数据波动大,不稳定1. 电源噪声。
2. 模拟信号源阻抗过高。
3. I2C总线干扰。
4. 未等待足够的转换时间。
1. 在PCF8591的VCC和GND引脚就近并联一个10uF电解电容和一个0.1uF瓷片电容去耦。
2. 对于电位器等信号源,问题不大。对于高阻抗传感器(如某些热敏电阻),考虑接入一个电压跟随器(运放)进行阻抗匹配。
3. 确保SCL和SDA线上有上拉电阻(通常板子上已集成,4.7kΩ-10kΩ)。
4.PCF8591_WriteCtrlByteI2C_Start读操作之间,增加至少100us的延时,确保转换完成。
自动增量模式读取的数据顺序错乱未处理PCF8591的“转换流水线”效应。采用“读取5字节,丢弃首字节”的方法。或者,更简单可靠的方法是:每次只读取一个通道,通过循环更改控制字节中的通道选择位来实现多路读取。虽然效率稍低,但逻辑清晰,不易出错,在蓝桥杯这种对速度要求不高的场合完全可行。
I2C通信完全无应答1. 硬件连接错误(线接反、虚焊)。
2. 从机地址错误。
3. 总线被锁死。
1. 断电,用万用表检查所有连线。
2. 确认A2,A1,A0引脚电平,计算正确的7位地址(通常是0x48)。
3. I2C总线可能因异常中断而锁死在低电平。尝试多次发送停止条件(I2C_Stop()),并重新初始化SDA和SCL为高电平输出,然后再次尝试起始条件。

独家避坑技巧:在编写I2C模拟时序时,最棘手的往往是时序的细微偏差。一个非常有效的调试方法是“软件示波器”法:在每一个I2C_SCLI2C_SDA电平变化的地方,用一个空闲的IO口(比如P1.0)产生一个极短的高电平脉冲。然后用示波器同时观察SCL、SDA和这个脉冲信号,你就能清晰地看到代码执行到哪一步时电平发生了变化,从而精准定位时序错误。如果没有示波器,也可以将这个脉冲连接到LED,通过LED的闪烁模式来定性判断程序是否卡在某个循环里。

5. 性能优化与竞赛应用拓展

5.1 代码优化与执行效率

在蓝桥杯比赛中,虽然对实时性要求不是极端苛刻,但优雅高效的代码总能让你更从容。针对PCF8591的操作,可以做一些优化:

  1. 减少不必要的延时Delay5us()Delay_ms(1)中的延时值,可以在确保通信稳定的前提下,通过实验找到最小值。例如,在24MHz晶振下,可能只需要2-3微秒的延时。使用STC-ISP的软件延时计算器生成更精确的微秒级延时函数。
  2. 合并操作:如果你需要同时更新DAC输出并启动下一轮ADC转换,完全可以在一次I2C写传输中完成。先发送写地址(0x90),再发送控制字节,接着发送DAC值。这样只用了一次起始和停止条件,效率更高。
  3. 状态机设计:对于需要周期性采集多路信号并处理的赛题,可以将PCF8591的读写操作放入定时器中断服务程序中,用状态机来管理“启动转换->延时->读取数据->处理数据->启动下一次转换”的流程,避免在主循环中阻塞等待。

5.2 在典型赛题中的应用场景拆解

掌握了PCF8591的基本操作,我们来看看它在往届蓝桥杯赛题中可能扮演的角色:

  • 简易数字电压表:核心就是读取AIN0的ADC值。难点在于将0-255的数字量转换为实际的电压值并显示在数码管或LCD上。公式为:电压 = (ADC值 / 255) * 参考电压(Vref)。如果Vref接的是VCC(5V),那么每个数字量代表的电压就是约0.0196V。显示时可能需要做浮点运算或整数放大处理。
  • 光线/温度控制系统:AIN1接光敏,AIN2接热敏。通过ADC读取其电阻分压后的电压,根据电压-光照/温度曲线(可能需要查表或简单公式)计算出当前环境参数。然后根据设定阈值,通过继电器或DAC控制LED亮度(模拟调光)来调节“环境”。
  • 波形发生器雏形:虽然PCF8591的DAC是8位,输出速度受I2C限制,但依然可以产生简单的波形。例如,在主循环中让DAC输出值从0递增到255再递减到0,循环往复,在AOUT引脚上用示波器就能看到一个三角波。更复杂的波形(如正弦波)可以预先计算一个数组,然后定时更新DAC值。
  • 多路数据采集系统:这是自动增量模式的用武之地。需要同时监测板载的电位器、光敏、热敏电阻状态。你可以定时(如每秒10次)调用Read_All_ADC_Channels函数,将四路数据存入数组,用于后续的数据分析、显示或上传。

一个综合应用的思路:赛题可能要求设计一个“智能光照调节系统”。你需要用光敏电阻(AIN1)感知环境光,用ADC读取;然后用一个算法(比如PID,在单片机中常用增量式PI)根据当前光强和目标光强的差值,计算出一个控制量;最后将这个控制量通过DAC输出,控制一个LED灯(或模拟调光电路)的亮度,使环境光稳定在目标值附近。这里,PCF8591就完成了“感知”和“执行”两个关键环节,而算法部分则是你代码能力的体现。

6. 进阶话题与深度思考

6.1 精度与分辨率辨析

常有人问:“这个8位ADC精度怎么样?”这里要分清两个概念:分辨率精度

  • 分辨率:指ADC能够区分的最小输入电压变化。对于8位ADC,总共有2^8=256个离散等级。如果参考电压Vref=5V,那么分辨率就是5V / 256 ≈ 19.5mV。这意味着输入电压变化小于19.5mV时,ADC输出数字可能不变。
  • 精度:指ADC转换结果与实际电压值之间的误差。它受到很多因素影响:参考电压的稳定性、内部DA转换器的线性度、积分非线性(INL)、微分非线性(DNL)、噪声等。PCF8591的典型精度可能在±1~2个LSB(最低有效位),也就是可能有19.5mV到39mV的绝对误差。

在蓝桥杯的赛题环境中,参考电压通常就是不稳定的板载5V电源,所以绝对精度并不高。但大多数赛题关注的是相对变化(比如电位器旋转角度与ADC值的关系)或阈值判断(光敏电阻电压高于/低于某个值),这时分辨率足够,精度的影响相对较小。如果赛题要求较高精度的测量,就需要考虑使用外部精密基准电压源(如TL431)为PCF8591的VREF引脚供电,并做好电源滤波。

6.2 抗干扰设计与软件滤波

在复杂的竞赛环境中,电源波动或数字噪声可能耦合到模拟信号中,导致ADC读数跳动。除了硬件上加滤波电容,软件上的滤波算法也至关重要。

  1. 算术平均滤波:最简单有效。连续采样N次,求和后取平均。
    #define N 10 unsigned char ADC_Filter_Average(unsigned char channel) { unsigned int sum = 0; unsigned char i, val; for(i=0; i<N; i++) { PCF8591_ReadADC(0x40 | channel, &val); // 0x40是基础控制字 sum += val; Delay_ms(5); // 间隔采样,避免相关性 } return (unsigned char)(sum / N); }
  2. 中位值平均滤波:采一组数据,去掉最大值和最小值,再对剩下的求平均。能有效抑制脉冲干扰。
  3. 一阶滞后滤波(低通滤波):适用于变化缓慢的信号。本次滤波结果 = α * 本次采样值 + (1-α) * 上次滤波结果,其中α为滤波系数(0<α<1)。这种方法计算量小,能很好地平滑数据。

选择哪种滤波,取决于信号特性和系统实时性要求。对于缓慢变化的温度、光照,一阶滞后滤波非常合适。

6.3 与其他模拟接口芯片的对比思考

在蓝桥杯的生态里,你可能还会遇到其他模拟接口芯片,比如ADC0804(并行接口ADC)或DAC0832(并行接口DAC)。与它们相比,PCF8591最大的优势就在于I2C总线。只需要两根线,就能实现4路输入1路输出,极大地节省了单片机的IO口资源,布线也简洁。缺点是速度相对较慢(受限于I2C速率,通常工作在100kHz或400kHz),且编程复杂度稍高(需要严格遵循I2C协议)。

理解PCF8591,本质上就是理解I2C和数模/模数转换的概念。这为你以后接触更复杂的I2C传感器(如OLED屏幕、陀螺仪、气压计等)打下了坚实的基础。在比赛时,如果赛题提供了新的I2C器件,你完全可以套用同样的“起始->发送地址->发送/接收数据->停止”的框架去驱动它,重点在于读懂新器件的寄存器地图(相当于PCF8591的控制字节)。

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

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

立即咨询