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

ホーム
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
    
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
RTCC リアルタイムクロック・カレンダ

18F26J50のRTCC機能と時計用クリスタルを使用し、PushSWで電源ON時に日時の初期設定が行えるカレンダー時計です。

使い方

SW1で
カーソル移動
 ⇒⇒⇒
SW2で
 数値設定
 ⇒⇒⇒

回路の説明

この回路は、基本回路のリアルタイムクロック RTCCに、PushSWによる時刻合わせを追加して、少し実用的にしました。SWによる入力回ををうまく使えば、アラーム等も実装することが可能です。

プログラム

電源ONで日時の設定画面になります。SW1,SW2を使い現在時刻に合わせます。
設定が終了すると、毎秒LEDが点滅し、画面も更新されます。

<プログラム>
main.c

//*********************************************************
//  PIC18F26J50 RTCC Program//        :  MPLAB xc8
//  カレンダー時計の初期時間設定は、プログラムの最初にセットされる。
//  時刻は、I2C接続のLCDに表示される。
//  外部 32.768KHz クリスタルを使用
//  Note:  4MHzシステムクロック
//     PORTB(7)pin28    :  LED
//     PORTB(5)pin26    :  SDA1
//     PORTB(4)pin25    :  SCL1
//     PORTB(3)pin24    :  SW2
//     PORTB(2)pin23    :  SW1
//*********************************************************

#include <xc.h>
#include <stdio.h>              // printfなどのライブラリ
#define _XTAL_FREQ 4000000      //delay_ms(x) のための定義

//*********************************************************************
//  調整用定数
//*********************************************************************
#define CONTRAST  0x28          // for 3.3V

//*********************************************************************
// もし、RTCC クロックがずれているようなら、
// RTCC キャリブレーションの値をセットし、毎分のクロック数を増減する。
//*********************************************************************
#define RTCCALIBRATION    0

#define    LED      PORTBbits.RB7       // 毎秒間隔で点滅
#define    SW2      PORTBbits.RB3       // SW2で数値を+1
#define    SW1      PORTBbits.RB2       // SW1で次の数

//-------------- コンフィグレーション ----------------------
     #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, RTCOSC = T1OSCREF
     #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

//******************* プロトタイプ *******************************
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);
unsigned char getNum(unsigned char,char,char,char);
char getDigit(char,char,char,char );

//*********************************************************
// LCD で printf関数が使用できるようにするため putch を設定する
void putch(char data) {
    LCD_dat(data);      // LCD への一文字表示関数
}

//*********************************************************
void main(void){
    // --------- 時計初期値 BCDで設定する ---------
    char year  = 0x12;       // 2012年
    char month = 0x01;         //  1月
    char day   = 0x01;         //  1日
    char hour  = 0x12;         // 12時
    char min   = 0x00;         //  0分
    char sec   = 0x00;         //  0秒
    OSCCON = 0b01100011;       // 内部クロック4Mhz
    ANCON0 = 0b00011111;       // すべてのポートをデジタルに設定
    ANCON1 = 0b00011111;       //
    T1CON = 0b00001000;        // T1 Oscを有効にする
    TRISBbits.TRISB7 = 0;      // LED用に出力設定
    // I2C設定 -----------------------------------------------
    TRISBbits.TRISB4 = 1;      // SCL1(RB4)を入力に設定
    TRISBbits.TRISB5 = 1;      // SDA1(RB5)を入力に設定
    INTCON2bits.RBPU = 0;      // 弱プルアップ ON
    SSP1STAT = 0b10000000;     // スルーレート制御はOff
    SSP1ADD  = 9;              // クロック設定 100k@4MHz
    SSP1CON1 = 0b00101000;     // I2C Master modeにする
    // LCD初期化 ---------------------------------------------
    LCD_int();
    // --------- 年月日設定画面 ---------------
    printf("Set  >20%02X/%02X/%02X", year, month, day);
    LCD_posyx(1,0);
    printf("Date    %02X:%02X:oo", hour, min);
    // --------- PushSWでの初期値設定 ---------
    year  = getNum( year,99,0,8);   // 年の設定
    month = getNum(month,19,0,11);  // 月の設定
    day   = getNum(  day,39,0,14);  // 日の設定
    // --------- 時間設定画面 -----------------
    LCD_posyx(0,5);
    LCD_dat(' ');
    LCD_posyx(1,0);
    printf("Time >");
    // --------- PushSWでの初期値設定 ---------
    hour  = getNum(hour,29,1,8);    // 時の設定
    min   = getNum( min,59,1,11);   // 分の設定
    // --------- 日時表示画面 -----------------
    LCD_posyx(0,0);
    printf("Date  20%02X/%02X/%02X", year, month, day);
    LCD_posyx(1,5);
    printf("   %02X:%02X:%02X", hour, min, sec);
    // --------- RTCC 日時の設定 (起動時のみ) ---------
    // RTCC レジスタを unlock し、RTCCの設定する
    EECON2 = 0x55;
    EECON2 = 0xAA;
    RTCCFGbits.RTCWREN = 1;
    RTCCFGbits.RTCPTR1 = 1;    // RTCVALHにアクセスする毎に
    RTCCFGbits.RTCPTR0 = 1;    // RTCPTRは自動的に -1 する
    RTCVALL = year;            // 年
    RTCVALH = 0xFF;            // 未設定
    RTCVALL = day;             // 日
    RTCVALH = month;           // 月
    RTCVALL = hour;            // 時
    RTCVALH = 0x01;            // 曜日(0:日、1:月、…)
    RTCVALL = sec;             // 秒
    RTCVALH = min;             // 分
    RTCCFGbits.RTCEN = 1;      // RTCC を有効に
    RTCCAL = RTCCALIBRATION;   // キャリブレーション値セット
    while(1){
        RTCCFGbits.RTCPTR1 = 1;         // RTCPTR のセット
        RTCCFGbits.RTCPTR0 = 1;
        while(RTCCFGbits.RTCSYNC != 1); // RTCの位上げ中に、読み込ぬよう
        while(RTCCFGbits.RTCSYNC == 1); // SYCが「0」になるまで待つ
        LED = !LED;
        year  = RTCVALL;
        day   = RTCVALH;     // RTCPTR を -1 するためのダミー読み込み
        day   = RTCVALL;
        month = RTCVALH;
        hour  = RTCVALL;
        sec   = RTCVALH;     // RTCPTR を -1 するためのダミー読み込み
        sec   = RTCVALL;
        min   = RTCVALH;
        // --------- 上段にカーソル移動 ------------------
        LCD_posyx(0,6);
        printf("20%02X/%02X/%02X", year, month, day);
        // --------- 下段にカーソル移動 ------------------
        LCD_posyx(1,8);
        printf("%02X:%02X:%02X", hour, min, sec);
    }
}
//****** 2桁の初期値を入力する関数 *************************
//  num   入力される初期数    top  値の最大値
//  y     十の位のLCD縦位置   x    十の位のLCD横位置
//*******************************************************
char getNum(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位設定
    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回確認
            __delay_ms(1);          // 1mS遅延
        }while(OnSW);
        while(SW1 & SW2);           // SWのPushを待つ
        if(!SW1)return num;         // SW1で終了
        if(!SW2){                   // SW2で数値を+1
            num++;
            num %= top;
            LCD_dat(num+0x30);      // 数値表示
            LCD_posyx(y,x);         // カーソルを戻す
        }
    }
}

//********************************************************************
// I2C 関連 
//********************************************************************
//-------- ByteI2C送信
void i2cByteWrite(char addr, char cont, char data){
    SSP1CON2bits.SEN = 1;      // Start condition 開始
    while(SSP1CON2bits.SEN);   // Start condition 確認
    i2cTxData(addr);           // アドレス送信
    i2cTxData(cont);           // 制御コード送信
    i2cTxData(data);           // データ送信
    SSP1CON2bits.PEN = 1;      // Stop condition 開始
    while(SSP1CON2bits.PEN);   // Stop condition 確認    
}
//-------- Data送信
void i2cTxData(char data){
    PIR1bits.SSP1IF = 0;       // 終了フラグクリア
    SSP1BUF = data;            // データセット
    while(!PIR1bits.SSP1IF);   // 送信終了待ち
}
//********************************************************************
// 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 
}