树莓派上实现TSL2561对光照强度的获取

TSL2561是一块小型的可编程数字光强测量芯片,外部引脚仅由3.3v电源引脚、SDA引脚、SCL引脚、中断控制引脚、地引脚组成。适合利用树莓派开发板或STM32型单片机来进行编程开发,本博客讲述如何在树莓派上对照着TSL2561用户手册编程实现对TSL2561光强的读取。

I2C总线协议

TSL2561数据传输的原理遵循IIC(I2C)总线协议,仅依靠一条时钟线和一条数据总线即可完成光强数据的传输。有关I2C总线协议的详细内容见百度百科:I2C总线
树莓派内核已对该协议进行了封装和包含,用户直接启动i2c总线传输接口即可:

sudo raspi-config

树莓派上实现TSL2561对光照强度的获取_第1张图片
进入选项5“Interfacing Options”后,再选择i2c选项,进行配置。

TSL2561在树莓派上的接线

树莓派上实现TSL2561对光照强度的获取_第2张图片
直接按照以上引脚图去逐一连接芯片的四个管脚(除去INT)即可,其中电源引脚应接在3.3V引脚上

安装wiringPi接口函数库

无论是利用Python语言还是C语言,在对树莓派上所接入设备进行操作时,都需要引入wiringPi
wiringPi下载网址:wiringPi函数库下载

打开网页后,点击其中任何一个版本中的“snapshot”,即可下载。下载完成之后,将下载的压缩文件传输到树莓派开发板上,解压缩,cd进入对应的文件目录下,执行“./build”程序后,完成安装,此时可利用“gpio -v”或“gpio readall”命令来检查安装是否成功

查看TSL2561的设备地址和寄存器

软件包i2c-tools中包含与i2c设备操作有关的命令。若树莓派上未安装i2c-tools的软件包,则先安装:

sudo apt-get install i2c-tools

查看TSL2561设备的地址:

sudo i2cdetect -y 1

正常执行时,可以发现设备地址为0x39

查看TSL2561内部寄存器的值:

sudo i2cdump -f -y 1 0x39

多执行几次后,发现寄存器的值不改变,这是因为TSL2561还没有启动,要在后续的程序中通过写入命令控制字的方法才可以实现光强的读取。

TSL2561寄存器访问

TSL2561启动、寄存器访问、数据的读取都是通过写命令控制字的方法来实现的,TSL2561的用户手册里面给出了对应寄存器的名称、用途和访问方法:
树莓派上实现TSL2561对光照强度的获取_第3张图片
上面是TSL2561内部所有寄存器的类型以及对应的地址,而读取光强仅需利用其中的命令寄存器(command)、控制寄存器(control)和数据寄存器(Ch,Dh,Eh,Fh)。数据寄存器中的值经过位运算和加法运算之后,便可生成对应ADC通道(ADC channel)内的采样值,即:
Channel_0 = DATA0HIGH<<8 + DATA0LOW;
Channel_1 = DATA1HIGH<<8 + DATA1LOW;

下面给出命令寄存器的操作要点:
树莓派上实现TSL2561对光照强度的获取_第4张图片
一个命令寄存器有8位,其中最高位CMD必须设置为1才可以正常访问,ADDRESS位有3位,对应着上一张图片里面数据寄存器的地址。例如要访问数据寄存器Ch,就应该将命令寄存器设置为10001100B,即0x8c,当不需要访问数据寄存器时,ADDRESS直接写为0000B(0x0)即可。由此可见,命令寄存器在TSL2561内部的地址是0x80

下面是有关控制寄存器的说明:
树莓派上实现TSL2561对光照强度的获取_第5张图片
TSL2561的启动取决于控制寄存器中的POWER位,图片里面已经说明了启动方法与停止方法,就是把POWER位置为11B是启动,置为00是关闭。其他位是保留位,无需考虑操作,直接置0即可。

光强值的计算

写入控制寄存器控制字使得TSL2561成功启动,并且正常读取到四个数据寄存器中的值之后,就可以按照用户手册中的计算公式进行光强计算了:
树莓派上实现TSL2561对光照强度的获取_第6张图片
TSL2561有两种封装类型,本人手头上的芯片属于图中所述的第二种,所以计算光强时就使用第二种封装类型里面的公式就行了,芯片的封装类型在购买来之前的包装袋上有说明。

程序源码

Python版本

#!/usr/bin/python
##File name: tsl2561.py
import wiringpi as wpi
import time

TSL2561_ADDR			= 0x39
POWER_UP          		= 0x03
POWER_DOWN        		= 0x00
CONTROL_REG       		= 0x80
DATA0_LOW				= 0x8c
DATA0_HIGH			  	= 0x8d
DATA1_LOW				= 0x8e
DATA1_HIGH				= 0x8f


##Register initialization
tsl_fd = wpi.wiringPiI2CSetup(TSL2561_ADDR)


wpi.wiringPiI2CWrite(tsl_fd, CONTROL_REG)
wpi.wiringPiI2CWrite(tsl_fd, POWER_UP)

time.sleep(0.5)

##Read the values from Ch,Dh,Eh,Fh
wpi.wiringPiI2CWrite(tsl_fd, DATA0_LOW)
data0_low = wpi.wiringPiI2CRead(tsl_fd)

wpi.wiringPiI2CWrite(tsl_fd, DATA0_HIGH)
data0_high = wpi.wiringPiI2CRead(tsl_fd)

wpi.wiringPiI2CWrite(tsl_fd, DATA1_LOW)
data1_low = wpi.wiringPiI2CRead(tsl_fd)

wpi.wiringPiI2CWrite(tsl_fd, DATA1_HIGH)
data1_high = wpi.wiringPiI2CRead(tsl_fd)


##Convert the register values into sampling values(channel_0 & channel_1)
chn0=256*data0_high+data0_low
chn1=256*data1_high+data1_low


##According to the 'div', calculate the size of lux
if chn0==0:
    lux = 0
elif chn0!=0:
        div=float(chn1)/float(chn0)
        if (div>0 and div<=0.5):
            lux =  (0.304*chn0-0.062*chn0*(div**1.4))
        if (div>0.5 and div<=0.61):
			lux = (0.0224*chn0-0.031*chn1)
        if (div>0.61 and div<=0.8):
			lux = (0.0128*chn0-0.0153*chn1)
        if (div>0.8 and div<=1.3):
			lux = (0.00146*chn0-0.00112*chn1)
        if (div >1.3):
            lux = 0


##Display the Lux
print '%.3f' % lux

##End of sampling
wpi.wiringPiI2CWrite(tsl_fd, CONTROL_REG)
wpi.wiringPiI2CWrite(tsl_fd, POWER_DOWN)

C版本(编译命令:gcc tsl2561.c -lwiringpi -o tsl2561

/*File name: tsl2561.c*/
#include 
#include  /*This must be included but some manuals don't mention*/
#include 
#include 
#include 
#include 
#include 
#include 

#define CONTROL_REG						0x80
#define SENOR_ADDR 						0x39
#define REG_COUNT						4
#define POWER_UP						0x03
#define POWER_DOWN						0x00

enum								//Define the command control words.
{
		data0_low = 0x8c,
		data0_high,
		data1_low,
		data1_high,
};								
int reg_addr[REG_COUNT] = {data0_low, data0_high, data1_low, data1_high};


int main(int argc, char **argv)
{
	int 				tsl_fd = -1;
	int 				i;
	int 				chn_0 = 0;
	int 				chn_1 = 0;
	int 				reg_data[REG_COUNT];
	double				div = 0;
	double 				lux = 0;
    
    tsl_fd = wiringPiI2CSetup(SENOR_ADDR);	       			//This funtion returns a fd for next reading step.
	if(tsl_fd < 0)
	{
		printf("Fail to read the relative i2c-dev file: %s", strerror(errno));
		return -1;
	}
	
	wiringPiI2CWrite(tsl_fd, CONTROL_REG);
	wiringPiI2CWrite(tsl_fd, POWER_UP);
	sleep(1);

	for(i = 0; i0 && div<=0.5)
	{
		lux = 0.304*chn_0-0.062*chn_0*pow(div,1.4);
    }
    if(div>0.5 && div<=0.61)
    {
    	lux = 0.0224*chn_0-0.031*chn_1;
    }
    if(div>0.61 && div<=0.8)
    {
    	lux = 0.0128*chn_0-0.0153*chn_1;
    }
    if(div>0.8 && div<=1.3)
    {
    	lux = 0.00146*chn_0-0.00112*chn_1;
    }
    if(div>1.3)
    {
    	lux = 0;
    }

end:	printf("%.3f\n", lux);
		
		wiringPiI2CWrite(tsl_fd, CONTROL_REG);
		wiringPiI2CWrite(tsl_fd, POWER_DOWN);
		
    	return 0;
}

两种版本的代码逻辑是一样的,这里简要地提一下:

  1. 光强读取的流程为:设置启动、读取寄存器值、计算光强、关闭ADC计数通道;
  2. 程序里面的tsl_fd是一个文件描述符。在打开Linux系统下的I2C设备文件时,返回的是该文件的文件描述符。
  3. wiringPiI2CSetup的作用是初始化所有与I2C传输协议有关的引脚,其参数是I2C设备的地址;
  4. wiringPiI2CWrite的作用是写一个寄存器的值,第一个参数是已经打开的I2C设备文件描述符,第二个是操作的寄存器的地址或将要写入寄存器的值。一般写一个寄存器,先调用该函数写入寄存器的地址,然后调用该函数写入寄存器的值;
  5. wiringPiI2CRead的作用是读一个寄存器的值,返回值就是该寄存器的当前值。一般在调用wiringPiI2CWrite后使用该函数;
  6. 当channel0的值为0,或是两个通道的值均为负数时,说明ADC通道采样异常,此时返回的光强都是0;
  7. 设置芯片启动之后,都要让程序sleep些许时间,是因为ADC采样需要时间,否则最终显示的结果就都会是0;
  8. I2C设备启动之后,读取完数据,这是就应该在控制寄存器中写入00B的控制字,即停止采样,之后才能结束进程。

可能出现的问题以及解决方案

倘若在运行Python版本的程序时,抛出了“No module wiringpi”的错误,可以尝试用以下方法解决:
依次执行以下Linux命令:

sudo apt-get install python-dev python-pip
sudo pip install wiringpi2

可以知道,出现这种错误是因为缺少Python的wiringPi链接库,安装之后即可解决问题。

你可能感兴趣的:(树莓派开发)