LPC810M021FN8 (3) -- SCT を PWM として使う (1)

今回は SCT (State Configurable Timer) を PWM として使うための初期設定プログラムを示します。
SCT モジュール自体や、プログラムの詳しい説明は次回以降に回します。
プログラムの開発環境としては、トランジスタ技術 2012 年 10 月号付属の DVD-ROM に収録されている

Keil/ARM MDK-Lite uVision V4.53

に、アドオン、

LPC 32-bit Arm-based Microcontrollers|NXP

を適用して、LPC8xx 用の定義ファイルを追加したものを使いました。
「V4.60 用のアドオン」となっていますが、V4.53 に対しても問題なく LPC8xx 用の定義が追加されます。
さらに、ちょっと「チグハグ」ですが、Keil 側で用意してあるインクルード・ファイル ("LPC8xx.h") は使わず、LPCOpen V2.01

LPC 32-bit Arm-based Microcontrollers|NXP

のインクルード・ファイル ("chip.h" など) を主に使っています。
下に SCT (State Configurable Timer) を PWM として使うための初期設定を行う LPC8xx_pwm_setup() 関数を示します。
16 ビット x 2 のカウンタとして使うモード (UNIFY でない) で、「L」側だけを使い、「H」側は全く使っていません。

// setup SCT (State Configurable Timer) to:
//   "Single Edge PWM mode" for PWM_mode=0
//   "Double Edge PWM mode" for PWM_mode=1
// if "Double Edge PWM mode",
//   actual PWM period = 2*(PWM_peri - 1)
//
void LPC8xx_pwm_setup(LPC_SCT_T *pSCT,    // "LPC_SCT"
                      uint16_t  PWM_peri, // PWM period 
                      int       PWM_mode, // "0" : SE-PWM
                                          // "1" : DE-PWM
                      int       int_en    // interrupt setup enable
                     )
{
  int n;
	
// initailize (bus clock ON and reset)	
  Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_SCT);
  Chip_SYSCTL_PeriphReset(RESET_SCT);
// set SCT config
  pSCT->CONFIG =  ( SCT_CONFIG_16BIT_COUNTER  // not UNIFY
                  | SCT_CONFIG_CLKMODE_BUSCLK // use BUS clock
                  );
// EVENT4 setting for counter_L LIMIT
  pSCT->MATCH[4].L     = (PWM_peri - 1);
  pSCT->MATCHREL[4].L  = (PWM_peri - 1); // set PWM period
  pSCT->EVENT[4].CTRL  = (  4        // MATCHSEL = 4
                         | (1 << 12) // COMBMODE = MATCH only
                         );
  pSCT->EVENT[4].STATE = 1;        // enable EVENT4 at STATE=0
  pSCT->LIMIT_L        = (1 << 4); // EVENT4 for LIMIT
// EVENT_n/CTOUT_n setting
  for (n = 0; n < 4; n++) {
// SETCLR_n = 0x01 (SET/CLR reversed by direction)
// for "Double Edge mode"
    pSCT->OUTPUTDIRCTRL |= (PWM_mode << (2*n)); 
    pSCT->EVENT[n].CTRL  = (  n        // MATCHSEL = n
                           | (1 << 12) // COMBMODE = MATCH only
                           );
    pSCT->EVENT[n].STATE = 1;        // enable EVENT_n at STATE=0
    pSCT->OUT[n].CLR     = (1 << n); // clear CTOUT_n at EVENT_n
    pSCT->MATCH[n].L     = (PWM_peri >> 1);
    pSCT->MATCHREL[n].L  = (PWM_peri >> 1); // initial duty
// output clear setting for "Single Edge mode"
    pSCT->OUT[n].SET     = ((0 == PWM_mode) << 4); // set CTOUT_n at EVENT4
// conflict resolution for "Single Edge mode" (SET_n wins)
    pSCT->RES           |= ((0 == PWM_mode) << (2*n));
  } // for (n = 0; ...
  if (int_en) { // interrupt enable
    pSCT->EVEN = (1 << 4);    // enable EVENT4 interrupt
    NVIC_EnableIRQ(SCT_IRQn); // enable SCT interrupt
  } // if (int_en)
// set SCT control and start timer (HALT=0, STOP=0)
  pSCT->CTRL_L = ( SCT_CTRL_BIDIR_L(PWM_mode) // bidirectional if  "Double Edge mode"
                 | SCT_CTRL_CLRCTR_L     // clear counter
                 | SCT_CTRL_PRE_L(1 - 1) // prescaler = 1/1
                 );
} // void LPC8xx_pwm_setup()

SCT モジュールの 4 つある出力 (CTOUT_0 〜 CTOUT_3) をすべて PWM 用に割り当ててあります。
実際にピンに出力するためには「スイッチ・マトリクス」で指定する必要があります。
たとえば、CTOUT_0 のみ必要な場合には、CTOUT_0 だけ指定すればよく、ピン出力指定されなかった他の出力はチップ内部で信号変化するだけなので、気にする必要はありません。
LPC8xx_pwm_setup() 関数の引数は 4 個で、

void LPC8xx_pwm_setup(LPC_SCT_T *pSCT,    // "LPC_SCT"
                      uint16_t  PWM_peri, // PWM period 
                      int       PWM_mode, // "0" : SE-PWM
                                          // "1" : DE-PWM
                      int       int_en    // interrupt setup enable
                     )

最初の引数には SCT へのポインタ、「LPC_SCT」を与えます。
現状の LPC8xx には SCT は 1 組しかないので、余り意味を持ちませんが、USART0、USART1 のように、モジュールの「インスタンス」が複数ある場合には、ひとつの初期化関数を共通に使えるメリットがあります。
3 番目の引数には「Single Edge PWM」モードに設定する場合「0」、「Double Edge PWM」モードに設定する場合「1」を与えます。
「Single/Double Edge」というのは NXP 社での呼び方で、他社での呼び方と対応させると、

  • PWM 波の片方のエッジの位相関係が不変で、もう一方のエッジの位相だけが変化する PWM 方式は、
Atmel Fast PWM
STMicro Edge-aligned PWM
NXP Single Edge PWM
Edge-aligned PWM (LPC18xx/LPC43xx)

となり、

  • PWM 波の両方のエッジが互いに反対方向へ位相変化する方式は
Atmel Frequency and Phase Correct PWM
STMicro Center-aligned PWM
NXP Double Edge PWM
Center-aligned PWM (LPC18xx/LPC43xx)

となります。
2 番目の引数には PWM 周期を与えます。
これは「Single Edge PWM」(以下「SE-PWM」と略) モードでは、そのまま PWM 1 サイクル分の周期となりますが、「Double Edge PWM」(以下「DE-PWM」と略) モードでは、

実際の PWM 周期 = 2 × (PWM_peri - 1)

となります。
4 番目の引数は割り込み設定のためのフラグで、「0」なら割り込み不使用、「1」なら割り込み使用のための設定をします。
割り込みハンドラは下の例のように作成します。

void SCT_IRQHandler( void )
{
  const uint32_t limit_ev_mask = (1 << 4); // EVENT4 (counter limit) mask
	
  if (limit_ev_mask & LPC_SCT->EVFLAG) { // EVENT4 occurred
    LPC_SCT->EVFLAG = limit_ev_mask; // clear event flag
   ...
   <割り込み処理>
  ...
  } // if (limit_ev_mask & ...
} // void SCT_IRQHandler()

Cortex-M プロセッサでは、割り込みハンドラは一般の void 関数と同じ書き方でよく、特に割り込みハンドラであることを宣言する必要はありません。
ただし、関数名は割り込み要因に対応してあらかじめ決められている名前にしないと割り込みベクタに組み込まれません。
SCT の割り込みの場合には「SCT_IRQHandler()」とする必要があります。
SCT に対する割り込みは 1 本なので、SCT 内のどの割り込み要因によって発生したのかを弁別する必要があります。
SCT の EVENT4 を使って PWM 1 周期分の完了を通知しているので、(1 << 4) のビット・パターンと LPC_SCT->EVFLAG レジスタとの AND を取って判断します。
LPC_SCT->EVFLAG レジスタにビット・パターンを書き戻しているのは割り込みフラグクリアのためです。
上の例で「...<割り込み処理>...」で示してある部分に PWM 1 周期完了時に必要な割り込み処理を記述します。
割り込みを使わず、ポーリングで行う場合でも同様にして、 LPC_SCT->EVFLAG レジスタの読み込み/チェック/書き戻しで PWM 1 周期の完了を判断します。
PWM のデューティーは SCT の MATCHREL[n] (Match Reload) レジスタに設定します。
例えば、CTOUT_0 出力のデューティー設定は、

  LPC_SCT->MATCHREL[0].L = dac_value;

のようにします。
例えば、PWM 周期 256 を指定して初期設定した場合の、MATCHREL レジスタに設定する値と、実際の PWM 波のデューティーとの関係は、

  • Single Edge PWM の場合
レジスタ
設定値
実際の
デューティー
0 1/256
n (n + 1)/256
255 256/256 = 100 [%]
  • Double Edge PWM の場合
レジスタ
設定値
実際の
デューティー
0 0/255 = 0 [%]
n n/255
255 255/255 =100 [%]

となります。
デューティー 0 % は DC (直流) の「L」レベル、デューティー 100 % は DC の「H」レベルです。