# 前言 # 经过这么几个月的奋战,CT107D开发板上的功能基本摸索得差不多了。今天呢,搞了一下PCF8591. # 芯片简介 # ## 概述 ## PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591具有4个模拟输入、1个模拟输出和1个串行I²C总线接口。PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程,允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。 ## 功能 ## PCF8591的功能包括多路模拟输入、内置跟踪保持、8-bit模数转换和8-bit数模转换。PCF8591的最大转化速率由I2C总线的最大速率决定。 ## 原理图 ## ![1.png][1] ## 引脚图 ## ![2.png][2] ## PCF8591的器件地址 ## ![Snip20200222_1.png][3] ## PCF8591的控制寄存器 ## ![Snip20200222_2.png][4] # 程序实现 # 现在我们已经大致了解了PCF8591这款芯片了,现在我们来看看怎么反对他操作。 由于本文章只对其做AD转换所以我们来看看AD转换的时序就行了。 ![Snip20200222_3.png][5] ## PCF8591读取操作 ## 读取的第一个字节是包含上一次转换结果 将上一个字节读取时,才开始进行这次转换的采样。 读取的第二个字节才是这次的转换结果。 所以读取转换结果的步骤是:发送转换命令,将上次的结果读走,然后等一会儿,然后读取结果。 ## 示例程序 ## ``` /************************************************************ * 函数名 : Pcf8591ReadByte * 函数功能 : 读取一个转换值 * 输入 : * 输出 : dat ************************************************************/ unsigned char Pcf8591ReadByte() { unsigned char dat; I2cStart(); I2cSendByte(READADDR);//发送读器件地址 dat=I2cReadByte();//读取数据 I2cStop(); //结束总线 return dat; } ``` ## 数据转换 ## 由于我们通过芯片转换读取出来的值是0-255,并不能表示电压值。所以我们需要通过公式简单转换一下。 转换公式:电压值=读取值/255*5 通过这么一个公式所得出来的值就是实际的电压值了。 最后再配合数码管显示就可以实现输出了。 ## 数码管经验分享 ## 借这个机会我想做一个关于数码管的经验分享。 在我们对数码管进行动态显示的时候,需要不停的去扫描她,但是呢,在进行一些通信协议操作的时候,往往会出错。于是我们就想到了一个办法,就是在进行通信操作之前我们把定时器关掉,但是这样的话在操作通信的时候数码管会闪一下,而且很明显。 所以我想到了一个办法,把定时一次扫描8次变成扫描一次,也就是说定时一次我们就显示一位,这样在进行通信的时候关掉定时器基本看不到什么闪烁。 # 完整程序 # 最后我贴上我写的完整程序 ``` #include #include #include #define uint unsigned int #define uchar unsigned char //共阳数码管数码管段码 uchar code duan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff}; //数码管位码 uchar code wei[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //8位数码管缓存区 uchar smg_count[]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; //延迟1ms程序 void Delay1ms(uint timee) //@12.000MHz { unsigned char i, j; while(timee --) { i = 12; j = 169; do { while (--j); } while (--i); } } //操作138译码器传输数据 //w:第几个寄存器 //dat:所需要传输的数据 void HC_138(uchar w,dat) { switch(w) { case 0:P2 &= 0x1f;break; case 1:P2 = (P2 &= 0x1f) | 0x20;break; case 2:P2 = (P2 &= 0x1f) | 0x40;break; case 3:P2 = (P2 &= 0x1f) | 0x60;break; 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; } P0 = dat; } //数码管显示子程序 void smg_display() { uchar i; //给数码管送入位码数据 HC_138(6,wei[i]); //给数码管送入段码数据 HC_138(7,*(smg_count + i)); Delay1ms(2); if(++i == 8) { i = 0; } } void Timer0Init(void) //定时器0初始化@12.000MHz { AUXR |= 0x80; //选择1T模式 TMOD &= 0xF0; //模式选择 TL0 = 0x20; //送入初值 TH0 = 0xD1; //送入初值 TF0 = 0; //清空标志位 TR0 = 1; //打开定时器 EA = 1; //打开总中断 ET0 = 1; //打开定时器0中断 } //adc转换标志位 bit adc_flag = 0; //主程序 void main() { uchar team; //暂存读取数据 int team1; int team2; HC_138(4,0xff); //关LED灯 HC_138(5,0x00); //关蜂鸣器、继电器 Timer0Init(); //定时器0初始化 init_adc(); //PCF8592初始化 while(1) { if(adc_flag) { adc_flag = 0; //转换标志位清零 TR0 = 0; //关闭定时器,防止通信协议出错 team = Read_adc(); //开始读取所转换的值 TR0 = 1; //打开定时器 //电压转换公式:电压值=读取值/255*5 这里是保留小数后面两位; team1 = team * 5 / 255 *100; //分别把值赋给数码管 smg_count[5] = duan[(team1/100)] & 0x7f; smg_count[6] = duan[team1%100/10]; smg_count[7] = duan[team%10]; } } } //定时器0服务函数 void timer0() interrupt 1 { //静态变量,用于存放定时次数 static uchar flag = 0; flag ++; //如果到了50ms则启动转换 if(flag == 50) { adc_flag = 1; flag = 0; } //数码管显示函数 smg_display(); } ``` # 本次文章就写到这里啦!感谢观看哦!!!# [1]: https://www.qingfengblog.com/usr/uploads/2020/02/928623926.png [2]: https://www.qingfengblog.com/usr/uploads/2020/02/3731754518.png [3]: https://www.qingfengblog.com/usr/uploads/2020/02/4202903876.png [4]: https://www.qingfengblog.com/usr/uploads/2020/02/364457129.png [5]: https://www.qingfengblog.com/usr/uploads/2020/02/2342053042.png Last modification:February 22, 2020 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 0 如果觉得我的文章对你有用,请随意赞赏