温度に比例した電圧を出力する温度センサー(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回とし見た目の安定性を出しています。
/*************************************
* File: main.c
* 温度をLCDに表示する。
* 秋月(ACM1602NI)I2C LCD表示器を使用
* 1MHz (内部クロック)
* RB4 SDA
* RB6 SCL
* RC2 温度センサー MCP9700
* PIC16F18346 MPLAB XC8 v2.10
* Created on May 22, 2020, 2:37 PM
**************************************/
#include <xc.h>
#include <stdio.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 = ON,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 = ON,CP = OFF,CPD = OFF
// ********************************************************
void main() {
int temp = 0;
i2c_MasterInit(100000); // I2Cを初期化
LCD_int(); // LCDを初期化
// ADC ---------------------------------------
ANSC2 = 1; // ANC2(RC2)はアナログ入力に設定
TRISC2 = 1;
ADACT = 0b00000000; // AD変換はプログラムの命令で開始
ADCON1 = 0b10000011; // 右詰め出力, AD変換クロックFosc/2
// 正基準電圧 FVR
ADCON0 = 0b01001001; // アナログ入力 ANC2(RC2),ADC ON
FVRCON = 0b10000001;
// -------------------------------------------
printf("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);
}
temp /= 16;
LCD_posyx(1,1); // 下段にカーソル移動
printf("%2d.%d c",temp/10,temp%10); // 結果表示
__delay_ms(1000); // 更新は毎秒
}
}
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 *);
i2cLCD.c
// file: i2cLCD.c
#include <xc.h>
#include "i2cLCD.h"
#include <stdio.h>
//******************************************************
// LCD で printf関数が使用できるようにするため putch を設定する
//******************************************************
void putch(char data) {
LCD_dat(data); // LCD への一文字表示関数
}
//********************************************************************
// I2C 関連
//********************************************************************
void i2c_MasterInit(const unsigned long baud){
// pin設定 ------------------------------------------------
TRISB4 = 1; // RB4を入力
TRISB6 = 1; // RB6を入力
ANSB4 = 0; // RB4をデジタル
ANSB6 = 0; // RB6をデジタル
ODCB4 = 1; // RB4をオープンドレイン
ODCB6 = 1; // RB6をオープンドレイン
// PPS設定 -----------------------------------------------
SSP1CLKPPS = 0x0E; // RB6をCLK入力に指定
RB6PPS = 24; // RB6をCLK出力に指定
SSP1DATPPS = 0x0C; // RB4をDATに入力指定
RB4PPS = 25; // RB4をDAT出力に指定
// SSP1設定 -----------------------------------------------
SSP1STAT = 0b10000000; // スルーレート制御はOff
SSP1ADD = (_XTAL_FREQ/(4*baud))-1; // SSP1ADD = 0x77;
SSP1CON1 = 0b00101000; // I2C Master modeにする
}
char i2c_Write(char data){
SSP1BUF = data; // データセット
PIR1bits.SSP1IF = 0; // 終了フラグクリア
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
}