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

2023-05-16

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

I2C总线协议

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

sudo raspi-config

树莓派配置界面
进入选项5“Interfacing Options”后,再选择i2c选项,进行配置。

TSL2561在树莓派上的接线

树莓派
直接按照以上引脚图去逐一连接芯片的四个管脚(除去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内部所有寄存器的类型以及对应的地址,而读取光强仅需利用其中的命令寄存器(command)、控制寄存器(control)和数据寄存器(Ch,Dh,Eh,Fh)。数据寄存器中的值经过位运算和加法运算之后,便可生成对应ADC通道(ADC channel)内的采样值,即:
Channel_0 = DATA0HIGH<<8 + DATA0LOW;
Channel_1 = DATA1HIGH<<8 + DATA1LOW;

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

下面是有关控制寄存器的说明:
在这里插入图片描述
TSL2561的启动取决于控制寄存器中的POWER位,图片里面已经说明了启动方法与停止方法,就是把POWER位置为11B是启动,置为00是关闭。其他位是保留位,无需考虑操作,直接置0即可。

光强值的计算

写入控制寄存器控制字使得TSL2561成功启动,并且正常读取到四个数据寄存器中的值之后,就可以按照用户手册中的计算公式进行光强计算了:
在这里插入图片描述
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 <wiringPi.h>
#include <wiringPiI2C.h> /*This must be included but some manuals don't mention*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <errno.h>

#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; i<REG_COUNT; i++)
	{
		wiringPiI2CWrite(tsl_fd, reg_addr[i]);			//Write command control words.
		reg_data[i] = wiringPiI2CRead(tsl_fd);			//Read the data from the registers.
	}
	
	chn_0 = reg_data[1]*256 + reg_data[0];				//channel_0 = DATA0_HIGH<<8 + DATA0_LOW;
	chn_1 = reg_data[3]*256 + reg_data[2];				//channel_1 = DATA1_HIGH<<8 + DATA1_LOW;

	if(chn_0==0)
	{
		lux = 0;
		goto end;
	}
	if(chn_0<0 || chn_1<0)
	{
		lux = 0;
		goto end;
	}

	div = (double)chn_1 / (double)chn_0;

	if(div>0 && 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链接库,安装之后即可解决问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

树莓派上实现TSL2561对光照强度的获取 的相关文章

  • 一种简单有效的锂电池充电均衡电路

    这个均衡电路用的是三个一模一样的并联稳压电路组成的 xff0c 每个电池上并一个 电路原理图如下 xff1a 每个稳压电源都调节到4 2V 均衡的原理是 xff0c 当电池电压都小于4 2V时 xff0c 并联稳压电路不起作用 xff0c
  • 基于opencv对图片的二进制流进行编解码

    span class token keyword import span cv2 span class token keyword import span numpy span class token keyword as span np
  • STM32与传感器串口通讯问题

    前言 STM32F407ZGT6使用串口通信发送指令给传感器 xff0c 让传感器返回测得的数据 1 过程 大家看我这个程序 xff0c 在main函数前面我定义了一个read instruction数组 xff0c 存放读取传感器的指令
  • 检测到"_ITERATOR_DEBUG_LEVEL"的不匹配项

    最近在项目中遇到了问题 xff0c 编译器提示 检测到 34 ITERATOR DEBUG LEVEL 34 的不匹配项 xff0c 上网查找后发现是编译Release版本用到了DEBUG库的原因 xff0c 其中也提供了在预编译中加入 3
  • C语言实现单链表的逆置

    单链表的逆置是一个非常经典的问题 xff0c 这里利用两个思想进行解决 首先 xff0c 我们需要看下原理图 xff0c 其实两个思想都是一样的 xff0c 都是使后一个的节点的 next 指针指向前一个节点 xff0c 依次递推 xff0
  • UNIX下C语言的图形编程-curses.h函数库

    相信您在网路上一定用过如 tin elm 等工具 这些软体有项共同的特色 即他们能利用上下左右等方向键来控制游标的位置 除此之外 这些程式 的画面也较为美观 对 Programming 有兴趣的朋友一定对此感到好奇 也 许他能在 PC 上用
  • 如何同时启动多个Tomcat服务器

    这篇文章转载自 如何同时启动多个Tomcat服务器 conf子目录中打开server xml文件 xff0c 查找以下三处 xff1a 1 修改http访问端口 xff08 默认为8080端口 xff09 span class hljs t
  • 找到合适的方案记录服务端日志

    做过服务端开发的同学都清楚日志是多么的重要 你要分析应用当天的 PV UV 你需要对日志进行统计分析 你需要排查程序 BUG 你需要寻找日志中的异常信息等等 所以 建立一套合适的日志体系是非常有必要的 日志体系一般都会遵循这么几个原则 根据
  • doPDF——word转为PDF软件的使用方法

    doPDF软件下载链接 xff0c 版本doPDF 7 3 379 点击打开链接 1 下载好doPDF软件我们就可以动手安装了 安装到选择打印机的界面 软件使用方法如图所示 xff08 备注我用的是word2007 xff0c 可能与200
  • 过去的 2017 年

    过去的 2017 年分为两个部分 xff0c 前半部分偏忙碌 xff0c 个人时间较少 xff0c 但是收获甚微 xff1b 后半部分进入了一个学习的环境 xff0c 最主要的就是个人可自由支配的时间多了 xff0c 留给了我很多思考的时间
  • Android四大组件详解

    注 xff1a 本文主要来自网易的一个博主的文章 xff0c 经过阅读 xff0c 总结 xff0c 故留下文章在此 Android四大基本组件介绍与生命周期 Android四大基本组件分别是Activity xff0c Service服务
  • Socket通信原理和实践

    我们深谙信息交流的价值 xff0c 那网络中进程之间如何通信 xff0c 如我们每天打开浏览器浏览网页时 xff0c 浏览器的进程怎么与web服务器通信的 xff1f 当你用QQ聊天时 xff0c QQ进程怎么与服务器或你好友所在的QQ进程
  • linux下查看和添加PATH环境变量

    linux下查看和添加PATH环境变量 PATH xff1a 决定了shell将到哪些目录中寻找命令或程序 xff0c PATH的值是一系列目录 xff0c 当您运行一个程序时 xff0c Linux在这些目录下进行搜寻编译链接 编辑你的
  • Linux 内存映射函数 mmap()函数详解

    一 概述 内存映射 xff0c 简而言之就是将用户空间的一段内存区域映射到内核空间 xff0c 映射成功后 xff0c 用户对这段内存区域的修改可以直接反映到内核空间 xff0c 同样 xff0c 内核空间对这段区域的修改也直接反映用户空间
  • Cygwin获取root权限

    1 找到cygwin 的etc目录中有一个名为passwd的文件 2 用写字板打开passwd 这个文件 xff0c 找到以下部分 xff0c 把其中的windows用户名换成root xff08 共3处都改过来 xff09 Adminis
  • Linux Shell 只列出目录的方法

    在实际应用中 xff0c 我们有时需要仅列出目录 xff0c 下面是 4 种不同的方法 1 利用 ls 命令的 d 选项 xff1a ls d Desktop pic shell src 2 利用 ls 命令的 F 选项 xff1a ls
  • 读《Linux内核设计与实现》我想到了这些书

    从题目中可以看到 xff0c 这篇文章是以我读 Linux内核设计与实现 而想到的其他我读过的书 xff0c 所以 xff0c 这篇文章的主要支撑点是 Linux内核 开始读这本书已经是很久以前的事了 xff0c 不过 xff0c 由于时间
  • 计算机保研复习

    操作系统 1 进程间的通信方式 无名管道pipe xff1a 管道是一种半双工的通信方式 xff0c 数据只能单向流动 xff0c 而且只能在具有亲缘关系的进程间使用 进程的亲缘关系通常是指父子进程关系 命名管道FIFO xff1a 有名管
  • Gyro陀螺仪 > MPU 6000 vs ICM 20689

    目录 参考 MPU 6000和ICM 20689对比 陀螺仪的选择 xff1a 采样率与噪声 参考 Gyro MPU 6000 vs ICM 20689 IntoFPV Forum FPV Drone Flight Controller E
  • PV操作与信号灯及例子+三大操作系统共同点的线程通信

    看待技术问题要瞄准其本质 xff0c 不管是Linux VxWorks还是WIN32 xff0c 其涉及到多线程的部分都是那些内容 xff0c 无非就是线程控制和线程通信 xff0c 它们的许多函数只是名称不同 xff0c 其实质含义是等价

随机推荐