第21回 ブレッドボード回路の動作確認

今回はブレッドボードの回路が正しく動作するか確認します。

目次

動作確認内容

今回は、組み立てたブレッドボードが正しく動作するか確認します。動作確認用のプログラムを書き込み動作確認しますが、このプログラムは以下の動作をします。

  • 温湿度・気圧センサから湿度、気圧データを1秒に1回取得
  • 温度センサから温度を1秒に1回取得
  • 取得したデータをLCDモジュールに1秒に1回表示
  • 2個のLEDを1秒ごとに点滅

温度、湿度、気圧は現在の値が表示されますので、大体の値が正しいか確認してみてください。温度は気温計があれば確認できますが、体感温度から大きく変わっていなければ問題ないと思います。湿度は冬は30〜40%ぐらい、夏でしたら60%〜90%ぐらいのことが多いと思います。

なお、気圧は気象庁が発表した値からかなりずれているケースがあります。気圧のデータは以下のサイトで確認できます。

気象庁アメダスデータ

ここで気象庁が発表している気圧データは海抜0メートルの値です。標高が10m高くなるごとに約1hPa(ヘクトパスカル)低くなりますので、例えば海岸近くでもマンションの上層階に住んでいる場合は数hPa低くなります。また、内陸部に住んでいる方は意外に標高が高いこともありますので、例えば標高100mの場合は10hPaぐらいずれます。測定している地点の標高に注意して確認してみてください。

なお、気象庁が発表する気圧データは、日光、軽井沢、富士山、河口湖については、現地の観測所で測定したデータになります(これらの地点以外は海抜0メートルの値です)。

プログラム書き込み時の電源

基礎編・応用編で製作したタイマーは、プログラムを書き込む時の電源はPICKitから供給していました。

実践編では、電源はPICKitから供給せず、電池から供給しますので、注意してください。

理由ですが、PICKitの電源と3端子レギュレータの出力は接続されています。以下の回路図で赤色の部分は接続されています。

Pic practice 21 pickit3 power

PICKitから電源を供給すると、3端子レギレータの出力端子に電圧がかかってしまいます。3端子レギレータは入力電圧がない状態で出力に電圧をかけると壊れる可能性がありますので、実践編ではPICKitから電源は供給せず、電池→3端子レギュレータ経由で電源を供給します。

プロジェクト新規作成

それでは、MPLABX IDEを立ち上げて、新規プロジェクトを作成しましょう。

Fileメニューから「New Project…」を選択します。最初に以下のダイアログか表示されますので、「Microchip Embedded」「Standalone Project」が選択されていることを確認し、Nextボタンをクリックします。

Pic practice 21 new project 1

次にPICマイコンの種類を選択するダイアログになりますので、Device欄に直接「pic16f18857」と入力し、「PIC16F18857」を選択、Nextボタンをクリックします。

Pic practice 21 new project 2

次のダイアログでは書き込みツールを選択しますので、「PICkit3」または「PICkit4」を選択し、Nextボタンをクリックします。

PICkit選択

コンパイラの選択画面になりますので、XC8を選択します。バージョンは古すぎなければ大丈夫です。選択後、Nextボタンをクリックします。

コンパイラ選択

最後にプロジェクト名を設定するダイアログになりますので、Project Nameに「InfoPanel_Test」などと入力し、EncodingはUTF-8を選択します。プロジェクト名ですが、今回は動作確認用のプロジェクトで、次回以降、別の新規プロジェクトを作成しプログラムをゼロから作っていきますので、テスト用のプロジェクト名にしてください。

Pic practice 21 new project 5

Finishボタンをクリックすると、新規プロジェクトが作成されます。

プロジェクト設定確認

次にプロジェクトの設定を確認します。具体的には、プログラム書き込み時、PICKitから電源が供給されない設定になっているかの確認です。

プロジェクト名を右クリックし、表示されるメニューから「Properties」を選択します。

Pic practice 21 setting 1

次に、Propertiesダアログで、PICkitを選択し、右側の領域の上のメニューからPowerを選択、Power target circuit from PICkitのチェックボックスがオフになっていることを確認します。

PICkit電源供給設定の確認

確認できたらOKボタンをクリックしてダイアログを閉じます。

main.cファイル作成

次に、新規ファイルを作成しますので、最初にプロジェクトのSource Filesフォルダを右クリック、表示されるメニューからNew→main.cを選択します。

Pic practice 21 new file 1

選択後、ファイル名設定のダイアログが表示されますので、File Name欄に「main」と入力、OKボタンをクリックします。

Pic practice 21 new file 2

main.cファイルが作成されます。作成されたmain.cファイルはすでに何行かコードが書かれていますので、それらを全て削除して、以下のプログラムをコピペします。プログラムの右上にコピーボタンがあります。クリックするとクリップボードにコピーされます。

/*
 * BME280温湿度大気圧センサ
 * ATD7410温度センサモジュール
 * LCDモジュール
 * 制御プログラム (試作版)
 * 
 * https://tool-lab.com/
 *
 * 更新
 *   2018. 2.12: 新規作成
 *   2018. 4. 1: 動作周波数を4MHz、I2Cクロック周波数を100kHzに変更
 *   2024. 2. 3: putch関数の引数の型をuint8_tからcharに変更
 * 
 */

//
// PIC16F18857コンィグレーション設定
//
// CONFIG1
#pragma config FEXTOSC = OFF    // External Oscillator mode selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINT1  // Power-up default value for COSC bits (HFINTOSC (1MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; i/o or oscillator function on OSC2)
#pragma config CSWEN = ON       // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable bit (FSCM timer enabled)
//
// CONFIG2
#pragma config MCLRE = OFF      // Master Clear Enable bit (IO)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config LPBOREN = OFF    // Low-Power BOR enable bit (ULPBOR disabled)
#pragma config BOREN = ON       // Brown-out reset enable bits (Brown-out Reset Enabled, SBOREN bit is ignored)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (VBOR) set to 1.9V on LF, and 2.45V on F Devices)
#pragma config ZCD = OFF        // Zero-cross detect disable (Zero-cross detect circuit is disabled at POR.)
#pragma config PPS1WAY = ON     // Peripheral Pin Select one-way control (The PPSLOCK bit can be cleared and set only once in software)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a reset)
//
// CONFIG3
#pragma config WDTCPS = WDTCPS_31// WDT Period Select bits (Divider ratio 1:65536; software control of WDTPS)
#pragma config WDTE = OFF       // WDT operating mode (WDT Disabled, SWDTEN is ignored)
#pragma config WDTCWS = WDTCWS_7// WDT Window Select bits (window always open (100%); software control; keyed access not required)
#pragma config WDTCCS = SC      // WDT input clock selector (Software Control)
//
// CONFIG4
#pragma config WRT = OFF        // UserNVM self-write protection bits (Write protection off)
#pragma config SCANE = available// Scanner Enable bit (Scanner module is available for use)
#pragma config LVP = ON         // Low Voltage Programming Enable bit (Low Voltage programming enabled. MCLR/Vpp pin function is MCLR.)
//
// CONFIG5
#pragma config CP = OFF         // UserNVM Program memory code protection bit (Program Memory code protection disabled)
#pragma config CPD = OFF        // DataNVM code protection bit (Data EEPROM code protection disabled)

//
// ヘッダファイル
//   int_t型を使用するため、stdint.hをインクルード
//   printfを使用するため、stdio.hをインクルード
//
#include <xc.h>
#include <stdint.h>
#include <stdio.h>


//
// 定数
//

// I2C
#define I2C_ACK  0x00
#define I2C_NACK 0xff

// LCDモジュール
#define LCD_I2C_ADDRESS 0x7c  // LCDモジュールのI2Cアドレス

// ADT7410温度センサ
#define ADT7410_I2C_WRITE_ADDRESS 0x90  // ADT7410のI2Cアドレス(Write)
#define ADT7410_I2C_READ_ADDRESS  0x91  // ADT7410のI2Cアドレス(Read)
#define ADT7410_CONFIG_ADDRESS    0x03  // コンフィグレーション設定アドレス
#define ADT7410_TEMP_ADDRESS      0x00  // 温度格納アドレス(上位8ビット)
#define ADT7410_CONFIG_VALUE      0x80  // 設定値(16-bit分解能指定、他はデフォルト)

// SPIピン設定
#define SPI_SCLK   LATCbits.LATC7
#define SPI_MISO   PORTCbits.RC6
#define SPI_MOSI   LATCbits.LATC5
#define SPI_SS     LATCbits.LATC4



//
// 関数プロトタイプ宣言
//

// LCDモジュール表示制御関数
void lcdInitialize(void);               // LCD初期化
void lcdClearDisplay(void);             // ディスプレイ全消去
void lcdSendCommand(uint8_t);           // コマンド送信
void lcdSendData(uint8_t);              // 1文字表示
void lcdSendString(char *);             // 文字列表示
void lcdLocateCursor(uint8_t,uint8_t);  // カーソル位置指定

// LCDモジュールI2C制御関数
void lcdGenerateI2CData(uint8_t, uint8_t, uint8_t);

// ADT7410温度センサ制御関数
void  adt7410Config(uint8_t);       // 動作設定
float adt7410GetTemperature(void);  // 温度取得

// I2Cプロトコル各信号の生成関数
void    i2cProtocolStart(void);        // スタートビット生成
void    i2cProtocolRepeatStart(void);  // リピートスタートビット生成
void    i2cProtocolStop(void);         // ストップビット生成
void    i2cProtocolSendData(uint8_t);  // 1バイトデータ送信
uint8_t i2cProtocolReceiveData(void);  // バイトデータ受信
uint8_t i2cProtocolAckCheck(void);     // ACK信号チェック
void    i2cProtocolSendAck(void);      // ACK送信
void    i2cProtocolSendNack(void);     // NACK送信

//   BME280センサー制御
void     bme280Initialization(void);
void     bme280ForcedMeasurement(void);
void     bme280ReadoutTrimmingParameters(void);
void     bme280WriteRegister(uint8_t reg_address, uint8_t data);
void     bme280ReadoutMeasuredRawData(void);
int32_t  bme280CompensateTemperature(void);
uint32_t bme280CompensatePressure(void);
uint32_t bme280CompensateHumidity(void);

// SPI通信制御関数
uint16_t spiRead2bytesData(uint8_t reg);
uint8_t  spiRead1ByteData(uint8_t reg);
void     spiWrite8bitSignal(uint8_t data);
uint8_t  spiRead8bitSignal();



// クロック周波数
// __delay_ms()関数が時間基準に使用する
#define _XTAL_FREQ 4000000

// グローバル変数
//
// BME280温湿度・気圧読み取りデータ
uint32_t hum_raw, temp_raw, pres_raw;
int32_t  temp_fine;

// BME280気温補正データ
uint16_t dig_T1;
int16_t  dig_T2;
int16_t  dig_T3;

// BME280湿度補正データ
uint8_t  dig_H1;
int16_t  dig_H2;
uint8_t  dig_H3;
int16_t  dig_H4;
int16_t  dig_H5;
int8_t   dig_H6;

// BME280気圧補正データ
uint16_t dig_P1;
int16_t  dig_P2;
int16_t  dig_P3;
int16_t  dig_P4;
int16_t  dig_P5;
int16_t  dig_P6;
int16_t  dig_P7;
int16_t  dig_P8;
int16_t  dig_P9;


//
// main関数
//
void main(void) {

    // 動作周波数設定
    OSCCON1bits.NOSC = 0b110;   // 内部クロック使用
    OSCCON1bits.NDIV = 0b0000;  // 分周1:1
    OSCFRQbits.HFFRQ = 0b010;   // 4MHz
    
    // ピン属性設定
    ANSELA = 0b00000000;
    ANSELB = 0b00000000;
    ANSELC = 0b00000000;
    TRISA  = 0b00000000;
    TRISB  = 0b00000000;
    TRISC  = 0b01001100;
    
    // SPI信号線初期設定
    SPI_SCLK = 0;
    SPI_MOSI = 0;
    SPI_SS   = 1;

    
    //
    // I2C通信ピンのPPS設定
    //
    // 設定ロック解除
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x00;

    // SCL, SDAピンの割り当て
    SSP1DATPPSbits.SSP1DATPPS = 0x12;   // RC2をMSSP1:SDA1に設定
    SSP1CLKPPSbits.SSP1CLKPPS = 0x13;   // RC3をMSSP1:SCL1に設定
    RC3PPS = 0x14;   // RC3をMSSP1:SCL1に設定
    RC2PPS = 0x15;   // RC2をMSSP1:SDA1に設定

    // 設定ロック
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x01;
    
    
    //
    // I2C通信設定
    //
    // SMP Standard Speed; CKE disabled; 
    SSP1STAT = 0x80;
    // SSPEN enabled; CKP Idle:Low, Active:High; SSPM FOSC/4_SSPxADD_I2C; 
    SSP1CON1 = 0x28;
    // SBCDE disabled; BOEN disabled; SCIE disabled; PCIE disabled; DHEN disabled; SDAHT 300ns; AHEN disabled; 
    SSP1CON3 = 0x08;
    // Baud Rate Generator = 100kHz 
    SSP1ADD = 0x09;
    
    // LCDモジュール電源安定化時間待ち
    __delay_ms(100);
    
    // LCD初期化
    lcdInitialize();                    // LCD初期設定
    lcdClearDisplay();
    
    // ADT7410温度センサ初期化
    adt7410Config(ADT7410_CONFIG_VALUE);

    // BME280初期化
    //   コントロールレジスタ、補正係数取得
    bme280Initialization();

    // 実際の温湿度
    //
    // 
    float actual_temp;
    float actual_hum;
    float actual_press;

    // 補正データ
    int32_t  compensated_temp;
    uint32_t compensated_hum;
    uint32_t compensated_press;

    
    //
    while(1){

        //
        lcdLocateCursor(0, 0);
        printf("%3.2f", adt7410GetTemperature());
        lcdSendData(0xdf);
        lcdSendData('C');

        //
        // SPI通信で温湿度センサの値を取得
        bme280ForcedMeasurement();
        bme280ReadoutMeasuredRawData();

        // 取得したデータをキャリブレーション
        compensated_temp  = bme280CompensateTemperature();
        compensated_hum   = bme280CompensateHumidity();
        compensated_press = bme280CompensatePressure();

        // 実際の温湿度に変換
        actual_temp  = (float)compensated_temp  / 100.0;
        actual_hum   = (float)compensated_hum   / 1024.0;
        actual_press = (float)compensated_press / 100.0;

        printf("  %3.0f%%", actual_hum);
        lcdLocateCursor(0, 1);
        printf("%4.2fhPa", actual_press);
        
        //
        LATBbits.LATB0 = 1;
        LATBbits.LATB1 = 1;
        __delay_ms(1000);
        LATBbits.LATB0 = 0;
        LATBbits.LATB1 = 0;
        __delay_ms(1000);
    }

}


//
// ADT7410温度センサ関数
//

// 動作設定
void adt7410Config(uint8_t config_value) {
    
    i2cProtocolStart();                              // スタートビット生成
    i2cProtocolSendData(ADT7410_I2C_WRITE_ADDRESS);  // I2Cアドレス送信
    i2cProtocolSendData(ADT7410_CONFIG_ADDRESS);     // 書き込みアドレス送信
    i2cProtocolSendData(config_value);               // 設定値送信
    i2cProtocolStop();                               // ストップビット生成

}

// 温度取得
float adt7410GetTemperature(void) {
    
    uint8_t temp_high, temp_low;
    int16_t temp_value;
    
    i2cProtocolStart();
    i2cProtocolSendData(ADT7410_I2C_WRITE_ADDRESS);
    i2cProtocolSendData(ADT7410_TEMP_ADDRESS);
    i2cProtocolRepeatStart();
    i2cProtocolSendData(ADT7410_I2C_READ_ADDRESS);
    temp_high = i2cProtocolReceiveData();
    i2cProtocolSendAck();
    temp_low  = i2cProtocolReceiveData();
    i2cProtocolSendNack();
    i2cProtocolStop();
    
    if( temp_high & 0x80 ) {
        temp_value = ( (temp_high & 0x7f) << 8 ) + temp_low - 32768;
    } else {
        temp_value = ( (temp_high & 0x7f) << 8 ) + temp_low;
    }

    return (float)temp_value / 128.0;
    
}



//
// LCD制御関数
//

void putch(char character) {

    lcdSendData(character);

}

//
// LCDモジュールに制御コードまたはデータを送信
//
void lcdGenerateI2CData(uint8_t address, uint8_t control_code, uint8_t data) {
    
    i2cProtocolStart();                 // スタートビット生成
    i2cProtocolSendData(address);       // アドレス送信
    i2cProtocolSendData(control_code);  // 制御コード送信
    i2cProtocolSendData(data);          // データ送信
    i2cProtocolStop();                  // ストップビット生成

    return;
}

//
// 1バイトデータ送信
//   0x40の後にデータを送信
void lcdSendData(uint8_t data){

    // データ送信の場合は制御コードは0x40
    lcdGenerateI2CData(LCD_I2C_ADDRESS, 0x40, data);
    
    // ウエイト
    __delay_us(60);

    return;
}

//
// コマンド送信
//   0x00の後にコマンドを送信
//
void lcdSendCommand(uint8_t command){

    // コマンド送信の場合は制御コードは0x00
    lcdGenerateI2CData(LCD_I2C_ADDRESS, 0x00, command);

    // ウエイト
    __delay_ms(20);
}

//
// ディスプレイ消去
//
void lcdClearDisplay(void){
    
    lcdSendCommand(0x01);

}

//
// カーソル位置移動
//   移動後に文字データを送信するとその位置から表示される
//
void lcdLocateCursor(uint8_t position_x, uint8_t position_y){
    
    uint8_t address;
    
    // Y座標位置のアドレス算出
    switch( position_y ) {
        // 1行目の場合
        case 0:
            address = 0x00 + 0x80;
            break;
        // 2行目の場合
        case 1:
            address = 0x40 + 0x80;
            break;
    }
    
    // X座標位置を加算して表示位置アドレス送信
    lcdSendCommand( address += position_x);
    
    return;
}

//
// 文字列を送信
//
void lcdSendString(char *str){
    
    // strの文字列を*strが0になるまでLCDモジュールに送信
    while(*str) {
        lcdSendData(*str++);
    }
    
    return;
}


//
// LCDモジュール初期化
//
void lcdInitialize(void){

    // 電源が安定するまでの時間待ち
    //__delay_ms(100);
    
    // 初期化コマンド送信
    lcdSendCommand(0x38); // 
    lcdSendCommand(0x39); //
    lcdSendCommand(0x14); //
    lcdSendCommand(0x70); //
    lcdSendCommand(0x56); // 5Vの時は50でOK。3.3Vの場合はBooster=ON、コントラストをあげる
    lcdSendCommand(0x6c); //
    
    // 時間待ち
    __delay_ms(300);
    
    // 初期化コマンド続き
    lcdSendCommand(0x38); //
    lcdSendCommand(0x0c); //
    lcdSendCommand(0x01); //
    // lcdSendCommand(0x06);
    
    return;
}


//
// I2Cプロトコル制御関数
//

// スタートビット生成
void i2cProtocolStart() {
    
    // SSPxCON2レジスタのSENビットを1に設定すると
    // スタートビットが発行される
    // 発行が完了するとSENに0がセットされるので
    // 発行完了までwhile文で待つ
	SSP1CON2bits.SEN = 1;
	while (SSP1CON2bits.SEN) {}

	return;
}

// リスタートビット生成
void i2cProtocolRepeatStart() {
    
	SSP1IF = 0;
	SSP1CON2bits.RSEN = 1;
	while (SSP1CON2bits.RSEN) {}

	return;
}

// ストップビット生成
void i2cProtocolStop() {
    
	SSP1IF = 0;
	SSP1CON2bits.PEN = 1;
	while (SSP1CON2bits.PEN) {}
	SSP1IF = 0;

	return;
}

// 1バイトデータ送信
void i2cProtocolSendData(uint8_t data) {
    
	SSP1IF = 0;
	SSP1BUF = data;
	while (!SSP1IF) {}

	return;
}

// 1バイトデータ受信
uint8_t i2cProtocolReceiveData() {
    
	SSP1IF = 0;
	SSP1CON2bits.RCEN = 1;
	while (SSP1CON2bits.RCEN) {}

	return SSP1BUF;
}

// Ackステータスチェック
uint8_t i2cProtocolAckCheck() {
    
	uint8_t data;

	if (SSP1CON2bits.ACKSTAT) {
		data = I2C_NACK;
	} else {
		data = I2C_ACK;
	}

	return data;
}

// Ack送信
void i2cProtocolSendAck() {
    
    // ACKDTにACKをセット(負論理なので0を設定)
	SSP1CON2bits.ACKDT = 0;

    // NACK信号生成
	SSP1CON2bits.ACKEN = 1;
	while (SSP1CON2bits.ACKEN) {}

	return;
}

// Nack送信
void i2cProtocolSendNack() {
    
    // ACKDTにNACKをセット(負論理なので1を設定)
	SSP1CON2bits.ACKDT = 1;

    // NACK信号生成
	SSP1CON2bits.ACKEN = 1;
	while (SSP1CON2bits.ACKEN) {}

	return;
}



//
// BME280初期化関数
//
void bme280Initialization(void) {

    // 動作パラメータ設定
    uint8_t t_sb    = 0;  // スタンドバイ時間は使用しない
    uint8_t filter  = 0;  // フィルタOFF
    uint8_t spi3or4 = 0;  // SPIは4線式
    uint8_t osrs_t  = 1;  // 温度オーバーサンプリング x1
    uint8_t osrs_p  = 1;  // 大気圧オーバーサンプリング x1
    uint8_t osrs_h  = 1;  // 湿度オーバーサンプリング x1
    uint8_t Mode    = 0;  // 設定後はスリープモードに移行

    // 設定値をフォーマットに合わせる
    uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | Mode;
    uint8_t config_reg    = (t_sb << 5) | (filter << 2) | spi3or4;
    uint8_t ctrl_hum_reg  = osrs_h;

    // BME280動作パラメータ書き込み
    bme280WriteRegister(0xF2, ctrl_hum_reg);
    bme280WriteRegister(0xF4, ctrl_meas_reg);
    bme280WriteRegister(0xF5, config_reg);
    __delay_ms(1000);

    // 補正値読み込み
    //   センサごとに固定ちのため、初期化時のみ読み込む
    bme280ReadoutTrimmingParameters();

}

void bme280ForcedMeasurement(void) {

    // 動作パラメータ設定
    uint8_t t_sb    = 0;  // スタンドバイ時間は使用しない
    uint8_t filter  = 0;  // フィルタOFF
    uint8_t spi3or4 = 0;  // SPIは4線式
    uint8_t osrs_t  = 1;  // 温度オーバーサンプリング x1
    uint8_t osrs_p  = 1;  // 大気圧オーバーサンプリング x1
    uint8_t osrs_h  = 1;  // 湿度オーバーサンプリング x1
    uint8_t Mode    = 1;  // 測定指示

    // 設定値をフォーマットに合わせる
    uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | Mode;
    uint8_t config_reg    = (t_sb << 5) | (filter << 2) | spi3or4;
    uint8_t ctrl_hum_reg  = osrs_h;

    // BME280動作パラメータ書き込み
    bme280WriteRegister(0xF2, ctrl_hum_reg);
    bme280WriteRegister(0xF4, ctrl_meas_reg);
    bme280WriteRegister(0xF5, config_reg);

    // 測定待ち
    //   statusレジスタを監視して測定完了を判断すべきだが
    //   while文での動作ができなかったので時間待ちで代用
    __delay_ms(10);
    
    // この段階でBME280の温湿度大気圧レジスタに測定値が格納されている
    // このあと自動的にスリープモードに入る

}


//
// キャリブレーションデータ読み込み
//
void bme280ReadoutTrimmingParameters(void) {

    // 気温データ
    dig_T1 = spiRead2bytesData(0x88);
    dig_T2 = (int16_t)spiRead2bytesData(0x8A);
    dig_T3 = (int16_t)spiRead2bytesData(0x8C);

    // 気圧データ
    dig_P1 = spiRead2bytesData(0x8E);
    dig_P2 = (int16_t)spiRead2bytesData(0x90);
    dig_P3 = (int16_t)spiRead2bytesData(0x92);
    dig_P4 = (int16_t)spiRead2bytesData(0x94);
    dig_P5 = (int16_t)spiRead2bytesData(0x96);
    dig_P6 = (int16_t)spiRead2bytesData(0x98);
    dig_P7 = (int16_t)spiRead2bytesData(0x9A);
    dig_P8 = (int16_t)spiRead2bytesData(0x9C);
    dig_P9 = (int16_t)spiRead2bytesData(0x9E);

    // 湿度データ
    dig_H1 = spiRead1ByteData(0xA1);
    dig_H2 = (int16_t)spiRead2bytesData(0xE1);
    dig_H3 = spiRead1ByteData(0xE3);
    dig_H4 = (int16_t)((spiRead1ByteData(0xE4) << 4) | (spiRead1ByteData(0xE5) & 0x0F));
    dig_H5 = (int16_t)((spiRead1ByteData(0xE6) << 4) | (spiRead1ByteData(0xE5) >> 4));
    dig_H6 = (int8_t)spiRead1ByteData(0xE7);

}

//
// BME280動作パラメータ設定レジスタ書き込み
//
void bme280WriteRegister(uint8_t reg_address, uint8_t data) {

    // スレーブセレクトアクティブ
    SPI_SS = 0;

    // アドレス指定(書き込みは最上位ビット0)
    spiWrite8bitSignal(reg_address & 0b01111111);

    // データ書き込み
    spiWrite8bitSignal(data);

    // スレーブセレクトインアクティブ
    SPI_SS = 1;
  
}

// 
// 温湿度・気圧データ読み込み
// 
void bme280ReadoutMeasuredRawData() {

    uint32_t data[8];
    uint8_t i;

    // スレーブセレクトアクティブ
    SPI_SS = 0;

    // 読み込み開始アドレス指定
    spiWrite8bitSignal(0xF7 | 0b10000000);
    // データ読み込み
    for(i=0; i<8; i++){
      data[i] = spiRead8bitSignal();
    }

    // スレーブセレクトインアクティブ
    SPI_SS = 0;

    // 読み込みしたデータから気温、湿度、気圧データを生成
    pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); //0xF7, msb+lsb+xlsb=19bit
    temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); //0xFA, msb+lsb+xlsb=19bit
    hum_raw  = (data[6] << 8) | data[7];  //0xFD, msb+lsb=19bit(16:0)

}

//
// 気温キャリブレーション
//   BME280データシートの計算プログラム使用
// 
int32_t bme280CompensateTemperature() {
  
    int32_t var1, var2, T;

    var1 = ((((temp_raw >> 3) - ((int32_t)dig_T1<<1))) * ((int32_t)dig_T2)) >> 11;
    var2 = (((((temp_raw >> 4) - ((int32_t)dig_T1)) * ((temp_raw>>4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;
    temp_fine = var1 + var2;
    T = (temp_fine * 5 + 128) >> 8;

    return T; 

}


//
// 気圧キャリブレーション
//   BME280データシートの計算プログラム使用
// 
uint32_t bme280CompensatePressure() {

    int32_t var1, var2;
    uint32_t P;

    var1 = (((int32_t)temp_fine)>>1) - (int32_t)64000;
    var2 = (((var1>>2) * (var1>>2)) >> 11) * ((int32_t)dig_P6);
    var2 = var2 + ((var1*((int32_t)dig_P5))<<1);
    var2 = (var2>>2)+(((int32_t)dig_P4)<<16);
    var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((int32_t)dig_P2) * var1)>>1))>>18;
    var1 = ((((32768+var1))*((int32_t)dig_P1))>>15);
    if (var1 == 0)
      return 0;

    P = (((uint32_t)(((int32_t)1048576)-pres_raw)-(var2>>12)))*3125;
    if(P<0x80000000)
      P = (P << 1) / ((uint32_t) var1);   
    else
      P = (P / (uint32_t)var1) * 2;

    var1 = (((int32_t)dig_P9) * ((int32_t)(((P>>3) * (P>>3))>>13)))>>12;
    var2 = (((int32_t)(P>>2)) * ((int32_t)dig_P8))>>13;
    P = (uint32_t)((int32_t)P + ((var1 + var2 + dig_P7) >> 4));

    return P;

}

//
// 湿度キャリブレーション
//   BME280データシートの計算プログラム使用
// 
uint32_t bme280CompensateHumidity() {

    int32_t v_x1;

    v_x1 = (temp_fine - ((int32_t)76800));
    v_x1 = (((((hum_raw << 14) -(((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1)) + 
             ((int32_t)16384)) >> 15) * (((((((v_x1 * ((int32_t)dig_H6)) >> 10) * 
             (((v_x1 * ((int32_t)dig_H3)) >> 11) + ((int32_t) 32768))) >> 10) + ((int32_t)2097152)) * 
             ((int32_t) dig_H2) + 8192) >> 14));
    v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
    // v_x1 = (v_x1 < 0 ? 0 : v_x1);
    if( v_x1 < 0 ){
        v_x1 = 0;
    }
    // v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
    if( v_x1 > 419430400 ) {
        v_x1 = 419430400;
    }

    return (uint32_t)(v_x1 >> 12);

}


//
// SPIデータを指定アドレスから2バイト読み込み
// 
uint16_t spiRead2bytesData(uint8_t reg) {
  
    uint16_t data1, data2;
    uint16_t data;

    // スレーブセレクトアクティブ
    SPI_SS = 0;

    // 読み込みデータアドレス指定 (読み込みアドレスは最上位ビットを1にする)
    spiWrite8bitSignal(reg | 0b10000000);
    // データを2バイト読み込み
    data1 = spiRead8bitSignal();
    data2 = spiRead8bitSignal();
    //16ビットデータ生成
    data = (data2 << 8) | data1;

    // スレーブセレクトインアクティブ
    SPI_SS = 1;

    return data;

}


//
// SPIデータを指定アドレスから1バイト読み込み
// 
uint8_t spiRead1ByteData(uint8_t reg) {

    uint8_t data;

    SPI_SS = 0;
    spiWrite8bitSignal(reg);
    __delay_us(10);
    data = spiRead8bitSignal();
    SPI_SS = 1;

    return data;

}

//
// SPIデータ8ビット書き込み
//   プリミティブな関数であるため
//   スレーブセレクト信号はこの関数の前後で制御すること
void spiWrite8bitSignal(uint8_t data) {

    for (int i=7; i>=0; i--) {
      SPI_SCLK = 0;
      if( data & (1<<i) ) {
          SPI_MOSI = 1;
      } else {
          SPI_MOSI = 0;
      }
      SPI_SCLK = 1;
    }

}

//
// SPIデータ8ビット読み込み
//   プリミティブな関数であるため
//   スレーブセレクト信号はこの関数の前後で制御すること
//
uint8_t spiRead8bitSignal() {

    uint8_t read_data = 0;

    for (int i=7; i>=0; i--) {
      read_data <<= 1;
      SPI_SCLK = 0;
      SPI_MOSI = 0;
      SPI_SCLK = 1;
      if(SPI_MISO){
        read_data |= 1;
      }
    }

    return read_data;

}

プログラムをコピペ後、ファイルを保存します。

動作確認

それでは、プログラムを書き込んで動作確認します。

最初にブレッドボードに電池ボックスのリード線をブレッドボードに差し込みます。3端子レギュレータの入力端子とGND端子に接続する点に注意してください。

Pic practice 21 battery breadboard

次に、PICKit3をピンヘッダに接続します。1ピン側に注意します。

PICkitコネクタ1番ピンの位置

準備ができたら、電池ボックスのスイッチをONにして、MPLABX IDEのプログラム書き込みボタンをクリックします。

Pic practice 21 program 1

いつものダイアログが表示されますので、無視してOKボタンをクリックします。

Pic practice 21 program 2

書き込みが終わるとLCDモジュールに現在の気温、湿度、気圧が表示され、LEDが点滅します。

LCDモジュールの表示内容確認

LCDモジュールに現在の気温、湿度、気圧がそれっぽい値でしたら、ちょっと環境を変えて数値が変化するか確認してみましょう。

違う部屋に持って行ったり、湿度センサに息をかけたりして数値が変化するか確認します。

また気圧センサはかなり敏感なので、50cm程度持ち上げたり、キッチンやバスルームの換気扇を入れると気圧の変化がわかると思います。

ここまで確認できれば、ブレッドボードの回路動作は問題ありません。

それでは、次回からプログラムをゼロから作っていきます。最初はデータ通信とは何か、というところから説明します。

更新履歴

日付内容
2018.3.31新規投稿
2018.12.9PICkit4記載追加
2019.5.14BME280の強制測定モードの際、測定が完了するまで待つ処理(10ms)を追加
2024.2.3putch関数の引数をuint8_tからcharに変更(コンパイラのチェック基準が厳しくなり、uint8_tの場合はエラーになるため)
通知の設定
通知タイミング
guest
19 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
しゅう☆
しゅう☆
2 月 前

こちらの講座でPICマイコンに興味を持ち、基礎編、応用編と試してきました。
ソースファイルは大体コピペで何とか動いていたのですが、こちらのページの長いソースはコンパイルエラーで先へ進めませんでした。

MPLAB X V6.15
XC8 v2.45
Pickit5

おそらくMPLAB Xのバージョンが新しいせいだろうと考えて、
MPLAB XやXC8について検索したところ
下記サイトの説明によると型定義のチェックが厳しくなっているとのことでした。

chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/https://gihyo.jp/assets/files/book/2018/978-4-7741-9649-7/download/202003_additional_Information.pdf

私の環境だと、339行目の

”void putch(uint8_t character) {"を
”void putch(char character) {"に変更して
コンパイルおよび書き込みすることが出来ました。
gonta
gonta
9 月 前

この講座で、ボチボチとPICマイコンを学ばせて頂いています。
実践編もやっとここまでたどり着きました。
2年前にブレッドボードに組み立てた回路に最終回のサンプルプログラムをコピペしてほくそ笑んでいましたが、最近もう一組を、又ブレッドボードに組み、この21回のサンプルプログラムをコピペして書き込みしましたが、書き込みエラーが出て書き込めません。IDEのバージョンはV6.10 コンパイラーはXC8 V2.36です。
古いパソコンのIDEV5.5で試しましたが,ダメでした。 
チョット横になったときにふと気がつき、PICを回路から外して、PIKkitから直接書き込んだらどうなるかと試してみました。18857をブレッドボードから外して、秋月で2年前に買ったPIKkit書き込みアダプターで書き込むと、すんなりと書き込めました。当然、pikkitからの5V給電を可にしてです。
まだ、すっきりしたわけではありませんが、これで先の回に進む気が出て来てました。

gonta
gonta
返信  管理者
9 月 前

早速返信を頂き有り難う御座います。
PICマイコンには、このような状況があるとのことで,ちょっと気が収まりました。
PICマイコンとセンサー回路の電源を分ければと言うことは布団の中で気がつきましたが、そうするとセンサーに5Vがかかるのでは無いかと気になります。
これからこの講座を学んで行くにはPICマイコンを回路から外して単体で書き込みをするのが頻発するはずです。今はブレッドボードに直接18857を取り付けていますが、これからはゼロプレッシャーソケットを使ってみようと思いつきました。

gonta
gonta
返信  gonta
7 月 前

その後の結果です。
朝方布団の中で気がつきました。 この実践編では、AVRを使って3.3Vを作っていますので,Pickit4に供給する5VをAVRの前から取れば良いのでは無いかとのことで、早速試しました。
すんなりと書き込みが出来る様になりました。
回答の中で書かれていたことがやっと理解できました。
現在、PIC12F1822に第44回のLCD動作確認プログラムをコピペして、LCDの動作設定を一つづつ確認しようと思っています。
何か問題があればお教え願います。

aaa
aaa
2 年 前

説明をほぼ見ないで、組み立てだけしてコピペしましたが
中々書き込めないので少し前のページを読んだりテスターで異常が無いか等して10時間位色々原因を考えましたが全く分かりませんでした
ふと今さっきpic3のピン数が画像より多いい事に気づいて5本にしたら無事
書き込めました。はんだつけもど素人ですが奇跡的にモニターは作動しているようです
感動とお礼を伝えたい為投稿しました
ど素人ですがこれからもご指導よろしくお願いします

石垣安弘
石垣安弘
3 年 前

先ほどの質問で、講座の回数を間違えました。第39回と書きましたが、正しくは、第21回でした。謹んで訂正いたします。

管理者
管理者
返信  石垣安弘
3 年 前

ご質問どうもありがとうございました。また、お返事が遅くなってしまい申し訳ございませんでした。

「関数宣言やメイン関数のmainまで太字のエラーと表示」というのは、エディタ上でコードに赤線が引かれて左端にエラーマークが表示されるという意味でしょうか。そうであれば、一度ビルドボタンをクリックしてビルドしてみてください。本当にエラーであればエラーにログが表示されますので、その内容をお教えいただければと思います。またエラーが出ずに「Successful」になればビルドはできています。

また、モジュール単体での動作確認ですが、テストプログラムを書けばよいのではないかと思います。(質問の意図が取り違えていたら申し訳ございません)
例えば大気圧センサーであれば、デバイスのIDを取得するプログラムを作成して、デバッグモードで取得した値を確認する、などです。

石垣安弘
石垣安弘
3 年 前

昨年末から、PICマイコンを使ってなにか作りたいと思い、この講座を知り、やっとここまで来ましたが、ブレッドボード試作検証の第39回のプログラムをコピペして、ビルドしましたところ、関数宣言やメイン関数のmainまで太字のエラーと表示されてしまいました。
使っているのは、MPLAB x IDEが、Ver5.45 コンパイラは XC8 Ver2.31 です。
質問1:私の結果は、IDEとコンパイラーのバージョンの違いなのではと思っていますが如何でしょうか。 
質問2:実践編で使っているLCDデバイス、温度センサーモジュールそして気圧センサーモジュールなどを単体で、動作するかどうかを判定する方法はあるのでしょうか。というのは、LCDデバイスなどは、半田付けでブリッジしているかどうか等が気になるからです。通電すれば少しでも光ってくれれば、安心します。 この講座とは関係ない質問でしたら、勘弁願います。

石垣安弘
石垣安弘
3 年 前

claynets様 
早速の回答有難う御座います。 エラーの症状も説明できない初心者ですので、ご容赦願います。 もう一度ビルドしてみて、もしエラーが出たらそのままコピーしてきます。

石垣安弘
石垣安弘
3 年 前

ご指摘のように再度ビルドした結果のコメントをそのままコピペしました。長すぎて申し訳ありません。

make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory ‘C:/Users/jwoya/MPLABXProjects/InfoPanel_Test.X’
make -f nbproject/Makefile-default.mk dist/default/production/InfoPanel_Test.X.production.hex
make[2]: Entering directory ‘C:/Users/jwoya/MPLABXProjects/InfoPanel_Test.X’
“C:Program FilesMicrochipxc8v2.31binxc8-cc.exe” -mcpu=16F18857 -Wl,-Map=dist/default/production/InfoPanel_Test.X.production.map -DXPRJ_default=default -Wl,–defsym=__MPLAB_BUILD=1 -mdfp=”C:/Program Files/Microchip/MPLABX/v5.45/packs/Microchip/PIC16F1xxxx_DFP/1.5.133/xc8″ -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -msummary=-psect,-class,+mem,-hex,-file -ginhx032 -Wl,–data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -std=c99 -gdwarf-3 -mstack=compiled:auto:auto -Wl,–memorysummary,dist/default/production/memoryfile.xml -o dist/default/production/InfoPanel_Test.X.production.elf build/default/production/newmain.p1 build/default/production/newmain1.p1
newmain1.c:171:: error: (237) function “_main” redefined
newmain1.c:296:: error: (237) function “_adt7410Config” redefined
newmain1.c:307:: error: (237) function “_adt7410GetTemperature” redefined
newmain1.c:339:: error: (237) function “_putch” redefined
newmain1.c:348:: error: (237) function “_lcdGenerateI2CData” redefined
newmain1.c:362:: error: (237) function “_lcdSendData” redefined
newmain1.c:377:: error: (237) function “_lcdSendCommand” redefined
newmain1.c:389:: error: (237) function “_lcdClearDisplay” redefined
newmain1.c:399:: error: (237) function “_lcdLocateCursor” redefined
newmain1.c:424:: error: (237) function “_lcdSendString” redefined
newmain1.c:438:: advisory: (1) too many errors (11)
(908) exit status = 1
nbproject/Makefile-default.mk:154: recipe for target ‘dist/default/production/InfoPanel_Test.X.production.hex’ failed
make[2]: Leaving directory ‘C:/Users/jwoya/MPLABXProjects/InfoPanel_Test.X’
nbproject/Makefile-default.mk:91: recipe for target ‘.build-conf’ failed
make[1]: Leaving directory ‘C:/Users/jwoya/MPLABXProjects/InfoPanel_Test.X’
nbproject/Makefile-impl.mk:39: recipe for target ‘.build-impl’ failed
make[2]: *** [dist/default/production/InfoPanel_Test.X.production.hex] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2

BUILD FAILED (exit value 2, total time: 8s)

管理者
管理者
返信  石垣安弘
3 年 前

エラーログどうもありがとうございました。
ログを見ると、「newmain.c」というファイル名のプログラムをコンパイルしたあと、続いて「newmain1.c」というプログラムをコンパイルしようとしてエラーになっています。

作成されたプロジェクトに2つのファイルが存在していませんでしょうか。

石垣安弘
石垣安弘
3 年 前

早速の回答有難う御座います。
ご指摘頂いたファイルは、この後、確認いたします。

自分なりに考えて、第50回の最後のプログラムを別のプロジェクトを作って試してみました。
結果はすんなりとビルドも出来、書き込みも出来たという画面上の結果ですが、なんの表示も出てきません。
ブレットボード上の回路で見落としているミスがあるかもしれませんので、じっくりとチェックしてみます。

石垣安弘
石垣安弘
3 年 前

やっと、LCDモジュールにデータが表示されました。データ波形を小型オシロで眺めながらほくそ笑んでいます。夕方、LCDモジュール変換基板がやっと届き、慎重に半田付けしたのに、何の反応も無い、改めてプログラムを再度書き込むも、書き込みエラーの赤字表示どこかがおかしいと、よくよく見ると、PICkit4につなぐラインを一つ間違えて指していた。改めて書き込んで、LCDを見ると表示してる!
実践編もまだ半分以上残っています。MPLAB X IDEもまださっぱりです。が、Arduinoも目の前をちらついています。
まずは、中間のお礼です。有難う御座います。

管理者
管理者
返信  石垣安弘
3 年 前

LCDモジュールが動作したとのことでよかったです!

この入門シリーズでは基本的な要素技術を説明したつもりですので、ぜひ世の中にない自分だけのオリジナル作品を作られて見てください。自分でオリジナルで制作したものが動くと、本当に感動します。

石垣安弘
石垣安弘
3 年 前

ご指摘頂いた件を調べました。
1)ファイル我に点有るのではとの指摘通り、Linker Filesに、newmain.cがあり、Souce Filesに
  newmain1.cが有りました。 どちらかのファイルを削除する方法を今は知りません。
2)こちらの方が大変なんですが、ブレットボード上の回路を再点検しましたところ、
  LCDモジュール変換基板を表裏逆に半田付けしていました。 変換基板入手まで、
  動作確認はお預けです。

目次