失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 树莓派——TSL2561获取光强数值(C语言)

树莓派——TSL2561获取光强数值(C语言)

时间:2021-12-24 14:56:07

相关推荐

树莓派——TSL2561获取光强数值(C语言)

TSL2561获取光强

一、TSL2561光强传感器二、使能内核I2C驱动模块三、TSL2561寄存器的访问四、C语言获取光强代码

一、TSL2561光强传感器

TSL2561是一款高速、低功耗、宽量程、可编程灵活配置的光强度数字转换芯片,特性如下。适合利用树莓派开发板或STM32型单片机来进行编程开发。了解详细芯片手册点击 : datasheet

◇ 可编程配置许可的光强度上下阈值,当实际光照度超过该阈值时给出中断信号;

◇ 数字输出符合标准的SMBus(TSL2560)和I2C(TSL2561)总线协议;

◇ 模拟增益和数字输出时间可编程控制;

◇ 1.25 mm×1.75 mm超小封装,在低功耗模式下,功耗仅为0.75 mW;

◇ 自动抑制50 Hz/60 Hz的光照波动。

引脚功能

内部结构和工作原理

TSL256x 是第二代周围环境光强度传感器,其内部结构如下图所示。*通道0和通道1是两个光敏二极管,其中通道0对可见光和红外线都敏感,而通道1仅对红外线敏感。积分式A/D转换器对流过光敏二极管的电流进行积分,并转换为数字量,在转换结来后将转换结果存入芯片内部通道0和通道1各自的寄存器中。当一个积分周期完成之后,积分式A/D转换器将自动开始下一个积分转换过程。微控制器和TSL2560可通过标准的SMBus(System Management Bus)V1.1或V2.0实现,TSL2561则可通过I2C总线协议访问。

硬件设计

TSL2561能够通过I2C总线访问,所以硬件接口电路很简单。假如所选用的微控制器带有I2C总线控制器,则将该总线的时钟线和数据线直接和TSL2561的I2C总线的SCL和SDA分别相连;假如微控制器内部没有上拉电阻,则还需要再用2个上拉电阻接到总线上。假如微控制器不带I2C总线控制器,则将TSL2561的I2C总线的SCL和SDA和普通I/O口连接即可:但编程时需要模拟I2C总线的时序来访问TSL2561,INT引脚接微控制器的外部中断。硬件连接如下图所示。

软件设计

微控制器能够通过I2C总线协议对TSL2561进行读写。写数据时,先发送器件地址,然后发送要写的数据。TSL2561的写操作过程如下:先发送一组器件地址;然后写命令码,命令码是指定接下来写寄存器的地址00h~0fh和写寄存器的方式,是以字节、字或块(几个字)为单位进行写操作的:最后发送要写的数据,根据前而命令码规定写寄存器的方式,能够连续发送要写的数据,内部写寄存器会自动加1。

TSL2561模块与树莓派的连接

直接按照以上引脚图去逐一连接芯片的四个管脚(除去INT)即可,其中电源引脚应接在3.3V引脚上

二、使能内核I2C驱动模块

TSL2561数据传输的原理遵循IIC(I2C)总线协议,仅依靠一条时钟线和一条数据总线即可完成光强数据的传输。有关I2C总线协议的详细内容见上篇博客:I2C总线

1. 配置内核启动后自动加载I2C驱动

pi@raspberrypi:~ $ sudo raspi-config

该配置会将/boot/config.txt 文件中的下面这个选项打开:

dtparam=i2c_arm=on

2.安装i2c的相关驱动

重启树莓派之后会发现系统启动之后会自动安装i2c的相关驱动

pi@raspberrypi:~ $ sudo rebootpi@raspberrypi:~ $ sudo apt-get install i2c-toolspi@raspberrypi:~ $ lsmod | grep i2c

查看设备地址命令:

pi@raspberrypi:~ $ sudo i2cdetect -y 1

具体如图

使用i2cdetect命令可以查看到SHT21温湿度传感器设备地址0x39。

三、TSL2561寄存器的访问

对TSL256x的控制是通过对其内部的16个寄存器的读写来实现的,其地址如下表所列。TSL2561启动、寄存器访问、数据的读取都是通过写命令控制字的方法来实现的,TSL2561的用户手册里面给出了对应寄存器的名称、用途和访问方法:

上面是TSL2561内部所有寄存器的类型以及对应的地址,而读取光强仅需利用其中的命令寄存器(command)、控制寄存器(control)和数据寄存器(Ch,Dh,Eh,Fh)。数据寄存器中的值经过位运算和加法运算之后,便可生成对应ADC通道(ADC channel)内的采样值,所以没必要单独介绍数据寄存器,直接套公式即可,即:

Channel_0 = DATA0HIGH<<8 + DATA0LOW;Channel_1 = DATA1HIGH<<8 + DATA1LOW;

1.命令寄存器

CMD设置为1才可以正常访问ADDRESS位有3位,对应着上一张图片里面数据寄存器的地址。

例如要访问数据寄存器Ch,就应该将命令寄存器设置为10001100B,即0x8c,当不需要访问数据寄存器时,ADDRESS直接写为0000B(0x0)即可。由此可见,命令寄存器在TSL2561内部的地址是0x80

2.控制寄存器

TSL2561的启动取决于控制寄存器中的POWER位,其他位是保留位,无需考虑操作,直接置0即可。POWER位置为11B,即0x03是启动POWER位置为00B,即0x00是关闭

3.光强度值的计算

写入控制寄存器控制字使得TSL2561成功启动,并且正常读取到四个数据寄存器中的值之后,就可以按照用户手册中的计算公式进行光强计算了:

TSL2561有两种封装类型,我使用的芯片属于图中所述的第二种,所以计算光强时就使用第二种封装类型里面的公式就行了,芯片的封装类型在购买来之前的包装袋上有说明。

四、C语言获取光强代码

程序代码主要基于用户空间使用i2c_dev来进行编程,详情了解点击 i2c_dev 博客

(1)代码模块

根据寄存器的功能不同,单独设计功能模块,便于函数在其他地方的调用,也使主函数更加简洁.

打开设备对应节点模块启动/关停模块读写模块光强计算模块主函数

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <math.h>#include <errno.h>#include <time.h>#include <sys/ioctl.h>#include <linux/i2c.h>#include <linux/i2c-dev.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define TSL2561_I2C_ADDR0x39 /* 设备地址为0x39 */ #define CONTROL_REG 0x80 /* 命令寄存器在TSL2561内部的地址是0x80 */#define REG_COUNT 4/* 寄存器数量 */ #define POWER_UP 0x03 /* 上电 */#define POWER_DOWN 0x00 /* 断电 */#define ON 1 /*用于启动或关闭*/#define OFF 0/*datasheet中4个数据寄存器的地址是依次递增,所以运用枚举即可*/enum{/* Channel_0 = DATA0HIGH<<8 + DATA0LOW */DATA0LOW = 0x8c,DATA0HIGH,/* Channel_1 = DATA1HIGH<<8 + DATA1LOW */DATA1LOW,DATA1HIGH,};int s_tsl_fd = -1;static const int regs_addr[REG_COUNT]={DATA0LOW, DATA0HIGH, DATA1LOW, DATA1HIGH};/*初始化模块:打开对应的设备节点*/int tsl2561_init(void){if( (s_tsl_fd = open("/dev/i2c-1", O_RDWR)) < 0 ){printf("open /dev/i2c-1 error!\n");return -1;}printf("open /dev/i2c-1 successfully! s_tsl_fd = %d\n", s_tsl_fd);return s_tsl_fd;}/*启动模块:上电/断电*/int tsl2561_power(int cmd){struct i2c_msgmsg;struct i2c_rdwr_ioctl_data data;unsigned charbuf[2];/*设置 i2c_msg 的结构体*/msg.addr = TSL2561_I2C_ADDR; /*从机地址*/msg.flags = 0; /*读写标志*/msg.len = 1; /*数据长度*/msg.buf = buf; /*msg里的buf为指向buf[]的数据指针*//*设置 i2c_rdwr_ioctl_data 的结构体*/data.msgs = &msg; /*msgs为指向 i2c_msgs 的指针*/data.nmsgs = 1;/*消息个数*//*写入命令寄存器地址,开始i2c层通信*/msg.buf[0] = CONTROL_REG;if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 ){printf("%s() ioctl failure: %s\n", __func__, strerror(errno));return -1;}/*通过ON/OFF进行上电/断电*/if(cmd){msg.buf[0] = POWER_UP;}else{msg.buf[0] = POWER_DOWN;}/*再次写入命令,进行通信*/if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 ){printf("%s() ioctl failure: %s\n", __func__, strerror(errno));return -1;}return 0;}/*读写模块:写入命令字,读取寄存器数据*/int tsl2561_read_reg(unsigned char regaddr, unsigned char *regval){struct i2c_msg msg;struct i2c_rdwr_ioctl_datadata;unsigned char buf[2];msg.addr= TSL2561_I2C_ADDR;msg.flags=0;msg.len= 1;msg.buf= buf;msg.buf[0] = regaddr;data.nmsgs= 1;data.msgs= &msg;if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 ){printf("%s() ioctl failure: %s\n", __func__, strerror(errno));return -1;}memset(buf, 0, sizeof(buf));msg.addr= TSL2561_I2C_ADDR;msg.flags=I2C_M_RD;msg.len= 1;msg.buf= buf;data.nmsgs= 1;data.msgs= &msg;if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 ){printf("%s() ioctl failure: %s\n", __func__, strerror(errno));return -1;}*regval = msg.buf[0];return 0;}/*光强获取模块:启动tsl2561,读写数据寄存器,拿到值进行光强计算 */float tsl2561_get_lux( float *lux){int i;unsigned char reg_data[REG_COUNT];unsigned char buf;int chn0_data = 0;int chn1_data = 0;floatdiv = 0.0;//floatlux = 0.0;tsl2561_power(ON);sleep(1);for(i=0; i<REG_COUNT; i++){tsl2561_read_reg(regs_addr[i], &reg_data[i]); /* 将定义的全局变量数组regs_addr[REG_COUNT]利用循环依次传入,拿到的数据依次填入定义的局部变量reg_data[REG_COUNT]*/}/*将拿到的数据套公式计算*/chn0_data = reg_data[1]*256 + reg_data[0]; /* Channel0 = DATA0HIGH<<8 + DATA0LOW */chn1_data = reg_data[3]*256 + reg_data[2]; /* channel1 = DATA1HIGH<<8 + DATA1LOW */if( chn0_data<=0 || chn1_data<0 ){*lux = 0.0;goto OUT;}div = (float)chn1_data / (float)chn0_data;if( div>0 && div<=0.5 )*lux = 0.304*chn0_data-0.062*chn0_data*pow(div,1.4);else if( div>0.5 && div<=0.61 )*lux = 0.0224*chn0_data-0.031*chn1_data;else if( div>0.61 && div<=0.8 )*lux = 0.0128*chn0_data-0.0153*chn1_data;else if( div>0.8 && div<=1.3 )*lux = 0.00146*chn0_data-0.00112*chn1_data;else if( div>1.3 )*lux = 0.0;//printf("TSLl2561 get lux: [%.3f]\n", *lux);OUT:tsl2561_power(OFF);return 0;}int main(int argc, char **argv){float lux = 0.0;tsl2561_init();tsl2561_get_lux(&lux);printf("TSLl2561 get lux: [%.3f]\n", lux);return 0;}

运行结果

(2)注意事项

编译加-lm

使用math.h中声明的库函数还有一点特殊之处,gcc命令行必须加-lm选项,因为数学函数位于libm.so库文件中(这些库文件通常位于/lib目录下),-lm选项告诉编译器,我们程序中用到的数学函数要到这个库文件里找。打印文件描述符的值

调用open()打开文件而返回的文件描述符的值,打印观察是否为正常值,如果是012,那么系统则会报错不能使用ioctl(),因为因为0是标准输入,1是标准输出,2是标准出错,文件描述符的正确与否会导致相关API的调用失败。

如果觉得《树莓派——TSL2561获取光强数值(C语言)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。