NTCサーミスタ + MCP3425で温度を計測

投稿日 2014/10/05

村田製作所のNTCサーミスタとmicrochip社の16bit ΔΣADコンバータを組み合わせて温度を計測してみました。

 

最近はデジタル値で読み取れ、高精度な温度センサーが出回っているので、手持ちのサーミスタは出番がありませんでしたが、16bitのΔΣADコンバータとの組み合わせで試してみることにしました。

 

使用したNTCサーミスタは村田製作所のNXFT15XH103FA2Bです。

このサーミスタは25℃ 10KΩの特性のモデルです。

 

ΔΣADコンバータは、超小型のMCP3425です。このADコンバータは差動入力型ですが、マイナス側をGNDに落としてシングルエンドで使いました。制御はI2Cです。

 

NTCサーミスタについて

 

NTCサーミスタは負の温度特性をもつサーミスタです。つまり温度が高くなると抵抗値が小さくなります。いろいろな規格のものがありますが、今回使用したものは25℃での抵抗値が10KΩ B定数 3380のものです。

18f4553_ntc_thermistor_1.jpg

NTCサーミスタ

マッチ棒と比べてもかなり小さい

 

非常に小さく、細いリードの先にマッチ棒の先端より小さな(幅1.6mmより小さい)温度検出部があります。サーミスタのよいところは、小さく軽量のため、敏感に温度に反応することです。手でつまむと体温に近づきますが、放すとすぐに室温に戻ります。ちゃんと温度結合させれば、電子回路基板の特定の部品などの局所的な温度の計測にも使えそうです。

 

MCP3425について

18f4553_ntc_thermistor_2.jpg

 microchip社の16bit ΔΣADコンバータ MCP3425

6ピン SOT23 超小型パッケージ

 

MCP3425は設定により12, 14, 16bitのレゾリューションが選べます。動作は、シングルショットとコンティニュアスがあります。シングルショットは毎回変換指示を与えて変換します。コンティニュアスは連続変換です。プログラマブル・ゲイン・アンプを内蔵しており、ゲイン1,2,4,8倍が選べます。内蔵の基準電圧源は2.048Vです。

 

入力は差動入力ですが、シングルエンドでも使えます。

 

特徴

 小型のSOT-23 6ピンパッケージ

 12(15SPS),14(60SPS),16bit(240SPS) ΔΣ ADC

 差動入力 1チャネル

 セルフ・キャリブレーション

 INL: 10ppm/FSR

 基準電圧源内蔵 2.048V ±0.05%

 プログラマブルゲインアンプ(PGA)内蔵  x1,2,4,8

 コンエィニュアス・モードとワンショット・モード 

 I2Cインターフェース

 電源電圧 2.7 - 5.5V

 パワーオンリセット(POR)

 消費電流 連続モード 145uA(TYP)、

   ワンショットモード 9.7uA/16bit、2.4uA/14bit、0.6uA/12bit(TYP)

 温度環境範囲 -40℃ - 125℃

 

制御はI2Cで行います。スレーブアドレスは0xD0です。下位4bitは工場出荷時に指定した値にしてくれるようですが、指定がなければ0になっているようです。(秋月電子で扱っているものは0xD0のようです。)

 

内部に設定を行うためのコンフィグレーション・レジスタがありますがアドレスは付いていません。(言い換えれば0xD0のWRITEレジスタがコンフィグレーション・レジスタとも言える)このため、WRITEモードのスレーブアドレスのあとに続く8bitがコンフィグレーションレジスタに書き込まれます。

 

今回は16bit シングルショット、ゲイン8で使いました。

 

変換データの読み込みは、READモードでスレーブアドレスを送ると、MCP3425から3バイトが送り返されてきます。最初の2バイトがデータ、最後の1バイトがコンフィグレーション・レジスタの内容です。

 

コンフィグレーション・レジスタのRDY(負論理)ビットを見て、0ならば前の2byteが変換されたデータだと確認できます。16bitレゾリューションの場合、15SPSなので、変換1回に約67mSかかります。ラフに100mS程度待って、一応RDYビットを確認してデータを使う程度でよいかと思います。

 

計測回路

 

抵抗RとNTCサーミスタの抵抗値で分圧回路を形成し、NTCサーミスタ両端の電圧を計測します。

 

電圧源はマイコンなどの電源からとりますので、3.3Vです。ただし十分精度の高い3.3Vとします。

 

   3.3V○----/ / / /---○---( / / / / )---- GND

                           R        |    Thermistor

                                     |

                                   Vout

 

                                 分圧回路

 

Rは様々考えられますが、サーミスタにあまり大きな電流を流すと自身が発熱すると思われるので、なるべく高い抵抗値にしたいところです。そうすると相対的にVout(サーミスタの両端電圧)が低下します。MCP3425は幸いプログラマブル・ゲイン・アンプが内蔵されているので、使用温度範囲で最適なVoutになる(基準電圧の2.048Vを超えない)よう、エクセル等を使って計算しRとゲインを決めておきます。

 

今回はRを560KΩとし、アンプのゲインは8倍としました。

 

NTCサーミスタは負の温度特性なので温度が低くなると抵抗値が上がり、両端の電圧が高くなります。

18f4553_ntc_thermistor_3.jpg

NTCサーミスタは負の温度特性

560KΩとサーミスタで3.3Vを分圧しアンプで8倍すると

-10℃くらいまでが基準電圧2.048Vの範囲となる

 

各温度におけるNTCサーミスタの抵抗値は、以下の計算式で求めることができます。

 

25℃の抵抗値 Tr25 : 10000Ω

B定数: B 3380

絶対温度: Tk 273.15℃

抵抗値を知りたい温度: T

温度Tでの抵抗値: Tr

 

Tr = Tr25 x exp(B  x ( 1 / (T - Tk) - 1 / (25 - Tk))

 

25℃の抵抗値を計算すると、

Tr = 10000 x exp(3380 x ( 1 / (25 - 273.15) - 1 / (25 - 273.15)) = 10000

当然10000Ωになります。

 

50℃の場合は、

Tr = 10000 x exp(3380 * ( 1 / (50 - 273.15) - 1 / (25 - 273.15)) = 4160

4.16KΩ。

 

というように計算できます。

 

肝心の温度は、

T = 1  /  ( log( Tr  / Tr25) / B + (1 / (25 + 273.15))) - 273.15

で求まりますが、この式ではサーミスタの抵抗値(Tr)が必要です。

 

Trは、先の分圧回路での抵抗値と電圧との比で求めます。

 

3.3V : Vout = 560KΩ : Tr

 

Tr = (Vout  * 560000) /  (3.3 - Vout)

 

Trが求まれば、以下のように温度が計算できます。

 

たとえばVoutが0.0579Vの場合、

Tr = (0.0579 * 560000) / (3.3 - 0.0579) = 10000Ω

T = 1 / ( log(10000  / 10000.0) / 3380 + (1 / (25 + 273.15))) - 273.15 = 25

18f4553_ntc_thermistor_4.jpg

左から

温度、サーミスタの抵抗値、分圧電圧、分圧電圧の8倍

(Cal)は計算式で求めたサーミスタ抵抗値と温度

 

PICの温度上昇を計測してみる

 

前述のようにNTCサーミスタは非常に小型で軽量です。このため電子部品等の局所的な温度を計測するには便利です。

 

今回は高速(20MHz)と低速(5MHz)で実行中のPICのチップ表面温度を計測してみました。室温の変化が影響するので正確ではありませが、この程度の温度変化はわかるというくらいのものです。

 

サーミスタに近い導線を触るだけで敏感に反応します。

18f4553_ntc_thermistor_5.jpg

NTCサーミスタでPICのチップ表面温度を計測中

 

 

PIC 18F4553のチップ表面温度(室温で変わります)

Fosc 20MHzで実行中のチップ表面温度:24.82℃

Fosc  5MHzで実行中のチップ表面温度:22.78℃

 

ソースコード

 

キャラクタLCD表示i2c_aqm0802a.cやi2c.cは省略

 

main.c

 

#include <xc.h>
#include <p18f4553.h>
#include <stdio.h>
#include <math.h>

 

//Fosc = 20MHz

#pragma config CPUDIV = OSC2_PLL3 //[Primary Oscillator Src: /2][96 MHz PLL Src: /3]

//Fosc = 5MHz

//#pragma config CPUDIV = OSC2_PLL3 //[Primary Oscillator Src: /2][96 MHz PLL Src: /3]
#pragma config PLLDIV = 5 //Divide by 5 (20 MHz oscillator input)
#pragma config USBDIV = 2 //USB clock source comes from the 96 MHz PLL divided by 2
#pragma config FCMEN = OFF //Fail-Safe Clock Monitor disabled
#pragma config IESO = OFF //Oscillator Switchover mode disabled
#pragma config FOSC = HS //HS oscillator (HS)
#pragma config PWRT = OFF //PWRT disabled
#pragma config VREGEN = OFF //USB voltage regulator disabled
#pragma config BORV = 3 //Minimum setting
#pragma config BOR = ON //Brown-out Reset enabled in hardware only (SBOREN is disabled)
#pragma config WDTPS = 32768 //1:32768
#pragma config WDT = OFF //WDT disabled (control is placed on the SWDTEN bit)
#pragma config CCP2MX = OFF //CCP2 input/output is multiplexed with RB3
#pragma config PBADEN = OFF //PORTB<4:0> pins are configured as digital I/O on Reset
#pragma config MCLRE = ON //MCLR pin enabled; RE3 input pin disabled
#pragma config LPT1OSC = OFF //Timer1 configured for higher power operation
#pragma config STVREN = ON //Stack full/underflow will cause Reset
#pragma config DEBUG = OFF //Background debugger disabled, RB6 and RB7 configured as general purpose I/O pins
#pragma config ICPRT = OFF //ICPORT disabled
#pragma config LVP = OFF //Single-Supply ICSP disabled
#pragma config XINST = OFF //Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
#pragma config CP0 = OFF //Block 0 (000800-001FFFh) is not code-protected
#pragma config CP1 = OFF //Block 1 (002000-003FFFh) is not code-protected
#pragma config CP2 = OFF //Block 2 (004000-005FFFh) is not code-protected
#pragma config CP3 = OFF //Block 3 (006000-007FFFh) is not code-protected
#pragma config CPB = OFF //Boot block (000000-0007FFh) is not code-protected
#pragma config CPD = OFF //Data EEPROM is not code-protected
#pragma config WRT0 = OFF //Block 0 (000800-001FFFh) is not write-protected
#pragma config WRT1 = OFF //Block 1 (002000-003FFFh) is not write-protected
#pragma config WRT2 = OFF //Block 2 (004000-005FFFh) is not write-protected
#pragma config WRT3 = OFF //Block 3 (006000-007FFFh) is not write-protected
#pragma config WRTB = OFF //Boot block (000000-0007FFh) is not write-protected
#pragma config WRTC = OFF //Configuration registers (300000-3000FFh) are not write-protected
#pragma config WRTD = OFF //Data EEPROM is not write-protected
#pragma config EBTR0 = OFF //Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks
#pragma config EBTR1 = OFF //Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks
#pragma config EBTR2 = OFF //Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks
#pragma config EBTR3 = OFF //Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks
#pragma config EBTRB = OFF //Boot block (000000-0007FFh) is not protected from table reads executed in other blocks

 

#define LED PORTEbits.RE2               //Sens Time

 

void i2c_init(void);
void i2c_enable(void);
void lcd_init(void);
void lcd_clear(void);
void lcd_write(unsigned char, unsigned char, const unsigned char*);

void MCP3425_Set_configuration(unsigned char);
void MCP3425_Get_data_configuration(unsigned char *);

 

//Fosc = 20MHz

#define _XTAL_FREQ 20000000             //__delay_ms

//Fosc = 5MHz

#define _XTAL_FREQ 5000000             //__delay_ms

 

void delay_us(int time){
    int i;
    for(i = 0; i < time; i++) __delay_us(1);
}

 

void delay_ms(int time){
    int i;
    for(i = 0; i < time; i++) __delay_ms(1);
}

 

void init(void){
    OSCCONbits.SCS = 0;                //Primary OSC
    ADCON1  = 0b00001111;             // All Digital
    CMCON   =   0b00000111;           // No Comparator
    TRISA   = 0b00000000;
    TRISB   = 0b00000000;
    TRISC   = 0b00000000;
    TRISD   = 0b00000000;
    TRISE   =   0b00000011;
    LATA    = 0b00000000;
    LATB    = 0b00000000;
    LATC    = 0b00000000;
    LATD    = 0b00000000;
    LATE    = 0b00000000;
}

 

void main(void){
    unsigned char get_value_byte[3];
    unsigned int get_value_int;
    float get_value_float, temp;
    float Tr;
    char string[10];

    init();

    i2c_init();
    i2c_enable();

    lcd_init();
    lcd_clear();

    lcd_write(0, 0, "Temp.   ");
    while(1){
        LED = 1;
        MCP3425_Set_configuration(0b10001011); //Continuous mode, 15SPS(16bit), Gain x8
        delay_ms(1);
        MCP3425_Get_data_configuration(get_value_byte);
        get_value_int = get_value_byte[0] << 8 | get_value_byte[1];
        get_value_float = (((float)get_value_int * 2.048) / 32768) / 8;
        Tr = (get_value_float * 560000.0) / (3.3 - get_value_float);
        temp = 1 / ( log(Tr / 10000.0) / 3380.0 + (1 / (25.0 + 273.15))) - 273.15;
        sprintf(string, "%5.2fc", temp);
        lcd_write(2, 1, string);
        LED = 0;
        delay_ms(100);
    } //while
} //main


 

i2c_mcp3425.c

 

#include <xc.h>
#include <p18f4553.h>

 

#define MCP3425_I2C_ADDR 0xD0
#define WRITE_MODE 0x00
#define READ_MODE 0x01

#define PROVE PORTCbits.RC0             //Prove

 

void delay_us(int);
void delay_ms(int);

 

void MCP3425_Set_configuration(unsigned char data);
void MCP3425_Get_data_configuration(unsigned char *get_value);

 

void MCP3425_Set_configuration(unsigned char byte){
 SSPCON2bits.SEN = 1;
 while(SSPCON2bits.SEN == 1);

 

 SSPBUF = MCP3425_I2C_ADDR | WRITE_MODE;
 while(SSPSTATbits.BF);
 delay_us(1);
 if (SSPCON2bits.ACKSTAT){
  SSPCON2bits.PEN = 1;
  while(SSPCON2bits.PEN);
  return;
 }

 

 SSPBUF = byte;
 while(SSPSTATbits.BF);
 delay_us(1);
 if (SSPCON2bits.ACKSTAT){
  SSPCON2bits.PEN = 1;
  while(SSPCON2bits.PEN);
  return;
 }

 

 delay_us(1);
 SSPCON2bits.PEN = 1;
 while(SSPCON2bits.PEN);
 return;
}

 

void MCP3425_Get_data_configuration(unsigned char *get_value)

 PROVE = 1; //波形観測用トリガ
 SSPCON2bits.SEN = 1;
 while(SSPCON2bits.SEN == 1);

 

 SSPBUF = MCP3425_I2C_ADDR | READ_MODE;
 while(SSPSTATbits.BF);
        delay_us(1);
 if (SSPCON2bits.ACKSTAT){
  SSPCON2bits.PEN = 1;
  while(SSPCON2bits.PEN);
  return;
 }

 

 delay_us(1);
 SSPCON2bits.RCEN = 1;
 while(SSPCON2bits.RCEN);
 while(SSPSTATbits.BF == 0);
 SSPCON2bits.ACKEN = 1;
 get_value[0] = SSPBUF;

 

 delay_us(1);
 SSPCON2bits.RCEN = 1;
 while(SSPCON2bits.RCEN);
 while(SSPSTATbits.BF == 0);
 SSPCON2bits.ACKEN = 1;
 get_value[1] = SSPBUF;

 

 delay_us(1);
 SSPCON2bits.RCEN = 1;
 while(SSPCON2bits.RCEN);
 while(SSPSTATbits.BF == 0);
 SSPCON2bits.ACKEN = 1;
 get_value[2] = SSPBUF;

 

 delay_us(1);
 SSPCON2bits.PEN = 1;
 while(SSPCON2bits.PEN);
 PROVE = 0;
 return;
}

 

 

 

(JF1VRR)