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接続する