LPC810M021FN8 (12) -- リセット・ピンを出力ポートとして使う

今回は「小ネタ」です。
LPC810M021FN8 では、8 ピン DIP の 1 番ピンがリセット端子になっていますが、「スイッチ・マトリクス」機能により IO ポート (P0_5) あるいは内蔵モジュールのいずれかの入出力端子として機能するように切り換えることができます。
もちろん、1 番ピンのファンクションを切り換えてしまえばリセット機能は失われてしまうので、ユーザ・プログラムの実行中に 1 番ピンからリセットをかけることはできなくなります。
それでも「パワーオン・リセット」は有効なので、5 番ピン (P0_1) をグラウンドに落としながら電源を入れると、強制シリアル ISP モードに入ることができ、ISP によるフラッシュ書き換えが可能です。
ここでは、1 番ピンを出力ポートとして使いつつ、リセット入力の機能も残す方法を示します。
出力したい信号について、次のような条件、

  • 変化が高速でない
  • 継続的には出力されない
  • 「アイドル状態」である時間が大部分を占め、「アクティブ状態」である割合が少ない

が満たされる場合にはリセット機能と共存することが可能です。
考え方としては、ポートの信号を変化させる際にリセット機能との切り替えも行い、ポート出力の「アイドル状態」が実はリセット入力状態であるようにすれば実現できます。
単なるポートの上げ下げよりは複雑な処理となるので、高速には変化させられず、また、絶え間なく出力するのには向きません。
この方式に向いている信号の例としては、シンセの場合で言えば、MIDI-to-CV コンバータの「ゲート信号」があげられます。
「ゲート信号」とは何かを簡単に言えば、人間が鍵盤を押している期間に出力される信号と言えますから、当然、高速な変化はせず、「秒」とか「ms」の程度のオーダーの変化になります。
また、演奏していない時には全くゲート信号は出力されません。
リセット機能から見れば、入力が「L」レベルに引っ張られるとリセットがかかり、「H」レベルでは通常の状態 (リセットされない状態を維持) ということになります。
したがって、出力したい信号の「アイドル状態」も「H」レベルでなければならず、つまり「負論理」にする必要があります。
また、リセット機能では、リセット入力端子はプルアップされており、リセット・スイッチ経由でグラウンドに落とす回路は「オープン・ドレイン」と見なすことができます。
これに合わせると、リセット機能と共存する信号出力については、

  • 負論理 (L アクティブ)
  • H レベル側はプルアップ
  • L レベル側はオープン・ドレイン

とすれば、回路的にも短絡などが生じません。
リセット共存ポートへ出力する手続きを具体的に示すと、まず初期化として、

  • P0_5 ポート (1 番ピン) を出力モードに GPIO モジュールで設定
  • P0_5 ポート (1 番ピン) を「プルアップ」、「オープン・ドレイン出力」となるように IOCON モジュールで設定

します。
この時点では GPIO は無効で、リセット機能を失っていないので、外部からリセットがかかります。
内部リセット入力回路のプルアップ、あるいは外付けプルアップにより「H」レベルが維持され、これが信号出力としては「アイドル状態」となります。
信号出力を「アサート」、つまり「L」レベルとする場合には、

  • P0_5 ポートのリセット機能を無効にする (GPIO が有効になる) ように SWM モジュールで設定
  • P0_5 ポートに「L」レベル出力

とします。
ここでリセット機能が無効化されるので、外部から「L」レベルに引っ張ってリセットはかからなくなります。
しかし、出力信号の前提から、この状態の割合は小さいのでリセットが長く続けば、「いつかは」リセットがかかることが期待できます。
信号出力を「ネゲート」、つまり「H」レベルとする場合には、

  • P0_5 ポートに「H」レベル出力
  • 適当なディレイの後に P0_5 ポートにリセット機能を割り当てる (GPIO は無効)

とします。
ここで、リセット機能を復活させるのに「ディレイ」を置いているのは、意図せずにリセットがかかるのを防ぐためです。
オープン・ドレイン出力 + プルアップという構成なので、「H」レベルを出力したつもりでも、実際の端子の電圧の立ち上がりに時間がかかり、まだ「L」レベルのままリセット機能に切り替わった場合には不正なリセットがかかる恐れがあります。
ここで、具体的なプログラムの断片を示すと、まず初期化として、

// Synth GATE OUTPUT on P0_5 (pin1 of DIP8)
#define GATE_BIT 5

void LPC8xx_GPIO_setup( void )
{
// initailize bus clock	
  Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_GPIO);
// set direction to output for Synth GATE output
  Chip_GPIO_SetPinDIROutput(LPC_GPIO_PORT, 0, GATE_BIT);  
} // void LPC8xx_GPIO_setup()

void LPC8xx_mrt_setup( void )
{
  Chip_MRT_Init();
  NVIC_EnableIRQ(MRT_IRQn); // enable MRT interrupt
} // void LPC8xx_mrt_setup()

を実行しておきます。
LPCOpen V2.01 の "chip.h" インクルード・ファイルを使っており、"Chip_ ... " というのは、そのインクルード・ファイル中で定義されているインライン関数です。
その機能は名前の通りで、”Chip_Clock_EnablePeriphClock()" で GPIO モジュールへのバス・クロック供給を開始し、"Chip_GPIO_SetPinDIROutput()" で P0_5 を出力モードに設定しています。
ポートを「H」にする場合のディレイに MRT (Multi-Rate Timer) を使っているので、LPC8xx_mrt_setup() 関数で MRT モジュールの初期化および割り込みイネーブルを行っています。
IO ポートのモード設定は IOCON モジュールで次のように行います。

void LPC8xx_pin_setup( void )
{
  Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_IOCON);
// Synth GATE OUTPUT
  LPC_IOCON->PIO0[IOCON_PIO5] 
     = (  PIN_OD_MASK                         // open-drain
       | (PIN_MODE_PULLUP << PIN_MODE_BITNUM) // pull-up
       ); 
} // void LPC8xx_pin_setup()

P0_5 ポートへの「L」レベル出力 (シンセへの GATE ON) は次のようになります。

void GATE_ON( void )
{
  Chip_SWM_DisableFixedPin(SWM_FIXED_RST);
  Chip_GPIO_SetPinOutLow(LPC_GPIO_PORT, 0, GATE_BIT);
} // void GATE_ON()

”Chip_SWM_DisableFixedPin()" が固定ピンに割り当てられている機能の無効化をする関数で、これはインライン関数ではなく、"swm_8xx.c" ファイルの中に記述があります。
P0_5 ポートへの「H」レベル出力 (シンセへの GATE OFF) は次のようになります。

LPC_MRT_CH_T *mrt_ch_p; // selected MRT ch reg pointer

// clock count to swith REST/GATE_OUT function
#define RST_SW_DELAY (300)  // 10 us = 300 clk @ 30 MHz

void GATE_OFF( void )
{
  uint32_t mrt_n; // instance number of Multi-Rate Timer 
  
  Chip_GPIO_SetPinOutHigh (LPC_GPIO_PORT, 0, GATE_BIT);
  mrt_n            = (LPC_MRT->IDLE_CH >> 4); // get free MRT ch
  mrt_ch_p         =  Chip_MRT_GetRegPtr(mrt_n);
  mrt_ch_p->CTRL   = (MRT_MODE_ONESHOT | MRT_CTRL_INTEN_MASK);
  mrt_ch_p->INTVAL =  RST_SW_DELAY; // interval count to reset enabled
} // void GATE_OFF()

GATE_OFF() 関数の後半部分が MRT の設定で、CPU クロックが 30 MHz の場合に 10 μs のディレイになるようにしています。
最後に MRT 割り込みハンドラを示します。 この中でリセット機能の復活を行っています。

// Multi-Rate Timer IRQ Handler
void MRT_IRQHandler( void )
{
// check for selected MRT ch interrupt occurred 
  if (mrt_ch_p && Chip_MRT_IntPending(mrt_ch_p)) { 
    Chip_MRT_IntClear(mrt_ch_p); // clear interrupt flag    
    Chip_SWM_EnableFixedPin(SWM_FIXED_RST); // enable RESET
  } // if (mrt_ch_p &&  ...
} // void MRT_IRQHandler()