ほんまかいな、そーかいな、南アフリカ と ちょびっと シドニー そして日本だ!

南アフリカ(2004年)と、シドニー(2005年)での生活の近況報告です。そして日本でも(2007年から)

MPL115A2とPIC 16F1827で気圧計を作ってみた

2012年12月28日 | Gadgets
気圧センサMPL115A2を使って気圧計を作ってみた。
このセンサは、I2Cでデータを取れるので、I2Cのお勉強と、今回初めて本格的に使用するLCDのお勉強をかねて作ってみた。
実際には、”PICとC言語の電子工作”という本のルーチンそのままなんだけど、一応、16F1827用に移植して使っている。
このPICは、廉価だけど、いろいろ機能豊富で、最近お気に入りになっている。
が、機能豊富な分、いろいろと欲が出て、”あれもつけたい、これもつけたい”と、やっていると、結局メモリが足らなくなってしまう。
今のところ、気圧と温度を表示するだけだが、これでHi-Tech C のコンパイル(フリー版なので最適化なし)でプログラムスペースの82%となっている。このあと、PCにシリアル出力したり、高度の表示を試みたが、どうしてもメモリーオーバーしてしまう。
シリアルのルーチンはともかく、高度の計算に累乗の計算が必要なのだが、Math.hをIncludeしないとpow関数が使えないのだが、pow使うと、とたんにメモリをオーバーしてしまう。かといって、X^yとやると、”整数でないとだめよ。”と、しかられてしまう。
そもそも、お勉強とかいいながら、実のところ、気圧の係数の計算がうまくいかず、あれこれ自分でトライしてみたが、夜が明けてしまい、”えーい、ままよ!”と、最後は、アプリケーションノートのサンプルをコピペしてしまった。このあたりをなんとかすると、もう少し機能を追加できるかもしれない。
シリアルの部分は、LCDのルーチンと重なる部分があるので、このあたりも何とかなるかも・・・。
とにかく、PCのプログラムと違って、メモリ、節約節約で倹約プログラムを強いられる。
ちなみに、MPL115で検索してみると、だいたい、皆さんArduino かAVRを使っていらっしゃる。
いいなぁ、Arduino 僕もそろそろ首を突っ込んでみようかなぁ・・・?

さて、ここからは、自分の備忘録的に・・・。
まず、I2Cのルーチンで、いくつかの変数、ACKENとかACKDTなどには、頭に"SSP1CON2bits."をつけないとならない。
そもそも、本のオリジナルのI2Cマスタのプログラムは、16F877A用なので、16F1827用に若干の修正が必要。
これは、個別にinclude ファイルの16F1827.hをみて確認する必要がある。
また、温度センサのA/D変換には、はまってしまった。
これまで、A/D変換は、Aポートを使っていたのだが、今回Bポートで同じようにプログラムして、いざ走らせて見ると、ちゃんとデータが取れない。
どうも、常にFF(10bitなので、3F?)になっている様子。ピンの電圧を見てみると、ほぼ、電源電圧。
TRISBは、入力にしたし、ANSELBも設定してあるし・・・???
あっちこっち調べて、やっとたどり着いたのがウィークプルアップ。
Aポートは、プルアップなしがデフォルトで何もしなくてよかったが、Bポートは、デフォルトがプルアップなので、該当ピンのプルアップをキャンセルする必要がある。(WPUB3=0; とか入れてやる)

さて、とりあえず基本機能はできたので、これからメモリとの戦いだなぁ・・・。

プログラムリストは、以下に。
ここのブログはテキストファイルの添付ができないので、こういうとき不便だなぁ・・・。本文に書き込むしかないもんね。
以下のプログラムのほかに、I2CとLCDのプログラムをインクルードしないとならないけど、基本的には、上記の"PICとC言語の電子工作"という本を見ていただければ、良いかと思いますが、本にも非営利の再配布を認めると書いてあるので、万が一必要な方がいらっしゃれば、コメントいただければ、移植版のI2CとLCDのルーチンを公開します。(不定期更新なのでコメントに気がつくまで、時間がかかるかもしれませんが・・・。)

以下プログラム
/* Project Brometer
PIC16F1827 with Pressure Sensor MPL115A2, Temp.Sensor LM61CIZ and Liquid Crystal Display SC1602
Comm by I2C
*/

/*PIN of 16F1827 asign
RA0 : LCD DB4 (pin11)     RB0 :
RA1 : LCD DB5 (pin12)     RB1 : I2C SDA (pin7 of MPL115)
RA2 : LCD DB6 (pin13)    RB2
RA3 : LCD DB7 (pin14)    RB3 : ADC (LM61CIZ Vout)
RA4 : LCD RS (pin 4)     RB4 : I2C SCL (pin8 of MPL115)
RA5 : ICSP MCLR      RB5 : USART Tx (Reserved)
RA6 : LCD R/W (pin 5)    RB6 : ICSPCLK (Header pin)
RA7 : LCD E (pin 6)      RB7 : ICSPDAT (Header pin)
*/
#include <pic.h>
#include "sc1602b.h"
#include "i2c.h"

#define _XTAL_FREQ 8000000 // delay用(クロック8MHzで動作時)

__CONFIG( WRT_OFF & MCLRE_OFF & PWRTE_ON & WDTE_OFF & FOSC_INTOSC & CP_OFF & CPD_OFF) ;
__CONFIG(PLLEN_OFF & STVREN_ON & BORV_25 & LVP_OFF);


void main(void){

unsigned char i,j;
unsigned char ack,Tsign,sgn;
unsigned char msb,lsb, sia0MSB,sia0LSB,sib1MSB,sib1LSB,sib2MSB,sib2LSB;
unsigned char sic12MSB,sic12LSB,sic11MSB,sic11LSB,sic22MSB,sic22LSB;
unsigned char I2CCoeff[12];
signed int sia0,sib1,sib2,sic12,sic11,sic22,siPcomp;
unsigned int uiPadc,uiTadc,v0,vdata,press,temp;
signed long lt1,lt2,lt3,si_c11x1,si_c12x2,si_c22x2,si_a1x1,si_a2x2,si_a11,si_a2,si_y1,si_a1;
float decPcomp;
double intf,decf;

OSCCON = 0b01110010; // 内部クロックは8MHzとする
OPTION_REG = 0b00000000;
TRISA = 0b00100000; // 1で入力 0で出力 RA0-RA7全て出力に設定(RA5は入力専用)
TRISB = 0b00001000; // RB0-RB2,RB4-RB7を出力に設定 RB3を入力(for ADC)
ANSELA = 0b00000000; //汎用ポート
ANSELB = 0b00001000; //汎用ポート RB3(AN9)をADCにセット
PORTA = 0b00000000 ; // RA出力ピンの初期化(全てLOWにする)
PORTB = 0b00000000 ; // RB出力ピンの初期化(全てLOWにする)
ADCON0 = 0b00100100;
ADCON1 = 0b10010011; //右寄せ, Fosc/8, FVR
FVRCON = 0b11000010; //FVR=2.048V enabled
APFCON1=1;

WPUB3=0; //ADC用のピン(RB3)のWeek Pull-up をキャンセル

sia0=0;sib1=0;sib2=0;sic12=0;sic22=0;
lsb=0;msb=0;
press=0;

lcd_init();
i2c_init();

// I2Cで気圧センサの係数値を取得
i2c_start();
i2c_write(0x60<<1);
i2c_write(0x04);
i2c_repstart();
i2c_write((0x60<<1)|1);

for(i=0;i<11;i++) I2CCoeff[i]=i2c_read(0);
I2CCoeff[i]=i2c_read(1);

i2c_stop();

// MPL115A Placing Coefficients into 16 bit variables
//====================================================
//coeff a0 16bit
sia0MSB= I2CCoeff[0];
sia0LSB= I2CCoeff[1];
sia0 = (signed int)sia0MSB <<8; //s16 type //Shift to MSB
sia0 += (signed int)sia0LSB & 0x00FF; //Add LSB to 16bit number
//coeff b1 16bit
sib1MSB= I2CCoeff[2];
sib1LSB= I2CCoeff[3];
sib1 = (signed int)sib1MSB <<8; //Shift to MSB
sib1 += (signed int)sib1LSB & 0x00FF; //Add LSB to 16bit number
//coeff b2 16bit
sib2MSB= I2CCoeff[4];
sib2LSB= I2CCoeff[5];
sib2 = (signed int)sib2MSB <<8; //Shift to MSB
sib2 += (signed int)sib2LSB & 0x00FF; //Add LSB to 16bit number
//coeff c12 14bit
sic12MSB= I2CCoeff[6];
sic12LSB= I2CCoeff[7];
sic12 = (signed int)sic12MSB <<8; //Shift to MSB only by 8 for MSB
sic12 += (signed int)sic12LSB & 0x00FF;
//coeff c11 11bit
sic11MSB= I2CCoeff[8];
sic11LSB= I2CCoeff[9];
sic11 = (signed int)sic11MSB <<8; //Shift to MSB only by 8 for MSB
sic11 += (signed int)sic11LSB & 0x00FF;
//coeff c22 11bit
sic22MSB= I2CCoeff[10];
sic22LSB= I2CCoeff[11];
sic22 = (signed int)sic22MSB <<8; //Shift to MSB only by 8 for MSB
sic22 += (signed int)sic22LSB & 0x00FF;


while(1) {

//-------------Read Padc, Tadc figure  I2Cで気圧、温度の値を取得 -----------------------
i2c_start();
i2c_write(0x60<<1);
i2c_write(0x12);
i2c_write(0x01);
i2c_stop();

__delay_ms(5);

i2c_start();
i2c_write(0x60<<1);
i2c_write(0x00);
i2c_repstart();
i2c_write((0x60<<1)|1);

uiPadc=i2c_read(0);
lsb=i2c_read(0);
uiPadc=((uiPadc<<8)|lsb)>>6;

uiTadc=i2c_read(0);
lsb=i2c_read(1);
uiTadc=((uiTadc<<8)|lsb)>>6;

i2c_stop();

//============= // Coefficient calc //=====================
//******* STEP 1 c11x1= c11 * Padc
lt1 = (signed long)sic11; // s(16,27) s(N,F+zeropad) goes from s(11,10)+11ZeroPad = s(11,22) => Left Justified = s(16,27)
lt2 = (signed long)uiPadc; // u(10,0) s(N,F)
lt3 = lt1 * lt2; // s(26,27) /c11*Padc
si_c11x1 = (signed long)(lt3); // s(26,27)- EQ 1 =c11x1 /checked
//divide this hex number by 2^30 to get the correct decimal value.
//b1 =s(14,11) => s(16,13) Left justified
//******* STEP 2 a11= b1 + c11x1
lt1 = ((signed long)sib1<<14); // s(30,27) b1=s(16,13) Shift b1 so that the F matches c11x1(shift by 14)
lt2 = (signed long)si_c11x1; // s(26,27) //ensure fractional bits are compatible
lt3 = lt1 + lt2; // s(30,27) /b1+c11x1
si_a11 = (signed long)(lt3>>14); // s(16,13) - EQ 2 =a11 Convert this block back to s(16,X)
//******* STEP 3 c12x2= c12 * Tadc
// sic12 is s(14,13)+9zero pad = s(16,15)+9 => s(16,24) left justified
lt1 = (signed long)sic12; // s(16,24)
lt2 = (signed long)uiTadc; // u(10,0)
lt3 = lt1 * lt2; // s(26,24)
si_c12x2 = (signed long)(lt3); // s(26,24) - EQ 3 =c12x2 /checked
//******* STEP 4 a1= a11 + c12x2
lt1 = ((signed long)si_a11<<11); // s(27,24) This is done by s(16,13) <<11 goes to s(27,24) to match c12x2's F part
lt2 = (signed long)si_c12x2; // s(26,24)
lt3 = lt1 + lt2; // s(27,24) /a11+c12x2
si_a1 =(signed long)(lt3>>11); // s(16,13) - EQ 4 =a1 /check
//******* STEP 5 c22x2= c22 * Tadc
// c22 is s(11,10)+9zero pad = s(11,19) => s(16,24) left justified
lt1 = (signed long)sic22; // s(16,30) This is done by s(11,10) + 15 zero pad goes to s(16,15)+15, to s(16,30)
lt2 = (signed long)uiTadc; // u(10,0)
lt3 = lt1 * lt2; // s(26,30) /c22*Tadc
si_c22x2 = (signed long)(lt3); // s(26,30) - EQ 5 /=c22x2
//******* STEP 6 a2= b2 + c22x2
//WORKS and loses the least in data. One extra execution. Note how the 31 is really a 32 due to possible overflow.
// b2 is s(16,14) User shifted left to => s(31,29) to match c22x2 F value
lt1 = ((signed long)sib2<<15); //s(31,29)
lt2 = ((signed long)si_c22x2>>1); //s(25,29) s(26,30) goes to >>16 s(10,14) to match F from sib2
lt3 = lt1+lt2; //s(32,29) but really is a s(31,29) due to overflow the 31 becomes a 32.
si_a2 = ((signed long)lt3>>16); //s(16,13)
//******* STEP 7 a1x1= a1 * Padc
lt1 = (signed long)si_a1; // s(16,13)
lt2 = (signed long)uiPadc; // u(10,0)
lt3 = lt1 * lt2; // s(26,13) /a1*Padc
si_a1x1 = (signed long)(lt3); // s(26,13) - EQ 7 /=a1x1 /check
//******* STEP 8 y1= a0 + a1x1
// a0 = s(16,3)
lt1 = ((signed long)sia0<<10); // s(26,13) This is done since has to match a1x1 F value to add. So S(16,3) <<10 = S(26,13)
lt2 = (signed long)si_a1x1; // s(26,13)
lt3 = lt1 + lt2; // s(26,13) /a0+a1x1
si_y1 = ((signed long)lt3>>10); // s(16,3) - EQ 8 /=y1 /check
//******* STEP 9 a2x2= a2 *Tadc
lt1 = (signed long)si_a2; // s(16,13)
lt2 = (signed long)uiTadc; // u(10,0)
lt3 = lt1 * lt2; // s(26,13) /a2*Tadc
si_a2x2 = (signed long)(lt3); // s(26,13) - EQ 9 /=a2x2
//******* STEP 10 pComp = y1 +a2x2
// y1= s(16,3)
lt1 = ((signed long)si_y1<<10); // s(26,13) This is done to match a2x2 F value so addition can match. s(16,3) <<10
lt2 = (signed long)si_a2x2; // s(26,13)
lt3 = lt1 + lt2; // s(26,13) /y1+a2x2
// FIXED POINT RESULT WITH ROUNDING:
siPcomp = (signed int)(lt3>>13); // goes to no fractional parts since this is an ADC count.
//decPcomp is defined as a floating point number.
//Conversion to Decimal value from 1023 ADC count value. ADC counts are 0 to 1023. Pressure is 50 to 115kPa correspondingly.
decPcomp = (((65.0/1023.0)*siPcomp)+50)*100;
press=(int)decPcomp;

//Calcuration for Temp in MPL115
// temp=25-(uiTadc-472)/5.35;
// if(temp<0){Tsign='-';} else{Tsign=' ';}


//ADC for Temperature of LM61CIZ
// ADCON0=0b00100111;
ADON=1;
__delay_ms(1);
GO_nDONE=1;
while(GO_nDONE);
vdata=ADRESH;
vdata<<=8;
vdata|=ADRESL;
if((vdata*2)>=600){ //In case Temp. >= 0
v0=((vdata*2)-600); // Vref(2.48V/1024=2mv/bit)
sgn=0;
}
else{ //In case Temp. < 0
v0=(600-(vdata*2));
sgn=1; // For minus sign LED on flag
}

temp=v0;
if(sgn==1){Tsign='-';} else{Tsign='+';}

// Output to LCD
lcd_lclr(0);
lcd_putui(press/10,4);
lcd_putch('.');
lcd_putui(press%10,1);
lcd_puts("hPa");
lcd_lclr(1);
lcd_locate(0,1);

lcd_putch(Tsign);
lcd_putui(v0/10,2);
lcd_putch('.');
lcd_putui(v0%10,1);
lcd_putch(0xdf);
lcd_putch('C');

for(j=0;j<200;j++)__delay_ms(10);

}
}

最新の画像もっと見る

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。