嵌入式萌新一枚,文章有解释不清或者错误的地方希望大佬能在评论区指正,感激不尽!
大概原理: 通过TSL2561芯片资料可以找到,TSL2561光照传感器上有两个采集环境光强的探测器,一个探测器采集红外光和可见光的数据,另一个探测器采集红外光的数据,每个探测器采集的数据按高八位和低八位分别存到两个寄存器上,所以我们一共要读取4个寄存器的数据。将四个寄存器的数据按特定的公式进行运算就可以得到环境可见光数据。
为什么要采集红外光的数据呢? 为了减小误差。因为红外光对传统硅探测器的影响比较大,所以干脆将红外光单独采集出来,两个探测器的数据进行一定的处理就可以得到精度较高的环境可见光数据。
Python编程大概思路:通过封装好的I2C协议函数进行读取数据操作。 先写两个函数,一个函数对传感器进行初始化配置,然后启动光传感器进行采集数据,然后将采集的数据存入数组,再将该数组作为返回值返回。第二个函数将第一个函数的返回值作为形式参数传入,再对其返回值按照公式进行处理 ,最后将处理完的数据打印出来
- 下图是TSL2561的寄存器分布图,我们采集光强要用到的寄存器分别是上面两个COMMAND和CONTROL寄存器和下面四个数据寄存器,它们的地址分别是0,1,C,D,E,F 。
我们所有寄存器控制都要通过命令寄存器也就是COMMAND寄存器来控制,所以给寄存器发送命令时高八位的最高位必须为1,低八位即为寄存器地址
控制寄存器CONTROL:传感器上的探测器需要通过此寄存器才能开启,给此寄存器发送命令0x03时,开启探测器功能,发送命令0x00时,关闭探测功能
- 下图是将采集到的数据按照此种方式进行处理
首先在命令行输入i2cdetect -y 1以查看传感器设备地址
pi@raspberrypi:~ $ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- 39 -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
我们可以看到I2C协议总线里只有一个从设备,那就是TSL2561传感器,地址为0x39
接着在命令行通过vim命令进入代码编辑界面
pi@raspberrypi:~ $ vim light.py
开始将两个包包含进来
#!/usr/lib/python3
import wiringpi
import time
开始定义第一个函数sum_Lux,将第一个数据寄存器地址赋值给reg_addr,定义一个长度为4的空列表(类似于C语言里面的数组),再传入一个空列表,用以存储处理后的数据
def sum_Lux(ch_a):
reg_addr=0x8C
reg_data=[0,0,0,0]
调用wiringpi包里的wiringPiI2CSetup()函数,返回值赋值给一个变量
该函数的原型为:int wiringPiI2CSetup(int devId);该函数会获取树莓派的版本幵依据此打开/dev 目录下对应的设备。返回值是标准的 Linux 文件句柄,如果有错误,则返回-1。
i2cfd = wiringpi.wiringPiI2CSetup(0x39)
接下来调用wiringpi包里的wiringPiI2CWriteReg8()函数,给控制寄存器发送命令0x03,使其开始探测功能
该函数的原型为:int wiringPiI2CWriteReg8(int fd, int reg, int data);
控制寄存器地址为0,给该寄存器发送命令的地址却是0x80,因为两个字节的地址高八位最高位是COMMAND寄存器,数值必须为1,所以第一个字节为8,第二个字节为控制寄存器地址
wiringpi.wiringPiI2CWriteReg8(i2cfd,0x80,0x03)
然后调用time包里的sleep函数,使程序延迟一段时间
因为传感器开启探测功能后需要有400ms时间让探测器探测数据并将数据存到数据寄存器里
time.sleep(0.5)
接着用一个for循环依次将数据寄存器的值取出来,并存到实现定义好的空列表里
利用wiringPiI2CWrite函数给寄存器发送命令,通知寄存器我将要读取你的数据
再调用wiringPiI2CReadReg8()函数读取寄存器数据
因为数据寄存器地址依次是C,D,E,F。所以地址加1即是下一个数据寄存器的地址
for i in [0,1,2,3]:
wiringpi.wiringPiI2CWrite(i2cfd,reg_addr)
reg_data[i]=wiringpi.wiringPiI2CReadReg8(i2cfd,reg_addr)
reg_addr+=1
然后将高八位数据左移8位与低八位数据相加,即可得到该探测器的实际数据
并将得到的实际数据存到列表中,最后返回列表
ch_a[0]=256*reg_data[1]+reg_data[0]
ch_a[1]=256*reg_data[3]+reg_data[2]
return ch_a
接着定义一个函数cal_Lux()用以处理上一个函数采集的数据,再将处理后的光强的值返回
def cal_Lux(ch):
bizhi=ch[1]/ch[0]
if(bizhi<=0):
return -1
elif bizhi<=0.52:
Lux=(0.0315*ch[0]-0.0593*ch[0]*((ch[1]/ch[0])**1.4))
elif bizhi<=0.65:
Lux=(0.0229*ch[0]-0.0291*ch[1])
elif bizhi<=0.80:
Lux=(0.0157*ch[0]-0.0180*ch[1])
elif bizhi<=1.3:
Lux=(0.00338*ch[0]-0.0026*ch[1])
else:
Lux=0
return Lux
定义一个空列表用以数据中转,再调用两个函数,将光强的真实值打印出来
CH = [0,0,0,0]
Lux = cal_Lux(sum_Lux(CH))
print("光感应强度为:",Lux)
代码拆分解释到此结束
下面是完整的python源代码
#!/usr/lib/python3.5
import wiringpi
import time
def cal_Lux(ch):
bizhi=ch[1]/ch[0]
if(bizhi<=0):
return -1
elif bizhi<=0.52:
Lux=(0.0315*ch[0]-0.0593*ch[0]*((ch[1]/ch[0])**1.4))
elif bizhi<=0.65:
Lux=(0.0229*ch[0]-0.0291*ch[1])
elif bizhi<=0.80:
Lux=(0.0157*ch[0]-0.0180*ch[1])
elif bizhi<=1.3:
Lux=(0.00338*ch[0]-0.0026*ch[1])
else:
Lux=0
return Lux
def sum_Lux(ch_a):
reg_addr=0x8C
reg_data=[0,0,0,0]
i2cfd=wiringpi.wiringPiI2CSetup(0x39)
wiringpi.wiringPiI2CWriteReg8(i2cfd,0x80,0x03)
time.sleep(0.5)
for i in [0,1,2,3]:
wiringpi.wiringPiI2CWrite(i2cfd,reg_addr)
reg_data[i]=wiringpi.wiringPiI2CReadReg8(i2cfd,reg_addr)
reg_addr+=1
ch_a[0]=256*reg_data[1]+reg_data[0]
ch_a[1]=256*reg_data[3]+reg_data[2]
return ch_a
CH=[0,0,0,0]
Lux=cal_Lux(sum_Lux(CH))
print("光感应强度为:",Lux)
在命令行执行
pi@raspberrypi:~ $ python light.py
即可显示光强数值
由于此Python代码完全是由C语言逐句改写,下面也贴一下用C语言实现的源代码
#include
#include
#include
#include
//编译的时候要链接-lm库和-lwiringPi库,lm库用以运行pow函数
//
int main(int argc , char **argv[])
{
double Lux ; //光强的值
double* sum_Lux(double* CH_L) ; //采集光强的原始值,返回数组首地址
double cal_Lux(double* ch); //将光强原始值进行处理,得到光强实际值
double CH[4] ; //存储光强原始值,
Lux = cal_Lux(sum_Lux(CH));
printf("%f\n",Lux);
return 0 ;
}
double cal_Lux(double* ch)
{
double bizhi ; //两个传感器原始值的比值
double Lux ; //光强实际值
bizhi = ch[1]/ch[0] ; //根据计算公式处理光强原始值
if(bizhi <= 0 )
return -1 ;
else if(bizhi <= 0.52)
Lux = (0.0315*ch[0] - 0.0593*ch[0]*pow(ch[1]/ch[0],1.4));
else if(bizhi <= 0.65)
Lux = (0.0229*ch[0] - 0.0291*ch[1]);
else if(bizhi <= 0.80)
Lux = (0.0157*ch[0] - 0.0180*ch[1]);
else if(bizhi <= 1.3)
Lux = (0.00338*ch[0] - 0.0026*ch[1]);
else
Lux = 0 ;
return Lux ;
}
double* sum_Lux(double* CH_L)
{
int reg_addr = 0x8C ; //数据寄存器初始地址0x8C
int i = 0 ;
int i2cfd ;
int reg_data[4];
i2cfd = wiringPiI2CSetup(0x39); //开启光传感器,将文件流赋值给i2c变量
wiringPiI2CWriteReg8(i2cfd,0x80,0x03); //给控制器寄存器发送指令0x03,命令传感器开始采集光强
sleep(1) ; //采集光强数据后延迟1秒,再对寄存器进行读取数据
for(i ; i < 4 ; i++)
{
wiringPiI2CWrite(i2cfd, reg_addr); //给寄存器发送指令,以便读取寄存器的数值
reg_data[i] = wiringPiI2CReadReg8(i2cfd,reg_addr); //开始读取寄存器数据,寄存器地址依次递增,采用八位函数读取,一次读取两个字节
reg_addr++ ;
}
CH_L[0] = 256 * reg_data[1] + reg_data[0]; //高八位数据左移8位,与低八位相加
CH_L[1] = 256 * reg_data[3] + reg_data[2];
return CH_L ; //返回数组首地址
}