12 May 2013

I2C温度センサーLM75ADをPIC12F1822で使う

コンピュータの温度センサーとしてよく使われているLM75が入手できたので、PICに接続してみる。

■ 検証環境
・NXP LM75AD
・Microchip PIC12F1822
・MPLAB X IDE 1.60 / MPLAB XC8 C Compiler V1.12

■ 参考回路図と通信規格

20130512-lm75-circuit.png
回路図ファイル(20130512-lm75-circuit.CE3)をダウンロードする

LM75のアドレスを、最終ビットを立ててアクセスすると、2バイトの温度データが返ってくる。

1番ピンがどこなのか、非常に分かりにくいが、一般的なパッケージ印刷では『ムカデ状のチップに横向きにシルク印刷されている場合、文字の左下が1番ピン』という慣用に従ってみると、うまくアクセスできた。

20130512-lm75-no1pin-fig.png


■ LM75センサーをユニバーサル基盤に無理やり取り付ける

20130512-lm75.jpg

表面実装用のチップなので、ハンダ付けに若干苦労しました。

■ PIC12F1822用プログラムのコア部分

センサーから得られた値は、8で割ると「℃」の整数の単位となる。実際にこのセンサーを使う場合には、8で割った余りも小数点以下の数値として使えるだろう。

main.c
#include <stdio.h>
#include <stdlib.h>
 
#include <xc.h>
#include "i2c-lib.h"
 
/* PIC Configuration 1 */
__CONFIG(FOSC_INTOSC &  // INTOSC oscillator: I/O function on CLKIN pin
        WDTE_OFF &      // WDT(Watchdog Timer) disabled
        PWRTE_ON &      // PWRT(Power-up Timer) disabled
        MCLRE_OFF &     // MCLR pin function is digital input
        CP_OFF &        // Program memory code protection is disabled
        CPD_OFF &       // Data memory code protection is disabled
        BOREN_OFF &     // BOR(Brown-out Reset) disabled
        CLKOUTEN_OFF &  // CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin
        IESO_OFF &      // Internal/External Switchover mode is disabled
        FCMEN_OFF);     // Fail-Safe Clock Monitor is disabled
 
/* PIC Configuration 2 */
__CONFIG(WRT_OFF &      // Flash Memory Self-Write Protection : OFF
        VCAPEN_OFF &    // VDDCORE pin functionality is disabled
        PLLEN_OFF &     // 4x PLL disabled
        STVREN_ON &     // Stack Overflow or Underflow will not cause a Reset
        BORV_HI &       // Brown-out Reset Voltage Selection : High Voltage
        DEBUG_OFF &     // In-Circuit Debugger disabled, ICSPCLK and ICSPDAT are general purpose I/O pins
        LVP_OFF);       // Low-voltage programming : disable
 
#ifndef _XTAL_FREQ
    /* 例:4MHzの場合、4000000 をセットする */
    #define _XTAL_FREQ 4000000
#endif
 
/* printf関数の出力先のスタブ関数を定義する */
void putch(unsigned char ch){
    i2c_lcd_putch(ch);
    return;
}
 
int main(int argc, char** argv) {
    // 基本機能の設定
    OSCCON = 0b01101010;        // 内部オシレーター 4MHz
    TRISA = 0b00101111;         // IOポートRA0(AN0),RA1(SCL),RA2(SDA),RA5(RX)を入力モード(RA3は入力専用)、RA4(TX)を出力モード
    ANSELA = 0b00000000;        // A/D変換をAN0,AN1,AN2,AN4を無効
    PORTA = 0;
 
    i2c_enable();
    OPTION_REGbits.nWPUEN = 0;  // I2C プルアップ抵抗 有効
    WPUA = 0b00000110;          // pull-up (RA1=SCL, RA2=SDA pull-up enable)
 
    while(1)
    {
        i2c_lcd_init();
        i2c_lcd_puts("LM75 temperature");
        i2c_lcd_set_cursor_pos(0x40);    // カーソルを2行目に移動
        printf("%03d deg-C", i2c_lm75_read_temperature());
        __delay_ms(500);
    }
    return (EXIT_SUCCESS);
}
i2c_lib.c
#include <stdio.h>
#include <stdlib.h>
 
#include <xc.h>
#include "i2c-lib.h"
 
#ifndef _XTAL_FREQ
    /* 例:4MHzの場合、4000000 をセットする */
    #define _XTAL_FREQ 4000000
#endif
 
// LM75アドレスは 0b1001000 = 0x48
#define LM75_I2C_ADDR 0b1001000
 
unsigned int i2c_lm75_read_temperature(void)
{
    unsigned int temperature;
    i2c_start();
    i2c_send_byte(LM75_I2C_ADDR<<1 | 1);  // i2cアドレスを1ビット左にシフトし、末尾にR/Wビット(Read=1)を付与
    temperature = i2c_read_byte(1) << 8;    // 読み取り後ACK
    temperature |= i2c_read_byte(0);        // 読み取り後NO_ACK
    temperature = (temperature >> 5) / 8;
    i2c_stop();
    return temperature;
}
 
// I2Cバスを有効化
void i2c_enable(void)
{
    SSP1STAT = 0b10000000;      // I2C 100kHz
    SSP1ADD = 9;                // I2Cバス Baud rate,  4MHz/((SSP1ADD + 1)*4) = 100kHz
    SSP1CON1 = 0b00101000;      // I2C有効, Master Mode
}
 
// I2Cバスを無効化
void i2c_disable(void)
{
    SSP1CON1 = 0b00001000;      // I2C無効, Master Mode
}
 
// I2C書き込みサイクルの開始(Start Conditionの発行)
void i2c_start(void)
{
    SSP1CON2bits.SEN = 1;   //  Start Condition Enabled bit
    i2c_wait();
}
 
// I2C書き込みサイクルの開始(Repeat Start Conditionの発行)
void i2c_repeat_start(void)
{
    SSP1CON2bits.RSEN = 1;  //  Start Condition Enabled bit
    i2c_wait();
}
 
// I2C書き込みサイクルの終了(Stop Conditionの発行)
void i2c_stop(void)
{
    SSP1CON2bits.PEN = 1;   // Stop Condition Enable bit
    i2c_wait();
}
 
// I2C通信がビジー状態を脱するまで待つ
void i2c_wait(void)
{
    while ( ( SSP1CON2 & 0x1F ) || ( SSP1STATbits.R_nW ) );
}
 
// I2Cバスにデータを送信(1バイト分)
void i2c_send_byte(unsigned char data)
{
    SSP1BUF = data;
    i2c_wait();
}
 
//I2Cバスからデータ受信
// ack=1 : 受信後ACKを送信し、次のデータを送るようスレーブデバイスに指示
// ack=0 : 受信後NO_ACKを送信し、これ以上受信しないことをスレーブデバイスに指示
unsigned char i2c_read_byte(int ack)
{
    SSP1CON2bits.RCEN = 1;
    i2c_wait();
    unsigned char data = SSP1BUF;
    i2c_wait();
 
    if(ack) SSP1CON2bits.ACKDT = 0;     // ACK
    else SSP1CON2bits.ACKDT = 1;        // NO_ACK
 
    SSP1CON2bits.ACKEN = 1;
 
    i2c_wait();
    return data;
}

■ 関連記事
I2C温度センサーLM75ADをRaspberry Piに接続する
Raspberry piにi2c LCD接続する