大気圧センサ-LPS331AP + PIC 18F4553

投稿日 2014/09/26

STマイクロシステムズ社のデジタル大気圧センサー LPS331APで大気圧を計測してみました。

 

MEMSテクノロジによって小型高精度のセンサーが入手できるようになりましたが、LPS331APもそのひとつです。

 

LPS331APには温度補正のため温度センサーも内蔵されており、読み取れるようになっています。精度は±2℃とよくないのですが、温度の表示もできるようにしました。

18F4553_LPS331_1.jpg

PIC 18F4553で大気圧センサーLPS331APを制御

 

LPS331APではマイコンインターフェースとしてI2C, SPIが使えますが、今回はI2Cで接続してみました。

 

表示にはI2C制御の小型のキャラクタLCD AQM0802Aを使用しました。

 

マイコンはPIC 18F4553です。

 

すべて電源電圧は3.3Vで統一。制御もすべてI2Cで行えます。

 

開発環境はMPLAB XIDEとXC18コンパイラ Pickit3です。

今回のような電源電圧がMax 3.3vのデバイスを使う場合、Pickit3をICSPに接続して書き込むとき、電圧生成機能をONで、設定電圧が5Vになっているような場合、デバイスを壊してしまう恐れがありますので、注意が必要です。

 

LPS331APは超小型で、パッケージがHCLGA-16なので、実装は個人では手に負えません。幸い秋月電子がDIP化したモジュールを販売しているので使用しました。

 

LPS331AP DIP化モジュールにはI2Cのプルアップ抵抗が搭載されていますが使用せず(J1, J2はオープン)、今回は外付けで5.6KΩを接続しました。

 

インターラプトは使わず1Hzの周期ポーリングとしました。このためINT1, INT2は使いません。

 

I2CモードにするためCSはVddにつないでおきます。

 

Vdd(3.3V), Vss(GND), I2Cの制御信号SCL, SDAをつないでおきます。PIC18F4553側はRB0, RB1を使用しました。

 

I2C制御小型キャラクタLCD AQM0802Aの制御プログラムは省略します。関連記事をご参照ください。

オンボード I2C 8x2 キャラクタLCD AQM0802A

 

デジタル大気圧センサーLP331APの制御方法

 

 I2Cによるハンドリングは一般的な方法と同じですが、例によってタイミング的なところをカットアンドトライしなければなりませんでした。

 

タイミング(適当なディレイ)が合っていないと、気圧760.00Hpが読み込こまれ、一見故障しているように見えますが、執念深く調整します。


ネットを調べると、760.00Hpで変化しない場合は壊れているとのことですが、あきらめるのは早いようです。気圧が760.00Hpを示していても、WHO_AM_I(0x0F)レジスタを読み込むと、ちゃんと0xBBが返ります。

結局ソースコードにようにI2Cのハンドラ内でdelay_us()を適当に挿入することによって解決しました。

 

制御はまず、WHO_AM_Iレジスタ(0x0F)を読み込んで0xBBが返ることを確認します。

 

次にCTRL_REG1に0x90を書きます。これでパワーオンするとともに、周期を1Hzに設定します。

 

簡易的な使い方では、設定はこれだけで、このあとSTATUS_REGのステータスビット(温度用と気圧用が別になっている)を確認し、1がセットされたらPRESS_POUT_XL_REH, PRESS_OUT_L, PRESS_OUT_Hを読み込み、それらをLONG(4byte)の変数下位3byteに読み込んで、4096で割りfloatの変数に代入するだけです。

 

今回は平均化処理は行わずに表示しています。(このため少数以下がめまぐるしく変わります。)

 

 

ソースコード

 

i2c_lcd.c、 i2c.c、ftostring()、delay_ms()、dilay_us()は省略

ftostring()関数は、電子工作の実験室 後閑氏によります。

 

main.c

 

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

 

#define SW1 PORTEbits.RE0
#define SW2 PORTEbits.RE1
#define LED PORTEbits.RE2               //Sens Time

 

#define REF_P_XL    0x08
#define REF_P_L     0x09
#define REF_P_H     0x0A
#define WHO_AM_I    0x0F
#define RES_CONF    0x10
#define CTRL_REG1   0x20
#define CTRL_REG2   0x21
#define CTRL_REG3   0x22
#define INT_CFG_REG 0x23
#define INT_SOURCE_REG  0x24
#define THS_P_LOW_REG   0x25
#define THS_P_HIGH_REG  0x26
#define STATUS_REG  0x27
#define PRESS_POUT_XL_REH   0x28
#define PRESS_OUT_L 0x29
#define PRESS_OUT_H 0x2A
#define TEMP_OUT_L  0x2B
#define TEMP_OUT_H  0x2C
#define AMP_CTRL    0x30

 

#pragma config PLLDIV   = 5          // (20MHz crystal)
#pragma config CPUDIV   = 0             // 1/2 Fosc = 10MHz
#pragma config USBDIV   = 2          // Clock source from 96MHz PLL/2
#pragma config FOSC     = HS
#pragma config FCMEN    = OFF
#pragma config IESO     = OFF
#pragma config PWRT     = OFF
#pragma config BOR      = ON
#pragma config BORV     = 3
#pragma config VREGEN   = ON       //USB Voltage Regulator = ON
#pragma config WDT      = OFF
#pragma config WDTPS    = 32768
#pragma config MCLRE    = ON
#pragma config LPT1OSC  = OFF
#pragma config PBADEN   = OFF
#pragma config CCP2MX   = OFF
#pragma config STVREN   = ON
#pragma config LVP      = OFF
#pragma config ICPRT    = OFF        // Dedicated In-Circuit Debug/Programming = OFF
#pragma config XINST    = OFF        // Extended Instruction Set = OFF
#pragma config CP0      = OFF
#pragma config CP1      = OFF
#pragma config CP2      = OFF
#pragma config CP3      = OFF
#pragma config CPB      = OFF
#pragma config CPD      = OFF
#pragma config WRT0     = OFF
#pragma config WRT1     = OFF
#pragma config WRT2     = OFF
#pragma config WRT3     = OFF
#pragma config WRTB     = OFF        // Boot Block Write Protection = OFF
#pragma config WRTC     = OFF
#pragma config WRTD     = OFF
#pragma config EBTR0    = OFF
#pragma config EBTR1    = OFF
#pragma config EBTR2    = OFF
#pragma config EBTR3    = OFF
#pragma config EBTRB    = OFF

 

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

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*);

unsigned char ADT7410_Get_regvalue(unsigned char);
void LPS331AP_Put_regvalue(unsigned char, unsigned char);
unsigned char LPS331AP_Get_regvalue(unsigned char);

void itostring(unsigned char, int, unsigned char*);
void ftostring(int, int, float, unsigned char*);

unsigned char interval_flag = 0;
int temp_value;

int loop_cnt, stop_cnt, total_time;
char string[10];
float temp, pressure;

 

void interrupt isr(void){

        interval_flag = 1;

        LED = 1;

        INTCONbits.TMR0IF = 0;   // Clear Interrupt Flag

        TMR0H = 255 - 76 + 1;      // 1s

        TMR0L = 0;
}

 

void init(void){
        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 high_byte, low_byte, dummy;
        union pressuer_data {
                unsigned char pressure_byte[4];
                unsigned long pressure_LONG;
         } s;


         init();

         i2c_init();
         i2c_enable();

         lcd_init();
         lcd_clear();

         lcd_write(0, 0, "JF1VRR");
         delay_ms(1000);

         T0CON = 0x87;               //10bit mode, 1:256
         TMR0H = 255 - 76 + 1;
         TMR0L = 0;
         INTCONbits.TMR0IE = 1;

         temp = 0.0;
         interval_flag = 1;

         INTCONbits.PEIE = 1;
         INTCONbits.GIE = 1;

         dummy = LPS331AP_Get_regvalue(WHO_AM_I);
         if(dummy != 0xBB) {
                  lcd_write(0, 0, "Fail 331");
                  while(1);
         } else
                  lcd_write(0, 0, "OK 331  ");

         delay_ms(1000);
         LPS331AP_Put_regvalue(CTRL_REG1, 0x90);

         while(1){
         if(interval_flag){
                 while((LPS331AP_Get_regvalue(STATUS_REG) & 0x01) == 0);
                 high_byte = LPS331AP_Get_regvalue(TEMP_OUT_H);
                 low_byte = LPS331AP_Get_regvalue(TEMP_OUT_L);
                 temp_value = (high_byte << 8) + low_byte;
                 temp = 42.5 + ((float)temp_value / 480.0);
            
                 while((LPS331AP_Get_regvalue(STATUS_REG) & 0x02) == 0);
                 s.pressure_byte[3] = 0;
                 s.pressure_byte[2] = LPS331AP_Get_regvalue(PRESS_OUT_H);
                 s.pressure_byte[1] = LPS331AP_Get_regvalue(PRESS_OUT_L);
                 s.pressure_byte[0] = LPS331AP_Get_regvalue(PRESS_POUT_XL_REH);
                 pressure = s.pressure_LONG / 4096.0;
                      
                 ftostring(4, 2, temp, string);
                 string[7] = 0;
                 lcd_write(0, 0, string);
                 lcd_write(7, 0, "C");
                 ftostring(4, 2, pressure, string);
                 string[7] = 0;
                 lcd_write(0, 1, string);
                 lcd_write(7, 1, "H");

                 interval_flag = 0;
                 LED = 0;
         } //if
    } //while
} //main

 

i2c_lps331ap.c

 

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

#define LPS331AP_I2C_ADDR 0xB8
#define WRITE_MODE 0x00
#define READ_MODE 0x01

 

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

 

void LPS331AP_Put_regvalue(unsigned char reg_address, unsigned char data);
unsigned char LPS331AP_Get_regvalue(unsigned char reg_address);

 

void LPS331AP_Put_regvalue(unsigned char reg_address, unsigned char byte){
        SSPCON2bits.SEN = 1;
        while(SSPCON2bits.SEN == 1);

 

        SSPBUF = LPS331AP_I2C_ADDR | WRITE_MODE;
        while(SSPSTATbits.BF);
         if (SSPCON2bits.ACKSTAT){

                SSPCON2bits.PEN = 1;

                while(SSPCON2bits.PEN);
                return;

        }

 

        delay_us(100);

        SSPBUF = reg_address;
        while(SSPSTATbits.BF);
        if (SSPCON2bits.ACKSTAT){
                SSPCON2bits.PEN = 1;

                while(SSPCON2bits.PEN);
                return;

        }

 

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

 

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

 

unsigned char LPS331AP_Get_regvalue(unsigned char reg_address){
        unsigned char reg_value;

 

        SSPCON2bits.SEN = 1;
        while(SSPCON2bits.SEN == 1);

 

        SSPBUF = LPS331AP_I2C_ADDR | WRITE_MODE;
        while(SSPSTATbits.BF);
        if (SSPCON2bits.ACKSTAT){
                SSPCON2bits.PEN = 1;
                while(SSPCON2bits.PEN);
                return(0);

        }

 

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

 

        delay_us(10);
        SSPCON2bits.RSEN = 1;
        while(SSPCON2bits.RSEN);

 

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

 

        delay_us(10);
        SSPCON2bits.RCEN = 1;
        while(SSPCON2bits.RCEN);
        reg_value = SSPBUF;
        while(SSPSTATbits.BF);
        SSPCON2bits.ACKDT = 1;

 

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

 

 

 

 

 

 

(JF1VRR)