大気圧センサ(LPS331AP)


大気圧センサ(LPS331AP)より測定データを得るにはI2CとSPIの二つの方法があります。最初にI2Cで実験します。 I2Cを選ぶにはLPS331APのCSをハイにし、SCLとSDAでCPUとやりとりします。 CPUのPC4, 5は汎用ポートとしてI2Cのマスターの動作をさせます。(AVRのI2Cは使わない)
なお、LPS331APは電源は3.3VですのでPCA9306でレベル変換します。

I2Cを使用する場合
コントロール・バイトは書き込みは0xb8、読み出しは0xb9です。(SDOはロー)
@レジスタ0x0fを読むと0xbbが返ってくれば大成功です。
Aレジスタ0x20に0x90または0x94を書き込めば、気圧、温度を一秒毎に取ります。
Bデータを取るタイミングはレジスタ0x27のビット0、1を利用すると良いでしょう。 気圧データがそろうとビット1が1、温度データがそろうとビット0が1になります。 レジスタ0x29を読むとビット1が0、レジスタ0x2bを読むとビット0が0となります。
Cレジスタ0x28、0x29、0x2a、0x2b、0x2cを読み込み(I2Cの場合、オートインクリメントの場合は0x28を0xa8にする) 計算すると気圧、温度が得られます。
気圧 = ((レジスタ0x2aの値)×65536 + (レジスタ0x29の値)×256 + (レジスタ0x28の値)) / 4096
温度 = 42.5 + ((レジスタ0x2cの値)×256 + (レジスタ0x2bの値)) / 480

SPIを使用する場合
LPS331APのCS-----CS(CPUのPC5)
LPS331APのSPC----SCK(CPUのPC4)
LPS331APのSDI----SDO(CPUのPC3)
LPS331APのSDO----SDICPUのPC2)
のように接続します。SPIは高速のため、ロジックレベル変換用ICは TXS0108E を使用します。
あとは、I2Cを使用する場合の@ABCのようになります。


LPS331AP_i2c_kairo.gif

回路図 (I2Cを使用)


LPS331AP_spi_kairo.gif

回路図 (SPIを使用)


LPS331AP_result.jpg

表示画面


LPS331AP_bb.JPG

実験風景 (I2Cを使用)


How to Compile (コンパイルのしかた)

I2Cを使用のプログラム
/*
 *  LPS331AP_i2c.c
 *  大気圧センサ「LPS331AP」のレジスタをI2Cで読み書きし、
 *  得られた気圧、温度をRS-232C(115200 8-N-1)で出力する。
 *  ATmega328P
 *
 *
 *  Fuses(システムクロック:20MHz)
 *
 *              水晶
 *  EXTENDED    0xFD
 *  HIGH        0xD9
 *  LOW         0xF7
 */

#include <stdio.h>
#include <stdlib.h>                 /* abs() */
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 20.0E6                /* 20 MHz */
#include <util/delay.h>

#define SCL_OUT DDRC |= _BV(5)
#define SDA_OUT DDRC |= _BV(4)
#define SCL_IN DDRC &= ~_BV(5)
#define SDA_IN DDRC &= ~_BV(4)
#define SCL_H PORTC |= _BV(5)
#define SDA_H PORTC |= _BV(4)
#define SCL_L PORTC &= ~_BV(5)
#define SDA_L PORTC &= ~_BV(4)
#define SCL (PINC & _BV(5))
#define SDA (PINC & _BV(4))
/* 変更無し */
#define ACK 0
/* ハードに合わせて変更する (SDAのビット) */
#define NAK _BV(4)

#define PS_WRITE 0xb8
#define PS_READ 0xb9

#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

void delay_us(unsigned short t){while(t--) _delay_us(1);}
///void delay_ms(unsigned short t){while(t--) _delay_ms(1);}
void initUSART0(void);
void Send0(char *str);
int ps_write_i2c(unsigned char addr, unsigned char *data, unsigned char size);
int ps_read_i2c(unsigned char addr, unsigned char *data, unsigned char size);

int main(void)
{
    int result;
    unsigned char buffer[5];
    char buffer2[16];
    unsigned char status;
    long tmp;
    short tmp2;
    short press, temp;

    /* ATmega328P */
    DDRB = 0b00000000;      /* XTAL2 XTAL1 SCK   MISO  MOSI  ----- ----- ----- */
    DDRC = 0b00100000;      /* xxxxx RESET SCL   SDA   ----- ----- ----- ----- */
    DDRD = 0b00000000;      /* ----- ----- ----- ----- ----- ----- TXD   RXD   */
    PORTB = 0b00111111;
    PORTC = 0b00001111;
    PORTD = 0b11111100;

    initUSART0();
    sei();

    Send0("START\r");

    /* レジスタ0x0fを読むと0xbbが返ってくれば大成功です。 */
    result = ps_read_i2c(0x0f, buffer, 1);
    if(result != -1){
        sprintf(buffer2, "WHO AM I = 0x%02x\r", buffer[0]);
        Send0(buffer2);
    }
    else
        Send0("Error1\r");

    /* レジスタ0x20に0x90または0x94を書き込めば、気圧、温度を一秒毎に取ります。 */
    buffer[0] = 0x94;
    result = ps_write_i2c(0x20, buffer, 1);
    if(result == -1)
        Send0("Error2\r");

    while(1){
        /* データを取るタイミングはレジスタ0x27のビット0、1を利用すると良いでしょう。 */
        /* 気圧データがそろうとビット1が1、温度データがそろうとビット0が1になります。 */
        /* レジスタ0x29を読むとビット1が0、レジスタ0x2bを読むとビット0が0となります。 */  
        result = ps_read_i2c(0x27, &status, 1);
        if(result == -1){
            Send0("Error3\r");
            status = 0;
        }

        if((status & 0x03) == 0x03){
            /* レジスタ0x28、0x29、0x2a、0x2b、0x2c読み込み込む。 */
            /* (オートインクリメントの場合は0x28を0xa8にする) */
            result = ps_read_i2c(0xa8, buffer, 5);
            if(result != -1){
                /* 気圧 = ((レジスタ0x2aの値)×65536 + (レジスタ0x29の値)×256 */
                /* + (レジスタ0x28の値)) / 4096 */
                tmp = buffer[2]; tmp <<= 8;
                tmp += buffer[1]; tmp <<= 8;
                tmp += buffer[0];
                press = (short)round(tmp / 4096.0);

                /* 温度 = 42.5 + ((レジスタ0x2cの値)×256 + (レジスタ0x2bの値)) / 480 */
                tmp2 = buffer[4]; tmp2 <<= 8;
                tmp2 += buffer[3];
                temp = (short)round(425.0 + tmp2 / 48.0);
                sprintf(buffer2, "%d hPa    %d.%d℃\r", press,
                                            temp / 10, abs(temp % 10));
                Send0(buffer2);
            }
            else
                Send0("Error4\r");
        }
    }
}

void initUSART0(void)
{
    UBRR0 = 21;             /* U2X0 = 1, 115200ボー/20MHzクロック */
    UCSR0A = 0b00000010;    /* U2X0 = 1 */
    UCSR0B = 0b00001000;    /* 送信イネーブル */
    UCSR0C = 0b00000110;    /* 8ビット, パリティなし, ストップ・ビット1 */
}

/*
 *  1データ送信
 */
void Tx0(char data)
{
    while((UCSR0A & _BV(5)) == 0)
        ;
    UDR0 = (unsigned char)data;
}

/*
 *  文字列を送信
 */
void Send0(char *str)
{
    while(*str != '\0'){
        Tx0(*str);
        str++;
    }
}

void start_i2c(void)
{
    SCL_OUT; SDA_OUT;

    SCL_L;
    SDA_H;
    SCL_H;
    delay_us(1);            /* TSU:STA = 600ns */

    SDA_L;
    delay_us(1);            /* THD:STA = 600ns */
    SCL_L;

    SCL_OUT; SDA_IN;
}

void stop_i2c(void)
{
    SCL_OUT; SDA_OUT;

    SCL_L;
    SDA_L;

    SCL_H;
    delay_us(1);            /* TSU:STO =  600ns */
    SDA_H;
    delay_us(1);            /* TBUF = 1300ns */
    delay_us(1);

    SCL_L;

    SCL_OUT; SDA_IN;
}

/*
 * 戻り値 ACK:0, NAK:SDA
 */
unsigned char writebyte(unsigned char data)
{
    unsigned char i;

    SCL_OUT; SDA_OUT;

    for(i=0; i<8; i++){
        if(data & 0x80)
            SDA_H;
        else
            SDA_L;

        SCL_H;
        delay_us(1);        /* THIGH = 600ns */
        SCL_L;
        delay_us(1);        /* TLOW = 1300ns */
        delay_us(1);

        data <<= 1;
    }   

    SCL_OUT; SDA_IN;

    SCL_H;
    delay_us(1);            /* THIGH = 600ns */
    i = SDA;
    SCL_L;
    delay_us(1);            /* TLOW = 1300ns */
    delay_us(1);

    return i;
}

/*
 * 戻り値 読み込みデータ(8ビット)
 */
unsigned char readbyte(void)
{
    unsigned char i, data;

    data = 0;
    for(i=0; i<8; i++){
        data <<= 1;

        SCL_H;
        delay_us(1);        /* THIGH = 600ns */
        if(SDA)
            data++;
        SCL_L;
        delay_us(1);        /* TLOW = 1300ns */
        delay_us(1);
    }

    return data;
}

void ack(void)
{
    SCL_OUT; SDA_OUT;

    SDA_L;
    SCL_H;
    delay_us(1);            /* THIGH = 600ns */
    SCL_L;
    delay_us(1);            /* TLOW = 1300ns */
    delay_us(1);

    SCL_OUT; SDA_IN;
}

void nak(void)
{
    SCL_OUT; SDA_OUT;

    SDA_H;
    SCL_H;
    delay_us(1);            /* THIGH = 600ns */
    SCL_L;
    delay_us(1);            /* TLOW = 1300ns */
    delay_us(1);

    SCL_OUT; SDA_IN;
}


/*
 * 戻り値 0:成功, -1:失敗
 *
 * コントロール・バイトの書き込み
 */
int send_ctrl(unsigned char ctrl)
{
    unsigned char ret;

    start_i2c();
    ret = writebyte(ctrl);
    if(ret == NAK)
        return -1;

    return 0;
}

/*
 * 戻り値 0:成功, -1:失敗
 *
 * コントロール・バイトの書き込み以外はACKをチェックしてません
 */
int ps_write_i2c(unsigned char addr, unsigned char *data, unsigned char size)
{
    int ret;
    unsigned char i;

    ret = send_ctrl(PS_WRITE);
    if(ret == -1) return -1;
    writebyte(addr);
    for(i=0; i<size; i++)
        writebyte(*data++);
    stop_i2c();
    return 0;
}

/*
 * 戻り値 0:成功, -1:失敗
 *
 * コントロール・バイトの書き込み以外はACKをチェックしてません
 */
int ps_read_i2c(unsigned char addr, unsigned char *data, unsigned char size)
{
    int ret;
    unsigned char i;

    ret = send_ctrl(PS_WRITE);
    if(ret == -1) return -1;
    writebyte(addr);
    ret = send_ctrl(PS_READ);
    if(ret == -1) return -1;
    for(i=0; i<size-1; i++){
        *data++ = readbyte();
        ack();
    }
    *data = readbyte();
    nak();
    stop_i2c();
    return 0;
}


How to Compile (コンパイルのしかた)

SPIを使用のプログラム
/*
 *  LPS331AP_spi.c
 *  大気圧センサ「LPS331AP」のレジスタをSPIで読み書きし、
 *  得られた気圧、温度をRS-232C(115200 8-N-1)で出力する。
 *  ATmega328P
 *
 *
 *  Fuses(システムクロック:20MHz)
 *
 *              水晶
 *  EXTENDED    0xFD
 *  HIGH        0xD9
 *  LOW         0xF7
 */

#include <stdio.h>
#include <stdlib.h>                 /* abs() */
#include <avr/io.h>
#include <avr/interrupt.h>
///#define F_CPU 20.0E6             /* 20 MHz */
///#include <util/delay.h>

#define CS_H PORTC |= _BV(5)
#define SCK_H PORTC |= _BV(4)
#define SDO_H PORTC |= _BV(3)
#define CS_L PORTC &= ~_BV(5)
#define SCK_L PORTC &= ~_BV(4)
#define SDO_L PORTC &= ~_BV(3)
#define SDI (PINC & _BV(2))

#define NOP() __asm__ __volatile__("NOP"::)
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

///void delay_us(unsigned short t){while(t--) _delay_us(1);}
///void delay_ms(unsigned short t){while(t--) _delay_ms(1);}
void initUSART0(void);
void Send0(char *str);
void ps_write_spi(unsigned char addr, unsigned char *data, unsigned char size);
void ps_read_spi(unsigned char addr, unsigned char *data, unsigned char size);

int main(void)
{
    unsigned char buffer[5];
    char buffer2[16];
    unsigned char status;
    long tmp;
    short tmp2;
    short press, temp;

    /* ATmega328P */
    DDRB = 0b00000000;      /* XTAL2 XTAL1 SCK   MISO  MOSI  ----- ----- ----- */
    DDRC = 0b00111000;      /* xxxxx RESET CS    SCK   SDO   SDI   ----- ----- */
    DDRD = 0b00000000;      /* ----- ----- ----- ----- ----- ----- TXD   RXD   */
    PORTB = 0b00111111;
    PORTC = 0b00110011;
    PORTD = 0b11111100;

    initUSART0();
    sei();

    Send0("START\r");

    /* レジスタ0x0fを読むと0xbbが返ってくれば大成功です。 */
    ps_read_spi(0x0f, buffer, 1);
    sprintf(buffer2, "WHO AM I = 0x%02x\r", buffer[0]);
    Send0(buffer2);

    /* レジスタ0x20に0x90または0x94を書き込めば、気圧、温度を一秒毎に取ります。 */
    buffer[0] = 0x94;
    ps_write_spi(0x20, buffer, 1);
 
    while(1){
        /* データを取るタイミングはレジスタ0x27のビット0、1を利用すると良いでしょう。 */
        /* 気圧データがそろうとビット1が1、温度データがそろうとビット0が1になります。 */
        /* レジスタ0x29を読むとビット1が0、レジスタ0x2bを読むとビット0が0となります。 */  
        ps_read_spi(0x27, &status, 1);
        if((status & 0x03) == 0x03){
            /* レジスタ0x28、0x29、0x2a、0x2b、0x2c読み込み込む。 */
            ps_read_spi(0x28, buffer, 5);
            /* 気圧 = ((レジスタ0x2aの値)×65536 + (レジスタ0x29の値)×256 */
            /* + (レジスタ0x28の値)) / 4096 */
            tmp = buffer[2]; tmp <<= 8;
            tmp += buffer[1]; tmp <<= 8;
            tmp += buffer[0];
            press = (short)round(tmp / 4096.0);

            /* 温度 = 42.5 + ((レジスタ0x2cの値)×256 + (レジスタ0x2bの値)) / 480 */
            tmp2 = buffer[4]; tmp2 <<= 8;
            tmp2 += buffer[3];
            temp = (short)round(425.0 + tmp2 / 48.0);
            sprintf(buffer2, "%d hPa    %d.%d ℃\r", press,
                                            temp / 10, abs(temp % 10));
            Send0(buffer2);
        }
    }
}

void initUSART0(void)
{
    UBRR0 = 21;             /* U2X0 = 1, 115200ボー/20MHzクロック */
    UCSR0A = 0b00000010;    /* U2X0 = 1 */
    UCSR0B = 0b00001000;    /* 送信イネーブル */
    UCSR0C = 0b00000110;    /* 8ビット, パリティなし, ストップ・ビット1 */
}

/*
 *  1データ送信
 */
void Tx0(char data)
{
    while((UCSR0A & _BV(5)) == 0)
        ;
    UDR0 = (unsigned char)data;
}

/*
 *  文字列を送信
 */
void Send0(char *str)
{
    while(*str != '\0'){
        Tx0(*str);
        str++;
    }
}

void ps_write_spi(unsigned char addr, unsigned char *data, unsigned char size)
{
    unsigned char tmp;
    unsigned char i, j;

    CS_L;

    /* R /W[7], M /S[6], AD[5:0] */
    tmp = addr;
    if(size != 1)
        tmp |= _BV(6);

    /* アドレス */
    for(i=0; i<8; i++){
        SCK_L;      /* SCK パルス出力 */
        if(tmp & 0x80){
            /* 1 */
            SDO_H;
        }
        else{
            /* 0 */
            SDO_L;
        }
        tmp <<= 1;
        SCK_H;
    }

    /* データ送信 */
    for(j=0; j<size; j++){
        tmp = *(data + j);
        for(i=0; i<8; i++){
            SCK_L;      /* SCK パルス出力 */
            if(tmp & 0x80){
                /* 1 */
                SDO_H;
            }
            else{
                /* 0 */
                SDO_L;
            }
            tmp <<= 1;
            SCK_H;
        }
    }

    CS_H;
}

void ps_read_spi(unsigned char addr, unsigned char *data, unsigned char size)
{
    unsigned char tmp;
    unsigned char i, j;

    CS_L;

    /* R /W[7], M /S[6], AD[5:0] */
    tmp = addr;
    tmp |= _BV(7);
    if(size != 1)
        tmp |= _BV(6);

    /* アドレス */
    for(i=0; i<8; i++){
        SCK_L;      /* SCK パルス出力 */
        if(tmp & 0x80){
            /* 1 */
            SDO_H;
        }
        else{
            /* 0 */
            SDO_L;
        }
        tmp <<= 1;
        SCK_H;
    }

    /* データ受信 */
    for(j=0; j<size; j++){
        tmp = 0;
        for(i=0; i<8; i++){
            SCK_L;      /* SCK パルス出力 */
            NOP();
            NOP();
            tmp <<= 1;
            if(SDI){
                /* 1 */
                tmp |= 1;
            }
            SCK_H;
        }
        *(data + j) =tmp;
    }

    CS_H;
}


真空管
戻る