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的光照波動,
- 引腳功能
| 引腳 | 功能 |
|---|---|
| VIN | 電源供電(3.3V) |
| GND | 接地 |
| SCL | I2C時鐘線 |
| SDA | I2C地址線 |
| INT | 中斷控制 |
-
內部結構和作業原理
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 reboot
pi@raspberrypi:~ $ sudo apt-get install i2c-tools
pi@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_ADDR 0x39 /* 設備地址為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_msg msg;
struct i2c_rdwr_ioctl_data data;
unsigned char buf[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_data data;
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;
float div = 0.0;
//float lux = 0.0;
tsl2561_power(ON);
sleep(1);
for(i=0; i<REG_COUNT; i++)
{
tsl2561_read_reg(regs_addr[i], ®_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的呼叫失敗,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286687.html
標籤:其他
上一篇:正點原子Linux板對接廣和通L610 ECM聯網測驗
下一篇:【嵌入式】串口控制
