/****************************************************************** * PIC18F26J50 * カレンダー時計の初期時間設定は、プログラムの最初にセットされる。 * 時間は、I2C接続のLCDに表示される。外部に 32.768KHz クリスタルを使用 * * I/O Pins Used: * PORTB(7)pin28 : LED2 * PORTB(6)pin27 : LED1 * PORTB(5)pin26 : SDA1 * PORTB(4)pin25 : SCL1 * PORTB(3)pin24 : SW2 * PORTB(2)pin23 : SW1 * * ここまで、「はじめてのPIC」さんのオリジナルコメント *==================================================================== * PIC18F_RTCC_CLOCK_V1a.c * * <V1からの更新点> * @ 温度センサIC:ADT7410を追加し、カレンダー時計表示の他 *   温度表示が出来る様にした。 * * 2016/9/26 * 作成:N.Ishii ********************************************************************/ #include #include #include #include #include "L_i2c1_LCD.h" // I2C接続LCD関数 #include // printfなどのライブラリ // もし、RTCC クロックがずれているようなら、 // RTCC キャリブレーションの値をセットし、毎分のクロック数を増減する。 #define RTCCALIBRATION 0 #define LED2 LATBbits.LATB7 // 毎秒間隔で点滅 #define LED1 LATBbits.LATB6 #define SW2 PORTBbits.RB3 // SW2で数値を+1 #define SW1 PORTBbits.RB2 // SW1で次の数 #define ADT7410_ADDR 0x90 // 温度センサースレーブアドレス(素の0x48を1bit左シフト+R・W/ビット) // コンフィグレーションの設定 #pragma config RTCOSC = T1OSCREF #pragma config WDTEN = OFF, PLLDIV = 2, STVREN = ON, XINST = OFF #pragma config CPUDIV = OSC1, CP0 = OFF #pragma config OSC = INTOSC, T1DIG = OFF #pragma config LPT1OSC = OFF, FCMEN = OFF, IESO = OFF #pragma config WDTPS = 32768 #pragma config DSWDTOSC = INTOSCREF #pragma config DSBOREN = OFF, DSWDTEN = OFF, DSWDTPS = 8192 #pragma config IOL1WAY = OFF, MSSP7B_EN = MSK7 #pragma config WPFP = PAGE_1, WPEND = PAGE_0, WPCFG = OFF #pragma config WPDIS = OFF // プロトタイプの宣言 int _user_putc (char c); // ユーザー出力のプロトタイプ unsigned char getNum(unsigned char,char,char,char); char getDigit(char,char,char,char ); void DispWeek(char week); // 追加 N.I void tModeSet(void); void tRead(void); void lDisp(int tempt, char flg); void tDisp(void); int tdata; //現在温度(センサーフォーマット) #pragma code // ++++++++++++++++ ユーザー出力の出力を定義 ++++++++++++++ int _user_putc (char c){ LCD_dat(c); // 一文字出力する命令を指定 } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void main(void){ // --------- 時計初期値 BCDで設定する --------- unsigned char year = 0x16; // 2016年 unsigned char month = 0x09; // 9月 unsigned char day = 0x13; // 13日 unsigned char week = 0x01; // 火曜:追加 N.I unsigned char hour = 0x12; // 12時 unsigned char min = 0x00; // 0分 unsigned char sec = 0x00; // 0秒 char CntSW ; // SW状態・カウント char tens, ones ; // SW状態・カウント OSCCON = 0b01100011; // 内部クロック4Mhz LATB = 0; // TRISB = 0b00111111; // SCL1、SDA1(RB4)は入力に設定 INTCON2bits.RBPU = 0; // PortBを弱プルアップ ANCON0 = 0b11111110; // すべてデジタルに設定 ANCON1 = 0b00011111; OpenTimer1(TIMER_INT_OFF & // Secondary Oscを RTCC T1_SOURCE_PINOSC & // で利用するため T1_PS_1_1 & // OSC1を有効にする T1_OSC1EN_ON & T1_SYNC_EXT_OFF, 0); OpenI2C1(MASTER, SLEW_OFF); // I2Cマスターモード指定 SSP1ADD = 9; // Board Rate 100kHz LCD_int(); // LCDを初期化 tModeSet(); //センサーのモードセット // --------- 年月日・曜日/時分設定画面 --------------- fprintf(_H_USER, "20%02X/%02X/%02X(%02X )", year, month, day, week); LCD_posyx(1,0); fprintf(_H_USER, "%02X:%02X:oo", hour, min); // --------- PushSWでの初期値設定(年月日・曜日) --------- year = getNum( year,99,0,2); // 年の設定 month = getNum(month,19,0,5); // 月の設定 day = getNum( day,39,0,8); // 日の設定 week = getNum( week,6,0,11); // 曜日の設定:追加 N.I // --------- PushSWでの初期値設定(時分) --------- hour = getNum(hour,29,1,0); // 時の設定 min = getNum( min,59,1,3); // 分の設定 //// ここまでの設定(SW1で最後の項目の分まで送って、SW2で合わせ(或いはそのままで))、SW1で送ると //// 日時表示に移行し、RTCCレジスタの設定を行い、00秒から時計が動き始める。 //// コメント追記:N.I /// --------- 日時表示画面 (設定終了直後)----------------- LCD_posyx(0,0); fprintf(_H_USER, "20%02X/%02X/%02X", year, month, day); // 年/月/日の後に、曜日を表示する。→ 例:(Mon) DispWeek(week); // 時刻表示 LCD_posyx(1,0); fprintf(_H_USER, "%02X:%02X:%02X", hour, min, sec); // --------- RTCC 日時の設定 (起動時のみ) --------- EECON2 = 0x55; // RTCC レジスタを EECON2 = 0xAA; // unlock しRTCCの RTCCFGbits.RTCWREN = 1; // 設定準備をする RTCCFGbits.RTCPTR1 = 1; // RTCVALHにアクセスする毎に RTCCFGbits.RTCPTR0 = 1; // RTCPTRは自動的に -1 する RTCVALL = year; // 年 RTCPTR<1:0>= 11(この部分追記:N.I) RTCVALH = 0xFF; // 未設定 RTCVALL = day; // 日 RTCPTR<1:0>= 10 RTCVALH = month; // 月 RTCVALL = hour; // 時 RTCPTR<1:0>= 01 RTCVALH = week; // 曜日(0:日、1:月、…) RTCVALL = sec; // 秒 RTCPTR<1:0>= 00 RTCVALH = min; // 分 RTCCFGbits.RTCEN = 1; // RTCC を有効に RTCCAL = RTCCALIBRATION; // キャリブレーション値セット while(1){ RTCCFGbits.RTCPTR1 = 1; // RTCPTR のセット RTCCFGbits.RTCPTR0 = 1; // RTCPTR<1:0>= 11(0x03):年レジスタの位置にする 追記:N.I while(RTCCFGbits.RTCSYNC != 1); // RTCの位上げ中に、読み込ぬよう while(RTCCFGbits.RTCSYNC == 1); // SYCが「0」になるまで待つ // LED2=!LED2; year = RTCVALL; day = RTCVALH; // RTCPTR を -1 するためのダミー読み込み // これで、RTCPTR<1:0>= 10(0x02):月/日レジスタの位置にするなる 追記:N.I day = RTCVALL; month = RTCVALH; // これを実行後、自動的に、RTCPTR は -1されRTCPTR<1:0>= 01(0x01):週/時レジスタの位置になる。 hour = RTCVALL; week = RTCVALH; // 追加:N.I // これを実行後、自動的に、RTCPTR は -1されRTCPTR<1:0>= 00(0x00):分/秒レジスタの位置になる、 sec = RTCVALL; min = RTCVALH; // --------- RTCC 日時の表示 ----------------- LCD_posyx(0,0); fprintf(_H_USER, "20%02X/%02X/%02X", year, month, day); // 年/月/日の後に、曜日を表示する。→ 例:(Mon) DispWeek(week); // 時刻表示 LCD_posyx(1,0); fprintf(_H_USER, "%02X:%02X:%02X", hour, min, sec); // センサー読み取り〜温度表示 tRead(); tDisp(); // センサーワンショット起動(測定のときだけウェイクアップするため、ADT7410の消費電力はかなり少なくなる) tModeSet(); } } //****** 2桁の初期値を入力する関数 ******************************************************* // num 入力される初期数 top 値の最大値 // y 十の位のLCD縦位置 x 十の位のLCD横位置 //**************************************************************************************** unsigned char getNum(unsigned char num,char top,char y,char x){ char tens, ones; LCD_cmd(0x0D); // 表示点滅 top = top/10; tens = num >> 4; ones = num & 0xF; tens=getDigit(tens,top,y,x); // 10位設定 // ones=getDigit(ones,9,y,x+1); // 1位設定 if(x != 11) ones=getDigit(ones,9,y,x+1); // 1位設定: 週設定値以外の最大値は、9 else ones=getDigit(ones,6,y,x+1); // 週設定値の最大値は、6 LCD_cmd(0x0C); // 表示点滅を解除 return ((tens << 4) + ones) ; // 値計算 } //****** 1桁の初期値を入力する関数 *********************************************** // num 入力される初期数 top 値の最大値 // y 十の位のLCD縦位置 x 十の位のLCD横位置 //********************************************************************************* char getDigit(char num,char top,char y,char x){ char OnSW; top++; LCD_posyx(y,x); // 表示位置 while(1){ OnSW = 5; // SWがOnでないことを確認する do{ if(SW1 & SW2)OnSW--; else OnSW = 5; // 1mSecおきに5回確認 // →1mS周期でのセンスはチャッタと見なす。追記:N.I Delay1KTCYx(12); // 1mSec遅延 }while(OnSW); while(SW1 & SW2); // SWのPushを待つ // → 押されてない状態を確認してから初めてSW ON待ちを行っている。追記:N.I if(!SW1)return num; // SW1で終了 if(!SW2){ // SW2で数値を+1 num++; num %= top; LCD_dat(num+0x30); // 数値表示 LCD_posyx(y,x); // カーソルを戻す } } } //***************************************************************** // 曜日表示関数: 追加 N.I //***************************************************************** void DispWeek(char week){ if (week == 0) LCD_ROMstr("(Sun)"); if (week == 1) LCD_ROMstr("(Mon)"); if (week == 2) LCD_ROMstr("(Tue)"); if (week == 3) LCD_ROMstr("(Wed)"); if (week == 4) LCD_ROMstr("(Thu)"); if (week == 5) LCD_ROMstr("(Fri)"); if (week == 6) LCD_ROMstr("(Sat)"); } //// 以下、温度センサ関係 //// //***************************************************************** // MODE SET(13BIT ONESHOT) //***************************************************************** void tModeSet(void){ IdleI2C(); // アイドル確認 StartI2C(); // スタートコンディション発行 WriteI2C(ADT7410_ADDR); // デバイスアドレス(Wモード)送信 WriteI2C(0x3); //レジスタアドレス書き込み WriteI2C(0x20); //13ビットワンショットモード StopI2C(); //ストップシーケンス } //***************************************************************** // 温度値の読み取り //***************************************************************** void tRead(void){ IdleI2C(); // アイドル確認 StartI2C(); // スタートコンディション発行 WriteI2C(ADT7410_ADDR); // デバイスアドレス(Wモード)送信 WriteI2C(0); // 温度値(上位8bit)レジスタアドレス送信 RestartI2C(); // リスタートコンディション発行 WriteI2C(ADT7410_ADDR + 1); // デバイスアドレス(Rモード)送信 tdata= ReadI2C(); // 上位データ読み取り(ReadI2C()実行後、SSPBUFの内容が戻る) tdata <<= 8; AckI2C(); // ACK送信 tdata |= ReadI2C(); // 下位データ読み取り後、上位と合成 NotAckI2C(); // NOTACK送信 StopI2C(); // ストップコンディション発行 } //***************************************************************** // 温度の液晶表示(下関数の下請け関数) //***************************************************************** void lDisp(int tempt, char flg){ unsigned char n1, n2, n3, n4; n1 = tempt / 1000; tempt = tempt % 1000; n2 = tempt / 100; tempt = tempt % 100; n3 = tempt / 10; n4 = tempt % 10; if(flg) LCD_dat('-'); else LCD_dat(' '); if(n1) LCD_dat(n1 + 0x30); else LCD_dat(' '); if (n1 || n2) LCD_dat(n2 + 0x30); else LCD_dat(' '); LCD_dat(n3 + 0x30); LCD_dat('.'); LCD_dat(n4 + 0x30); LCD_dat('゚'); LCD_dat('C'); } //***************************************************************** // 温度を表示(メインでこの関数を実行するだけ) //***************************************************************** void tDisp(void){ int temp; int tempHL; unsigned char nf; //負フラグ // 温度計算 nf = 0; temp = (unsigned int)tdata; if(temp & 0x8000) nf = 1; temp = temp >> 3; temp = temp & 0x1fff; if(nf){ temp -= 8192; temp = -temp; //絶対値にする } temp = temp*10; //10倍の整数で扱う temp += 8; //四捨五入 temp = temp >> 4; // 1/16 // 現在温度の表示 LCD_posyx(1,8); // 150degCmax lDisp(temp, nf); }