/********************************************************************* * メイン・プログラム:PIC24F_BME280モジュールテスト_3 * OLEDに温湿度・気圧を表示してみる。 * * 0.96インチカラーOLED(aitendo): NVK-064SC012F * 温湿度・気圧センサモジュール:BME280 * * 原典参考先: * @ スイッチ・サイエンスの記事 * A 京谷氏著書「グラフィック表示モジュール応用製作集」 * * Condition: * 8MHz Internal RC oscillator * Fcy=8MHz/2=4MHz, Tcy=250ns (電池駆動を想定し、最高速:16M/4で使用) * * CPU: PIC24FJ64GA002 * * 初期デバッグ年月日: 2020/11/14 N.Ishii * <更新履歴> * xxxxxx *********************************************************************/ #include "p24FJ64GA002.h" #include "SSD1332_OLEDlibPIC24F.h" #include "skI2C_PIC24F_lib.h" #include "stdio.h" /// コンフィギュレーション ビットの設定 _CONFIG1( JTAGEN_OFF // JTAGの使用:無効 & GCP_OFF // コードプロテクト:しない & GWRP_OFF // 書き込みプロテクト:しない & BKBUG_OFF // バックグラウンドデバッグ:無効 & COE_OFF // クリップオンエミュレーション:無効 & ICS_PGx1 // EMUC/EMUDをPCG1/PGD1と共用 & FWDTEN_OFF // WDT:無効 ) /// Fosc = FRC (8MHz) _CONFIG2( IESO_OFF // 2速度スタートアップ:無効 & FNOSC_FRC // 発振器の選択:FRCを選択する & FCKSM_CSDCMD // クロック切り替え・クロックモニタ:供に無効 & OSCIOFNC_ON // OSCOピン機能:RA3ポートとする & IOL1WAY_OFF // RPレジスタプロテクト:プロテクトしない & I2C1SEL_PRI // I2C1のピン選択:主ピンを使用する & POSCMOD_NONE // 主発振器:使用しない ) //------------------------------------------------------------------------------------- #define BME280_ADRS 0x76 // BME280のI2Cアドレス #define BME280_ID 0x60 // BME280のChip ID /// 受信データバッファ unsigned char rec_data[24]; // 読出し時の最大バイト数=24バイト:温度・気圧補正値を連続して読込む時) // レジスタ数(総バイト数)は全部で、56個(バイト単位) /// センサー固有の補正パラメータ格納用変数宣言 unsigned int dig_T1; int dig_T2; int dig_T3; unsigned int dig_P1; int dig_P2; int dig_P3; int dig_P4; int dig_P5; int dig_P6; int dig_P7; int dig_P8; int dig_P9; unsigned char dig_H1; int dig_H2; unsigned char dig_H3; int dig_H4; int dig_H5; char dig_H6; /// 生データ格納用変数宣言 unsigned long hum_raw, temp_raw, pres_raw; /// 補正計算関連変数宣言 long t_fine; // 表示値を、浮動小数点型に宣言し、0.0に初期化 double temp_act = 0.0, press_act = 0.0,hum_act=0.0; long temp_cal; // 温度補正結果格納変数を、符号付32bit整数型に宣言 unsigned long press_cal,hum_cal; // 気圧・湿度補正結果格納変数を、符号無し32bit整数型に宣言 //------------------------------------------------------------------------ /// プロトタイピング void WriteBME280(unsigned char, unsigned char); void ReadBME280(unsigned char, char); char BME280_Init(char,char); void ReadData(void); void ReadTrim(void); long calibration_T(long); unsigned long calibration_P(long); unsigned long calibration_H(long); //------------------------------------------------------------------------- int main(void) { char ans; char buf[32]; //// I/O設定 AD1PCFG = 0xFFFF; // A/D Digi 選択:全デジタルI/Oに設定 CLKDIV = 0x0000; // System Clock devider 1:1 /// Setup PORT In/Out TRISB = 0x000F; // RB4-15は、OLED制御出力 RB2 is SDA2 (Hi), RB3 is SCL2 (Hi), RB4:入力 他は未使用入力 TRISA = 0x0006; // RA0= OLED_VCC_ON出力, RA3= 赤LED、RA4= OLED_RESET出力、他は入力 /// Set Pull Up CNPU1 = 0x0030; // Pull Up Port is RB0(ピン1), RB1(ピン2) 今回は、ここにSW未使用 TRISAbits.TRISA3= 0; // 赤LED:デバッグ用 LATAbits.LATA3 = 1; // 赤LED消灯 /// Initialize I2C2 I2C2BRG = 0x27; // (Fcy/Fscl-FCY/10E6)-1=(4E6/100E3-4E6/10E6)-1=38.6=39 I2C2CON = 0x8000; // Enable I2C2 OLEDInit(); // OLEDコントローラー初期化 GCls(BLACK); // 画面消去 OLEDDispON(); // OLED表示スタート OLED_Str(0, 0, "Start Test", CYAN, BLACK); delay_ms(3000); // 3秒後に開始 GCls(BLACK); // クリア /// センサの初期化を行う ans = BME280_Init(BME280_ID ,BME280_ADRS); if (ans == 0) { OLED_Str(0, 0, "Init OK", CYAN, BLACK); sprintf((char *)buf,"%x", rec_data[0]); // "0x60" OLED_Str(0, 2, "ID= ", CYAN, BLACK); OLED_Str(4, 2, buf, CYAN, BLACK); ReadTrim(); //補正データの読み取り(合計32バイト) delay_ms(1000); // データレートが1Hzなので1秒後から開始する GCls(BLACK); } else { OLED_Str(0, 0, "Init NG", CYAN, BLACK); while(1); // プログラム終了 } /// メイン・ループ while(1) { /// 生データ取得 ReadData(); /// 補正値を基に補正計算 temp_cal = calibration_T(temp_raw); press_cal = calibration_P(pres_raw); hum_cal = calibration_H(hum_raw); /// 補正計算結果を、実際の表示値に換算 temp_act = (double)temp_cal / 100.0; press_act = (double)press_cal / 100.0; hum_act = (double)hum_cal / 1024.0; /// 液晶に表示 sprintf((char *)buf,"%.2f ", temp_act); buf[6]= '゚'; buf[7]= 'C'; OLED_Str(0, 0, buf, RED, BLACK); // 温度(℃)の表示 sprintf((char *)buf,"%.2f ", hum_act); buf[6] = '%'; OLED_Str(0, 2, buf, CYAN, BLACK); // 湿度(%)の表示 sprintf((char *)buf,"%.2f ", press_act); buf[8] = 'h'; buf[9] = 'P'; buf[10] = 'a'; OLED_Str(0, 4, buf, YELLOW, BLACK); // 気圧(hPa)の表示 delay_ms(1000); } } /********************************************************************* * BME280 I2C書き込み関数(1バイト書込み) * * <引数> * @ reg_adrs:レジスタ・アドレス *  A data:書込みデータ * ***********************************************************************/ void WriteBME280(unsigned char reg_adrs, unsigned char data) { I2C_Start(BME280_ADRS,RW_0); // スタートコンディションを発行〜 I2Cアドレス + Wビットを送信〜 ACK応答が、'0' になるまで待つ I2C_Send(reg_adrs); // レジスタ・アドレス送信〜 ACK応答が、'0' になるまで待つ I2C_Send(data); // 当該レジスタへデータを書き込む I2C_Stop(); // ストップコンディションを発行する } /*********************************************************************************** * BME280 I2C読込み関数 * * <引数> * @ start_adrs:読出し開始レジスタ・アドレス *  A byte_count:読出しバイト数(1〜8バイト) * ************************************************************************************/ void ReadBME280(unsigned char start_adrs, char byte_count) { unsigned char i; I2C_Start(BME280_ADRS,RW_0); // スタートコンディションを発行〜I2Cアドレス + Wビットを送信〜ACK応答が、'0' になるまで待つ I2C_Send(start_adrs); // 読出し開始番地送信〜 ACK応答が、'0' になるまで待つ I2C_rStart(BME280_ADRS,RW_1) ; // リピート・スタートコンディションを発行する〜I2Cアドレス + Rビットを送信〜ACK応答が、'0' になるまで待つ //// 当該BME280レジスタのデータを、開始アドレスから指定バイト数読出す。 (アドレスは自動インクリメント) for (i = 0; i != byte_count-1; ++i) { rec_data[i] = I2C_Receive(ACK); // 最終アドレスの前までは、読出し後、マスタへ、ACK送信 } /// 最終アドレスになって抜けて来る rec_data[i] = I2C_Receive(NOACK); // 最終アドレスの読出し後、マスタへ、NACK送信 I2C_Stop() ; // ストップコンディションを発行する } /************************************************ * BME280 初期化関数 *************************************************/ char BME280_Init(char id ,char Sensor_adrs) { char ans; /// デバイスの識別IDをチェックする処理 ReadBME280(0xD0, 1); // 読込んだIDは、rec_data[0]に格納されている。 if (rec_data[0] == BME280_ID) { //// IDが一致したならデバイスを初期化する処理 ans= 0; /// 初期値テーブルの値を、当該レジスタに書込む。 WriteBME280(0xF2, 0x01); // Ctri Hum REG: 湿度測定の有効:オーバーサンプリング値= x1 WriteBME280(0xF4, 0x27); // Ctri Meas REG: 温度・気圧の有効:オーバーサンプリング値= x1、ノーマルモード(繰り返し測定) WriteBME280(0xF5, 0xA0); // Config REG:スタンバイ時間:1秒、フィルタ無し、I2C制御で使用 } else ans = 1 ; // IDが一致しない return ans ; } /************************************************************** * 測定値の生データ読込み関数 * 各32bitで、*_raw変数に格納 ***************************************************************/ void ReadData(void) { ReadBME280(0xF7, 8); // 読込んだ各測定生データは、rec_data[0]〜[7]に、バイト型で格納されている。 /// 読込んだ各バイトデータを、ワード型で、それぞれの生データ変数に格納 pres_raw= ((unsigned long)rec_data[0] << 12) | ((unsigned long)rec_data[1] << 4) | ((unsigned long)rec_data[2] >> 4); temp_raw= ((unsigned long)rec_data[3] << 12) | ((unsigned long)rec_data[4] << 4) | ((unsigned long)rec_data[5] >> 4); hum_raw = ((unsigned long)rec_data[6] << 8) | (unsigned long)rec_data[7]; } /****************************************** * センサー補正データの取得 *******************************************/ void ReadTrim(void) { /// calibレジスタ・アドレス:0x88〜0x9Fまでの24バイトを配列(rec_data{})に読み込む ReadBME280(0x88, 24); // rec_data{0}〜 rec_data{23}に、バイト型で格納 /// まずここまで読み込んだ補正係数を、補正変数に格納 dig_T1= ((unsigned int)rec_data[1] << 8) | (unsigned int)rec_data[0]; dig_T2= ((int)rec_data[3] << 8) | (int)rec_data[2]; dig_T3= ((int)rec_data[5] << 8) | (int)rec_data[4]; dig_P1= ((unsigned int)rec_data[7] << 8) | (unsigned int)rec_data[6]; dig_P2= ((int)rec_data[9] << 8) | (int)rec_data[8]; dig_P3= ((int)rec_data[11] << 8) | (int)rec_data[10]; dig_P4= ((int)rec_data[13] << 8) | (int)rec_data[12]; dig_P5= ((int)rec_data[15] << 8) | (int)rec_data[14]; dig_P6= ((int)rec_data[17] << 8) | (int)rec_data[16]; dig_P7= ((int)rec_data[19] << 8) | (int)rec_data[18]; dig_P8= ((int)rec_data[21] << 8) | (int)rec_data[20]; dig_P9= ((int)rec_data[23] << 8) | (int)rec_data[22]; /// calibレジスタ・アドレス:0xA1の1バイトを配列(rec_data{})に読み込む ReadBME280(0xA1, 1); // rec_data{0}に、バイト型で格納 /// 読み込んだ補正係数を、補正変数に格納 dig_H1= rec_data[0]; /// calibレジスタ・アドレス:0xE1〜0xE7までの7バイトを配列(rec_data{})に読み込む ReadBME280(0xE1, 7); // rec_data{0}〜 rec_data{6}に、バイト型で格納 /// 読み込んだ補正係数を、補正変数に格納(ここで全32バイト終了) dig_H2 = ((int)rec_data[1] << 8) | (int)rec_data[0]; dig_H3 = rec_data[2]; dig_H4 = ((int)rec_data[3] << 4) | (0x0F & (int)rec_data[4]); dig_H5 = ((int)rec_data[5] << 4) | (((int)rec_data[4] >> 4) & 0x0F); dig_H6 = (char)rec_data[6]; } /************************************************ * 温度の補正計算 *************************************************/ long calibration_T(long adc_T) { long var1, var2, T; var1= ((((adc_T >> 3) - ((long)dig_T1<<1))) * ((long)dig_T2)) >> 11; var2= (((((adc_T >> 4) - ((long)dig_T1)) * ((adc_T>>4) - ((long)dig_T1))) >> 12) * ((long)dig_T3)) >> 14; t_fine= var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } /************************************************ * 気圧の補正計算 *************************************************/ unsigned long calibration_P(long adc_P) { long var1, var2; unsigned long P; var1= (((long)t_fine)>>1) - (long)64000; var2= (((var1>>2) * (var1>>2)) >> 11) * ((long)dig_P6); var2= var2 + ((var1*((long)dig_P5))<<1); var2= (var2>>2)+(((long)dig_P4)<<16); var1= (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((long)dig_P2) * var1)>>1))>>18; var1= ((((32768+var1))*((long)dig_P1))>>15); if (var1 == 0) return 0; P= (((unsigned long)(((long)1048576)-adc_P)-(var2>>12)))*3125; if (P < 0x80000000) P = (P << 1) / ((unsigned long) var1); else P = (P / (unsigned long)var1) * 2; var1= (((long)dig_P9) * ((long)(((P>>3) * (P>>3))>>13)))>>12; var2= (((long)(P>>2)) * ((long)dig_P8))>>13; P= (unsigned long)((long)P + ((var1 + var2 + dig_P7) >> 4)); return P; } /************************************************ * 湿度の補正計算 *************************************************/ unsigned long calibration_H(long adc_H) { long v_x1; v_x1= (t_fine - ((long)76800)); v_x1= (((((adc_H << 14) -(((long)dig_H4) << 20) - (((long)dig_H5) * v_x1)) + ((long)16384)) >> 15) * (((((((v_x1 * ((long)dig_H6)) >> 10) * (((v_x1 * ((long)dig_H3)) >> 11) + ((long) 32768))) >> 10) + ((long)2097152)) * ((long) dig_H2) + 8192) >> 14)); v_x1= (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((long)dig_H1)) >> 4)); v_x1= (v_x1 < 0 ? 0 : v_x1); v_x1= (v_x1 > 419430400 ? 419430400 : v_x1); return (unsigned long)(v_x1 >> 12); }