MPLAB® Code Configurator(RTCC編)

今回は、RTCC編です。今回の目標は、”前回のI2C編でセットアップしたLCDに今回のRTCC編でセットアップする日時を表示する。”です。よって前回のソフト、ハード共に変更を入れて行きます。先ずはハードから

追加するのは発振器とコンデンサー2つ

RTCC機能を使う為には下記の様にコンデンサー2つと発振器をPICの11,12ピンにつなぐ必要が有ります。

また発振器の周波数は、32.768kHzと決まっています。ちなみに2個のコンデンサーは22pFです。これら、発振器とコンデンサは秋月さんで購入しました。

RTCCのセットアップ

次はソフトです。何時通り PIC24FJ64GA002用の空プロジェクト、”RTCC_test” を作成し、MCCを起動して下さい。

  1. MCC起動時の画面です。今回はRTCC用に発振器を追加しているので、Use Secondary Oscillator にチェック。そうするとピン配置図の11,12番ピンの色と表示が変わります。使用するピンを示しています。
  1. Device Resources で RTCCを選択すると
  2. PeripheralsにRTCCが追加されます。
    • Enable RTCCにはチェックが入っているのでそのまま
    • この時計マークをクリックすると初期時間の設定が出来ます
  1. 今回は秒毎に割り込みを掛け、その時点での時刻を表示したいので以下の様な設定にしました。
    • Enable RTCC Interruptをチェック
    • Alarm Enableをチェック
    • Alarm FrequencyをEvery secに選択
  2. 今回は、RTCCの他に表示用LCDのI2Cの設定も必要です。Device Resourcesから ”I2C1” を選択して下さい。I2C1に関しては何も変更しないのI2C1追加後、Genate ボタンを押してコードを作成。

プロジェクトを表示するとこんな感じでファイルが作成されました。rtcc.cがRTCC用に作成されたファイルです。

rtcc.cの中身をみると下記の関数が定義されていました。

番号関数説明
1 void RTCC_Initialize(void); 初期設定
2 bool RTCC_TimeGet(struct tm *currentTime); 時間の読取
3 void RTCC_TimeSet(struct tm *initialTime); 時間の設定
4 bool RTCC_BCDTimeGet(bcdTime_t *currentTime);  時間の読取(BCD) 
5 void RTCC_BCDTimeSet(bcdTime_t *initialTime); 時間の設定(BCD)
6 void RTCC_TimeReset(bool reset); 時間にリセット
7 void RTCC_CallBack(void); 割り込みCallBack
8 void _RTCCInterrupt( void ); 割り込み関数

これらの関数を用いて時間表示プログラムを以下の様にコーディングします。まずは main.c のコーディング。

main.c

/**
  Section: Included Files
*/
#include "mcc_generated_files/mcc.h"

#define FCY 4000000UL
#include <libpic30.h>

#define LCD_ADRS 0x3E
char moji[] ="AQM1602XA-RN-GBW";

int time_fl;

void writeCommand(uint8_t t_command)
{
    uint8_t     data[2];
    I2C1_MESSAGE_STATUS     pstatus;

    data[0] = 0x00;
    data[1] = t_command;

    I2C1_MasterWrite(data, 2, LCD_ADRS,  &pstatus);

    __delay_ms(10);
}

void writeData(uint8_t t_data)
{
    uint8_t     data[2];
    I2C1_MESSAGE_STATUS     pstatus;

     data[0] = 0x40;
     data[1] = t_data;

    I2C1_MasterWrite(data, 2, LCD_ADRS, &pstatus);

   __delay_ms(1);
}

void init_LCD()
{
    __delay_ms(100);
    writeCommand(0x38); // FUNCTION SET
    __delay_ms(20);
    writeCommand(0x39); // IS=1
    __delay_ms(20);
    writeCommand(0x14); // INT OSC FREQUENCY
    __delay_ms(20);
    writeCommand(0x78); // CONTRAST SET 0,1,2,3
    __delay_ms(20);
    writeCommand(0x55); // CONTRAST SET 4,5
    __delay_ms(20);
    writeCommand(0x6C); // F0LLOWER CONTROL
    __delay_ms(20);
    writeCommand(0x38); // IS=0
    __delay_ms(20);
    writeCommand(0x0C); // Display ON
    __delay_ms(20);
    writeCommand(0x01); // Clear Display
    __delay_ms(20);
    writeCommand(0x06); // Entry Mode
    __delay_ms(20);
  }

void conv_time(int t_data, char *str)
{
    str[0]  = t_data & 0xf0; 
    str[0]  >>= 4; 
    str[0]  += 0x30;
    str[1] = (t_data & 0xf) + 0x30;
}
/*
                         Main application
 */
int main(void)
{
    
    char week[7][5]={"Sun.", "Mon.","Tue.","Wed.","Thu.","Fri.","Sat."};
    int i;
    bcdTime_t  currentTime;
    
    // initialize the device
    SYSTEM_Initialize();

    I2C1_Initialize();
    init_LCD();
     
    RTCC_Initialize();
    
    currentTime.tm_year = 0x21;
    currentTime.tm_mon = 0x12;
    currentTime.tm_mday = 0x31;
    currentTime.tm_wday = 0x05;
    currentTime.tm_hour = 0x23;
    currentTime.tm_min = 0x59;
    currentTime.tm_sec = 0x50;
    RTCC_BCDTimeSet(&currentTime);

    while(1)
     {
        time_fl = 1;
        writeCommand(0x02); // Retern Home
        RTCC_BCDTimeGet(&currentTime);
        moji[0] =' 2';
        moji[1] = '0';
        conv_time(currentTime.tm_year, &moji[2]);
        moji[4] = ' ';
        conv_time(currentTime.tm_mon, &moji[5]);
        moji[7] = '/';
        conv_time(currentTime.tm_mday, &moji[8]);
        moji[10] = ' ';
        for(i = 0; i < 4; i ++) moji[11 + i] = week[currentTime.tm_wday][i];
        for(i = 0; i < 15; i ++)  writeData(moji[i]);
        
        writeCommand(0x40+0x80); // ADRESS SET
        conv_time(currentTime.tm_hour, moji);
        moji[2] = ':';
         conv_time(currentTime.tm_min, &moji[3]);
        moji[5] = ':';
        conv_time(currentTime.tm_sec, &moji[6]);
        for(i = 0; i < 8; i ++)  writeData(moji[i]);

        while(time_fl) ;
     
     }

     return 1;
}
/**
 End of File
*/
  • 4行:#include “mcc_generated_files/mcc.h”
    • 相変わらず、system.h ではエラーがでます。
  • 6,7行: __delay_ms();用に追加
  • 85行:I2Cの初期設定
  • 86行:LCDの初期設定
  • 88行:RTCCの初期設定
  • 90から97行:
    • 時間を設定しています。
    • 設定時間は、2021年12月31日金曜日23時59分50秒です。
    • 10秒待つと全ての項目が変わります。
  • 102から121行
    • ここで日時の表示を行っています。
    • LCDの1行目に 年 月日 曜日 が表示されます。
    • 2行目には 時 分 秒 が表示されます。
  • 123行
    • この time_fl 変数を1秒ごとの割り込みで 0か1 に設定して時間を表示させています。

次は rtcc.c の変更です。今回はタイマーの時と違い、割り込みに対する処理を rtcc.c にコーディングします。先ずは初期設定関数を見て見ます。

rtcc.c

void RTCC_Initialize(void)
{

   // Set the RTCWREN bit
   __builtin_write_RTCWEN();

   RCFGCALbits.RTCEN = 0;
   
   if(!RTCCTimeInitialized())
   {
       // set RTCC time 2021-12-03 05-01-13
       RCFGCALbits.RTCPTR = 3;        // start the sequence
       RTCVAL = 0x21;    // YEAR
       RTCVAL = 0x1203;    // MONTH-1/DAY-1
       RTCVAL = 0x505;    // WEEKDAY/HOURS
       RTCVAL = 0x113;    // MINUTES/SECONDS
   }

   // set Alarm time 2021-12-03 05-01-13
   ALCFGRPTbits.ALRMEN = 0;
   ALCFGRPTbits.ALRMPTR = 2;
   ALRMVAL = 0x1203;
   ALRMVAL = 0x505;
   ALRMVAL = 0x113;

   // ALRMPTR MIN_SEC; AMASK Every SEC; ARPT 0; CHIME disabled; ALRMEN enabled; 
   ALCFGRPT = 0x8400;

   // RTSECSEL Alarm Pulse; 
   PADCFG1 = 0x00;
           
   // Enable RTCC, clear RTCWREN
   RCFGCALbits.RTCEN = 1;
   RCFGCALbits.RTCWREN = 0;

   //Enable RTCC interrupt
   IEC3bits.RTCIE = 1;
}

 時間や割り込みの設定を行っています。27行の ALCFGRPT = 0x8400; ここでアラームの条件を設定していますが26行にコメントに有る様にこれでは、1秒に一回アラームを出して繰り返しが無い設定になっています。後で分かったのですが、MCCの設定の画面で繰り返し数を指定する箇所が有りました。ここで、回数に0xFFを指定すると無限に繰り返す事になる様です。MCCを立ち上げて下記の様に設定し、Generate して下さい。

新しいコードが作成さる時に前回のコードと違う箇所を下記の様に指摘してくれます。ここが変わるけど良いかって聞いてる感じです。青い小さな矢印(行番号90の横)をクリックすれば新しいコードに変更れます。

コード的には、 ALCFGRPT = 0x8400; が  ALCFGRPT = 0xC4FF; に変わるのみです。これならソースを直した方が早いか。正確には上のコメント行の内容も変わっていますが。

実際の割り込み処理は以下で行っています。RTCC割り込みが発生すると、void attribute ( ( interrupt, no_auto_psv ) ) _ISR _RTCCInterrupt( void )が実行されます。ここでさらに、RTCC_CallBack(); が呼ばれているます。割り込み処理はここで行います。実際のコードは

  • 先ず、外部変数 int time_fl を宣言
  • その変数を割り込みがかかる度に反転する。(0−>1,1−>0)

ただこれだけです。これで、main()関数内で time_flが1秒間隔で反転しその都度時間が表示される仕組みです。

rtcc.c

extern int time_fl;
void __attribute__ ((weak)) RTCC_CallBack(void)
{
    time_fl = ! time_fl;
   // Add your custom callback code here
}

/* Function:
  void __attribute__ ( ( interrupt, no_auto_psv ) ) _ISR _RTCCInterrupt( void )

  Summary:
    Interrupt Service Routine for the RTCC Peripheral

  Description:
    This is the interrupt service routine for the RTCC peripheral. Add in code if 
    required in the ISR. 
*/
void __attribute__ ( ( interrupt, no_auto_psv ) ) _ISR _RTCCInterrupt( void )
{
	// RTCC callback function 
	RTCC_CallBack();
	
    /* TODO : Add interrupt handling code */
    IFS3bits.RTCIF = false;
}

実行結果

2021年12月31日23時59分50秒に時刻をセットしています。10秒待つと表示が全部更新されます。新年を迎えました。

次回は

後は、SPI、外部割り込み、パワーセーブ位でしょうか。何かやります。