LPC810メモ:Flash IAP

UM10601「Chapter 22: LPC81x Flash ISP and IAP programming」の、IAPを使う例。

前提とする環境はこちら→LPC810メモ 共通の準備

  • Flash IAP
    • 本来はプログラムを格納するためのflash romの領域を操作する機能(In-Application programming)
    • 電源をoffにしても保持したい情報の記録に利用できる。
  • この例の内容
    • 電源onすると、flashの領域にあるアドレス'0x00000FC0'に記載された値を1増やす。
    • その値の回数だけLEDを点滅させる(他は何もしない)。
    • 結果として、電源off/onを繰り返す毎に、'0x00000FC0'の値が1づつ増え、LEDの点滅回数が増える。
      • 毎回、対象のセクタ(sector3、0x00000C00 - 0x00000FFF)はまるごと消去されるので、他のアドレスの値は0xffになる。

サンプルコード

#include "LPC8xx.h"

// UM10601 22.5.2 IAP commands
// に従った宣言
typedef void (*IAP)(unsigned int [],unsigned int[]);
IAP iap_entry = (IAP)0X1FFF1FF1UL;

int main(void) {
    // 引数を渡す配列と、結果を受けとる配列を準備
    // UM10601 22.5.2 IAP commands
    unsigned int command_param[5];
    unsigned int status_result[4];

    // sector3、page63の先頭アドレス、0x00000FC0の値を読み込む
    uint8_t value = *((uint8_t *)0x00000FC0UL);

    // 書き込むための領域(最低1page、すなわち64byte)を準備。
    uint8_t buffer[64] = {0};

    // 0byte目に、読んだ値 + 1 を格納しておく。
    // (この値が、後のAPIでsector3、page63の先頭アドレス、0x00000FC0で書き込まれる)
    buffer[0] =  value+ 1; 

    // 割り込み禁止
    // (割り込み禁止にしないで使う方法もあるようだが面倒なので調べない)
    __disable_irq();
    
    // ここからAPIを呼ぶ。
    // 呼び方は、「UM10601  22.5.2 IAP commands」を見る。
    // command_param[]に引数をいれて、
    // result_param[]で結果を受け取る。
    // (本例では結果は全く使ってない)

    // 消去の準備
    // UM10601  22.5.2.1 Prepare sector(s) for write operation (IAP)
    command_param[0] = 50; // Command code
    command_param[1] = 3;   // sector 3から、
    command_param[2] = 3;  // sector 3までを対象とする。
    iap_entry(command_param, status_result); // API呼び出し

    // 消去
    // UM10601  22.5.2.3 Erase Sector(s) (IAP)
    command_param[0] = 52;  // Command code
    command_param[1] = 3;    // sector 3から、
    command_param[2] = 3;   // sector 3までを対象とする。
    command_param[3] = 12000; // 動作クロックは12MHz(12000KHz)
    iap_entry(command_param, status_result); // API呼び出し

    // 書き込みの準備(消去の準備と同じ)
    // UM10601  22.5.2.1 Prepare sector(s) for write operation (IAP)
    command_param[0] = 50;
    command_param[1] = 3;
    command_param[2] = 3;
    iap_entry(command_param, status_result);

    // 書き込み
    // UM10601  22.5.2.2 Copy RAM to flash (IAP)
    command_param[0] = 51; // Command code
    command_param[1] = 0x00000FC0UL; // 書き込み先の先頭アドレス(sector3, page63)
    command_param[2] = (unsigned int)buffer; // 書き込み元の先頭アドレス
    command_param[3] = 64; // 64byte書き込む(これが最低値)。
    command_param[4] = 12000; // 動作クロックは12MHz(12000KHz)
    iap_entry(command_param, status_result);

    // 割り込み有効化
    __enable_irq();

    // 0x00000FC0ULの値の数だけ、8番pinのLEDを点滅
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);
    LPC_GPIO_PORT->DIR0 |= (1<<0);
    for(uint32_t i = 0; i < *((uint8_t *)0x00000FC0UL) * 2; i++) {
        LPC_GPIO_PORT->NOT0 |= (1<<0);
        for(volatile int i = 0; i < 200000; i++) {}
    }

    return 0 ;
}

接続

補足/雑感

  • flashについての知識、多分常識なんだろうけど知らなかったために、それなりにハマった。
    • flashは基本、消去してからじゃないと書き込めない。
      • 正確には、消去しなくても、各bit毎に1を0にすることはできる。しかし逆はできない。つまり、0xff->0xfeにすることはできるが(最下位bitを0にする)、0x00を0x01にすることはできない。
    • 消去した時の値は0xffになる。
      • 書き込みでは0を1にする事しかできないんだから当たり前だ。でも、最初は0になるっていう先入観でハマった。
  • flashに書き込まれている値は、flash magicのメニュー、[ISP]->[Display Memory]で参照可能。
    • 便利だけど、自分の環境だと、これを使うと頻繁にflash magicごと落ちる。
  • 本例では完全無視だが、APIの成否はresult[0]に入っているstatus codeを見る。
    • UM10601 22.5.2.11 IAP Status codes
  • セクタやページとアドレスの対応表はここ
    • UM10601 22.4.1 Flash configuration
  • セクタは1KB単位で、ページは64byte単位。
  • 書き込みはページ単位(64byte)からできるが、消去は実質セクタ単位かもしれない。
    • 一応、ページ単位の消去コマンド(22.5.2.10 Erase page)もあるようだ。
    • ただ、「22.5.1.7 Copy RAM to flash...」の説明、同じページに16回書いたら、次はセクタまるごと消去しなくてはならない、って意味だと思う(自信ないが)。
    • でもそうだとすると、flash IAPを使うと実質的にセクタ1つ(1KB)を占有してしまうのか。4KBしかないflashのうち1KBなので厳しい気がする。読み間違ってるかもしれない。
    • あるいは、用途によっては16回書ければ十分だったりするのかな。実際に何に使うか考えずに試してるから、この辺の感覚がわからない。

みっかぼの無料Androidアプリはこちら。