0.本节任务
了解PCF8591的使用方法,并利用PCF8591检测模拟电压信号(模数转换)。
1.工作原理 PCF8591简介
PCF8591的引脚和原理图连接 PCF8591是一种单芯片、单电源、低功耗的8位COMS数据采集器件,具有4个模拟输入、1个模拟输出和IIC总线。
PCF8591引脚说明 AINO~3是四个模拟输入,VSS为负电源,VDD为正电源,VREF为参考电压(蓝桥杯平台接VCC),EXT是内部/外部时钟的切换开关(为1时允许从OSC输入时钟信号,但是蓝桥杯平台接地了),OSC是外部时钟信号的输入端,A0~A2为IIC相关的硬件地址(蓝桥杯平台接地),AGND为模拟地(蓝桥杯平台接地),SCL和SDA为IIC的信号线。
蓝桥杯单片机平台的原理图 IIC内容详见,本节不再做介绍:
PCF8591的设备地址 PCF8591的设备地址如下图所示,根据蓝桥杯给出原理图连接,写地址为0x90(1001 0000),读地址为0x91(1001 0001):
PCF8951的控制字节 PCF8951的控制字节如下图所示:
第0、1位控制AD转换的信道,可以选择四个端口的作为模拟信号的输入端,00为信道0,01为信道1,10为信道2,11为信道3; 第3位是自动增量标志位,若置1,每次检测完一个信道后会自动检测下一个信道;
第4位恒为0;
第5、6位控制模拟输入的模式,不同模式有不同数量的信道,一般用单端输入(00)较多;
第7位控制模拟信号输出/输入,AD转换置0,DA转换置1(有资料写只有DA转换必须置1,AD转换置0,1均可);
第8位恒为0;
蓝桥杯单片机中AIN3与Rb2可调电阻相连,所以一般可以使用0000 0011(0x03)(AD转换,单端输入,AIN3作为模拟输入的信道)作为控制字发送。 开始AD转换 通过datasheet和相关资料,PCF8951的开始AD转换分两步: ①.IIC开始->写PCF8951(0x90)->写控制字->IIC停止(第一步datasheet中似乎没有明确写出,它只说了DA转换需要写控制字???) ②.IIC开始->读PCF8951->PCF8951发送数据...->IIC停止 值得注意的是,第二部开始读PCF8951后,其发送的第一个数据是上一次转换的数据,如果是刚刚上电,则第一个数据是0x80.
datasheet中给出的A/D转换示意 AD转换单端输入特性如下图所示: 根据之前的原理图连接,VREF = VCC(5v),VAGND= 0,就可以算出Vlsb约等于0.0195v,假设模拟输入VAIN为3V,则对应横坐标属于154这个阶梯上,此时PCF8951发送的数据为0x9A. 2.程序设计 设定PCF8591为AD转换模式,并通过AIN3检测Rb2可变电阻上电压。 IIC驱动代码仍是官方给出的代码,我们只需要控制好读写地址和控制字就行:#include "reg52.h"#include "intrins.h"#define DELAY_TIME 5/** 定义I2C总线时钟线和数据线 */sbit scl = P2^0;sbit sda = P2^1;/*** @brief I2C总线中一些必要的延时** @param[in] i - 延时时间调整.* @return none*/void i2c_delay(unsigned char i){ do { _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); } while(i--); }/*** @brief 产生I2C总线启动条件.** @param[in] none* @param[out] none* @return none*/void i2c_start(void){ sda = 1; scl = 1; i2c_delay(DELAY_TIME); sda = 0; i2c_delay(DELAY_TIME); scl = 0; }/*** @brief 产生I2C总线停止条件** @param[in] none* @param[out] none.* @return none*/void i2c_stop(void){ sda = 0; scl = 1; i2c_delay(DELAY_TIME); sda = 1; i2c_delay(DELAY_TIME); }/*** @brief I2C发送一个字节的数据** @param[in] byt - 待发送的字节* @return none*/void i2c_sendbyte(unsigned char byt){ unsigned char i;// EA = 0; //关闭中断,避免因为中断而影响总写读写的时序,导致读写失败。 for(i=0; i<8; i++){ scl = 0; i2c_delay(DELAY_TIME); if(byt & 0x80){ sda = 1; } else{ sda = 0; } i2c_delay(DELAY_TIME); scl = 1; byt <<= 1; i2c_delay(DELAY_TIME); } EA = 1;// scl = 0; }/*** @brief 等待应答** @param[in] none* @param[out] none* @return none*/unsigned char i2c_waitack(void){ unsigned char ackbit; scl = 1; i2c_delay(DELAY_TIME); ackbit = sda; //while(sda); //wait ack scl = 0; i2c_delay(DELAY_TIME); return ackbit;}/*** @brief I2C接收一个字节数据** @param[in] none* @param[out] da* @return da - 从I2C总线上接收到得数据*/unsigned char i2c_receivebyte(void){ unsigned char da; unsigned char i;// EA = 0; for(i=0;i<8;i++){ scl = 1; i2c_delay(DELAY_TIME); da <<= 1; if(sda) da |= 0x01; scl = 0; i2c_delay(DELAY_TIME); } EA = 1;// return da; }/*** @brief 发送应答** @param[in] ackbit - 设定是否发送应答* @return - none*/void i2c_sendack(unsigned char ackbit){ scl = 0; sda = ackbit; //0:发送应答信号;1:发送非应答信号 i2c_delay(DELAY_TIME); scl = 1; i2c_delay(DELAY_TIME); scl = 0; sda = 1; i2c_delay(DELAY_TIME);}/*** @brief 读写操作过程中一些必要的延时** @param[in] i - 指定延时时间* @return - none*/void operate_delay(unsigned char t){ unsigned char i; while(t--){ for(i=0; i<112; i++); }} 初始化和读PCF8591:#include "PCF8951_AD.h"#include "STC15F2K60S2.H" #include "intrins.h"#include "HC138.h"#include "LedBuzzerRelay.h"#include "SMG.h"#include "i2c.h"//初始化PCF8951,AD转换,单端输入,AIN3作为模拟输入信道void PCF8951Init(void){ i2c_start(); i2c_sendbyte(0x90);//写地址 i2c_waitack(); i2c_sendbyte(0x03); i2c_waitack(); i2c_stop(); operate_delay(10);} unsigned char ReadPCF8951(void){ unsigned char da; i2c_start(); i2c_sendbyte(0x91);//读地址 i2c_waitack(); da = i2c_receivebyte(); i2c_sendack(1); i2c_stop(); return da;} 主函数(这里只对读出来的数据显示了X上坐标,可以乘上分辨率得出实际电压):#include "STC15F2K60S2.H" #include "intrins.h"#include "HC138.h"#include "LedBuzzerRelay.h"#include "SMG.h"#include "i2c.h"#include "PCF8951_AD.h"void main(void){ unsigned char dat; AllOff(); PCF8951Init(); //LEDOn(); while(1){ dat = ReadPCF8951(); DisplaySMG(0,dat/100); DelaySMG(); DisplaySMG(1,(dat%100)/10); DelaySMG(); DisplaySMG(2,dat%10); DelaySMG(); }}
|