/* ======================================================== 自己アップデータ ======================================================== */ #pragma SFR #pragma di #pragma ei #pragma nop #pragma stop #pragma halt #include "incs_loader.h" #include #include "fsl_user.h" #include "i2c_ctr.h" #include "pool.h" #include "magic.h" #include "pm.h" // ======================================================== const u8 fsl_fx_MHz_u08 = 8; const u8 fsl_low_voltage_u08 = 1; // 自己フラッシュパラメータ #define SAM_BLOCK_SIZE 1024 // ↓256バイト以上はまとめてかけません。 #define SELF_UPDATE_BUFF_SIZE 256 #define SELF_UPDATE_SPLIT_WRITE_NUM ( SAM_BLOCK_SIZE / SELF_UPDATE_BUFF_SIZE ) #define SAM_WORD_SIZE 4 // ↓ブロック番号(1ブロック=1kB) #define INACTIVE_BOOTSECT_TOP 4 #define FIRM_TOP 8 #define FIRM_SIZE 12 #define UPDATE_BLOCK_LAST ( FIRM_TOP + FIRM_SIZE - 1 ) #ifdef _MCU_BSR_ #define ACKD ACKD1 #define ACKE ACKE1 #define COI COI1 #define IICAEN IICA1EN #define IICAPR0 IICAPR10 #define IICRSV IICRSV1 #define IICA IICA1 #define IICAIF IICAIF1 #define IICAMK IICAMK1 #define IICAPR1 IICAPR11 #define IICCTL0 IICCTL01 #define IICE IICE1 #define IICF IICF1 #define IICS IICS1 #define IICWH IICWH1 #define IICWL IICWL1 #define LREL LREL1 #define SPD SPD1 #define SPIE SPIE1 #define STCEN STCEN1 #define STD STD1 #define SVA SVA1 #define WREL WREL1 #define WTIM WTIM1 #endif // ======================================================== static void FSL_Open( void ); static void FSL_Close( void ); void firm_restore( ); static err my_FSL_Init(); static err firm_duplicate( u8 block_src, u8 block_dest ); #ifdef _DBG_LED_PRINT_ void alert( u8 ); void led_print( u8 ); #else # define alert( x ) ; # define led_print( x ) ; #endif // ======================================================== extern uni_pool pool; // 0.D以降 新アップデータ向け #define N_MGC_L 0x1FF6 #define N_MGC_T 0x4FF6 /* ======================================================== I2Cで受信して、 書き込み、 チェックOK → 新ファームに切り替えて再起動     NG → 旧(現)ファームに戻して再起動 (この関数からは戻りません) ======================================================== */ void firm_update( ) { u8 target_block; u8 split_write_count; // ブロックへちまちま書き込むカウンタ // 書き替え前準備 ///////////////////////////////////// my_FSL_Init(); /* ファームのバックアップ 開始アドレス、書き込み先の先頭”ブロック番号” (サイズは FIRM_SIZE) 0x2000 - 0x4FFF を 0x5000 - 0x7FFF (ブロック 20 - 31) にコピー */ firm_duplicate( FIRM_TOP, UPDATE_BLOCK_LAST +1 ); // 全ブロック削除 ///////////////////////////////////// // 電源断を判定するため、最初に全クラスタ消去する //(新ファームが書かれるところに残ってる、以前のファームのフッタを消したい) for( target_block = INACTIVE_BOOTSECT_TOP; target_block <= UPDATE_BLOCK_LAST; target_block += 1 ) { FSL_Erase( target_block ); } // 書き替え /////////////////////////////////////////// // ●ストップコンディションが来るまで続ける // ●終わったら、スタートアップルーチンに飛ぶ for( target_block = INACTIVE_BOOTSECT_TOP; target_block <= UPDATE_BLOCK_LAST; target_block += 1 ) { /* すでに消してある。でないと中断されたとき終了してるか判別出来ない // // 新ファーム領域削除 // FSL_Erase( target_block ); */ // 分割書き込み for( split_write_count = 0; ( ( split_write_count < SELF_UPDATE_SPLIT_WRITE_NUM ) && ( !SPD ) ); split_write_count += 1 ) { u8* p_buffer = &pool.self_update_work[0]; u16 buff_written_size = 0; // I2Cから書き込みデータをバッファにためる do { while( !IICAIF && !SPD ) { WDT_Restart( ); } IICAIF = 0; *p_buffer = IICA; WREL = 1; p_buffer += 1; buff_written_size += 1; } while( ( buff_written_size != SELF_UPDATE_BUFF_SIZE ) && !SPD ); // 書き込み // 最後だと、ゴミをパディングするが別にかまわない if( FSL_Write( ( fsl_u32 ) ( target_block * SAM_BLOCK_SIZE + split_write_count * SELF_UPDATE_BUFF_SIZE ), ( fsl_u08 ) ( SELF_UPDATE_BUFF_SIZE / SAM_WORD_SIZE ) ) != FSL_OK ) { // 書き込み後のチェックエラー while(1){} // リストア firm_duplicate( UPDATE_BLOCK_LAST +1, FIRM_TOP ); FSL_ForceReset(); // 戻ってこない // } } // 1ブロック書き込み完了。内部ベリファイを行う #ifdef _DBG_LED_PRINT_ P1.5 = 1; DBG_LED_on; #endif if( FSL_IVerify( target_block ) != FSL_OK ){ while(1){} // 再度消去→書き込み ベリファイを繰り返すだけじゃダメでした... todo…? // リストア firm_duplicate( UPDATE_BLOCK_LAST +1, FIRM_TOP ); FSL_ForceReset(); // 戻ってこない // } #ifdef _DBG_LED_PRINT_ DBG_LED_off; P1.5 = 0; #endif if( SPD ) { break; } } LREL = 1; // 書き込んだファームのチェック // { u8 i; u8 comp = 0; // ローダーのマジックと、本文の末尾のマジックは同じか確認 for( i = 0; i < sizeof( __TIME__ ); i++ ) { comp += ( *( u8 * ) ( N_MGC_L + i ) == *( u8 * ) ( N_MGC_T + i ) ) ? 0 : 1; } if( comp == 0 ) { // OK! FSL_InvertBootFlag( ); FSL_SwapBootCluster( ); // リセットせずに頭から。FSL_Closeは不要 // 戻ってこない // } else { // データ(マジックナンバーしか見てない)エラー // リストア firm_duplicate( UPDATE_BLOCK_LAST, FIRM_TOP ); FSL_ForceReset(); // リセット // 戻ってこない // } } } /* ========================================================  ■ファームをバックアップ領域からリストアします。  チェック後、最後の最後でブートスワップするので、  ここではブートスワップは不要です。 ======================================================== */ void firm_restore( ) { PM1.5 = 0; // バックアップは正常? // { u16 i; u8 comp = 0; for( i = 0; i < sizeof( __TIME__ ); i++ ) // sizeof( __TIME__ ) = 8 らし { comp += ( *( __far u8 * )( MGC_LOAD_BKUP + i ) == *( u8 * )( MGC_HEAD_BKUP + i ) ) ? 0 : 1; comp += ( *( u8 * )( MGC_HEAD_BKUP + i ) == *( u8 * )( MGC_FOOT_BKUP + i ) ) ? 0 : 1; } if( *( u8 * )( MGC_FOOT_BKUP ) == 0xFF ) { comp += 1; } if( comp != 0 ) { // バックアップ領域も壊れた... comp = 0; // 3.3Vが上がらないと困る EI( ); iic_mcu_start( ); RESET2_ast; FCRAM_RST_ast; GYRO_DISABLE(); PM_LDSW_on(); wait_ms( 1 + DELAY_PM_TW_PWUP ); PM_VDD_on( ); while(1) { WDT_Restart(); { // 赤LED ピコピコ comp++; P1.5 = ( comp == 1 || comp == 3 )? 1: 0; if( comp == 8 ) { comp = 0; } } { // 電源ボタンで電源off static u8 sw_hold_count; if( !SW_POW_n ) { sw_hold_count++; } else { sw_hold_count = 0; } if( sw_hold_count > 16 ) { sw_hold_count = 0; // 電源off PM_LDSW_off( ); break; } } // ウェイト for( i = 1; i != 0; i++ ) { NOP(); NOP(); NOP(); NOP(); } } { // ど、どうしよう…。 KRM = ( KR_SW_POW ); // Mask ではなく、Modeなのだそうだ。紛らわしい MK0 = 0xFFFF; MK1 = ~( INT_MSK1_KR ); MK2L = 0xFF; // PU5 そのまま PU7 = 0b00001000; // PWSWI PU20 = 0x00; // SW_HOME 停止 STOP( ); mcu_wdt_reset; } } } if( my_FSL_Init() != ERR_SUCCESS ){ alert(1); } /* ファームのリストア 0x4800 - 0x7FFF (ブロック 18 - 27) から 0x2000 - 0x47FF (ブロック 8 - 17) へコピー */ if( firm_duplicate( UPDATE_BLOCK_LAST +1, FIRM_TOP ) != ERR_SUCCESS ) { alert(2); } // todo //  リストア失敗したら、LEDちかちかとかさせて、サービス送りにしてもらう? // リブート if( FSL_InvertBootFlag() != ERR_SUCCESS ) { alert(3); } FSL_SwapBootCluster(); } // ======================================================== static void FSL_Open( void ) { /* save the configuration of the interrupt controller and set */ #ifdef FSL_INT_BACKUP fsl_MK0L_bak_u08 = MK0L; /* if (interrupt backup required) */ fsl_MK0H_bak_u08 = MK0H; /* { */ fsl_MK1L_bak_u08 = MK1L; /* */ fsl_MK1H_bak_u08 = MK1H; /* save interrupt controller */ fsl_MK2L_bak_u08 = MK2L; /* configuration */ fsl_MK2H_bak_u08 = MK2H; /* */ MK0L = FSL_MK0L_MASK; /* */ MK0H = FSL_MK0H_MASK; /* */ MK1L = FSL_MK1L_MASK; /* prepare interrupt controller */ MK1H = FSL_MK1H_MASK; /* for selfprogramming */ MK2L = FSL_MK2L_MASK; /* */ MK2H = FSL_MK2H_MASK; /* } */ #endif while( DST1 ){;} // DMA停止 DEN1 = 0; MK0 = 0xFFFF; MK1 = 0xFFFF; MK2 = 0xFFFF; /* LVIM = 0b00000010; LVIS = 0x08; LVIM = 0b10000010; */ FSL_FLMD0_HIGH; // フラッシュ書き替え許可 } /*----------------------------------------------------------------------------------------------*/ /* leave the "user room" and restore previous conditions */ /*----------------------------------------------------------------------------------------------*/ static void FSL_Close( void ) { // 何か後始末? FSL_FLMD0_LOW; // フラッシュライトプロテクト #ifdef FSL_INT_BACKUP MK0L = fsl_MK0L_bak_u08; /* do{ */ MK0H = fsl_MK0H_bak_u08; /* restore interrupt controller */ MK1L = fsl_MK1L_bak_u08; /* configuration */ MK1H = fsl_MK1H_bak_u08; /* */ MK2L = fsl_MK2L_bak_u08; /* */ MK2H = fsl_MK2H_bak_u08; /* } */ #endif } /* ========================================================  マイコン内でファームをコピーします。 __far u8 * p_rom コピー元の先頭アドレス block_dest コピー先の先頭ブロック コピー先に書けるようにmy_FSL_Initをあらかじめ実行する必要があります。 ======================================================== */ //static err firm_duplicate( __far u8 * p_rom, // u8 block_dest ) static err firm_duplicate( u8 block_src, u8 block_dest ) { u8 target_block; u8 split_write_count; // ブロックへちまちま書き込むカウンタ __far u8* p_src = ( __far u8* )( block_src * 0x400 ); led_print(1); // 書き込み先ブロックの数だけ繰り返す for( target_block = block_dest; target_block < ( block_dest + FIRM_SIZE ); target_block += 1 ) { led_print(2); WDT_Restart( ); // ブロック消去 while( FSL_BlankCheck( target_block ) != FSL_OK ) { led_print(3); FSL_Erase( target_block ); } led_print(4); // 分割書き込み分繰り返す for( split_write_count = 0; split_write_count < SELF_UPDATE_SPLIT_WRITE_NUM; split_write_count += 1 ) { u16 buff_written_size; u8* p_buff; WDT_Restart( ); // 書き込みデータをバッファにためる buff_written_size = 0; p_buff = &pool.self_update_work[0]; do { *p_buff = *p_src; p_src += 1; p_buff += 1; buff_written_size +=1; } while( buff_written_size != SELF_UPDATE_BUFF_SIZE ); // 書き込み if( FSL_Write( ( fsl_u32 ) ( target_block * SAM_BLOCK_SIZE + split_write_count * SELF_UPDATE_BUFF_SIZE ), ( fsl_u08 ) ( SELF_UPDATE_BUFF_SIZE / SAM_WORD_SIZE ) ) != FSL_OK ) { // リカバリはリブート時 // FSL_Close( ); led_print(5); // while(1){} return ( ERR_ERR ); } } led_print(6); // 1ブロック書き込み完了。内部電圧チェックを行う while( FSL_IVerify( target_block ) != FSL_OK ) { // リカバリはリブート時 todo // led_print(7); // while(1){} return ( ERR_ERR ); } } return( ERR_SUCCESS ); } /* ======================================================== ======================================================== */ static err my_FSL_Init() { u8 rv; RTCE = 0; // 書き替え前準備 // DI( ); FSL_Open( ); // 割り込み禁止など FSL_Init( &pool.self_update_work[0] ); // ライブラリ初期化。割り込み中断考慮せず rv = FSL_ModeCheck( ); // ライトプロテクトチェック。失敗することを考慮せず return( (err)rv ); } task_status_immed tski_mcu_reset() { // 普通に再起動 my_FSL_Init(); FSL_ForceReset(); // リセット FSL_Close( ); // 保険? // // mcu_wdt_reset; // WDTで再起動(テスト向け) return( ERR_SUCCESS ); // no reach } #ifdef _DBG_LED_PRINT_ void alert( u8 num ) { u8 i; while(1) { WDT_Restart(); P1.5 = 1; for( i = 0; i < num; i++ ) { DBG_LED_on; DBG_LED_off; } P1.5 = 0; } } void led_print( u8 num ) { u8 i; DBG_LED_on; for( i = 0; i < num; i++ ) { P1.5 = 1; P1.5 = 0; } DBG_LED_off; } #endif