LPC810メモ:SCTの例その2(音を鳴らすなど)

SCTの機能をいろいろ試した例。

前提とする環境はこちら→LPC810メモ 共通の準備
SCT全般のメモはこちら→LPC810メモ:SCTimer/PWM (SCT)について

試してみたかった機能は以下。

  • 16bitのタイマ2本(L/H)に分けて使う
  • bidirectinalモード(上限に達したらカウントダウンして、0とLIMITを往復するやつ
  • 入力pinの状態に応じたevent発生
  • 割り込み
  • stateの変更

実現した内容は以下。

  • 救急車風の「ピーポーピーポー」という音を鳴らす。
  • ボタンを押すと音が止まる、もう一度押すと再開

仕組みとしては以下。なお、stateは実質0,1しか使えないようだが、タイマL,Hそれぞれについて0,1が使える模様。2x2で4状態と言うこともできる気がする。

  • タイマL
    • match0で決まる周波数に応じて音を鳴らす(4番pinが出力、圧電スピーカをつなぐ)
    • ボタンを押されると、state0,1を切り替える
    • 音は、state0の時しか鳴らない
  • タイマH
    • match0で決まる間隔(0.5秒)毎に、state0,1を切り替える。
    • stateが切り替わる際に、タイマLのmatch0の値を書き換えて、周波数を変更。
      • これにより、「ピー」と「ポー」を入れ替える。

サンプルコード

#include "LPC8xx.h"

int main(void) {
    //  SWMのclockを有効化
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);

    //  5番pin(PIO_1)にSCT入力の0番(CTIN_0) を設定
    //  UM10601 9.5.6 Pin assign register 5
    LPC_SWM->PINASSIGN5 = 0x01ffffffUL;

    //  4番pin(PIO_2)にSCT出力の0番(CTOUT_0) を設定
    //  UM10601 9.5.7 Pin assign register 6
    LPC_SWM->PINASSIGN6 = 0x02ffffffUL;

    // RESET 以外の無効化
    LPC_SWM->PINENABLE0 = 0xffffffbfUL;

    // UM10601 10.3 Basic configuration
    // に従った準備
    //  SCTのclockを有効化(UM10601 4.6.13 System clock control register)
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<8);

    // Clear the SCT peripheral reset using the PRESETCTRL register (Table 19).
    LPC_SYSCON->PRESETCTRL &= ~(0x1<<8);
    LPC_SYSCON->PRESETCTRL |= (0x1<<8);

    // 今回は割り込みを使うので以下を設定
    // UM10601 3.4.1 Interrupt Set Enable Register 0 register
    NVIC->ISER[0] = (0x1 << 9);

    // 以下、SCTの設定
    // UM10601 10.6.1 SCT configuration register
    LPC_SCT->CONFIG =
        (0 << 0 )  // UNIFY = 0 -> 16bitのタイマ2本(L,H)で利用する
        | (1 << 17)  // AUTOLIMIT_L = 1 -> matchの0番(後で定義)を、カウントアップの上限とする
        | (1 << 18);  // AUTOLIMIT_H = 1 -> matchの0番(後で定義)を、カウントアップの上限とする

    // 以下、タイマL関連の設定
    LPC_SCT->CTRL_L  |=
           (1 << 4)           // BIDIR_L = 1 ->bidirectional mode
        |  (60 - 1 ) << 5; // PRE_L = 60 -> 60クロックで1カウント。bidirectionalなので、100KHz。

    //  カウンタにmatchする値を定義
    // UM10601 10.6.20 SCT match reload registers 0 to 4 (REGMODEn bit = 0)
    LPC_SCT->MATCHREL[0].L = (uint16_t)(100000 / 494); // 494Hz(ピーポーの「ピー」)
    LPC_SCT->MATCHREL[1].L = 0;

    // event0番(match 0、LIMITに達した時に発生)
    // 10.6.22 SCT event state mask registers 0 to 5
    // 10.6.23 SCT event control registers 0 to 5
    LPC_SCT->EVENT[0].STATE = 0x01; // state0で発生
    LPC_SCT->EVENT[0].CTRL =
        (0 << 0)    // MATCHSEL = 0 -> match0に対応
        |  (0 << 4)    // HEVENT = 0 -> タイマLに対応
        |  (1 << 12);  // COMBMODE = 1 -> match だけに対応

    // event1番(match 1、カウンタが0にもどって発生)
    LPC_SCT->EVENT[1].STATE = 0x01; // state0で発生
    LPC_SCT->EVENT[1].CTRL =
        (1 << 0)    // MATCHSEL = 1 -> match1に対応
        |  (0 << 4)    // HEVENT = 0 -> タイマLに対応
        |  (1 << 12);  // COMBMODE = 1 -> match だけに対応

    // event2番(state0時のin1の立下り、state1に遷移)
    LPC_SCT->EVENT[2].STATE = 0x01; // state0で発生
    LPC_SCT->EVENT[2].CTRL =
        (0 << 4)    // HEVENT = 0 -> タイマLに対応
        |  (0 << 6)    // IOSEL 入力1に対応
        |  (2 << 10)   // IOCOND fallに対応
        |  (2 << 12)  // COMBMODE = 2 -> I/O condition だけに対応
        |  (1 << 14)   // STATELD = 1 -> stateをSTATEVに設定
        |  (1 << 15);   // STATEV = 1 -> state を 1に変更。

    // event3番(state1時のin1の立下り、state0に遷移)
    LPC_SCT->EVENT[3].STATE = 0x02; // state1で発生
    LPC_SCT->EVENT[3].CTRL =
        (0 << 6)    // IOSEL 入力1に対応
        |  (2 << 10)   // IOCOND fallに対応
        |  (2 << 12)   // COMBMODE = 2 -> I/O condition だけに対応
        |  (1 << 14)   // STATELD = 1 -> stateをSTATEVに設定
        |  (0 << 15);  // STATEV = 0 -> state を 0に変更。

    // event2番でstop
    LPC_SCT->STOP_L = 1 << 2;

    // event3番でstart
    LPC_SCT->START_L = 1 << 3;

    // 出力の定義
    // UM10601 10.6.24 SCT output set registers 0 to 3
    // UM10601 10.6.25 SCT output clear registers 0 to 3
    // 出力0番
    LPC_SCT->OUT[0].SET = (1 << 0); // event 0 でset
    LPC_SCT->OUT[0].CLR = (1 << 1); // event 1 でclear

    // タイマL関連の設定、ここまで。

    // 以下、タイマH関連の設定
    LPC_SCT->CTRL_H |=
        (0 << 4)         // BIDIR_L = 0 -> こちらはbidirectional modeにしない
        |  (240 - 1 ) << 5; // PRE_L = 240 -> 240クロックで1カウント、50KHz相当。
    
    // match0の設定
    LPC_SCT->MATCHREL[0].H = 50000 / 2; // 2Hz

    // event4(state0でmatch 0に達した時に発生、割り込みを起こしてstate1に遷移)
    LPC_SCT->EVENT[4].STATE = 0x01; // state0で発生
    LPC_SCT->EVENT[4].CTRL =
        (0 << 0)    // MATCHSEL = 0 -> match0に対応
        |  (1 << 4)    // HEVENT = 1 -> タイマHに対応
        |  (1 << 12)   // COMBMODE = 1 -> match だけに対応
        |  (1 << 14)   // STATELD = 1 -> stateをSTATEVに設定
        |  (1 << 15);  // STATEV = 1 -> state を 1に変更。

    // event4(state1でmatch 0に達した時に発生、割り込みを起こしてstate0に遷移)
    LPC_SCT->EVENT[5].STATE = 0x02; // state1で発生
    LPC_SCT->EVENT[5].CTRL =
        (0 << 0)    // MATCHSEL = 0 -> match0に対応
        |  (1 << 4)    // HEVENT = 1 -> タイマHに対応
        |  (1 << 12)   // COMBMODE = 1 -> match だけに対応
        |  (1 << 14)   // STATELD = 1 -> stateをSTATEVに設定
        |  (0 << 15);  // STATEV = 0 -> state を 0に変更。

    // event4,5発生時には割り込みを発生させる。
    // 10.6.14 SCT flag enable register
    LPC_SCT->EVEN = 1 << 4 | 1 << 5;

    // 以上で設定完了、haltを解除してstart
    /// UM10601 10.6.2 SCT control register
    LPC_SCT->CTRL_L &= ~(1 << 2);  // HALT_L = 0
    LPC_SCT->CTRL_H &= ~(1 << 2);  // HALT_H= 0

    return 0 ;
}

extern "C" {
    // SCT割り込み用のハンドラを定義
    // 関数名は自動生成されたcr_startup_lpc8xx.cの中で定義されているもの
    void SCT_IRQHandler(void) {
        // どのイベントに対応する割り込みかで分岐
        // 10.6.15 SCT event flag register
        if(LPC_SCT->EVFLAG & (1 << 4)) {
            // タイマLのmatchを修正
            LPC_SCT->MATCHREL[0].L = (uint16_t)(100000 / 494); // 494Hz(ピーポーの「ピー」)

            // イベントを処理した旨を設定
            LPC_SCT->EVFLAG = 1 << 4;

        } else if(LPC_SCT->EVFLAG & (1 << 5)) {
            // タイマLのmatchを修正
            LPC_SCT->MATCHREL[0].L = (uint16_t)(100000 / 392);  // 392Hz(ピーポーの「ポー」)

            // イベントを処理した旨を設定
            LPC_SCT->EVFLAG = 1 << 5;

        }

        return;
    }
}

接続

補足/雑感

  • イベント2,3でタイマを止めているが、考えてみると不要か。event0,1はstate1では発生しないため、どうせ音はならない。
  • これの前にbidirectionalモードを32bitタイマ(UNIFY = 1)で動かそうとして失敗。なぜだかは理解できてない。
  • スイッチがプルアップして、押すとLOWになる方向なのは、5番を電源on時にLOWにしたくないから。
  • AUTOLIMITを、L,Hそれぞれ設定する必要があることに気づかず、ちょっとハマった。
  • 知識不足故に用途は良くわからないが、SCTの使い方自体はだいたいわかった気がする。
  • ただしcaptureっていうのは使ってみたい。
  • 割り込みを使ってみて思ったのが、なんとなく、SCTで割り込みを使ったら負けな気がした。

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