温度に比例した電圧を出力する温度センサー(MCP9700)を使って、測定した温度をLCDに表示するプログラムです。
MCP9700 は、
0度 - 500mv
20度 - 700mv
50度 - 1000mv
の出力なので、PIC内部で発生するFVR 1.024vをADC基準電圧にすることで、10 bit ADCの出力は、1 bit あたり 0.1度となり、大変に扱いやすいセンサーです。
I2CのSDA及びSCLラインにはプルアップ抵抗を取り付ける必要があります。この回路図には表現されていませんが、LCDモジュール内部に、10Kのプルアップ抵抗が内蔵されています。
プログラムを示します。
温度センサーからの直流電圧は外界ノイズの影響を受けやすいので、16回測定しでその平均値を表示します。さらに値の更新は1秒に1回とし見た目の安定性を出しています。16F18313は、プログラムメモリー容量が少ないため、printf関数を使う余裕がありません。そのため、独自関数 printTemp(int temp)で温度センサーから得られたデータを解釈してLCDに表示しています。
/************************************* * File: main.c * 温度をLCDに表示する。 * 秋月(ACM1602NI)I2C LCD表示器を使用 * 1MHz (内部クロック) * RA2 SDA * RA1 SCL * RA4 温度センサー MCP9700 * PIC16F18346 MPLAB XC8 v2.10 * Created on 2020-06-15 **************************************/ #include <xc.h> #include "i2cLCD.h" #define _XTAL_FREQ 1000000 // delay_ms(x) のための定義 #pragma config FEXTOSC = OFF,RSTOSC = HFINT1 // HFINTOSC (1MHz) #pragma config CLKOUTEN = OFF,CSWEN = OFF,FCMEN = OFF #pragma config MCLRE = OFF,PWRTE = OFF,WDTE = OFF,LPBOREN = OFF #pragma config BOREN = OFF,BORV = LOW,PPS1WAY = OFF,STVREN = ON #pragma config DEBUG = OFF #pragma config WRT = OFF,LVP = OFF,CP = OFF,CPD = OFF void printTemp(int temp); // ******************************************************** void main() { int temp = 0; i2c_MasterInit(100000); // I2Cを初期化 LCD_int(); // LCDを初期化 // ADC --------------------------------------- ANSA4 = 1; // ANA4(RA4)はアナログ入力に設定 TRISA4 = 1; ADACT = 0b00000000; // AD変換はプログラムの命令で開始 ADCON1 = 0b10000011; // 右詰め出力, AD変換クロックFosc/2 // 正基準電圧 FVR ADCON0 = 0b00010001; // アナログ入力 ANA4(RA4),ADC ON FVRCON = 0b10000001; LCD_ROMstr("Temp"); // 上段に"Temp" while(1){ temp = 0; for(char i=0;i<16;i++){ // 16回測定し平均する __delay_ms(5); // ADチャージ時間 GO = 1; // AD変換開始 while(GO); // AD変換終了待ち temp += (ADRES-500); // 温度 x 10に変換 } temp /= 16; LCD_posyx(1,1); // 下段にカーソル移動 printTemp(temp); // 結果表示 __delay_ms(1000); // 更新は毎秒 } } void printTemp(int temp){ // 温度 x 10 を表示する char digit; if(temp < 0){ // マイナスなら LCD_ROMstr("-"); // 負符号を表示 temp = abs(temp); // 絶対値を取る } else LCD_ROMstr(" "); // プラス digit = temp / 100; // 温度の十の位 if(digit==0)LCD_ROMstr(" "); // 0 なら抑制 else LCD_dat(digit + '0'); // 文字に変換し表示 digit = (temp - digit * 100) / 10; // 温度の一の位 LCD_dat(digit + '0'); // 文字に変換し表示 LCD_ROMstr("."); // 小数点表示 digit = temp % 10; // 温度の小数点以下 LCD_dat(digit+'0'); // 文字に変換し表示 LCD_ROMstr(" c"); // " c"表示 }
i2cLCD.h
/************************************* * File: i2cLCD.h * データをLCDに表示する。 * 秋月(ACM1602NI)I2C LCD表示器を使用 * 1MHz (内部クロック) * RB4 SDA * RB6 SCL * PIC16F18346 MPLAB XC8 v2.10 * Created on May 22, 2020, 2:37 PM **************************************/ //********************************************************************* // 調整用定数 //********************************************************************* #define CONTRAST 0x28 // for 3.3V // #define CONTRAST 0x18 // for 5.0V #define _XTAL_FREQ 1000000 // delay_ms(x) のための定義 // -------------- I2C関連定義 ---------------------------- // 各シーケンスの終了を待って復帰 // I2C特殊 condition #define i2c_Idle() while((SSP1CON2&0x1F)|(SSP1STATbits.R_nW)) #define i2c_Start() SSP1CON2bits.SEN=1;while(SSP1CON2bits.SEN) #define i2c_Stop() SSP1CON2bits.PEN=1;while(SSP1CON2bits.PEN) void i2c_MasterInit(const unsigned long baud); char i2c_Write(char data); // -------------- LCD関連定義 ---------------------------- void i2cByteWrite(char, char, char); void i2cTxData(char); void LCD_dat(char); void LCD_cmd(char); void LCD_clr(void); void LCD_posyx(char,char); void LCD_int(void); void LCD_str(char *); void LCD_ROMstr(const char *str);
i2cLCD.c
// file: i2cLCD.c #include <xc.h> #include "i2cLCD.h" //******************************************************************** // I2C 関連 //******************************************************************** void i2c_MasterInit(const unsigned long baud){ // pin設定 ------------------------------------------------ TRISA2 = 1; // RA2を入力 TRISA1 = 1; // RA1を入力 ANSA2 = 0; // RA2をデジタル ANSA1 = 0; // RA1をデジタル ODCA2 = 1; // RA2をオープンドレイン ODCA1 = 1; // RA1をオープンドレイン // PPS設定 ----------------------------------------------- SSP1CLKPPS = 0x01; // RA1をCLK入力に指定 RA1PPS = 24; // RA1をCLK出力に指定 SSP1DATPPS = 0x02; // RA2をDATに入力指定 RA2PPS = 25; // RA2をDAT出力に指定 // SSP1設定 ----------------------------------------------- SSP1STAT = 0b10000000; // スルーレート制御はOff SSP1ADD = (_XTAL_FREQ/(4*baud))-1; // SSP1ADD = 0x77; SSP1CON1 = 0b00101000; // I2C Master modeにする } char i2c_Write(char data){ PIR1bits.SSP1IF = 0; // 終了フラグクリア SSP1BUF = data; // データセット while(!PIR1bits.SSP1IF); // 送信終了待ち return SSP1CON2bits.ACKSTAT; // ACKなら「0」で復帰 } // NACKは「1」で復帰 //-------- ByteI2C送信 void i2cByteWrite(char addr, char cont, char data){ i2c_Idle(); // Bus アイドル確認 i2c_Start(); // Start bit送信 i2c_Write(addr); // アドレス送信 i2c_Write(cont); // 制御コード送信 i2c_Write(data); // データ送信 i2c_Stop(); // Stop bit送信 } //******************************************************************** // LCD 関連 //******************************************************************** //-------- 1文字表示 void LCD_dat(char chr){ i2cByteWrite(0x7C, 0x40, chr); __delay_us(50); // 50μsec } //-------- コマンド出力 void LCD_cmd(char cmd){ i2cByteWrite(0x7C, 0x00, cmd); if(cmd & 0xFC) // 上位6ビットに1がある命令 __delay_us(50); // 50usec else __delay_ms(2); // 2msec ClearおよびHomeコマンド } //-------- 全消去 void LCD_clr(void){ LCD_cmd(0x01); //Clearコマンド出力 } //-------- カーソル位置指定 void LCD_posyx(char ypos, char xpos){ unsigned char pcode; switch(ypos & 0x03){ case 0: pcode=0x80;break; case 1: pcode=0xC0;break; } LCD_cmd(pcode += xpos); } //-------- 初期化 void LCD_int(void){ __delay_ms(100); LCD_cmd(0x38); // 8bit 2行 表示命令モード LCD_cmd(0x39); // 8bit 2行 拡張命令モード LCD_cmd(0x14); // OSC BIAS 設定1/5 // コントラスト設定 LCD_cmd(0x70 + (CONTRAST & 0x0F)); LCD_cmd(0x5C + (CONTRAST >> 4)); LCD_cmd(0x6B); // Ffollwer __delay_ms(100); __delay_ms(100); LCD_cmd(0x38); // 表示命令モード LCD_cmd(0x0C); // Display On LCD_cmd(0x01); // Clear Display } //-------- 文字列出力 void LCD_str(char *str){ while(*str) //文字列の終わり(00)まで継続 LCD_dat(*str++); //文字出力しポインタ+1 } //-------- Rom 文字列出力 void LCD_ROMstr(const char *str){ while(*str) //文字列の終わり(00)まで継続 LCD_dat(*str++); //文字出力しポインタ+1 }