问题原由

粉丝提问,STM32 如何驱动 PCF8591?时间赶趟,小哈哥必须安排。

 

 

模块外观

 

 

原理图

 

PCF8591 模块原理图

 

模块说明:
  1. 模块所用芯片为 PCF8591T,贴片封装支持 4 路模拟电压采集信号输入(电压输入范围 0--5V)模块带电源指示灯 1 个(D1)模块带 DA 输出指示灯 1 个(D2),当 DA 输出电压达到一定值时,该指示灯亮,电压值越高,指示灯越亮模块集成 1 路 0--5V 电压输入采集(通过蓝白电位器调节输入电压)模块集成 1 路光敏电阻(型号:5537),可以通过 AD 采集环境光线强度模块集成 1 路热敏电阻(型号:MF58),可以通过 AD 采集环境温度
 
PCF8591 特点:
  1. 单电源供电;正常工作电源电压范围为 2.5V~6V;通过 I2C 总线完成数据的输入 / 输出;器件地址由 3 个地址引脚决定;

 

由上面的原理图可以看出 A0、A1、A2 与 GND 相连。

 

  1. 采样频率由 I2C 总线传输速率决定;4 路模拟量输入可编程为单端输入或差分输入;可配置转换通道号自动增加功能;模拟电压范围为 VSS~VDD;片上跟踪保持功能;8 位逐次逼近 A/D 转换;带有一路模拟量输出的 D/A 转换。

 

接线说明

接线示意图

 

PCF8591 模块 含义 STM32 核心板
SDA IIC 数据接口 U3R PB11
SCL IIC 时钟接口 U3T PB10
VCC 电源正 3V3
GND 电源负 GND

 

接线示意图

 

实际连接图

 

硬件连接

 

模块输出调试引脚

 

PCF8591 模块 含义
AOUT 模块 DA 输出端口
AIN3 模拟输入信号接口 3
AIN2 模拟输入信号接口 2
AIN1 模拟输入信号接口 1
AIN0 模拟输入信号接口 0
GND 模块的 GND
INPUT2 已连接至热敏电阻信号输入口
INPUT1 已连接至光敏电阻信号输入口
INPUT0 已连接至电位器信号输入口

 

跳线帽使用说明

 

  • AIN0 与 INPUT0 连接跳线帽时,选择电位器接入电路 AIN1 与 INPUT1 连接跳线帽时,选择光敏电阻接入电路 AIN2 与 INPUT2 连接跳线帽时,选择热敏电阻接入电路需要外接模拟信号输入时,请断开跳线帽,将该模拟信号接入 AIN0----AIN3

 

芯片手册

地址

 

 

从模块原理图得知 A0,A1,A2 接地,所以模块的地址为:1001000X,那么其地址就是 0x90 或者是 0x91,看是读还是写来决定。

 

最后一位,当是 0 时,表示下一个字节往总线上写数据;当是 1 时,表示下一个字节从总线上读取数据。

 

#define PCF8591Addr 0x90        // 写操作地址
PCF8591Addr|0x01                // 读操作地址

 

控制字

 

 

控制字位说明:

  • bit0-bit1:AD 转换通道选择,当值为 00 时,为 AIN0;bit2:自动增量使能 bit4-bit5:单端输入 / 差分输入选择,00 为单端输入 bit6:为 1 时为允许模拟电压输出

 

假设我需要从通道 0 单端获得 A/D 转换数据,那么 control byte 就是 01000000 = 0x40 。

 

所以我们获取四个通道 ADC 值的函数如下调用:

 

adcData[0] = PCF8591_ADC_Input(0x90 ,0x40);
adcData[1] = PCF8591_ADC_Input(0x90 ,0x41);
adcData[2] = PCF8591_ADC_Input(0x90 ,0x42);
adcData[3] = PCF8591_ADC_Input(0x90 ,0x43);

 

D/A 转换时序

 

D/A 转换时序图

 

具体实现代码如下:

 

u8 PCF8591_DAC_Output(unsigned char WriteAddr,unsigned char c,  unsigned char Val)
{
    IIC_Start();              // 启动总线
    
    IIC_Send_Byte(WriteAddr); // 发送写操作地址
   
    IIC_Wait_Ack();  
    
    IIC_Send_Byte(c);        // 发送控制字
   
    IIC_Wait_Ack();  
    
    IIC_Send_Byte(Val);     // 发送 DAC 的数值
   
    IIC_Wait_Ack();  
    
    IIC_Stop();             // 结束总线
    
    return(1);
}

 

输出 DAC 的值参考下面换算关系计算:

 

 

简单理解就是,发送的 DAC 数值IIC_Send_Byte(Val); ,将 Vref 分成 0xFF 份,Vagnd 对应第一份;Vref 对应最后一份。

 

A/D 转换时序

 

A/D 转换时序图

 

u8 PCF8591_ADC_Input(unsigned char WriteAddr,unsigned char Channel)
{
    u8 temp=0;
    
    IIC_Start();                  // 启动总线
    
    IIC_Send_Byte(WriteAddr);     // 发送写操作地址
   
    IIC_Wait_Ack();  
    
    IIC_Send_Byte(Channel);       // 写入控制字
   
    IIC_Wait_Ack();
    
    IIC_Stop();                   // 结束此次写操作,结束总线
    
    IIC_Start(); 
    
    IIC_Send_Byte(WriteAddr|0x01);// 发送读操作地址
   
    IIC_Wait_Ack();  
    
    temp=IIC_Read_Byte(0);
    
    IIC_Stop();                   // 结束总线
    
    return temp;
}

 

若要完成一次 AD 转换,应严格按照时序图进行:开启总线 -->发送写操作地址 -->等待 PCF8591 的应答 -->发送控制字 -->等待 PCF8591 的应答 -->结束总线 -->重新开启总线 -->发送读操作地址 -->等待 PCF8591 的应答 -->读取 ADC 数据(1 字节)-->发送非应答信号 -->停止总线

 

上面IIC_开头的函数都与 24C02 那个例程中的函数一致,不需要修改。

 

移植过程

在网文:STM32 I2C 通信操作 24C02 写数据、读数据 的实例代码基础上,完成移植,具体过程如下:

 

1. 将驱动文件拷贝到自己工程中 HARDWARE 目录下

 

 

2. 添加驱动文件所在文件夹至工程中

 

 

3. 添加 .c 文件至工程中

 

 

4. main 函数中调用

 

 

成果展示

先旋转电位器,然后用手遮挡光敏电阻,串口助手中具体现象如下:

 

 

资料获取

微信公众号后台回复“PCF8591”,可以下载工程源码。

 

推荐阅读

之前分享过的涉及 I2C 总线的内容:

STM32 I2C 通信操作 24C02 写数据、读数据

基于鸿蒙 OS 移植 OLED 驱动程序

基于鸿蒙系统的家庭燃气报警器

 

 1. 由于微信公众号近期改变了推送规则,如果你想第一时间看到我的文章,可以将「嵌入式从 0 到 1」星标。
 

这样操作后,我的每次新的推送才能第一时间出现在你的订阅列表中~

 

2. 点击文末右下角的「在看」,让更多的小伙伴知道我们这个知识分享平台,号主小哈会更有激情创作更多干货分享给大家~

 

3. 每周日、周三晚上更新文章,欢迎大家周一、周四早上来访。有问题随时留言交流。

 

欢迎关注

想进小哈哥技术交流群,请加程序员小哈个人微信,带你嵌入式入门进阶。