PCF8591使用及Python控制

PCF8591使用及Python控制

INTRODUCTION

  你们必定对于PCF8591芯片,对于其中的A0、A一、A二、channel0-channel三、AIN0-AIN三、AOUT、SCL、SDA等等以及I2C协议有着很大的困惑。同时也没有彻底理解Python代码中的0x40、0x4一、0x4二、0x4三、0x48究竟是什么,bus.write_byte(0x48, 0x40) bus.write_byte(0x48, 0x40, value) bus.read_byte(0x48)这些代码到底控制什么。python

  这篇文章将帮助你们理清以上内容。git

1、PCF8591与Raspberry Pi的关系

  作一个很简单的形象化,将整个系统看做一个进水/出水装置。
MASTER与SLAVER示意图
  装置有四根进水管,分别命名为AIN0, AIN1, AIN2, AIN3,还有惟一一根出水管,命名为AOUT。还有四个中转通道,分别命名为channel0, channel1, channel2, channel3,从四根进水管进来的水能够受控制地从某一个通道中流出,进入总管道,以后经过两根出水管道——SDA和SCL,进入一个水的加工处理储存设备。

微信

  Raspberry PI是“MASTER”,PCF是“SLAVER”,他们之间是主从关系。若是主器件要发送数据给从器件,则会启动数据传输,并发送数据至从器件,最后终止数据传输;若是主器件想要接受从器件发来的数据,一样也是发出命令启动数据传输,接收从器件发来的数据,最后终止接收过程。
  强调一点:每次数据传输都是由“MASTER主动开启的。
  是否清晰了一些?理清总的关系之后咱们再来看具体细节。

并发

2、PCF的地址与A0-A2引脚

  一个“MASTER”不可能只有一个“SLAVER”。因此对于“MASTER”来讲,须要给“SLAVERS”编号,以便控制、命令。这个编号也就是PCF地址。地址的编码规则以下图:
ADDRESS编码规则
  能够看出,这是一个七位二进制数,前四位是fixed number,后三位分别对应PCF的三个引脚——A0, A1, A2。每个引脚的值均可以是0或1,一共2^3=8种排列方式,也即一共8个可以使用的地址。因此一个“MASTER”最多能同时控制8个“SLAVER”。对于PCF8591来讲,其default address为0x48(十六进制数),转换为二进制数是1001000,因此能够知道他的A0、A一、A2三个引脚值均为0。

学习

  须要再提早强调一点,Python代码中全部0x十六进制数,只有0x48表明Address,也只有PCF拥有Address,其他全部十六进制数都不表明Address。(这是一个天坑)ui

3、I2C协议与SDA SCL

  “MASTER”与“SLAVERS”之间须要有沟通交流,给“SLAVER”分配工做或是让“SLAVER”汇报工做,并告知“SLAVER”如何作。因此他们之间有一个特定的沟通方式,这个沟通方式就叫I^2 C通讯协议(Inter-Integrated Circuit)。I^2 C总线须要两根线来实现链接于总线上的器件之间的信息传输,一根是SDA(Serial Data)串行数据线,另外一根是SCL(Serial Clock)串行时钟线。编码

  下图是AD convert的I2C数据传输流程。
I2C传输
  是否是很使人迷惑?不要紧,形象化的解释来了。以前有说过,I2C就是一种沟通方式,因此这个数据传输流程,就能够理解为在I2C这种特定沟通方式下的一次沟通。讲的更通俗一点,就是一次聊天对话。如何理解?

spa

  聊天的第一步,是拿出手机打开微信。(对应START condition,当SDA和SCL两条线的电平同时知足某一特定条件时,传输开始。)3d

  打开微信后须要找一个联系人才能聊天,咱们以前说“MASTER”有不少“SLAVERS”,这一步即是“MASTER”选择与某一个“SLAVER”对话。(对应ADDRESS。)code

  第三步,“MASTER”肯定此次对话的目的的给“SLAVER”分配活儿干,仍是让“SLAVER”干完活后汇报。(对应R/W,R means read,W means write,控制此次数据传输的目的是树莓派写入一个值到PCF中,仍是树莓派从PCF中读取一个值。)
能够看到有不少ACK。ACK means Acknowledge character,即确认字符,意思是数据接收者告诉数据传输者,“已收到”。

  以后即是数据传输,每传输一个byte的数据,都会反馈一个ACK确认数据收到。
chatting

4、Python代码控制读取/写入值

import smbus
import time

if __name__ == "__main__":
    bus=smbus.SMBus(1)
    bus.write_byte(0x48,0x42)
    bus.read_byte(0x48)
    while True:
        num= bus.read_byte(0x48)
        print(num)
        bus.write_byte_data(0x48,0x42,160)
        time.sleep(0.01)

  其实代码很是简单。以前参考文献给出的代码使人迷惑的缘由主要是自定义了不少方法。如今用最简单的办法写出来,用到的核心方法只有三个:

bus.read_byte(ADDRESS)
bus.write_byte(ADDRESS, CONTROL_byte)
bus.write_byte_data(ADDRESS, CONTROL_byte, value)

  顾名思义,read_byte就是从PCF中读取一个byte的数据, write_byte就是向PCF中写入一个byte的数据,write_byte_data就是像PCF中写入data。

  有人就会问了,write_byte和write_byte_data有什么区别?

  还记得在PART1中强调的点吗?每次数据传输必定是由“MASTER”主动开启的,因此write_byte的目的是明确“MASTER”与哪一个“SLAVER”对话,创建联系,并以一个“CONTROL byte”告知“SLAVER”如何工做,并不传输value。这也是为何代码中的第一步是bus.read_byte(0x48)

  树莓派主动开启对话,与0x48地址对应的PCF8591创建联系,并经过一个CONTROL byte明确工做模式(Analog Input Mode)。

  第二个问题来了,CONTROL byte是什么?那不是个地址吗?

  这时候应该要想起PART2强调的内容(只有0x48是地址)。write_byte方法的第二个传参是一个CONTROL byte。
analog input mode
  这是官方文档对于CONTROL byte的解释。8位二进制码,其中第一位和第五位都是fixed number—0,可被编写的部分是第二位——表明是否开启AOUT输出口,0表示不开启,1表示开启,只有该位是1的时候,DA才会工做,数字信号被转为模拟信号从AOUT端口输出;第三四位——表明四种不一样的analog input mode,用以明确AIN端口与channel之间的数据关系。
?
  在第一幅图中有一个大大的问号,analog input mode即是明确这个问号。有两种对应关系:一种是single-ended,即一对一。例如在00模式下,从channel0中读到的数据就是AIN0口输入的模拟量通过AD转换后的数字值,00模式的四个AIN端口都是分别一一对应于一个channel;在10模式下,AIN0和AIN1是一一对应channel0和channel1。另一种对应关系是differential,即差分。例如在01模式下,channel0的值是AIN0与AIN3的差值;在10模式下,channel2的值是AIN2与AIN3的差值。



  CONTROL byte的第七八位是AD channel number,用来控制从哪一个channel读取数据。第六位是auto-increment,值为1时,每次读取数据都会自动切换channel,第一次读channel0的数据,下一次读到的就是channel1的数据,以此类推。

  在Python代码中,CONTROL byte是一个十六进制数,转换为二进制后就是八位二进制。控制方法为:肯定CONTROL byte中可编码位上你想要的取值,获得一个八位二进制数,转为十六进制数,输入Python方法。

  讲到这里后就几乎结束了大部分的内容。继续代码示例,read_byte,读值过程不须要CONTROL byte,因此传参只有ADDRESS。

  能够看到,在真正读取想要的num以前,还有一次空读。这是由于PCF8591在接到读取命令后,同时进行上一次的转换数据的传输及本次的数据转换,因此第一次空读的数据是一个不肯定的数,第一次的空读只是为了启动下一次正常的读取。

bus.write_byte_data(0x48,0x42,160)

  write_byte_data这个方法再多解释一下,写入的值会直接做为一个digital value,通过转换后以电压形式的模拟量从AOUT口输出。咱们使用的PCF8591是一个8bits的AD/DA转换器,8bits的意思是AD/DA共有 2^8=256个刻度,因此传输的digital值范围是0-255。举个例子,你但愿从AOUT口输出一个3.13V的电压给LED灯,应该写入的值为3.13v/5v*255=160(这里ADC接5v的基准电压),可是实际上用电压表测得此时LED灯两端电压为2.64v,与理想值有必定偏差。

5、Something More

  我几乎尝试了全部的analog input mode,将麦克风接入不一样的AIN口,选择不一样的channel,用示波器/电压表+python画出的图进行了不少不少实验,在差分模式下仍是有不少难以解释的试验现象,可是基本能够确定整个原理是没有错的。

  补充一点,single-ended模式下,AD转换的模拟量就是该AIN口与GND的电压;differential模式下,AD转换的模拟量则是两个AIN口之间的电压差,因此若是只使用一个AIN口且还想要用差分模式,那么另外一个AIN口要记着接地。

6、Learning Experience

本次超过30个小时的学习过程获得的经验:
①永远不要认为你以前的理解都是对的。发现整个理解过程出现错误,有时 候有必要推倒重建。
②网上资料良莠不齐,错误百出,绝对不能盲目相信。
③实验要清楚记录条件及现象,否则会由于没记清现象/条件而作不少次重复实验。
④在学习一个全新的知识系统的过程当中会有不少信息涌入,每每在查找资料解决问题1的时候会发现问题2,而后注意力转移到问题2上,以后又会发现新的问题,进入一个死循环。因此须要适时中止,思考一下你是谁,你在哪儿,你要干什么。
⑤把问题记录下来,而后接着探索。颇有可能在次日的时候你回看第一天的问题,会发出感叹:我怎么会问这么愚蠢的问题。
⑥队友很重要。讨论/止损/提问/复盘/总结。





个人第一篇CSDN博客,但愿讲的清楚。但愿你们喜欢!!

相关文章
相关标签/搜索