嘗試使用TWI讀取溫度濕度傳感器hts221
hts221的介紹不細(xì)說(shuō),誤差什么的去看手冊(cè)吧~只說(shuō)說(shuō)它支持SPI 及 IIC通信,感覺(jué)相對(duì)來(lái)說(shuō),在傳感器方面似乎iic用的更多.因?yàn)橐灿玫搅撕芏嗥渌鹖ic的設(shè)備,所以直接使用lps22hb的iic了~
IIC通信就不多說(shuō)了,直接來(lái)看hts221 iic通信的格式:
address:0x5f, 所以如果是read address為0xbf, 如果是write address為0xbe.
和一般的iic讀寫過(guò)程沒(méi)有什么區(qū)別~
注意:datasheet中有這樣一段話:
After the start condition (ST) a slave address is sent, once a slave acknowledge (SAK) has been returned, an 8-bit sub-address (SUB) will be transmitted: the 7 LSB represents the actual register address while the MSB enables address autoincrement. If the MSB of the SUB field is ‘1’, the SUB (register address) will be automatically increased to allow multiple data read/write.
也就是說(shuō),hts221并不想其他IIC設(shè)備一樣,連續(xù)讀寫時(shí)自動(dòng)增加register地址,而是在最開(kāi)始配置地址時(shí)如果bit7為1,才會(huì)自加,否則會(huì)一直讀/寫那一個(gè)寄存器地址.(最開(kāi)始沒(méi)看到,發(fā)現(xiàn)連續(xù)讀總是一個(gè)值,仔細(xì)看了文檔才發(fā)現(xiàn).)
為了后面讀寫方便,寫個(gè)iic讀寫子函數(shù):
static uint32_t buf_read(uint8_t reg_addr, uint8_t * p_buf, uint32_t size)
{
uint8_t reg=reg_addr;
if(size>1)reg|=0x80;
return user_drv_twi_read(HTS221_ADDR,reg,p_buf,size);
}
static uint32_t reg_write(uint8_t reg_addr, uint8_t reg_val)
{
return user_drv_twi_tx_byte(HTS221_ADDR,reg_addr,reg_val);
}
Register address Map
之后就是register的配置了:
為了方便使用宏定義:
#define HTS221_WHO_AM_I_REG 0x0F
#define HTS221_AV_CONF_REG 0x10
#define HTS221_CTRL_REG1 0x20
#define HTS221_CTRL_REG2 0x21
#define HTS221_CTRL_REG3 0x22
#define HTS221_STATUS_REG 0x27
#define HTS221_HUMIDITY_OUT_L_REG 0x28
#define HTS221_HUMIDITY_OUT_H_REG 0x29
#define HTS221_TEMP_OUT_L_REG 0x2A
#define HTS221_TEMP_OUT_H_REG 0x2B
#define HTS221_CALIBRATION_REGS 0x30
#define HTS221_CALIBRATION_REGS_NUM 16
WHO_AM_I為只讀艰亮,值必須為0xbc,感覺(jué)可以使用讀這個(gè)寄存器來(lái)檢測(cè)硬件是否正常.
AV_CONF娃圆、CTRL_REG[1..3]是配置寄存器,手冊(cè)很清楚,根據(jù)手冊(cè)配置吧.
需要注意的是CALIB_0..F,校準(zhǔn)寄存器,讀取出來(lái)的值需要根據(jù)這些寄存器里的值通過(guò)計(jì)算獲得最終結(jié)果.
hts221初始化過(guò)程就很簡(jiǎn)單了:
配置配置寄存器,讀取校準(zhǔn)寄存器;當(dāng)hts221準(zhǔn)備好數(shù)據(jù)時(shí)就可以讀取數(shù)據(jù),通過(guò)校準(zhǔn)數(shù)據(jù)校準(zhǔn)值.
配置就不多說(shuō),看看校準(zhǔn)相關(guān)的地方
校準(zhǔn)
校準(zhǔn)參數(shù)存在寄存器0x30-0x3f,自然需要讀出.為了方便使用這些參數(shù),根據(jù)寄存器創(chuàng)建了一個(gè)結(jié)構(gòu)體(為了簡(jiǎn)單,直接copy自Nordic Thingy:52官方FW):
typedef struct
{
uint8_t H0_rH_x2;
uint8_t H1_rH_x2;
uint16_t T0_degC_x8;
uint16_t T1_degC_x8;
int16_t H0_T0_OUT;
int16_t H1_T0_OUT;
int16_t T0_OUT;
int16_t T1_OUT;
}drv_hts221_calib_t;
drv_hts221_calib_t p_calib;
uint8_t calib_raw[HTS221_CALIBRATION_REGS_NUM];
err_code = user_drv_twi_read(HTS221_ADDR,HTS221_CALIBRATION_REGS,calib_raw,HTS221_CALIBRATION_REGS_NUM);
if(err_code!=NRF_SUCCESS) return err_code;
p_calib->H0_rH_x2 = calib_raw[0];
p_calib->H1_rH_x2 = calib_raw[1];
p_calib->T0_degC_x8 = (uint16_t)calib_raw[2] + ((uint16_t)(calib_raw[5] & 0x03) << 8);
p_calib->T1_degC_x8 = (uint16_t)calib_raw[3] + ((uint16_t)((calib_raw[5] >> 2) & 0x03) << 8);
p_calib->H0_T0_OUT = (int16_t)calib_raw[6] + ((int16_t)calib_raw[7] << 8);
p_calib->H1_T0_OUT = (int16_t)calib_raw[10] + ((int16_t)calib_raw[11] << 8);
p_calib->T0_OUT = (int16_t)calib_raw[12] + ((int16_t)calib_raw[13] << 8);
p_calib->T1_OUT = (int16_t)calib_raw[14] + ((int16_t)calib_raw[15] << 8);
校準(zhǔn)參數(shù)獲取后就是獲取實(shí)際值計(jì)算了.
實(shí)際上就是個(gè)求解線性方程y=kx+b而已,給出的校準(zhǔn)值其實(shí)就是線性方程的兩個(gè)解,使用這兩個(gè)解求得線性方程的k b,將讀取出的值帶入求出即可.
官方給出的說(shuō)明與例子:
已知兩點(diǎn)坐標(biāo)(x1,y1)(x2,y2),已知(x,y)中的x求y
使用(y2-y1)/(x2-x1)=(y-y1)/(x-x1) ===>y= (y2-y1)*(x-x1)/(x2-x1)+y1.
注意,最后的結(jié)果中,溫度擴(kuò)大了8倍,濕度擴(kuò)大了2倍.
由于溫度精確到了小數(shù)點(diǎn)1位,所以擴(kuò)大了10倍后右移3位(相當(dāng)于除8)
濕度通常只需要精確到個(gè)位,所以直接右移1位(相當(dāng)于除2)
uint32_t err_code;
NRF_LOG_DEBUG("HTS221 handler:%d,%d",pin,action);
uint8_t sample_data[4];
err_code=buf_read(HTS221_HUMIDITY_OUT_L_REG,sample_data,4);
APP_ERROR_CHECK(err_code);
Humidity=(int16_t)sample_data[0] + ((int16_t)sample_data[1] << 8);
Temperature=(int16_t)sample_data[2] + ((int16_t)sample_data[3] << 8);
NRF_LOG_INFO("humidity:0x%x%x",sample_data[1],sample_data[0]);
NRF_LOG_INFO("Temperature:0x%x",Temperature);
int16_t tep=((((int16_t)Temperature-(int16_t)p_calib.T0_OUT)*((int16_t)p_calib.T1_degC_x8-(int16_t)p_calib.T0_degC_x8)/((int16_t)p_calib.T1_OUT-(int16_t)p_calib.T0_OUT)+(int16_t)p_calib.T0_degC_x8)*10)>>3;
int16_t hum=(((int16_t)Humidity-(int16_t)p_calib.H0_T0_OUT)*((int16_t)p_calib.H1_rH_x2-(int16_t)p_calib.H0_rH_x2)/((int16_t)p_calib.H1_T0_OUT-(int16_t)p_calib.H0_T0_OUT)+(int16_t)p_calib.H0_rH_x2)>>1;
//Humidity range
if(hum>100)hum=100;
else if(hum<0) hum=0;
NRF_LOG_INFO("Temperature:%d.%d Humidity:%d",tep/10,tep%10,hum);
到此基本就結(jié)束了,不過(guò)因?yàn)槭褂昧薶ts221的DRDY,所以在nrf52832中,讀取溫濕度時(shí)只是啟動(dòng)hts221了,在DRDY的事件中進(jìn)行讀取溫濕度值.
以下為主要的初始化及讀取溫濕度值代碼,與上面的有部分重復(fù).
void user_hts221_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
uint32_t err_code;
NRF_LOG_DEBUG("HTS221 handler:%d,%d",pin,action);
uint8_t sample_data[4];
err_code=buf_read(HTS221_HUMIDITY_OUT_L_REG,sample_data,4);
APP_ERROR_CHECK(err_code);
Humidity=(int16_t)sample_data[0] + ((int16_t)sample_data[1] << 8);
Temperature=(int16_t)sample_data[2] + ((int16_t)sample_data[3] << 8);
NRF_LOG_INFO("humidity:0x%x%x",sample_data[1],sample_data[0]);
NRF_LOG_INFO("Temperature:0x%x",Temperature);
int16_t tep=((((int16_t)Temperature-(int16_t)p_calib.T0_OUT)*((int16_t)p_calib.T1_degC_x8-(int16_t)p_calib.T0_degC_x8)/((int16_t)p_calib.T1_OUT-(int16_t)p_calib.T0_OUT)+(int16_t)p_calib.T0_degC_x8)*10)>>3;
int16_t hum=(((int16_t)Humidity-(int16_t)p_calib.H0_T0_OUT)*((int16_t)p_calib.H1_rH_x2-(int16_t)p_calib.H0_rH_x2)/((int16_t)p_calib.H1_T0_OUT-(int16_t)p_calib.H0_T0_OUT)+(int16_t)p_calib.H0_rH_x2)>>1;
//Humidity range
if(hum>100)hum=100;
else if(hum<0) hum=0;
NRF_LOG_INFO("Temperature:%d.%d Humidity:%d",tep/10,tep%10,hum);
user_ble_gatts_notify(tep);
}
void user_hts221_init()
{
uint32_t err_code;
uint8_t calib_raw[HTS221_CALIBRATION_REGS_NUM];
err_code=user_drv_twi_tx_byte(HTS221_ADDR,0x20,0x80); //active mode One-shot
err_code=user_drv_twi_tx_byte(HTS221_ADDR,0x22,0x84); //active low
uint8_t reg_val;
err_code=user_drv_twi_read_byte(HTS221_ADDR,HTS221_WHO_AM_I_REG,®_val);
if(err_code!=NRF_SUCCESS) NRF_LOG_ERROR("HTS221 user_drv_twi_read_byte error:%x",err_code);
if(reg_val!=HTS221_WHO_AM_I_VAL)
{
NRF_LOG_ERROR("HTS221 verify error:%x",reg_val);
} else NRF_LOG_INFO("HTS221 verify ok");
buf_read(HTS221_CALIBRATION_REGS,calib_raw,HTS221_CALIBRATION_REGS_NUM);
p_calib.H0_rH_x2 = calib_raw[0];
p_calib.H1_rH_x2 = calib_raw[1];
p_calib.T0_degC_x8 = (uint16_t)calib_raw[2] + ((uint16_t)(calib_raw[5] & 0x03) << 8);
p_calib.T1_degC_x8 = (uint16_t)calib_raw[3] + ((uint16_t)((calib_raw[5] >> 2) & 0x03) << 8);
p_calib.H0_T0_OUT = (int16_t)calib_raw[6] + ((int16_t)calib_raw[7] << 8);
p_calib.H1_T0_OUT = (int16_t)calib_raw[10] + ((int16_t)calib_raw[11] << 8);
p_calib.T0_OUT = (int16_t)calib_raw[12] + ((int16_t)calib_raw[13] << 8);
p_calib.T1_OUT = (int16_t)calib_raw[14] + ((int16_t)calib_raw[15] << 8);
if(!nrf_gpio_pin_read(HT_INT))
{
user_hts221_handler(HT_INT,NRF_GPIOTE_POLARITY_HITOLO);
}
if(!nrfx_gpiote_is_init())
{
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
}
nrf_drv_gpiote_in_config_t in_config =
{
.is_watcher = false,
.hi_accuracy = true,
.sense = NRF_GPIOTE_POLARITY_HITOLO,
.pull = NRF_GPIO_PIN_PULLUP,
};
err_code = nrf_drv_gpiote_in_init(HT_INT, &in_config, user_hts221_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(HT_INT, true);
}
void user_hts221_conversion_start()
{
uint32_t err_code;
NRF_LOG_DEBUG("HTS221 conversion start");
err_code=user_drv_twi_tx_byte(HTS221_ADDR,0x21,0x01);
APP_ERROR_CHECK(err_code);
}