Beaglebone Black 보드에서 LM92TMP513 칩을 테스트해 보았다. 이전 센서와는 달리 칩 형태라 납땜 하기 힘들어서 예전에 엘레파츠에서 구입한 SOIC 변환 보드를 사용했다. LM92는 몇 핀 안되어 간단히 테스트 해 볼 수 있지만 TMP513은 배선이 조금 있는 편이고 breadboard에서 하기는 좀 어려운 편.


칩 수급은 LM92와 TMP513은 Texas Instruments에서 샘플 오더한 것을 사용했다. TI는 샘플 오더하면 주문한 칩당 3개씩을 DHL로 배송해 주는데 3-4 business days면 도착. 요새 뭘 돈 주고 산 기억이 없다, 집이나 사무실에 굴러다니는 부속을 긁어모아서... -_-;


LM92는 단순해서 HT11과 마찬가지로 전원과 I2C만 연결하면 되고, TMP513은 내부 온도 센서 외에도 외부에 PNP TR을 다이오드처럼 사용하여 온도 센서로 사용하기에 이글 캐드로 연결 관계를 간단한 회로로 그렸다. 데이터시트에 따르면 TR은 hfe가 50~150인 아무거나 사용하면 될 것 같다(추천하는 것은 자기들이 검증한 2N3904지만).

TMP513은 온도 계측 외에 INA219 칩처럼 current와 bus voltage sensing이 가능해서 태양광 모듈 회로 설계에 넣어 예전에 열심히 테스트 한 적이 있다. INA219나 TMP513 같은 칩들은 전력 회로에서 analog 파트 계측을 매우 간단하게 구현 가능한 참, 좋은 칩들이다.


오늘 목표는 TMP513 및 HT11 칩의 온도 정밀도가 원하는 수준이 나오는 가를 LM92 칩 기준으로 검측하는 것이 목표. 하여튼 회로는 참고용이고, SOIC 변환 보드에 두 칩을 대충 납땜한 사진이 아래.



I2C가 편해서 소스 작성하는데 대충 30분이면 뚝딱. 취미 생활 중 일부는 업무에 활용.


lm92.c source

#include <stdio.h>

#include <unistd.h>

#include <time.h>

#include <sys/ioctl.h>

#include <linux/i2c-dev.h>

#include <fcntl.h>


#define I2C_DEV "/dev/i2c-1" // device driver name for linux i2c interface

#define LM92_ADDR 0x48


// INA219 register address

#define LM92_REG_READ 0x00

#define LM92_REG_CONFIG 0x01

#define LM92_CFG_FQUE (1<<4)

#define LM92_CFG_INTP (1<<3)

#define LM92_CFG_CRITP (1<<2)

#define LM92_CFG_INTMODE (1<<1)

#define LM92_CFG_SHUTDOWN (1<<0)

#define LM92_REG_CRIT 0x02

#define LM92_REG_TLOW 0x03

#define LM92_REG_THIGH 0x04

#define LM92_REG_MFGID 0x05


int lm92_measure()

{

static int fd = -1;

unsigned char buf[10];

int t;

if (fd == -1) {

if ((fd = open(I2C_DEV,O_RDWR)) < 0) {

printf("LM92: open error\n");

return 0;

}

if (ioctl(fd, I2C_SLAVE, LM92_ADDR) < 0) {

printf("LM92: can't set address\n");

return 0;

}

}


buf[0] = LM92_REG_READ;

if (write(fd, buf, 1) != 1) {

printf("LM92: write error\n");

return -9999;

}

if (read(fd, buf, 2) != 2) {

printf("LM92: read error\n");

return -9999;

}

int v = ((buf[0] << 8) | buf[1]) >> 3;

if ((v & 0x1000) != 0) 

t = (0x1fff-v) * -625;

else

t = v * 625;


return t;

}


#if 0

int main(int argc, char* argv[])

{

time_t st = time(NULL);

while(1) {

if (time(NULL) == st) {

usleep(1000);

continue;

}

st = time(NULL);

printf("t=%6.2f\n", lm92_measure()/10000.0);

}

return 0;

}

#endif



tmp513.c source


source에서 주의 사항

  • power measure 때문에 몇 가지 셋업이 추가되었는데, 굳이 제거하지 않았다. 파워 계측할 때는 션트 저항(R_SHUNT) 및 계측하고자 하는 전압의 예상 최대 값(TMP513_VMAX) , 예상 최대 전류(TMP513_AMAX)를 설정해 주면 그에 맞는 파라미터를 설정해 준다.
  • TMP513_VOFS 및 TMP513_AOFS는 계측 장비로 전압 및 전류를 측정한 후 캘리브레이션에 사용하는 값으로 처음에는 0을 지정해 주고, 계측 장비와의 오차만큼을 지정하면 보상하는 형태. 
  • 마찬가지로 TMP513_TEMP_* 시리즈의 define 역시 LM92와의 온도 오차를 측정한 후 측정치를 보상해 주기 위해 사용했다.
  • TMP513을 초기화 하기 위해, tmp513_measure() 함수에서 configuration value를 hard coding 했다. 회로도처럼 내부 온도 센서 및 외부의 두 개 온도 센서를 계측하기 위해 cfg 값의 몇몇 비트가 enable 되어 있다.


#include <errno.h>

#include <string.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <linux/i2c-dev.h>

#include <sys/ioctl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <time.h>


// circuit & measure constant


#define R_SHUNT (0.01) // Rs


#define TMP513_VMAX (15.0)

#define TMP513_AMAX (8.0)

#define TMP513_MAX_TEMP 4


#define TMP513_CAL ((0x7FFF * 4.096) / TMP513_VMAX)


// calibrated values

#define TMP513_VOFS (-0.0)

#define TMP513_AOFS (-0.0)


#define TMP513_TEMP_L_OFS (1.31353)

#define TMP513_TEMP_R1_OFS (1.31353)

#define TMP513_TEMP_R2_OFS (1.31353)

#define TMP513_TEMP_R3_OFS (0.0)


// i2c devices


#define I2C_DEV "/dev/i2c-1" // device driver name

#define TMP513_ADDR 0x5D // TMP513 default address


#define TMP513_REG_CONFIG 0x00

#define TMP513_CONFIG_MODE 0x7 // shunt and bus, continuous

#define TMP513_CONFIG_SADC 0xf // 128 samples for current

#define TMP513_CONFIG_BADC 0xf // 128 samples for bus voltage

#define TMP513_CONFIG_BRNG 0x1 // 32V FSR

#define TMP513_REG_CONFIG2 0x01

#define TMP513_REG_STATUS 0x02

#define TMP513_REG_VOLT 0x05

#define TMP513_REG_CURRENT 0x07

#define TMP513_REG_TEMP_L 0x08

#define TMP513_REG_TEMP_R1 0x09

#define TMP513_REG_TEMP_R2 0x0A

#define TMP513_REG_TEMP_R3 0x0B

#define TMP513_REG_CAL 0x15

#define TMP513_REG_NF1 0x16

#define TMP513_REG_NF2 0x17

#define TMP513_REG_NF3 0x18


// generic register functions for INA219, TMP513


static int rd_reg(int fd, int rn)

{

unsigned char buf[10] = { 0 };

buf[0] = rn;

if (write(fd, buf, 1) != 1) {

printf("write error: %s\n", strerror(errno));

return -1;

}


if (read(fd, buf, 2) != 2) {

printf("read error: %s\n", strerror(errno));

return -1;

}

return ((buf[0] << 8) | buf[1]) & 0xffff;

}

static int wr_reg(int fd, int rn, int v)

{

unsigned char buf[10] = { 0 };


buf[0] = rn;

buf[1] = (v >> 8) & 0xff;

buf[2] = v & 0xff;

if (write(fd, buf, 3) != 3) {

printf("write error: %s\n", strerror(errno));

return -1;

}

return 1;

}


// get program gain for INA219, TMP513


int get_pg(double Vrs)

{

if (Vrs < 0.040) 

return 0;

else if (Vrs < 0.080) 

return 1;

else if (Vrs < 0.160) 

return 2;

else if (Vrs < 0.320)

return 3;

else {

printf("setup: out of range. check R_SHUNT, V_MAX & A_MAX\n");

return 3;

}

}


int tmp513_measure(double* t, double *v, double *a)

{

static int fd = -1;

static int t_reg[TMP513_MAX_TEMP] = { TMP513_REG_TEMP_L, TMP513_REG_TEMP_R1, TMP513_REG_TEMP_R2, -1 };

static double t_ofs[TMP513_MAX_TEMP] = { TMP513_TEMP_L_OFS, TMP513_TEMP_R1_OFS, TMP513_TEMP_R1_OFS };

int temp, i, iv, ia;

if (fd == -1) {

if ((fd = open(I2C_DEV,O_RDWR)) < 0) {

printf("TMP513: open error\n");

return 0;

}

if (ioctl(fd, I2C_SLAVE, TMP513_ADDR) < 0) {

printf("TMP513: set address failed\n");

return 0;

}


int cfg;

double RL = TMP513_VMAX / TMP513_AMAX;

double Vrs = (R_SHUNT / (R_SHUNT + RL)) * TMP513_VMAX;

printf("TMP513: Vmax=%.2f, Amax=%.2f, Rl=%.2f, Rs=%.3f ohm, Vrs=%.3fV, PG=%d\n", TMP513_VMAX, TMP513_AMAX, RL, R_SHUNT, Vrs, get_pg(Vrs));

cfg = (TMP513_CONFIG_BRNG << 13) | (get_pg(Vrs) << 11) | (TMP513_CONFIG_BADC << 7) | (TMP513_CONFIG_SADC << 3) | TMP513_CONFIG_MODE;

wr_reg(fd, TMP513_REG_CONFIG, cfg);

wr_reg(fd, TMP513_REG_CAL, (int)TMP513_CAL);

cfg = (1 << 15) | (0 << 14) | (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) | (0x7 << 7);

// 15: continous conversion

// 14: remote channel 3 enable

// 13: remote channel 2 enable

// 12: remote channel 1 enable

// 11: local temp enable

// 10: resistance correction

// 9:7: conversion rate. 0=0.0625, 1=0.123, 2=0.25, 3=0.5, 4=1, 5=2, 6=4, 7=8

wr_reg(fd, TMP513_REG_CONFIG2, cfg);

wr_reg(fd, TMP513_REG_NF2, 0<<8);

printf("CONFIG: %04X (%d)\n", cfg, cfg);

printf("CAL   : %04X (%d)\n", (int)TMP513_CAL, (int)TMP513_CAL);

printf("\n");

}

iv = rd_reg(fd, TMP513_REG_VOLT);

if (iv == -1) 

printf("TMP513: voltage read error\n");

else 

*v = (iv >>3) * 0.004 + TMP513_VOFS;

ia = rd_reg(fd, TMP513_REG_CURRENT);

if (ia == -1) 

printf("TMP513: current read error\n");

else

*a =  ia / (TMP513_CAL / 4.096) + TMP513_AOFS;


for (i = 0; t_reg[i] != -1; i++) {

temp = rd_reg(fd, t_reg[i]);

if (temp == -1) {

printf("TMP513: temp%d read error\n", i);

continue;

}

if ((temp & 2) != 0) 

printf("temp%d: PVLD error\n", i);

if ((temp & 1) != 0)

printf("temp%d: diode open error\n", i);


double m = 0.0625;

if ((temp & 0x8000) != 0) {

temp = (~temp + 1) & 0x7fff;

m = -m;

}


t[i] = (temp >> 3) * m + t_ofs[i];


}


return 1;

}


#if 0

int main(int argc, char* argv[])

{

time_t st = time(NULL);

double temp[TMP513_MAX_TEMP];

double load_v, load_a;

while(1) {


if (time(NULL) == st) {

usleep(1000);

continue;

}

st = time(NULL);


tmp513_measure(temp, &load_v, &load_a);

printf("%.2f %.2f\n", temp[0], temp[1], temp[2]);

}

return 1;

}

#endif



HT11, LM92, TMP513의 온도 계측치를 logging 했다 (TMP513은 calibration을 한 값이 적용된 상태)


2013-12-17 21:29:28,1,0.43,0.10,34,25,25.56,25.56,25.31,25.25

2013-12-17 21:29:29,0,0.43,0.10,34,25,25.56,25.56,25.25,25.06

2013-12-17 21:29:30,2,0.43,0.10,34,25,25.62,25.56,25.25,25.25

2013-12-17 21:29:31,2,0.43,0.10,34,25,25.56,25.56,25.31,25.25

2013-12-17 21:29:32,2,0.43,0.10,34,25,25.62,25.56,25.44,24.94

2013-12-17 21:29:33,2,0.44,0.10,34,25,25.62,25.56,25.44,25.31

...

2013-12-17 21:30:01,2,0.43,0.10,34,25,25.62,25.56,25.31,25.06

2013-12-17 21:30:02,1,0.43,0.10,34,25,25.56,25.56,25.25,25.13

2013-12-17 21:30:03,2,0.43,0.10,34,25,25.62,25.56,25.19,25.38

2013-12-17 21:30:04,1,0.43,0.10,34,25,25.62,25.56,25.25,25.31

2013-12-17 21:30:05,1,0.43,0.10,34,25,25.62,25.56,25.25,25.06


측정 결과의 각 컬럼은 datetime, HT11 error, HT11 bit transfer error rate, HT11 checksum error, HT11 humidity, HT11 temp, LM92 temp, TMP513 local sensor temp., TMP513 ch#1 temp., TMP513 ch#2 temp. 순서. 정리하면,

  • HT11의 온도 값은 스펙에 의하면 ±1℃로 LM92와 대체로 일치.
  • TMP513의 내부 센서 역시 온도 오차가 ±1℃인데 calibration value를 적용하니 비교적 정밀
  • TMP513의 두 외부 센서는 약간의 오차가 보임
25℃ 에서는 LM92 대비 두 센서의 온도 정밀도가 매우 실용적인 수준이나, -40~100℃에서는 온도 드리프트가 꽤 있을 것으로 짐작됨. 하지만 다양한 환경에서 테스트를 해 볼 수 없는 형편이다. 

지금부터 내일 아침까지 대략 10시간 동안 logging을 해서 온도 변화에 따른 계측치가 선형으로 나오는 가를 검사할 것이다.

2013-12-18 30초당 한 번씩 logging을 해서 10시간 동안 모은 데이터로 그래프를 그렸다.





,