LPC1114FN28/102 (19) -- ICSP 接続で PIC32MX のフラッシュに書き込む (4)

前回述べた「キー・シーケンス」を発行したあと、ターゲットが正常に ICSP モードに移行したかどうかを簡単に直接的に調べる方法は存在しないようです。
プローブ側から ICSP モードで、まず IDCODE レジスタやステータス・レジスタを読み出して、期待した値が返ってこない場合に ICSP 接続が失敗したものと見なすようです。
LPC1114 の SPI (SSP0) まわりのプログラムの断片を下に示します。

#define CPUCLK_Hz   48000000UL
#define SCK0_Hz      1000000UL
#define SSP_CLK_DIV (CPUCLK_Hz/(2*SCK0_Hz))

void LPC11xx_SSP0_set_mode(int bits, int cpol, 
                           int cpha, int cdiv)
{
  register LPC_SSP_TypeDef *p = LPC_SSP0;
// set control registers
  p->CR0 = (       0
           |  (bits-1)       // xfer bits
           | (      0  << 4) // SPI mode
           | (    cpol << 6) // CPOL
           | (    cpha << 7) // CPHA
           | ((cdiv-1) << 8) // SCK0 = 48 [MHz]/(2*cdiv)
           );
} // void LPC11xx_SSP0_set_mode()

void LPC11xx_SSP0_setup( void )
{
  register LPC_SSP_TypeDef *p = LPC_SSP0;
  LPC_SYSCON->SSP0CLKDIV = 1;  // SSP0 clock = CCLK/1
// set control registers
  LPC11xx_SSP0_set_mode(4, // 4 bit xfer
                        0, // CPOL = 0
                        1, // CPHA = 1
                        SSP_CLK_DIV);
  p->CR1  = 0; // no loopback, SPI disabled, MASTER mode 
  p->CPSR = 2; // clock prescaler = 1/2 
  p->CR1  = (1 << 1);  // start SSP0 
} // void LPC11xx_SSP0_setup()

SSP0 の SPI モードでの動作を利用し、最初のキー・シーケンス発生と ICSP 接続との両方で使うので、SPI モードとして動作させるための設定を LPC11xx_SSP0_setup() 関数で行い、SPI モードでの動作パラメタの設定を LPC11xx_SSP0_set_mode() 関数で行うように分割しました。
初期設定としては、このほかに、SSP0 のリセット解除や、SSP0 の入出力信号が有効になるように IOCON レジスタを設定する必要がありますが、その部分は省略しました。
2012 年 10 月 3 日の記事 (→こちら) のプログラム・リストを参照してください。
SSP0 からデータを入出力する部分のプログラムを下に示します。

uint16_t LPC11xx_SSP0_xmit(uint16_t txd)
{
  register LPC_SSP_TypeDef *p = LPC_SSP0;
// check for Tx FIFO full  (TNF flag)
  while (0 == ((1 << 1) & p->SR)) {} 
  p->DR = txd;     // put Tx data to FIFO
// check for Rx FIFO empty (RNE flag)
  while (0 == ((1 << 2) & p->SR)) {} 
  return( p->DR ); // get Rx data from FIFO
} // uint16_t LPC11xx_SSP0_xmit();

SSP0 には Rx、Tx ともに 8 レベルの FIFO が備わっていますが、その機能は使わず、単に Tx データをデータ・レジスタに書き込んだら、送信完了まで待って Rx データを読み込んで戻るようになっています。
最初のキー・シーケンス送出でも、ICSP 接続でも、同じ LPC11xx_SSP0_xmit() 関数を使います。
キー・シーケンス送出部分のプログラムを下に示します。

void ICSP_enter_2wire( void )
{
// setup for key sequence output
// SCK idle state = 0
// shift MOSI at neg-edge, latch MISO at pos-edge (mode 0)
  LPC11xx_SSP0_set_mode(16, 0, 0, 
                        SSP_CLK_DIV); // 16 bit xfer
  ICSP_set_MCLR(0); // MCLR = L
  ICSP_set_MCLR(0); // MCLR = L
  ICSP_set_MCLR(1); // MCLR = H
  ICSP_set_MCLR(0); // MCLR = L
  LPC11xx_SSP0_xmit(0x4d43); // "MC"
  LPC11xx_SSP0_xmit(0x4850); // "HP"
  ICSP_set_MCLR(0); // MCLR = L
// setup for 2-wire ICSP mode
// SCK idle state = 0
// shift MOSI at pos-edge, latch MISO at neg-edge (mode 1)
  LPC11xx_SSP0_set_mode( 4, 0, 1, 
                        SSP_CLK_DIV); // 4 bit xfer
  ICSP_set_MCLR(1); // MCLR = H
} // void ICSP_enter_2wire()

まず、SSP0 を 16 ビット転送、SPI モード 0 (CPOL = 0, CPHA = 0) に設定します。
ICSP_set_MCLR() 関数は、単に PIC32MX の MCLR ピンに接続した出力ポートの値を設定するだけのものなので、その中身は省略します。
16 ビット転送モードに設定しているので、32 ビットのシーケンス "MCHP" を、16 ビットずつの "MC"、"HP" に分割して LPC11xx_SSP0_xmit() 関数を 2 回呼び出すことで実現しています。
その後、LPC11xx_SSP0_set_mode() 関数で 2-wire ICSP 用に 4 ビット転送、SPI モード 1 (CPOL = 0、CPHA = 1) に設定しています。
少し広い範囲で見た ICSP 転送の波形写真を下に示します。

上のトレースが PGEDx で、下のトレースが PGECx です。
関数内で SSP0 の 4 ビット転送の完了まで待ってから戻るので、次の転送までのプログラムの処理時間が、そのままギャップとなり、クロックが不連続に見えています。
データの転送効率としては落ちますが、オシロで波形を見る分には、この方が各転送単位を把握しやすくなります。
効率を考えるなら、FIFO は 8 レベルあるので、最大 8 転送分のデータを FIFO に積んで最大 32 クロック分が連続するようにできます。