/******************************************************************** * PIC32MX_RTC8564_Test_2.c * PIC32MX内蔵の、RTCCを使わず、I2C_RTCの、RTC8564を外付して * 時計動作を確認する。 * Test_2では、タイムアジャストとバックアップを追加した。 * * 実験ハードは、PIC32MXトレーニング基板を使用 * * Graphic LCD(Monochrome): SG12864A * ファイルシステム:MMD File System→ FAT16(FAT32互換) * * Condition: * 8MHz External X'tal Oscillator, 20x PLL (8MHzx20= 160MHz) * Fcy=160MHz/2=80MHz, Tcy=12.5ns * * CPU: PIC32MX340F256H * * N.Ishii 2014.10.31 *********************************************************************/ #include /* PIC32 peripheral library */ #include "glcd_lib32k.h" // コンフィギュレーション設定 // CPU=80MHz Peri=80MHz,HS+PLL,Divider=1/2,PLL=x20,WDT=Off #pragma config FNOSC=PRIPLL, POSCMOD=HS, FPLLIDIV=DIV_2 #pragma config FPLLMUL=MUL_20, FPBDIV=DIV_1, FPLLODIV=DIV_1 #pragma config FWDTEN=OFF, ICESEL=ICS_PGx2 // 液晶表示器メッセージ定数 char MsgClock_1[] = "'xx/mm/dd"; char MsgClock_2[] = "hh:mm:ss"; //--------------------------------------------------------------------------------------------------- // For I2C // initialize RTC8564 Side Reg Write Data unsigned char rtc_write_data_tbl[] = { 0x20, // [0]:Control1REG: Time Stop 0x00, // [1]:Control2REG: ALM INT.TIMMER INT Desable 0x00, // [2]:SecondsREG: Set 0 Set 0x30, // [3]:MinutesREG: Set 0 Min 0x14, // [4]:HourREG: Set 0 Hour 0x29, // [5]:DayREG 0x03, // [6]:WeekdaysREG: TUE 0x10, // [7]:MonthsREG 0x14, // [8]:YearsREG 0x80, // [9]:Minute_AlamREG: Not Used ALM Interrupt 0x80, // [10]:Hour_AlamREG 0x80, // [11]:Day_AlamREG 0x80, // [12]:Weekday_AlamREG 0x80, // [13]:Not Used CLKOUT 0x00, // [14]:Timer_contorolREG: Not Used Timer Interrupt 0x01 // [15]:Timer_downcount Set Vule }; unsigned char rec_data[16]; ////----------------------------------------------------------------------------------- // I2C define #define RTC8564_SLVADRS_WR 0xA2 #define RTC8564_SLVADRS_RD 0xA3 ////----------------------------------------------------------------------------------- /// A Variable (Global) short i; // グローバルの、iは未使用 char TimeAdjFlg= 0; char SelCount= 0; char YearSet= 0; char MonthsSet= 0; char DaysSet= 0; char WeekdaysSet= 0; char MinuteSet= 0; char SecondsSet= 0; char HourSet= 0; char TimeSetEnd = 0; /// Function Prottypes void RtcDisp(void); void WriteRTC(unsigned char start_adrs, unsigned char end_adrs); void ReadRTC(unsigned char start_adrs, unsigned char end_adrs); void time_adjust_mode(void); /*************************************************************** * Function Main * トレーニング基板の、単chオシロハードを、そのまま流用したので * 初期設定は、アナログ部を含め、そのままにした。 ****************************************************************/ int main(void) { //システム最適設定 SYSTEMConfigPerformance(80000000); mJTAGPortEnable(DEBUG_JTAGPORT_OFF); // JTAGを無効化 //I/O設定 AD1PCFG = 0xFFF8; // // GLCD制御信号レベルの初期化 LCD_E = 0; LCD_CS1 = 1; LCD_CS2 = 1; LCD_RW = 1; LCD_DI = 1; LCD_TRIS = 0x0000; // LCDデータバス(DB0-DB7)オール出力(TRISE= 0) TRISD = 0xFF17; // LCD_CS1,CS2,DI:全て出力・デバッグLED:出力・SW2-5:入力、他未使用ピンは入力設定 TRISF = 0xFFFC; // LCD_E,RW:全て出力、他未使用ピンは入力設定 TRISG = 0x028C; // SD_SDO,SCK:出力、SDI:入力・他未使用ピンは入力設定 TRISB = 0xFFDF; // RB5:SD_CS出力、RB4:SD_WE入力、RB3:SD_CD入力、RB2:AN2(トリガ入力)、RB1:AN1未使用、RB0:オシロ入力 CNPUE = 0x0060; // SD_CD(CN5),WE(CN6)のみ内部プルアップ LATDbits.LATD3 = 1; // デバッグLED消灯 /// 液晶表示器の初期化 lcd_Init(); lcd_Clear(0); lcd_Str(0, 0, "Start RTC Test"); //外部割込み許可 立ち上りエッジで優先レベル2 ConfigINT4(EXT_INT_ENABLE | RISING_EDGE_INT | EXT_INT_PRI_2); // SW2(RD11):TM_SEL_SW ConfigINT3(EXT_INT_ENABLE | RISING_EDGE_INT | EXT_INT_PRI_2); // SW3(RD10):TM_ADJ_SW ConfigINT2(EXT_INT_ENABLE | RISING_EDGE_INT | EXT_INT_PRI_2); // SW4(RD9) :TM_ENT_SW // 外部割り込みフラグのクリア mINT4ClearIntFlag(); mINT3ClearIntFlag(); mINT2ClearIntFlag(); /// I2Cの初期設定 100kbps 7bit address OpenI2C1(I2C_ON | I2C_7BIT_ADD, 398); //マルチベクタ割り込み設定、割り込み許可(SW外部割込みのみ) INTEnableSystemMultiVectoredInt(); delay_ms(1000); // RTC8564内部水晶振動子が、発振するまで待つ /// RTC8564初期化 /// <時計合わせについて> /// バックアップ電池交換時、HOLD SWを押しながら、電源ONすると、時計合わせモードになるので /// そこで、合わせた年月日、週、時分秒の値(BCD)を、RTCライトデータテーブルの該当位置に上書きし /// それを、制御レジスタ、アラームレジスタ等の初期値と共に、RTC8564レジスタに書込む。 /// 一旦電源を切って、普通に電源をONすると、ノーマルモードになる。 /// 尚、電池を抜いたまま、電源を入れた場合は、初期値は書込まれず、時計動作だけスタートする形になる。 if(!PORTDbits.RD11){ // HOLD SWが押されている場合は、時計合わせモードに移行する。 TimeAdjFlg= 1; time_adjust_mode(); } // RTC Time Start rtc_write_data_tbl[0] = 0x00; // コントロールレジスタ1の、bit5:STOPビット=0にして、時計動作をスタート WriteRTC(0,0); lcd_Clear(0); while(1){ RtcDisp(); } } ///----------------------------------------------------------------------------------------------------------------- /**************************************************************** * INT4外部割り込み処理(TM_SEL_SW:本番は、HOLD_SWと兼用を想定) *****************************************************************/ void __ISR(19, ipl2) INT4Handler(void) { if(TimeAdjFlg){ ++SelCount; if(SelCount == 8) SelCount= 1; /// TM_SEL_SWを押す度に、カーソルを設定項目の下に移動する。 switch(SelCount){ case 1: lcd_Str(5, 11, " "); // 1つ前に表示されているカーソル表示を消す lcd_Str(3, 5, "^"); // 年項目の下にカーソル表示(階乗記号を流用) break; case 2: lcd_Str(3, 5, " "); // 1つ前に表示されていたカーソル表示を消す lcd_Str(3, 8, "^"); // 月項目の下にカーソル表示(階乗記号を流用) break; case 3: lcd_Str(3, 8, " "); // 1つ前に表示されていたカーソル表示を消す lcd_Str(3, 11, "^"); // 日項目の下にカーソル表示(階乗記号を流用) break; case 4: lcd_Str(3, 11, " "); // 1つ前に表示されていたカーソル表示を消す lcd_Str(3, 13, "^"); // 週項目の下にカーソル表示(階乗記号を流用) break; case 5: lcd_Str(3, 13, " "); // 1つ前に表示されていたカーソル表示を消す lcd_Str(5, 5, "^"); // 時目の下にカーソル表示(階乗記号を流用) break; case 6: lcd_Str(5, 5, " "); // 1つ前に表示されていたカーソル表示を消す lcd_Str(5, 8, "^"); // 分項目の下にカーソル表示(階乗記号を流用) break; case 7: lcd_Str(5, 8, " "); // 1つ前に表示されていたカーソル表示を消す lcd_Str(5, 11, "^"); // 秒項目の下にカーソル表示(階乗記号を流用) break; default: ; break; } delay_ms(100); // チャッタリング回避 } mINT4ClearIntFlag(); // 割り込みフラグクリア } /***************************************************************** * INT3外部割り込み処理(TM_ADJ_SW:本番は、WRITE_SWと兼用を想定) ******************************************************************/ void __ISR(15, ipl2) INT3Handler(void) { if(TimeAdjFlg){ /// 各設定項目によって、それぞれのアジャスト処理に分岐する switch(SelCount){ case 1: ++YearSet; if (YearSet == 100) YearSet = 0; /// バイナリ→パックドデシマル→アスキー変換してから、液晶に設定値を表示する。(以下同様) lcd_Char1(2, 4, (YearSet/10)%10 + 0x30); // 上位桁 lcd_Char1(2, 5, YearSet%10 + 0x30); // 下位桁 break; case 2: ++MonthsSet; if(MonthsSet == 13) MonthsSet = 1; lcd_Char1(2, 7, (MonthsSet/10)%10 + 0x30); // 上位桁 lcd_Char1(2, 8, MonthsSet%10 + 0x30); // 下位桁 break; case 3: ++DaysSet; if(DaysSet == 32) DaysSet = 1; lcd_Char1(2, 10, (DaysSet/10)%10 + 0x30); // 上位桁 lcd_Char1(2, 11, DaysSet%10 + 0x30); // 下位桁 break; case 4: ++WeekdaysSet; if(WeekdaysSet == 7) WeekdaysSet = 0; lcd_Char1(2, 13, WeekdaysSet%10 + 0x30); // 週は1桁表示 break; case 5: ++HourSet; if(HourSet == 24) HourSet = 0; lcd_Char1(4, 4, (HourSet/10)%10 + 0x30); // 上位桁 lcd_Char1(4, 5, HourSet%10 + 0x30); // 下位桁 break; case 6: ++MinuteSet; if(MinuteSet == 60) MinuteSet = 0; lcd_Char1(4, 7, (MinuteSet/10)%10 + 0x30); // 上位桁 lcd_Char1(4, 8, MinuteSet%10 + 0x30); // 下位桁 break; case 7: ++SecondsSet; if(SecondsSet == 60) SecondsSet = 0; lcd_Char1(4, 10, (SecondsSet/10)%10 + 0x30); // 上位桁 lcd_Char1(4, 11, SecondsSet%10 + 0x30); // 下位桁 break; default: ; break; } delay_ms(100); // チャッタリング回避 } mINT3ClearIntFlag(); // 割り込みフラグクリア } /**************************************************** * INT2外部割り込み処理(TM_ENT_SW Read) ****************************************************/ void __ISR(11, ipl2) INT2Handler(void) { if(TimeAdjFlg){ /// バイナリ→BCD変換してから、該当RTCデータデーブル位置に上書きする。 rtc_write_data_tbl[8] = YearSet+6 * (YearSet/10); rtc_write_data_tbl[7] = MonthsSet+6 * (MonthsSet/10); rtc_write_data_tbl[6] = WeekdaysSet+6 * (WeekdaysSet/10); rtc_write_data_tbl[5] = DaysSet+6 * (DaysSet/10); rtc_write_data_tbl[4] = HourSet+6 * (HourSet/10); rtc_write_data_tbl[3] = MinuteSet+6 * (MinuteSet/10); rtc_write_data_tbl[2] = SecondsSet+6 * (SecondsSet/10); // 時計合わせしたデータを、RTC8564に書込む。 WriteRTC(0,15); // Write RTC_Reg 16bytes: adrs = 0 to 15 -> Time Stop TimeAdjFlg= 0; TimeSetEnd= 1; delay_ms(100); // チャッタリング回避 } mINT2ClearIntFlag(); // 割り込みフラグクリア } ///----------------------------------------------------------------------------------------------------------------- /***************************************************************** * RTCレジスタを読出し、液晶に、年/月/日、時:分 * を表示する。 ******************************************************************/ void RtcDisp(void) { ReadRTC(2,8); // rec_data[*] <-- Read SecondsREG 〜 YearsREG /// RTCレジスタ値は、BCDなので、これをアスキーに変換してから、 /// それぞれの、メッセージバッファ位置に、格納後、液晶表示する。 MsgClock_1[1] = (rec_data[8] >> 4) + 0x30; // 年:01〜99 MsgClock_1[2] = (rec_data[8] & 0x0F) + 0x30; MsgClock_1[4] = ((rec_data[7] & 0x1F) >> 4) + 0x30; // 月:01〜12 ※ 読出し時、b7は、Cビット、b6-5は不定なので、0x1Fでマスクが必要 MsgClock_1[5] = (rec_data[7] & 0x0F) + 0x30; MsgClock_1[7] = ((rec_data[5] & 0x3F) >> 4) +0x30; // 日:01〜31 ※ 読出し時、b7-6は不定なので、0x3Fでマスクが必要 MsgClock_1[8] = (rec_data[5] & 0x0F) + 0x30; lcd_Str(2, 3, MsgClock_1); // 時計表示_1:上段 /// 週は、メッセージバッファを介さず、週レジスタの値に対応する、週文字を /// そのまま、液晶に出力する。 /// ※ 週レジスタ:0〜7→ 読出し時、b3-7は不定なので、0x07でマスクが必要 if((rec_data[6] & 0x07) == 0) lcd_Str(2, 12, "(SUN)"); if((rec_data[6] & 0x07) == 1) lcd_Str(2, 12, "(MON)"); if((rec_data[6] & 0x07) == 2) lcd_Str(2, 12, "(TUE)"); if((rec_data[6] & 0x07) == 3) lcd_Str(2, 12, "(WED)"); if((rec_data[6] & 0x07) == 4) lcd_Str(2, 12, "(THU)"); if((rec_data[6] & 0x07) == 5) lcd_Str(2, 12, "(FRI)"); if((rec_data[6] & 0x07) == 6) lcd_Str(2, 12, "(SAT)"); MsgClock_2[0] = ((rec_data[4] & 0x3F) >> 4) + 0x30; // 時:00〜23 ※ 読出し時、b7-6は不定なので、0x3Fでマスクが必要 MsgClock_2[1] = (rec_data[4] & 0x0F) + 0x30; MsgClock_2[3] = ((rec_data[3] & 0x7F) >> 4) + 0x30; // 分:00〜59 ※ 読出し時、b7は不定なので、0x7Fでマスクが必要 MsgClock_2[4] = (rec_data[3] & 0x0F) + 0x30; MsgClock_2[6] = ((rec_data[2] & 0x7F) >> 4) + 0x30; // 秒:00〜59 ※ 読出し時、b7は、VLビットなので、0x7Fでマスクが必要 MsgClock_2[7] = (rec_data[2] & 0x0F) + 0x30; lcd_Str(4, 4, MsgClock_2); // 時計表示_2:下段 } ///------------------------------------------------------------------------------------------------------------ /****************************************** * 時計合わせモード *******************************************/ void time_adjust_mode(void) { /// トップメッセージ表示 lcd_Clear(0); lcd_Str(0, 0, "TIME ADJUST MODE"); /// 時計合わせの、初期画面(ALL '0'固定)表示 lcd_Str(2, 3, "'00/00/00(0)"); // 時計表示_1:上段 lcd_Str(4, 4, "00:00:00"); // 時計表示_2:下段 // Read SW Loop(SW処理は、全て外部割込みで行う。 while(TimeSetEnd != 1); } ///-------------------------------------------------------------------------------------------------------------- /********************************************************************* * RTC I2C書込み関数 * * <使い方> * @ 連続した、レジスタアドレスに、データを書込みたい時は、 *   その範囲の、レジスタにデータをセットした後に、その *   開始アドレスから終了アドレスまでを指定して、この関数を *   実行する。 *   但し、全16個のレジスタに初期値(テーブル)を書込む時は、 *   WriteRTC(0,15); *   を実行するだけでよい。 *   その配列名は、rtc_write_data_tblとする。([0]〜[15]) * * A 任意のレジスタに、1バイトのデータを書込みたい時には、 *   前もって、希望のレジスタ番地(配列の要素)に、データをセット *   してから、開始と終了アドレスを同じアドレスにして、この関数 *   を実行する。 ***********************************************************************/ void WriteRTC(unsigned char start_adrs, unsigned char end_adrs) { char i; /// START Bus Event StartI2C1(); // Startシーケンス /// Send Slave Address + Writebit IdleI2C1(); // wait MasterWriteI2C1(RTC8564_SLVADRS_WR); // RTCスレーブアドレス書き込み送信 // スレーブアドレスは、b7〜b1(7ビット)+b0:W/=0 /// Receive ACK From RTC8564 IdleI2C1(); // wait while(I2C1STATbits.ACKSTAT); // ACK応答が、'0' になるまで待つ(エラー処理は省略した) /// Send Write Start Address IdleI2C1(); MasterWriteI2C1(start_adrs); /// Receive ACK From RTC8564 IdleI2C1(); // wait while(I2C1STATbits.ACKSTAT); // ACK応答が、'0' になるまで待つ(エラー処理は省略した) /// Send RTC_REG Write Data (Write Address Auto Incriment) for (i = start_adrs; i != end_adrs + 1; ++i) { IdleI2C1(); MasterWriteI2C1(rtc_write_data_tbl[i]); // Receive ACK From RTC8564 IdleI2C1(); // wait while(I2C1STATbits.ACKSTAT); // ACK応答が、'0' になるまで待つ(エラー処理は省略した) } /// Stop Bus Event StopI2C1(); // Stopシーケンス delay_us(200); // Delay for 200uS (needs > 1.3uS next start) } /************************************************************** * RTC I2C読込み関数 * * <使い方> * 書込み関数と、同様です。(書込みのところを、読込みに置換えて説明文をみてください) ***************************************************************/ void ReadRTC(unsigned char start_adrs, unsigned char end_adrs) { char i; // START Bus Event StartI2C1(); // Startシーケンス /// Send Slave Address + Writebit IdleI2C1(); // wait MasterWriteI2C1(RTC8564_SLVADRS_WR); // RTCスレーブアドレス書き込み送信 // スレーブアドレスは、b7〜b1(7ビット)+b0:W/=0 /// Receive ACK From RTC8564 IdleI2C1(); // wait while(I2C1STATbits.ACKSTAT); // ACK応答が、'0' になるまで待つ(エラー処理は省略した // Send Read Start Address IdleI2C1(); MasterWriteI2C1(start_adrs); /// Receive ACK From RTC8564 IdleI2C1(); // wait while(I2C1STATbits.ACKSTAT); // ACK応答が、'0' になるまで待つ(エラー処理は省略した) // RESTART Bus Event RestartI2C1(); // RESTARTシーケンス // Send Slave Address + Readbit IdleI2C1(); // wait MasterWriteI2C1(RTC8564_SLVADRS_RD); /// Receive ACK From RTC8564 IdleI2C1(); // wait while(I2C1STATbits.ACKSTAT); // ACK応答が、'0' になるまで待つ(エラー処理は省略した) // Receive RTC_REG Read Data (Read Address Auto Incriment) for (i = start_adrs; i != end_adrs + 1; ++i) { IdleI2C1(); rec_data[i] = MasterReadI2C1(); // Send ACK From PIC if (i != end_adrs) { IdleI2C1(); I2C1CONbits.ACKDT = 0; // Specify ACK Tx I2C1CONbits.ACKEN = 1; // Master is Response Sq } } // Send NACK From PIC: End of Rx IdleI2C1(); I2C1CONbits.ACKDT = 1; // Specify NACK Tx I2C1CONbits.ACKEN = 1; // Master is Response Sq /// Stop Bus Event StopI2C1(); // Stopシーケンス delay_us(200); // Delay for 200uS (needs > 1.3uS next start) }