一起玩转树莓派——树莓派模数/数模转换实践

Posted 珲少

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起玩转树莓派——树莓派模数/数模转换实践相关的知识,希望对你有一定的参考价值。

一起玩转树莓派(7)——树莓派模数/数模转换实践

如果你跟随本系列博客阅读到此,那么我相信,你已经能够使用树莓派的GPIO接口做许多有趣的事情了。但是不知你是否有发现,GPIO功能接口无论是输入还是输出,都只有高电平或低电平这两种选项,抽象到数学中,即只有0和1两种数值选项,这种离散数值的信号我们将其称为数字信号。更多时候,我们需要使用的元件并非只有开和关两种状态,这时候就需要用数值连续的信号来表达,这种连续的信号我们称之为模拟信号。本篇博客,我们将初步涉猎在树莓派上如何处理模拟信号。

一、树莓派模数转换模块基础

其实,直接使用GPIO的功能引脚并非完全不能处理模拟信号,我们知道无论需要多少种状态信号,通过GPIO的电平高低来处理都是无法实现的,但是你一定还记得PWM脉冲宽度调制技术,这其实就是一种将数字信号转换成模拟信号的方式,例如在前面的RGB实验中,我们曾使用PWM技术来控制3种发光二极管的发光明暗程度,如果将二极管的全亮定义为值1,全暗定义为值0,则在明暗之间可以通过设置不同的占空比来实现,例如需要亮度值为0.5时,只需要设置占空比为50%即可。通过PWM技术,虽然我们每个精确的时刻输出的依然是数字信号(0或1),但最终作用到元件上的信号是模拟的,这就完成了最简单的数模转换。

同样的道理,对于某些外接元件来说,其输出的信号也可能是模拟的,例如温度、光亮传感器,如果直接通过GPIO来获取这些元件的信号是模拟的,解析会非常困难,这时我们就需要一个数模转换模块来帮助我们将其转换为数字信号。

1、关于模数转换

数模转换器,又称D/A转换器,简称DAC。用来把数字量转换成模拟量的器件,关于数模转换器的电路和工作原理,不在本篇博客的核心讨论范围内,如果你有兴趣可以查阅数字电路和模拟电路的相关书籍。对其对应,模数转换器,又称为A/D转换器,简称ADC。用来把模拟信号转换为数字信号。树莓派本身没有集成模数转换模块,我们需要外接一个转换单元,在本实验中,我们采用基于PCF8591芯片的模数转换模块。

2、PCF8591芯片

PCF8591是一个单片集成,单独供电,低功耗的8位数A/D转换器,其芯片本身如下图所示:

可以看到其左右各有8个引脚,引脚功能图如下:

如上图所示,其AIN0到AIN3都是模拟输入引脚,其用来输入外接元件的模拟信号,它还有1个模拟输入引脚AOUT,用来输出模拟信号。

A0,A1,A2这3个引脚用来进行硬件地址编程,在嵌入式编程中,每个可编程的引脚我们将其理解为一个二进制位,3个引脚可以表达0-7这8个数字(000,001,010,011,100,101,110,111),因此在同一个I2C总线上,实际可以同时连接8个PCF8591芯片进行工作(I2C总线我们后面会介绍)。

VSS是接地引脚,VDD是工作电压引脚,AGND是模拟信号接地引脚,VREF是基准电源引脚。

OSC引脚为外部时钟输入端或内部时钟输出端,EXT如果接地,表示使用内部时钟,否则表示使用外部时钟。

SCL和SDA是I2C总线接口引脚,我们使用这两个引脚来进行数据传输,后面会介绍。

下面是PCF8591工作的电路原理图:

温馨提示,在阅读本篇博客时,难度可能要比本系列的前几篇博客大,你可能会感觉云里雾里,不要着急,后面我们会通过实验来帮助你理解模数转换以及I2总线的工作方式,掌握了这些,你的树莓派开发本领将进一步提升。

3、PCF8591实验模块

上一小节我们介绍了PCF8591芯片的引脚用法,实际在本实验中,我们并不会直接使用这个芯片,而是使用基于PCF8591芯片的一个工作模块,如下图所示:

如上图所示,此模块功能非常丰富,我们逐一介绍。

左边一列引脚作用分别为:

  • AOUT :内部芯片的DA 输出接口
  • AIN0 :内部芯片的模拟输入接口 0
  • AIN1 :内部芯片的模拟输入接口 1
  • AIN2 :内部芯片的模拟输入接口 2
  • AIN3 :内部芯片的模拟输入接口 3

右边一列引脚的作用为:

  • SCL :I2C 通信接口
  • SDA :I2C 通信接口
  • GND :模块地
  • VCC :电源接口 外接 3.3v-5v

除此之外,P4,P5,P6三个地方我们可以通过接短路帽的方式来选择是否接入相关功能。

P4:接上短路帽,表示将热敏电阻接入电路。

P5:接上短路帽,表示将光敏电阻接入电路。

P6:接上短路帽,表示将0-5V的可调节电压接入电路。

如果P4,P5,P6都不接短路帽,可以使用外部元件通过4个AIN引脚进行输入。关于这几个功能如何使用,后面我们会介绍。

此模块的工作示意图如下:

4.I2C总线基础知识

I2C总线是有Philips公司开发的一种简单、双向的同步串行总线。只需要两根线即可实现连接在总线上的设备间进行通信。在I2C总线的通讯协议中,SDA用来传输数据,SCL是串行的时钟线,传输流程如下图所示:

只看图你可能会感觉非常疑惑,结合树莓派工作起来,其实要理解并不复杂,在I2C协议中,树莓派就是主机,连接的PCF8591就是从机,主机与从机间无论是发送数据还是接受数据,都是由主机先发起,形象化的流程如下:

1.主机触发开始通信条件(SDA和SCL电平满足一定条件)。

2.通过地址找到要通信的从机(即PCF8591的3个地址编程引脚设置的地址)。

3.主机发送工作命令,即指定要从从机读数据还是写数据,读哪个通道的数据等(PCF8591有4个输入通道)。

4.收到数据会发送ACK回执。

5.结束此次通讯后,主机发送完成指令给从机(SDA和SCL电平满足一定条件)。

5.几个重要的指标概念

1.位数

ADC元件的位数非常重要,它描述了一种可读概念,以PCF8591为例,其实8位的数模转换芯片,也就是说其输出的数字量是0到255之间的数值,一共256种(2的8次方)。

2.基准电压

基准电压用来标准数模转换芯片输出的数值与电压之间关系,例如基准电压为5V,则0到255这256个刻度相当于将5V的电压平均分成了255份,数值没增加1,相当于电压约增加0.019V。

二、实验:测定温度和光亮度

现在,我们已经准备好了需要使用到的基础知识,可以开始我们的试验了。PCF8591自带光感与温度传感器,我们尝试使用这两个传感器来读取环境的温度与亮度。

1、连线

首先,我们可以先使用3个短路帽将PCF8591模块的P4、P5、P6都进行短接,如下图所示:

短接后,相当于我们将PCF8591模块自带的可调节输出电压、光感信号输出和温度信号输出都接入了电路,可以直接通过I2C总线对其数据进行获取。

之后,我们将PCF8591模块模块的SCL、SDA分别连接树莓派对应功能的引脚,GND进行接地,VCC接5V。关于找到树莓派上这些引脚的位置,可以参考下面博客:

https://my.oschina.net/u/2340880/blog/5123429

笔者这里使用扩展板来接线,会更加直观方便,如下图所示:

现在,我们已经完成了接线部分的工作。

2、代码编写

因为需要使用到树莓派的I2C总线功能,在开始编码前,我们需要将树莓派的I2C功能开启,打开树莓派的配置页面,在其interfaces选项中打开SPI和I2C选项,如下图所示。

我们先将完整的实验代码奉上:

#coding:utf-8

#SMBus (System Management Bus,系统管理总线) 
import smbus   #在程序中导入“smbus”模块
import time

bus = smbus.SMBus(1)         #创建一个smbus实例

# 数据读取的方法
def read(chn): #channel
    if chn == 0:
        #发送一个控制字节到设备 表示要读取AIN0通道的数据
        bus.write_byte(0x48,0x00)   
    if chn == 1:
        #发送一个控制字节到设备 表示要读取AIN1通道的数据
        bus.write_byte(0x48,0x01)
    if chn == 2:
        #发送一个控制字节到设备 表示要读取AIN2通道的数据
        bus.write_byte(0x48,0x02)
    if chn == 3:
        #发送一个控制字节到设备 表示要读取AIN3通道的数据
        bus.write_byte(0x48,0x03)
    bus.read_byte(0x48)         # 空读一次,消费掉无效数据
    return bus.read_byte(0x48)  # 返回某通道输入的模拟值A/D转换后的数字值

if __name__ == "__main__":
    while True:
        print(\'电位计 AIN3 = \', 0.0196 * read(3))   #电位计模拟信号转化的数字值
        print(\'光敏电阻 AIN0 = \', 255 - read(0))   #光敏电阻模拟信号转化的数字
        print(\'热敏电阻 AIN1 = \',255-read(1))      #热敏电阻模拟信号转化的数字值
        time.sleep(2)

下面我们来详细解释下上面的代码,首先smbus是一个Python模块,我们之前并没使用过,我们这次实验不使用GPIO,使用smbus来进行I2C总线管理。

在核心逻辑执行前,首先需要进行总线管理器的实例化,使用如下方法:

bus = smbus.SMBus(1)

其中的参数表示要使用的I2C版本。

前面我们分析过,在I2C总线通信中,主机先要发送指令告诉从机要做什么,对应代码中就是write_byte方法,这个方法用来向PCF芯片发送一个8位的控制指令,通过查阅芯片手册,很容易找到这个8位的控制指令每一位的意义,如下图所示:

我们从左往右来分析上图。

对于上图,我们在多解释一下,这个8位的控制指令,第1位和第5位都是默认填充位,都用0填充,目前没有任何意义。

第2位表示是否开启AOUT输出口,只有这一位为1,数模转换才会工作,数字信号才会被转换为模拟信号从AOUT引脚输出。(本次实验我们暂不使用)

第3位和第4位用来编程控制输入模式,这两位可以组成的数值有00,01,10,11共四种,设置为00表示AIN端口与输入通道一一对应。01,10,11这3种模式通道的值都是经过 处理的,上图有明确的标注,比如在11模式下,通道0的值是AIN0和AIN1输入的差分,通道1的值是AIN2和AIN3输入的差分。(本次实验我们也无需使用这一功能)

第6位的作用是设置是否自动切换通道,当其设置为1时,每次读取数据后,通道都会切换,例如第一次读取通道0的数值,第二次会在读取时,会自动读取通道1的数值。

第7位和第8位用来指定从哪个通道读取数值,有4个通道可用,分别通过00,01,10,11进行设置。

我们再来看下write_byte方法:

bus.write_byte(0x48,0x00)   

这个方法有两个参数,第2个参数就是我们上面说的指令参数,其用16进制表示,例如0x03设置为:

第1位 第2位 第3位 第4位 第5位 第6位 第7位 第8位
0(占位) 0(不使用AOUT) 0(一一对应模式) 0(一一对应模式) 0(占位) 0(不启动切换通道) 1(设置要读取的通道) 1(设置要读取的通道)

 write_byte这个方法的第1个参数用来设置I2C总线中要为哪一个从机设置指令,PCF的地址设置是7位,查阅手册如下:

可以看到第4位到第7位都是固定的,前3位是可编程的,还记得PCF的A0到A3引脚吧,就是让主机来分配从机地址使用的。我们可以知道,PCF从机的地址一定在二进制数01001000(0x48)到01001111(0x4F)之间,要查阅当前连接到树莓派的PCF从机的地址,可以在树莓派终端输入如下指令:

sudo i2cdetect -y 1

效果如下图所示:

从图中可以看到,树莓派当前只连接了一个I2C从机模块,分配的地址是0x48。

现在,上面的代码对你来说非常好理解了,需要注意,其中除了0x48表示的是PCF从机的地址外,其他十六进制数都是指令。指令设置完成后,接下来就可以进行数据的通信,使用如下方法:

bus.read_byte(0x48)  

这个方法用来通过I2C总线从PCF读取数据,上面代码中我们在读取数据时连续调用了两次,这是因为PCF8591接收到读取数据的命令后会同时进行上一次转换数据的传输和本次数据的转换,空读一次可以消费掉不正确的数据。

还有一点需要注意,对于我们使用的PCF8591模块来说,其自带的可调节电压会输出到第4个通道,光敏传感器的数据会输出到第1个通道,热敏传感器的数据会输出到第2个通道,在本次实验中,第3个通道不被使用(如果你使用的PCF8591模块和笔者的不太一样,对应通道可能会不同,可以自行测试)。

三、尽情玩耍

现在,你可以尝试在树莓派上运行写好的程序,通过输出可以看到当前的可调节电压、环境亮度和环境温度的情况。你可以尝试使用螺丝刀对电位计进行调节,可以实时的看到输出电压的改变,可以关灯和开灯来改变环境亮度,你也可以将热敏传感器握在手中,这些都可以明显的从输出信息上看到环境数据的变化。如下图所示:

这次,关于树莓派编程,我们学到了一些新的东西,使用I2C总线和数模转换模块我们可以做出更多有意思的东西,比如红外控制的自制电视机?比如有有马达驱动的遥控小车?等等...。你可以做好准备,我们后面会一起开发一些更好玩的东西!

以上是关于一起玩转树莓派——树莓派模数/数模转换实践的主要内容,如果未能解决你的问题,请参考以下文章

一起玩转树莓派——DS18B20测温模块应用

一起玩转树莓派(22)——DS1302硬件时钟实践

树莓派通过模数转换芯片ADC0832读取LM35温度传感器数据

一起玩转树莓派——从双色LED灯开始

一起玩转树莓派(11)——使用LCD屏

一起玩转树莓派(13)——雨滴检测传感器