问题原由
粉丝提问,STM32 如何驱动 PCF8591?
时间赶趟,小哈哥必须安排。
模块外观
原理图
PCF8591 模块原理图
模块说明:
- 模块所用芯片为 PCF8591T,贴片封装支持 4 路模拟电压采集信号输入(电压输入范围 0--5V)模块带电源指示灯 1 个(D1)模块带 DA 输出指示灯 1 个(D2),当 DA 输出电压达到一定值时,该指示灯亮,电压值越高,指示灯越亮模块集成 1 路 0--5V 电压输入采集(通过蓝白电位器调节输入电压)模块集成 1 路光敏电阻(型号:5537),可以通过 AD 采集环境光线强度模块集成 1 路热敏电阻(型号:MF58),可以通过 AD 采集环境温度
PCF8591 特点:
由上面的原理图可以看出 A0、A1、A2 与 GND 相连。
- 采样频率由 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 写数据、读数据
基于鸿蒙系统的家庭燃气报警器
1. 由于微信公众号近期改变了推送规则,如果你想第一时间看到我的文章,可以将「嵌入式从 0 到 1」星标。
这样操作后,我的每次新的推送才能第一时间出现在你的订阅列表中~
2. 点击文末右下角的「在看」,让更多的小伙伴知道我们这个知识分享平台,号主小哈会更有激情创作更多干货分享给大家~
3. 每周日、周三晚上更新文章,欢迎大家周一、周四早上来访。有问题随时留言交流。
欢迎关注
想进小哈哥技术交流群,请加程序员小哈个人微信,带你嵌入式入门进阶。