一.PCF8591的使用
1.PCF8591介绍
PCF8591是单片、单电源低功耗的8位CMOS数据采集器件,具有IIC总线接口的8位A/D以及D/A转换器,有4路A/D转换输入,1路D/A模拟输出。使用iic通信
2.CT107D中PCF8591引脚介绍
其中AIN为思路输入通道连接,AIN1连接光敏电阻,AIN3连接RB2电位器P20对应SCL,P21对应sda需要在iic.c中补充。
3.控制字节
参考数据手册:
第7位:置1是允许DA按照写入的值输出 ,置0时会固定输出大约1v的电压
5-6位:
00:四路单端输入(常用)
01:三路差分输入
10:单端和差分输入
11:两路差分输入
(翻译的不一定准确)
第3位:自动增量选择位(一般置0)
1-2位:通道选择
二.数据帧介绍及示例代码
数据类型定义:
unsigned char adc1_value;
float adc1_volt;
unsigned int adc1_smg;
unsigned char adc2_value;
float adc2_volt;
unsigned int adc2_smg;
1.AD
流程:
- 发送Start信号
- 写入地址并接受应答
- 写入控制字并接受应答
- 接受数据并发送应答信号“1”
- 发送结束信号
虽然数据手册只给了一点,但在实际使用中,需要先选择AD转换的通道
- 发送Start信号
- 写入地址并接受应答
- 写入控制字并接受应答
- 发送结束信号
示例代码:
unsigned char Read_ADC(unsigned char Ain)//Ain:通道选择
{
unsigned char temp;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
if(Ain==1)
{
I2CSendByte(0x01);//选择channel2
}
if(Ain==3)
{
I2CSendByte(0x03);//选择channel2
}
I2CWaitAck();
I2CStop();
Display_Dynamic();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp=I2CReceiveByte();//获取8位数据
I2CSendAck(1);
I2CStop();
return temp;
}
void Read_Ain()//数据转化
{
adc1_value=Read_ADC(1);
adc1_volt=adc1_value/255.0*5;
adc1_smg=adc1_volt*100;
adc2_value=Read_ADC(3);
adc2_volt=adc2_value/255.0*5;
adc2_smg=adc2_volt*100;
}
2.DA
流程:
- 发送Start信号
- 写入地址并接受应答
- 写入控制字并接受应答
- 写入数据并接受应答
- 发送结束信号
示例代码
void Write_DAC(unsigned char dat)
{
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x40);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
碎碎念:如果要使用DA功能需要时刻保持控制字节第七位为1,即使是在AD控制中 (此时才会有符合需要的值输出),
三.练习
在数码管的1,2,3位显示光敏电阻上的分压,在6,7,8位显示电位器Rb2的分压,并将电位器Rb2上的电压通过DAC输出
示例代码:
#include <REGX52.H>
#include "iic.h"
code unsigned char SMG_NoDot[] =
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
code unsigned char SMG_Dot[] =
{0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0x88,0x83,0xc6,0xa1,0x86,0x8e};
unsigned char adc1_value;
float adc1_volt;
unsigned int adc1_smg;
unsigned char adc2_value;
float adc2_volt;
unsigned int adc2_smg;
void DelaySMG(unsigned int t)
{
while(t--);
}
void SelectHC573(unsigned char channel,unsigned char dat)
{
switch(channel)
{
case 4:
P2=(P2&0x1f)|0x80;
break;
case 5:
P2=(P2&0x1f)|0xa0;
break;
case 6:
P2=(P2&0x1f)|0xc0;
break;
case 7:
P2=(P2&0x1f)|0xe0;
break;
case 0:
P2=(P2&0x1f)|0x00;
break;
}
P0=dat;
P2=(P2&0x1f)|0x00;
}
void SMG_Display(unsigned char pos,unsigned char dat)
{
SelectHC573(6,0x01<<(pos-1));
SelectHC573(7,dat);
DelaySMG(500);
SelectHC573(6,0x01<<(pos-1));
SelectHC573(7,0xff);
}
void Display_Dynamic()
{
SMG_Display(6,SMG_Dot[adc1_smg/100]);
SMG_Display(7,SMG_NoDot[adc1_smg%100/10]);
SMG_Display(8,SMG_NoDot[adc1_smg%10]);
SMG_Display(1,SMG_Dot[adc2_smg/100]);
SMG_Display(2,SMG_NoDot[adc2_smg%100/10]);
SMG_Display(3,SMG_NoDot[adc2_smg%10]);
}
unsigned char Read_ADC(unsigned char Ain)
{
unsigned char temp;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
if(Ain==1)
{
I2CSendByte(0x41);//由于要使用DAC所以第七位置1
}
if(Ain==3)
{
I2CSendByte(0x43);
}
I2CWaitAck();
I2CStop();
Display_Dynamic();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
void Write_DAC(unsigned char dat)
{
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x40);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
void Read_Ain()
{
adc1_value=Read_ADC(1);
adc1_volt=adc1_value/255.0*5;
adc1_smg=adc1_volt*100;
adc2_value=Read_ADC(3);
adc2_volt=adc2_value/255.0*5;
adc2_smg=adc2_volt*100;
}
void Sys_Init()
{
SelectHC573(4,0xff);
SelectHC573(5,0x00);
}
void main()
{
Sys_Init();
while(1)
{
Display_Dynamic();
Read_Ain();
Write_DAC(adc1_value);
}
}
后续补充:上述代码发现显示的两个数据反了(数码管前三位应该显示的是电位器分压,实际是光敏电阻分压).查询资料发现原因:发送读取指令后读取的是上一次采集的值,所以解决方法也很简单,连续读取两次,舍弃第一次读取的值.修改的代码有点粗略,也希望有大佬指点更简介的代码
unsigned char Read_ADC(unsigned char Ain)
{
unsigned char temp;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
if(Ain==1)
{
I2CSendByte(0x41);
}
if(Ain==3)
{
I2CSendByte(0x43);
}
I2CWaitAck();
I2CStop();
Display_Dynamic();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp=I2CReceiveByte();
I2CSendAck(0);//发送应答位,继续接收数据
temp=I2CReceiveByte();
I2CSendAck(1);//发送非应答位,停止接受数据
I2CStop();
return temp;
}
附:
闲来无事看到板子上有个A/D排针,通过原理图发现与AIN0相连,突发奇想做一个小电压表.
unsigned char Read_ADC(unsigned char Ain)
{
unsigned char temp;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x00);
I2CWaitAck();
I2CStop();
Display_Dynamic();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
void Read_Ain()
{
adc1_value=Read_ADC(1);
adc1_volt=adc1_value/255.0*5;
adc1_smg=adc1_volt*100;
}
把上述代码替换一下,再选择好数码管就可以了,精度一般般