工具
1.Proteus 8 仿真器
2.keil 5 编辑器
原理图
讲解
PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591具有4个模拟输入、1个模拟输出和1个串行I²C总线接口。PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程,允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。
作用
AD (模数转换)
将模拟信号(电流或电压信号)装换成数字信号(电平信号)
DA (数模转换)
将数字信号(电平信号)装换成模拟信号(电流或电压信号)
特性
- 单独供电
- PCF8591的操作电压范围2.5V-6V
- 低待机电流
- 通过I2C总线串行输入/输出
- PCF8591通过3个硬件地址引脚寻址
- PCF8591的采样率由I2C总线速率决定
- 4个模拟输入可编程为单端型或差分输入
- 自动增量频道选择
- PCF8591的模拟电压范围从VSS到VDD
- PCF8591内置跟踪保持电路
- 8-bit逐次逼近A/D转换器
- 通过1路模拟输出实现DAC增益
引脚信息
引脚 | 说明 |
ANI0~AIN3 | 模拟信号输入端 |
A0~A2 | 引脚地址端 |
VDD | 电源正 |
VSS | 电源负 |
SDA | I2C数据线 |
SCL | I2C时钟线 |
OSC | 外部时钟输入端,内部时钟输出端 |
EXT | 内部、外部时钟选择线,使用内部时钟时 EXT 接地 |
AGND | 模拟信号地 |
AOUT | D/A转换输出端 |
VREF | 基准电源端(注意:不可超过芯片的最大电压值,同时基准电压参与采样输出值的计算) 数字值 = (模拟电压 / VREF) * 256 |
内部结构
功能描述
寻址
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
1 | 0 | 0 | 1 | A2 | A1 | A0 | R/W |
地址总是必须作为12c总线协议中的开始条件之后的第一个字节被发送。I2C总线系统中的每一片PCF8591都通过发送有效地址到该器件来激活。高四位为固定部分,低四位为可编程部分,A2,A1,A0是引脚地址,最低位是读写功能位:0是写,1是读。
控制
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
未用(写0) | D/A输出允许位 0禁止 1允许 | A/D输入方式选择位 00:4路单端输入 01:3路差分输入 10:单端与差分输入 11:2路差分输入 | 未用(写0) | 自动益增选择位 0禁止 1允许 | AD通道选择位 00:选择通道0 01:选择通道1 10:选择通道2 11:选择通道3 |
发送到PCF8591设备的第二个字节将存储在其控制寄存器中,并需要控制设备的功能。
控制寄存器的高4位用于使能模拟输出,并将模拟输入编程为单端或差分输入。
低4位选择由高4位所定义的模拟输入通道之一。
如果设置了自动增量标志,在每次A/D转换后,通道号会自动增加。
如果在使用内部振荡器的应用中需要自动增量模式,则应该设置控制字节(第6位)中的模拟输出使能标志。
这允许内部振荡器连续运行,从而防止由振荡器启动延迟导致的转换错误。上电复位后,控制寄存器的所有位都复位为逻辑0。为了省电,D/A转换器和振荡器被禁用。模拟输出被切换到高阻抗状态。
D5 D4 模拟输入配置方式
注意
读取数据时会先反馈上一次模数转换的数据
代码
采集通道1模拟电压值并在液晶显示
#include <reg52.h>
#include "Delay.H"
#include "LCD1602.H"
#include <intrins.H>
sbit SDA = P2^7; //数据
sbit SCL = P2^6; //时钟
sbit button =P1^7; //按钮
unsigned int num=0; //电压值
#define delay();{_nop_();_nop_();_nop_();_nop_();_nop_();} //五个机器周期 5微妙 一周期多长时间与晶振有关
/**
* @brief 开始信号
* @param
* @param
* @param
* @retval
*/
void start()
{
SDA=1;
SCL=1;
delay();
SDA=0;
delay();
SCL=0;
}
/**
* @brief 结束信号
* @param
* @param
* @param
* @retval
*/
void end()
{
SDA=0;
SCL=1;
delay();
SDA=1;
delay_ms(10);
}
// 发送
void send_data(unsigned char byte)
{
unsigned char i;
for(i=0;i<8;i++) //字节拆分按位传递
{ //SCL为高电平 读取SDA稳定数据 所以SDA变化在前
SDA=byte&(0x80>>i); //从最高位依次传递给SDA
delay();
SCL=1;
delay();
SCL=0;
}
}
// 接收
unsigned char read()
{
unsigned char i,byte=0x00;
SDA=1;
for(i=0;i<8;i++) //字节拆分按位接收
{
SCL=1;
delay();
if(SDA){byte|=(0x80>>i);}
delay();
SCL=0;
}
return byte;
}
// 假设SCL和SDA是控制I2C时钟线和数据线的宏或变量
// delay函数用于提供必要的延迟,确保时序正确
void send_sck(bit ACK) {
// 根据ACK的值设置数据线SDA
SDA = ACK; // 注意:通常ACK是低电平,NACK是高电平
delay(); // 保持SDA状态,等待从机读取
SCL = 1; // 将时钟线SCL拉高
delay(); // 等待时钟线稳定
SCL = 0; // 将时钟线SCL拉低,结束应答
delay(); // 等待时钟线稳定
SDA = 1; //释放数据线 线权交给从机
}
//接收应答
bit read_sck()
{
bit ACK;
SDA=1;
delay(); //5us
SCL=1;
delay(); //5us
ACK=SDA;
delay(); //5us
SCL=0;
return ACK;
}
/**
* @brief I2C发送
* @param address 器件地址 byte PCF8591芯片 第二字节紧跟着 控制地址
* @param
* @param
* @retval
*/
void I2C_send(unsigned char address,unsigned char byte)
{
start();
send_data(address);
read_sck();
send_data(byte);
read_sck();
end();
delay_ms(5);
}
/**
* @brief I2C读取
* @param address 器件地址
* @param
* @param
* @retval
*/
unsigned char I2C_read(unsigned char address)
{
unsigned char read_data;
start();
send_data(0x91); //器件
read_sck();
read();
send_sck(0); //应答
read_data=read();
send_sck(1); //非应答停止接收
end();
return read_data;
}
main(void)
{
LCD_Init(); //初始化液晶
LCD_ShowString(1,1,"Old_man");
LCD_ShowString(2,1,"0.00V");
while(1)
{
//按钮按下
if(!button)
{
while(!button);
//按钮抬起后
//I2C 读取PCF8591的值
I2C_send(0x90,0x01);
num=I2C_read(0x91); //读取的值
LCD_ShowNum(2,1,num*(5*1000/255)/1000,1); //整数部分
LCD_ShowString(2,2,"."); //小数点
LCD_ShowNum(2,3,num*(5*1000/255)/10%100,2); //小数部分
LCD_ShowString(2,5,"V");
}
}
}
实现
🚀本欧也处于学习阶段,所学所识将以笔记发布。
笔记会根据相关知识的接触而随时更新!
如果文章对你有帮助,请留下你宝贵的点赞吧👍
V:Werluo 本欧也很喜欢交朋友的哦!