PICでリアルタイムクロック

概要

PIC18F27J53は、リアルタイムクロック&カレンダー(RTCC)モジュールをハードウェアで持っています。今回はそれを使用するためのライブラリを作ったので紹介します。

自作のRTCCライブラリ

必要なソースファイル

  • My_RTCC.h
  • My_RTCC.c
  • My_button.h
  • My_button.c
  • main.c

概要

変数

このプログラムでは、時刻情報を持つ変数が3つある。

  1. PICのRTCCモジュールのレジスタ→カレンダー表記、16進法表記(※厳密には10進だが便宜上16進とする。)
  2. time_t構造体内のepoch(uint32_t)→2000年1月1日0時00分からの総秒数
  3. time_t構造体内のYY,MM,DD,EE,hh,mm,ss→それぞれ、年、月、日、曜日、時、分、秒の10進法表記。

以上3つがある。時刻が変わってもRTCC_loop()関数がすべての値を常に一致させているので安心して使用できる。

使い方

  1. TIOSCのピンに32.768kHzのクリスタルをつなぐ。(12pのコンデンサも)
  2. CONFIGを設定する。RTCOSC = T1OSCREF
  3. その次に、RTCC_init();を書く。(初期設定関数)
  4. mainループ内に、RTCC_loop();を書くこと。(時間の同期関数)
  5. 任意で時計合わせをする。→時計合わせをしたら、RTCC_from_caltime()かRTCC_from_epoch()を呼んで変更を適用する。
  6. 時計合わせには、adjust_time_toggle()を呼んでから、adjust_time_cursor()で、好きな項を選択する
  7. adjust_time_inc()とadjust_time_dec()で調整するとよい。
  8. 時計合わせが終わったら、adjust_time_toggle()をもう一度呼ぶと時計合わせ終了。

RTCCライブラリ

My_RTCC.h

/* 
 * File:   My_RTCC.h
 * Author: Ryotaro Onuki
 *
 * Created on 2015/03/19, 0:02
 * 
 * PIC18F28J53用リアルタイムクロックプログラム
 * ○概要
 * このプログラムでは、時刻情報を持つ変数が3つある。
 * 1.PICのRTCCモジュールのレジスタ→カレンダー表記、16進法表記(※厳密には10進だが便宜上16進とする。)
 * 2.time_t構造体内のepoch(uint32_t)→2000年1月1日0時00分からの総秒数
 * 3.time_t構造体内のYY,MM,DD,EE,hh,mm,ss→それぞれ、年、月、日、曜日、時、分、秒の10進法表記。
 * 以上3つがある。時刻が変わってもRTCC_loop()関数がすべての値を常に一致させているので安心して使用できる。
 * ○使い方
 * 1.TIOSCのピンに32.768kHzのクリスタルをつなぐ。(12pのコンデンサも)
 * 2.CONFIGを設定する。RTCOSC = T1OSCREF
 * 3.その次に、RTCC_init();を書く。(初期設定関数)
 * 4.mainループ内に、RTCC_loop();を書くこと。(時間の同期関数)
 * 5.任意で時計合わせをする。→時計合わせをしたら、RTCC_from_caltime()かRTCC_from_epoch()を呼んで変更を適用する。
 * 6.時計合わせには、adjust_time_toggle()を呼んでから、adjust_time_cursor()で、好きな項を選択する
 * 7.adjust_time_inc()とadjust_time_dec()で調整するとよい。
 * 8.時計合わせが終わったら、adjust_time_toggle()をもう一度呼ぶと時計合わせ終了。
 */

#ifndef MY_RTCC_H
#define	MY_RTCC_H

/** INCLUDES *******************************************************/
#include <xc.h>
#include <stdint.h>
#include "My_button.h"

/** VALUES *********************************************************/
#define MINUTE ((epoch_t)60)
#define HOUR ((epoch_t)60*60)
#define DAY ((epoch_t)60*60*24)

/** STRUCTURE ******************************************************/
// epoch型を定義。2000年1月1日0時00分からの総秒数。

typedef uint32_t epoch_t;

// 時刻合わせ用のフラグの構造体を定義。

typedef struct {

    union {
        uint8_t flags;

        struct {
            uint8_t ss : 1;
            uint8_t mm : 1;
            uint8_t hh : 1;
            uint8_t DD : 1;
            uint8_t MM : 1;
            uint8_t YY : 1;
        } flag;
    };
} edit_t;

// RTCC関連すべてを詰めた構造体。実際に使用する。

typedef struct {
    epoch_t epoch;
    uint8_t ss;
    uint8_t mm;
    uint8_t hh;
    uint8_t EE;
    uint8_t DD;
    uint8_t MM;
    uint8_t YY;
    uint8_t colon;
    uint8_t halfsec;
    edit_t edit;
} time_t;

/** VARIABLES ******************************************************/
// 表示用の曜日charをconstで定義。
extern const char weekday_3char[7][4];
// 現在時刻
extern time_t now;
// 時間が変わった時だけフラグが経つ。表示器の時刻の更新はこのフラグの監視をして行えばよい。
extern uint8_t time_change_flag;
// main_init()に書くこと。
void RTCC_init(void);
// Call this in the main loop
void RTCC_task(void);

/** USER FUNCTIONS *************************************************/
/** Transform time User Functions **/
// 時刻をRTCC基準でそろえる
void RTCC_from_RTCC(time_t *tm);
// 時刻をカレンダータイム基準でそろえる
void RTCC_from_caltime(time_t *tm);
// 時刻をエポック基準でそろえる
void RTCC_from_epoch(time_t *tm);
// return month length
uint8_t month_length(uint8_t year, uint8_t month);

/** LCD display **/
// 各項目表示用関数。ユーザーは使わない。
static void display_dec(char *str, uint8_t dec, uint8_t edit);
// 0802サイズ液晶用、文字列作成関数
void display_time_0802(time_t *tm, char *line0, char *line1);
// 1608サイズ液晶用、文字列作成関数
void display_time_1602(time_t *tm, char *line0, char *line1);

/** adjust the time **/
// 時刻編集モードを切り替える
void RTCC_adjust_time_toggle(time_t *tm);
// 編集中の時刻針を切り替える
void RTCC_adjust_time_cursor(time_t *tm);
// 編集中の時刻針を1つ増やす
void RTCC_adjust_time_inc(time_t *tm);
// 編集中の時刻針を1つ減らす
void RTCC_adjust_time_dec(time_t *tm);
// 3つのボタンで時刻合わせ
void RTCC_adjust_time_button(time_t *tm, button_t *mode, button_t *cnt_inc, button_t *cnt_dec);

/** FUNCTIONS ******************************************************/
/** en/decode number **/
// from decimal to hex
static uint8_t d_to_x(uint8_t dec);
// from hex to decimal
static uint8_t x_to_d(uint8_t hex);
// quotをdivで割って、余りを返す。よく使うので関数化した。
static epoch_t get_quot_rem(epoch_t *quot, uint8_t div);

/** Transform time **/
// RTCCをカレンダタイムに変換、上書き。
static void RTCC_to_caltime(time_t *tm);
// カレンダタイムをRTCCに変換、上書き。
static void caltime_to_RTCC(time_t *tm);
// Epochをカレンダタイムに変換、上書き。
static void epoch_to_caltime(time_t *tm);
// get epoch from caltime
static void caltime_to_epoch(time_t *tm);

#endif	/* MY_RTCC_H */

My_RTCC.c

#include "My_RTCC.h"

/** INCLUDES *******************************************************/
#include <stdio.h>
#include <string.h>

/** VALUES *********************************************************/


/** VARIABLES ******************************************************/
const char weekday_3char[7][4] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
time_t now;
uint8_t time_change_flag;

/** USER FUNCTIONS *************************************************/

void RTCC_init(void) {
    RTCCFGbits.RTCWREN = 1;
    if (RTCCFGbits.RTCWREN != 1) {
        EECON2 = 0x55;
        EECON2 = 0xAA;
        RTCCFGbits.RTCWREN = 1;
    }
    RTCCFGbits.RTCEN = 1;
    RTCCFGbits.RTCOE = 0;
    PADCFG1bits.RTSECSEL0 = 1;
    PADCFG1bits.RTSECSEL1 = 1;
    RTCCAL = 0x00;
    T1CONbits.T1OSCEN = 1;

    RTCC_from_RTCC(&now);
    if (now.DD == 0) {
        now.epoch = 0;
        RTCC_from_epoch(&now);
    }
}

void RTCC_task(void) {
    static uint8_t prev_halfsec;
    if (RTCCFGbits.RTCSYNC)return;
    now.halfsec = RTCCFGbits.HALFSEC;
    if (prev_halfsec != now.halfsec) {
        prev_halfsec = now.halfsec;
        now.colon = now.halfsec;
        time_change_flag = 1;
        RTCC_from_RTCC(&now);
    }
}

/** Transform time User Functions **/

void RTCC_from_RTCC(time_t *tm) {
    RTCC_to_caltime(tm);
    caltime_to_epoch(tm);
}

void RTCC_from_caltime(time_t *tm) {
    caltime_to_epoch(tm);
    epoch_to_caltime(tm);
    caltime_to_RTCC(tm);
}

void RTCC_from_epoch(time_t *tm) {
    epoch_to_caltime(tm);
    caltime_to_RTCC(tm);
}

uint8_t month_length(uint8_t year, uint8_t month) {
    if (month == 2) return 28 + !(year & 3)-!(year % 100)+!(year % 400);
    else return 31 - (((-(month & 1)^month)&13) == 4);

    //    switch (month) {
    //        case 1:
    //        case 3:
    //        case 5:
    //        case 7:
    //        case 8:
    //        case 10:
    //        case 12:
    //            return 31;
    //        case 4:
    //        case 6:
    //        case 9:
    //        case 11:
    //            return 30;
    //        case 2:
    //            if (year % 400 == 0)return 29;
    //            else if (year % 100 == 0)return 28;
    //            else if (year % 4 == 0) return 29;
    //            else return 28;
}

/** LCD display **/

static void display_dec(char *str, uint8_t dec, uint8_t edit) {
    char s[4];
    sprintf(s, "%02d", dec);
    if (edit && now.halfsec)strcat(str, "  ");
    else strcat(str, s);
}

void display_time_0802(time_t *tm, char *line0, char *line1) {
    RTCC_from_RTCC(&now);

    strcpy(line0, "");
    display_dec(line0, tm->YY, tm->edit.flag.YY);
    strcat(line0, "/");
    display_dec(line0, tm->MM, tm->edit.flag.MM);
    strcat(line0, "/");
    display_dec(line0, tm->DD, tm->edit.flag.DD);

    strcpy(line1, "");
    display_dec(line1, tm->hh, tm->edit.flag.hh);
    if (now.colon) strcat(line1, ":");
    else strcat(line1, " ");
    display_dec(line1, tm->mm, tm->edit.flag.mm);
    strcat(line1, "-");
    display_dec(line1, tm->ss, tm->edit.flag.ss);
}

void display_time_1602(time_t *tm, char *line0, char *line1) {
    RTCC_from_RTCC(&now);

    strcpy(line0, "");
    display_dec(line0, 20, tm->edit.flag.YY);
    display_dec(line0, tm->YY, tm->edit.flag.YY);
    strcat(line0, "/");
    display_dec(line0, tm->MM, tm->edit.flag.MM);
    strcat(line0, "/");
    display_dec(line0, tm->DD, tm->edit.flag.DD);
    strcat(line0, "(");
    strcat(line0, weekday_3char[tm->EE]);
    strcat(line0, ")");

    strcpy(line1, "        ");
    display_dec(line1, tm->hh, tm->edit.flag.hh);
    if (tm->colon) strcat(line1, ":");
    else strcat(line1, " ");
    display_dec(line1, tm->mm, tm->edit.flag.mm);
    strcat(line1, "-");
    display_dec(line1, tm->ss, tm->edit.flag.ss);
}

/** adjust the time **/

void RTCC_adjust_time_toggle(time_t *tm) {
    if (tm->edit.flags) {
        tm->edit.flags = 0;
    } else {
        tm->edit.flag.ss = 1;
    }
}

void RTCC_adjust_time_cursor(time_t *tm) {
    if (tm->edit.flag.ss)tm->edit.flags = 0x02;
    else if (tm->edit.flag.mm)tm->edit.flags = 0x04;
    else if (tm->edit.flag.hh)tm->edit.flags = 0x08;
    else if (tm->edit.flag.DD)tm->edit.flags = 0x10;
    else if (tm->edit.flag.MM)tm->edit.flags = 0x20;
    else if (tm->edit.flag.YY)tm->edit.flags = 0x01;
    else tm->edit.flags = 0x00;
}

void RTCC_adjust_time_inc(time_t *tm) {
    RTCC_from_RTCC(tm);
    if (tm->edit.flag.ss) tm->epoch += 1;
    if (tm->edit.flag.mm) tm->epoch += MINUTE;
    if (tm->edit.flag.hh) tm->epoch += HOUR;
    if (tm->edit.flag.DD) tm->epoch += DAY;
    if (tm->edit.flag.MM) tm->epoch += DAY * month_length(tm->YY, tm->MM);
    if (tm->edit.flag.YY) {
        for (uint8_t i = tm->MM; i <= 12; i++) {
            tm->epoch += DAY * month_length(tm->YY, i);
        }
        for (uint8_t i = 1; i < tm->MM; i++) {
            tm->epoch += DAY * month_length(tm->YY + 1, i);
        }
    }

    if (tm->epoch >= 3155760000)tm->epoch -= 3155760000;
    RTCC_from_epoch(&now);
    time_change_flag = 1;
}

void RTCC_adjust_time_dec(time_t *tm) {
    RTCC_from_RTCC(tm);
    if (tm->edit.flag.ss) tm->epoch -= 1;
    if (tm->edit.flag.mm) tm->epoch -= MINUTE;
    if (tm->edit.flag.hh) tm->epoch -= HOUR;
    if (tm->edit.flag.DD) tm->epoch -= DAY;
    if (tm->edit.flag.MM) tm->epoch -= DAY * month_length(tm->YY, tm->MM);
    if (tm->edit.flag.YY) {
        for (uint8_t i = tm->MM; i >= 1; i--) {
            tm->epoch -= DAY * month_length(tm->YY, i);
        }
        for (uint8_t i = 12; i > tm->MM; i--) {
            tm->epoch -= DAY * month_length(tm->YY - 1, i);
        }
    }

    if (tm->epoch >= 3155760000)tm->epoch += 3155760000;
    RTCC_from_epoch(&now);
    time_change_flag = 1;
}

void RTCC_adjust_time_button(time_t *tm, button_t *mode, button_t *cnt_inc, button_t *cnt_dec) {
    if (mode->flag.long_holding_3) {
        mode->flag.long_holding_3 = 0;
        RTCC_adjust_time_toggle(tm);
        mode->flags = 0;
        cnt_inc->flags = 0;
        cnt_dec->flags = 0;
    }
    if (tm->edit.flags) {
        if (mode->flag.press) {
            mode->flag.press = 0;
            RTCC_adjust_time_cursor(tm);
        }
        if (cnt_inc->flag.press) {
            cnt_inc->flag.press = 0;
            RTCC_adjust_time_inc(tm);
        }
        if (cnt_dec->flag.press) {
            cnt_dec->flag.press = 0;
            RTCC_adjust_time_dec(tm);
        }
    }
}

/** FUNCTIONS ******************************************************/

/** en/decode number **/

static uint8_t d_to_x(uint8_t dec) {
    // uint8_t quot = dec / 10;
    // return quot * 16 + dec - quot * 10;
    return dec / 10 * 6 + dec;
}

static uint8_t x_to_d(uint8_t hex) {
    return 10 * (0x0F & (hex >> 4)) + (0x0F & hex);
}

static epoch_t get_quot_rem(epoch_t *quot, uint8_t div) {
    // num /= div;
    // return rem;
    epoch_t num = *quot;
    return num - (*quot = num / div) * div; // returns rem
}

/** Transform time **/

static void RTCC_to_caltime(time_t *tm) {
    while (RTCCFGbits.RTCSYNC);

    RTCCFGbits.RTCPTR0 = 1;
    RTCCFGbits.RTCPTR1 = 1;

    tm->YY = x_to_d(RTCVALL); // YY
    uint8_t dumy = RTCVALH; // Reserved
    tm->DD = x_to_d(RTCVALL); // DD
    tm->MM = x_to_d(RTCVALH); // MM
    tm->hh = x_to_d(RTCVALL); // hh
    tm->EE = x_to_d(RTCVALH); // EE
    tm->ss = x_to_d(RTCVALL); // ss
    tm->mm = x_to_d(RTCVALH); // mm
}

static void caltime_to_RTCC(time_t *tm) {
    while (RTCCFGbits.RTCSYNC);
    RTCCFGbits.RTCPTR0 = 1;
    RTCCFGbits.RTCPTR1 = 1;
    RTCVALL = d_to_x(tm->YY);
    RTCVALH = 0x00;
    RTCCFGbits.RTCPTR0 = 0;
    RTCCFGbits.RTCPTR1 = 1;
    RTCVALL = d_to_x(tm->DD);
    RTCVALH = d_to_x(tm->MM);
    RTCCFGbits.RTCPTR0 = 1;
    RTCCFGbits.RTCPTR1 = 0;
    RTCVALL = d_to_x(tm->hh);
    RTCVALH = d_to_x(tm->EE);
    RTCCFGbits.RTCPTR0 = 0;
    RTCCFGbits.RTCPTR1 = 0;
    RTCVALL = d_to_x(tm->ss);
    RTCVALH = d_to_x(tm->mm);
}

static void epoch_to_caltime(time_t *tm) {
    // to get epoch on terminal
    // echo $(( (`date -d '2015/03/01' '+%s'` - `date -d '2000/01/01' '+%s'`) / 86400 ))
    static uint16_t day_cache = 0; //2015.03.01
    static uint8_t month_cache = 1;
    static uint8_t year_cache = 0;
    uint16_t day_since_epoch;
    uint16_t day;
    uint8_t year = 0;
    uint8_t month = 1; // month is one-based.
    epoch_t num;

    num = tm->epoch;
    tm->ss = get_quot_rem(&num, 60);
    tm->mm = get_quot_rem(&num, 60);
    tm->hh = get_quot_rem(&num, 24);
    day = num;
    day_since_epoch = day;

    num += 6; // 2000.1.1 is Saturday(6).
    tm->EE = get_quot_rem(&num, 7);

    //    if (day == day_cache) {
    //        year = year_cache;
    //        month = month_cache;
    //        day -= day_cache;
    //    }

    // day is zero - based here.
    while (day >= month_length(year, month)) {
        LATB2 = 1;
        day = day - month_length(year, month);
        if (month == 12) {
            month = 1; // month is one-based.
            year++;
            //3155760000 is 2000~2100?100?????
            if (year >= 100) {
                tm->epoch -= 3155760000;
                year = 0;
            }
        } else {
            month++;
        }
        month_cache = month;
        year_cache = year;
    }
    day_cache = day_since_epoch - day;
    day++; // day is one-based, not zero-based.

    tm->YY = year;
    tm->MM = month;
    tm->DD = day;
}

static void caltime_to_epoch(time_t *tm) {
    epoch_t epoch = 0;
    for (uint8_t year = 0; year < tm->YY; year++) {
        for (uint8_t month = 1; month <= 12; month++) {
            epoch += DAY * month_length(year, month);
        }
    }
    for (uint8_t month = 1; month < tm->MM; month++) {
        epoch += DAY * month_length(tm->YY, month);
    }
    epoch += DAY * (tm->DD - 1); //day is one-based.
    epoch += HOUR * tm->hh;
    epoch += MINUTE * tm->mm;
    epoch += tm->ss;

    tm->epoch = epoch;
}

ボタンライブラリ

My_button.h

/* 
 * File:   My_button.h
 * Author: kerikun11
 *
 * Created on 2015/02/26, 19:19
 */

#ifndef MY_BUTTON_H
#define	MY_BUTTON_H

#include <stdint.h>

//*************************** button ***************************//
//タイマー割り込み関数にbutton_timer_interrupt(&button_t,!SW)を書いておくこと。
// 48MHz, 16bit, prescaler=1:4

#define PRESS_LEVEL 2
#define LONG_HOLD_LEVEL1  16
#define LONG_HOLD_LEVEL2  60
#define LONG_HOLD_LEVEL3  150
#define LONG_HOLD_LEVEL4  300
#define LONG_HOLD_LEVEL5  500
#define LONG_HOLD_LEVEL6  700
#define LONG_HOLD_LEVEL7  900

typedef struct button {
    uint16_t cnt_sw;

    union {
        uint16_t flags;

        struct {
            uint8_t press : 1;
            uint8_t long_hold_1 : 1;
            uint8_t long_hold_2 : 1;
            uint8_t long_hold_3 : 1;
            uint8_t long_hold_4 : 1;
            uint8_t long_hold_5 : 1;
            uint8_t long_hold_6 : 1;
            uint8_t long_hold_7 : 1;
            uint8_t pressing : 1;
            uint8_t long_holding_1 : 1;
            uint8_t long_holding_2 : 1;
            uint8_t long_holding_3 : 1;
            uint8_t long_holding_4 : 1;
            uint8_t long_holding_5 : 1;
            uint8_t long_holding_6 : 1;
            uint8_t long_holding_7 : 1;
        } flag;
    };
} button_t;

void button_timer_interrupt(button_t *bt, uint8_t sw_value);

#endif	/* MY_BUTTON_H */

My_button.h

#include "My_button.h"

void button_timer_interrupt(button_t *bt, uint8_t sw_value) {
    if (sw_value) {
        if (bt->cnt_sw == LONG_HOLD_LEVEL7)bt->flag.long_holding_7 = 1;
        if (bt->cnt_sw == LONG_HOLD_LEVEL6)bt->flag.long_holding_6 = 1;
        if (bt->cnt_sw == LONG_HOLD_LEVEL5)bt->flag.long_holding_5 = 1;
        if (bt->cnt_sw == LONG_HOLD_LEVEL4)bt->flag.long_holding_4 = 1;
        if (bt->cnt_sw == LONG_HOLD_LEVEL3)bt->flag.long_holding_3 = 1;
        if (bt->cnt_sw == LONG_HOLD_LEVEL2)bt->flag.long_holding_2 = 1;
        if (bt->cnt_sw == LONG_HOLD_LEVEL1)bt->flag.long_holding_1 = 1;
        if (bt->cnt_sw == PRESS_LEVEL)bt->flag.pressing = 1;
        if (bt->cnt_sw < LONG_HOLD_LEVEL7 + 1) bt->cnt_sw++;
    } else {
        if (bt->cnt_sw >= LONG_HOLD_LEVEL7)bt->flag.long_hold_7 = 1;
        else if (bt->cnt_sw >= LONG_HOLD_LEVEL6)bt->flag.long_hold_6 = 1;
        else if (bt->cnt_sw >= LONG_HOLD_LEVEL5)bt->flag.long_hold_5 = 1;
        else if (bt->cnt_sw >= LONG_HOLD_LEVEL4)bt->flag.long_hold_4 = 1;
        else if (bt->cnt_sw >= LONG_HOLD_LEVEL3)bt->flag.long_hold_3 = 1;
        else if (bt->cnt_sw >= LONG_HOLD_LEVEL2)bt->flag.long_hold_2 = 1;
        else if (bt->cnt_sw >= LONG_HOLD_LEVEL1)bt->flag.long_hold_1 = 1;
        else if (bt->cnt_sw >= PRESS_LEVEL)bt->flag.press = 1;
        bt->cnt_sw = 0;
        bt->flags &= 0x00FF;
    }
}

使用例

UARTで毎秒時刻を送信します。

main.c

/* 
 * Project: RTCC-Sample.X
 * File:    main.c
 * Author:  Ryotaro Onuki
 *
 * Created on 2015/10/06, 14:23
 */

#include <xc.h>
#include <stdint.h>
#include <stdio.h>
// CONFIG1L
#pragma config WDTEN = OFF, PLLDIV = 2, CFGPLLEN = ON, STVREN = OFF, XINST = OFF
// CONFIG1H
#pragma config CPUDIV = OSC1, CP0 = OFF
// CONFIG2L
#pragma config OSC = INTOSCPLL, SOSCSEL = HIGH, CLKOEC = OFF, FCMEN = OFF, IESO = OFF
// CONFIG2H
#pragma config WDTPS = 1024
// CONFIG3L
#pragma config DSWDTOSC = T1OSCREF, RTCOSC = T1OSCREF, DSBOREN = OFF, DSWDTEN = OFF, DSWDTPS = G2
// CONFIG3H
#pragma config IOL1WAY = OFF, ADCSEL = BIT12, MSSP7B_EN = MSK7
// CONFIG4L
#pragma config WPFP = PAGE_127, WPCFG = OFF
// CONFIG4H
#pragma config WPDIS = OFF, WPEND = PAGE_WPFP, LS48MHZ = SYS48X8

#define _XTAL_FREQ 48000000

#include "My_RTCC.h"
#include "My_button.h"

// I/O Mapping
#define MODE_BUTTON PORTBbits.RB0
#define INC_BUTTON PORTBbits.RB1
#define DEC_BUTTON PORTBbits.RB2

#define LED0 LATAbits.LATA0

button_t mode;
button_t inc;
button_t dec;

void interrupt ISR(void) {
    if (PIE1bits.TMR1IE && PIR1bits.TMR1IF) {
        PIR1bits.TMR1IF = 0;
    }
    if (PIE2bits.TMR3IE && PIR2bits.TMR3IF) {
        PIR2bits.TMR3IF = 0;
        LED0 = !LED0;
        button_timer_interrupt(&mode, !MODE_BUTTON);
        button_timer_interrupt(&inc, !INC_BUTTON);
        button_timer_interrupt(&dec, !DEC_BUTTON);
    }
    if (PIR1bits.RCIF && PIE1bits.RCIE) {
        while (!PIR1bits.TXIF); // 送信完了まで待つ。
        TXREG1 = RCREG1; // 受信データをそのまま送信
    }
}

void main_init(void) {
    OSCCONbits.IRCF = 7;
    OSCTUNEbits.PLLEN = 1;
    OSCCONbits.SCS = 0;
    TRISA = 0b00100000; // IO,IO,Vcap,IO,IO,IO,IO,IO
    TRISB = 0b00110111; // IO,IO,SDA,SCL,IO,IO,IO,IO
    TRISC = 0b10111010; // RX,TX,D+,D-,Vusb,IO,T1OSI,T1OSO
    ANCON0 = 0b11111111; // x,x,x,AN4,AN3,AN2,AN1,AN0
    ANCON1 = 0b11111111; // x,x,x,x,AN12,AN11,AN10,AN9,AN8
    INTCON2bits.RBPU = 0; // Port:B Pull-up enable
    // Timer 1 init
    T1CONbits.TMR1CS = 2; // Clock = T1OSC
    T1CONbits.T1OSCEN = 1; // Drive Crystal
    T1CONbits.T1CKPS = 0; // No devide
    T1CONbits.nT1SYNC = 1; // No Sync
    T1CONbits.RD16 = 0; // Timer1 = 2 x 8bit timer
    T1CONbits.TMR1ON = 1; // module enable
    IPR1bits.TMR1IP = 0; // low priority
    PIR1bits.TMR1IF = 0;
    PIE1bits.TMR1IE = 1;
    INTCONbits.PEIE = 1;
    // Timer 3 init
    T3CONbits.TMR3CS = 0; // Clock = Fosc/4
    T3CONbits.T3OSCEN = 0; // Not Drive Crystal
    T3CONbits.T3CKPS = 2; // clock/4
    T3CONbits.RD163 = 0; // Timer3=16bit timer
    T3CONbits.TMR3ON = 1; // module enable
    IPR2bits.TMR3IP = 0; // low priority
    PIR2bits.TMR3IF = 0;
    PIE2bits.TMR3IE = 1; // interrupt enable
    INTCONbits.PEIE = 1;
    // RTCC init
    RTCC_init();
    // UART init
    TXSTA1bits.TX9 = 0; // 0:8-bit
    TXSTA1bits.TXEN = 1; //1:enable
    TXSTA1bits.SYNC = 0; // 0:Asynchronous mode
    TXSTA1bits.BRGH = 0; // 1:High Speed
    RCSTA1bits.SPEN = 1; // 1:Serial Port enable
    RCSTA1bits.RX9 = 0; // 0:8-bit
    RCSTA1bits.CREN = 1; // 1:continuous receive enable
    BAUDCON1bits.BRG16 = 0; // 1:use 16-bit SPBRG
    SPBRG1 = _XTAL_FREQ / 64 / 9600 - 1;
    SPBRGH1 = 0;
    IPR1bits.RC1IP = 0; //0:low priority
    PIE1bits.RCIE = 1;
    INTCONbits.PEIE = 1;
}

void delay(uint16_t t) {
    while (t--)__delay_ms(1);
}

int main(void) {
    main_init();
    INTCONbits.GIE = 1;

    while (1) {
        RTCC_task();
        RTCC_adjust_time_button(&now, &mode, &inc, &dec);

        if (time_change_flag) {
            time_change_flag = 0;
            char str[30];
            sprintf(str, "\t20%02d/%02d/%02d(%s)%02d:%02d-%02d\n",
                    now.YY,
                    now.MM,
                    now.DD,
                    weekday_3char[now.EE],
                    now.hh,
                    now.mm,
                    now.ss);
            for (uint8_t i = 0; str[i]; i++) {
                while (!PIR1bits.TXIF); //送信完了まで待つ
                TXREG1 = str[i];
            }
        }
    }

    return 0;
}