PIC16F18313の基本動作から応用プログラムまでを学びます。

ホーム
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
    
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
温度計 LCDに表示する
2020−06−14

温度に比例した電圧を出力する温度センサー(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
}