麺類はお好きですか。 本格的な生めんもカップ麺も、麺類の調理に欠かせないのがタイマです。 この作品は、部品点数を極力減らしたラーメンタイマです。
このアプリケーションの回路図は、以下のようになっています。 押しボタン一個の操作で6個のLEDと圧電スピーカを駆動しています。
このアプリケーションでは、以下の部品を使用しています。
| 部品番号 | 品名 | 規格 | 個数 | メーカ | 調達先 |
|---|---|---|---|---|---|
| U1 | マイコン | MC9RS08KA2CPC | 1 | Freescale | digi-key |
| - | ICソケット | 8P DIP | 1 | - | 秋月電子 |
| R1 | カーボン抵抗 | 2.2kΩ 1/4W | 1 | KOA | 秋月電子 |
| C1,C2 | 積層セラミックコンデンサ | 0.1µF 50V | 2 | ムラタ製作所 | 秋月電子 |
| C3 | アルミ電解コンデンサ | 47µF 25V | 1 | Ruby-con | マルツパーツ |
| LED0,LED1 | 赤色発光ダイオード | OSDR5113A | 2 | OptoSupply | 秋月電子 |
| LED2,LED3 | 黄色発光ダイオード | OSNG5113A | 2 | OptoSupply | 秋月電子 |
| LED4,LED5 | 緑色発光ダイオード | OSYL5113A | 2 | OptoSupply | 秋月電子 |
| SW1 | 押しボタンスイッチ | DP1-120 | 1 | フジソク | 梅澤無線 |
| BAT1 | ボタン電池ホルダ | CH25-2032 | 1 | SHOGYO | 秋月電子 |
| - | ボタン電池 | CR2032 | 1 | GP | 秋月電子 |
| PS1 | 圧電スピーカ | PS1240 | 1 | TDK | digi-key |
| HD1 | ボックス・ピン・ヘッダ | 2×3 | 1 | - | ダイセン電子 |
| - | 片面ユニバーサル基板 | 72mm×48mm | 1 | - | 秋月電子 |
| - | ワッシャつきなべビス | 3mm×10mm | 4 | - | 廣杉計器 |
| - | スペーサ | M3 10mm | 4 | - | 廣杉計器 |
このアプリケーションでは、LEDに電流制限抵抗を入れていません。 これは、MC9RS08KA2の出力電流が多くないという事実を利用したものです。 下のグラフは、流れる電流に対する"H"出力ポートと"L"出力ポートの 電位差(VOH-VOL)とLEDの順方向電圧(VF)をモデル化したものです。 これら二つのグラフの交点でLEDが点灯します。
設計では、電源電圧3Vのときに、 "H"出力ポートと"L"出力ポートで0.5Vずつ電圧降下が起き、 それによって出力電流は約5mAに制限されます。 これは、電源電圧が3Vであったときに限った計算です。 したがって、この回路に5V電源を与えると マイコンとLEDに大電流が流れてしまい破壊する可能性がありますのでご注意ください。
このアプリケーションのソフトウェアについて解説します。
このアプリケーションは、押しボタン一個で操作します。
最初にタイマの時間を設定します。 押しボタンを押すと2分から7分の間でLEDの表示が移動していきます。 好みの時間の位置でLEDを点灯させてから、押しボタンを2秒間長押しすると、 タイマの時間測定が始まります。
設定した時間になったら音楽が鳴り始めます。 押しボタンを押すと音楽が止まります。
このアプリケーションの状態遷移図は、このようになっています。
それぞれの状態は、赤、緑、青の三色に分けられおり、以下のような意味づけがされています。
赤色の状態は、マイコンがキーボード割り込み(KeyBoard Interrupt: KBI)を 待ちながら、低消費電力モードにいることを示します。 この状態へは、タイマの操作が行われなくなってから、 おおむね5分で遷移します。 この状態での消費電力は、可能な限り削減され、 約1µAの消費電流を実現しています。
緑色の状態は、マイコンがリアルタイム割り込み(Real-Time Interrupt: RTI)により 8ミリ秒ごとの周期的な処理を行っていることを示しています。 この状態は、マイコンが人間からの働きかけを待っている状態で、 比較的低消費電力になっています。
青色の状態は、マイコンがモジュロ・タイマ(Modulo Timer: MTIM)の割り込みにより おおむね300マイクロ秒ごとの周期的な処理を行っていることを示しています。 この状態は、マイコンがタイマ設定時間を数えたり、 圧電スピーカから音を出したりしながら、 頻繁に処理を行っている状態です。
このアプリケーションのプログラムは、以下のようになっています。
//******************************************************************* //* //* NoodleTimer.c : //* //* Copyright 2007 (C) noritan.org //* //******************************************************************* #include <hidef.h> /* for EnableInterrupts macro */ #include "derivative.h" /* include peripheral declarations */
#include 文は、 CodeWarrior が作成したままの物を使用しています。
ここから、デバッグ時のレジスタの初期値が定義されます。
#ifdef DEBUG //============================================================== //= Debug Configuration. //============================================================== //============================================================== //= Initialization value for SPMSC1 register. //= //= In DEBUG case, LVD cause RESET in RUN and STOP. //============================================================== const byte SPMSC1_INIT = 0b01011100; // |||||||+-- BGBE=0 (no bandgap) // ||||||+--- Reserved // |||||+---- LVDE=1 (LVD enable) // ||||+----- LVDSE=1 (LVD enable in STOP) // |||+------ LVDRE=1 (Reset by LVD enable) // ||+------- LVDIE=0 (INT by LVD disable) // |+-------- LVDACK=1 (Clear LVDF) // +--------- LVDF=0 (N/A) //============================================================== //============================================================== // Initialization value for SOPT register. // // In DEBUG case, STOP instruction and BKGD pin are enabled. //============================================================== const byte SOPT_INIT = 0b00100010; // |||||||+-- RSTPE=0 (no RESET*) // ||||||+--- BKGDPE=1 (BKGD enable) // |||+++---- Reserved // ||+------- STOPE=1 (STOP available) // |+-------- COPT=0 (COP rate) // +--------- COPE=0 (no COP) //==============================================================
| レジスタビット | 設定 | 詳細 |
|---|---|---|
| BGBE | 0 | バンドギャップ電圧は使用しません。 |
| LVDE | 1 | LVD機能を使います。 |
| LVDSE | 1 | STOP状態でもLVD機能を使います。 |
| LVDRE | 1 | LVD機能でリセットを発生させます。 |
| LVDIE | 0 | LVD機能では割り込みを発生させません。 |
| LVDACK | 1 | もし、LVDFがセットされていたら、クリアします。 |
| RSTPE | 0 | RESET*端子をリセット機能に使用しません。 |
| BKGDPE | 1 | BKGD端子をBDM機能に使用します。 |
| STOPE | 1 | STOP命令が実行できます。 |
| COPT | 0 | COP機能を使用しません。 |
| COPE | 0 |
ここでは、デバッグ時に使用するレジスタの初期値を決定しています。 この中には、リセット後に一回だけ書き込むことが出来るレジスタも含まれているので、 よく考えて設定する必要があります。
続いて、リリース時のレジスタの初期値が定義されます。
#else // !DEBUG //============================================================== //= Release Configuration. //============================================================== //============================================================== //= Initialization value for SPMSC1 register. //= //= In RELEASE case, LVD cause RESET in RUN. //============================================================== const byte SPMSC1_INIT = 0b01010100; // |||||||+-- BGBE=0 (no bandgap) // ||||||+--- Reserved // |||||+---- LVDE=1 (LVD enable) // ||||+----- LVDSE=0 (LVD disable in STOP) // |||+------ LVDRE=1 (Reset by LVD enable) // ||+------- LVDIE=0 (INT by LVD disable) // |+-------- LVDACK=1 (Clear LVDF) // +--------- LVDF=0 (N/A) //============================================================== //============================================================== // Initialization value for SOPT register. // // In RELEASE case, STOP instruction is enabled. //============================================================== const byte SOPT_INIT = 0b00100000; // |||||||+-- RSTPE=0 (no RESET*) // ||||||+--- BKGDPE=0 (BKGD disable) // |||+++---- Reserved // ||+------- STOPE=1 (STOP available) // |+-------- COPT=0 (COP rate) // +--------- COPE=0 (no COP) //============================================================== #endif // !DEBUG
| レジスタビット | 設定 | 詳細 |
|---|---|---|
| BGBE | 0 | バンドギャップ電圧は使用しません。 |
| LVDE | 1 | LVD機能を使います。 |
| LVDSE | 0 | STOP状態ではLVD機能を使いません。 |
| LVDRE | 1 | LVD機能でリセットを発生させます。 |
| LVDIE | 0 | LVD機能では割り込みを発生させません。 |
| LVDACK | 1 | もし、LVDFがセットされていたら、クリアします。 |
| RSTPE | 0 | RESET*端子をリセット機能に使用しません。 |
| BKGDPE | 0 | BKGD端子を汎用出力として使用します。 |
| STOPE | 1 | STOP命令が実行できます。 |
| COPT | 0 | COP機能を使用しません。 |
| COPE | 0 |
リリース時には、消費電力を抑制する目的で、STOP状態の時にはLow-Voltage Detect (LVD) が 動作しないように設定します。
また、リリース時には、Background Debug Mode (BDM)を使用しません。
//==============================================================
// STOP instruction.
//==============================================================
#pragma INLINE
void stop(void) {
__asm("STOP");
}
//==============================================================
// WAIT instruction.
//==============================================================
#pragma INLINE
void wait(void) {
__asm("WAIT");
}
ここでは、 STOP 命令と WAIT 命令をプログラム内に埋め込む関数を定義しています。 これらの関数には、 "#pragma INLINE" 文が付加されているので、 実際には、他の関数の中で展開されたうえでコンパイルされます。
//============================================================== // Port Declaration // // Beeper is assigned to PTA0 // Start button is assigned to PTA2 //============================================================== #define BEEP PTAD_PTAD0 #define BEEP_MASK PTAD_PTAD0_MASK #define START PTAD_PTAD2 // Start is assigned to PTA2.
このアプリケーションでは、PTA0に圧電スピーカを PTA2に押しボタンスイッチを接続しています。 以下のプログラムをできるだけ抽象化できるように、ここでマクロの宣言を行っています。
//==============================================================
// Flag Declaration
//
// blank : indicates LEDs are put off.
// quiet : indicates BEEPER does not work.
//==============================================================
typedef struct {
byte blank:1; // BLANK flag.
byte quiet:1; // QUIET flag.
} Flag;
このアプリケーションでは、 "flag"という変数をビット操作可能なフラグの集合として使用しています。 ここでは、この変数の中のフラグの構成を宣言しています。
| フラグ名称 | 用途 |
|---|---|
| blank | このフラグは、LED表示装置が消灯されているかどうかを示します。 LED表示プログラムは、このフラグの値を調べて、表示装置に送るデータを切り替えます。 |
| quiet | このフラグは、音楽を演奏するときに音を停止させます。 発音プログラムは、このフラグを使って、音と音の間に停止時間を作ります。 |
//============================================================== // RTI initialization. // RTI period: 8msec // const byte SRTISC_INIT = 0b01000001; // |||||+++-- RTIS=001 (8msec period) // ||||+----- Reserved // |||+------ RTIE=0 (no interrupt) // ||+------- RTICLKS=0 (internal 1kHz) // |+-------- RTIACK=1 (clear RTIF) // +--------- RTIF=0 (no effect) const byte SRTISC_DISABLE = 0b01100000; // |||||+++-- RTIS=000 (disable) // ||||+----- Reserved // |||+------ RTIE=0 (no interrupt) // ||+------- RTICLKS=1 (trimmed 1kHz) // |+-------- RTIACK=1 (clear RTIF) // +--------- RTIF=0 (no effect) //==============================================================
| レジスタビット | 設定 | 詳細 | |
|---|---|---|---|
| INIT | DISABLE | ||
| RTIS | 001 | 000 | 使用時のみ8ミリ秒ごとにRTIイベントを発生させます。 |
| RTIE | 0 | 0 | RTIイベントが発生しても割り込みを発生させません。 割り込みが必要な場合には、別途RTIEフラグをセットします。 |
| RTICLKS | 0 | 1 | RTIのクロック源として1kHzの内部発振器を使います。 このクロック源は、低消費電力ですが精度の低いクロックなので、 時間測定には使用していません。 非使用時には、消費電力を減らすため、内部発振器を止めます。 |
| RTIACK | 1 | 1 | もし、RITFがセットされていたら、クリアします。 |
ここでは、 RTI モジュールの設定レジスタの初期値を定義しています。 このアプリケーションでは、 Real-Time Interrupt (RTI) モジュールを使用して、 8ミリ秒ごとに割り込みを発生させています。
また、RTIモジュールを使用しないときには、 消費電力を抑えるため、RTI機能と内部発振器を止めてしまいます。
//============================================================== // MTIM initialization // MTIM clock: Freq=250kHz, Period=4usec // const byte MTIMCLK_INIT = 0b00000100; // ||||++++-- PS=0100 (x16 prescale) // ||++------ CLKS=00 (BUSCLK) // ++-------- Reserved // // // MTIMMOD as key-response: 334usec (1 / (2 * 1.497kHz)) const byte MTIMMOD_3000 = 83-1; // 250kHz/3000Hz = 83 // // MTIMMOD as accurate timer: 1msec (1 / 1000Hz) const byte MTIMMOD_1000 = 250-1; // 250kHz/ 250 = 1000Hz // // MTIMMOD to play MUSIC: based on 250kHz const byte P_H2 = 253-1; const byte P_C3 = 239-1; const byte P_C3s = 225-1; const byte P_D3 = 213-1; const byte P_D3s = 201-1; const byte P_E3 = 190-1; const byte P_F3 = 179-1; const byte P_F3s = 169-1; const byte P_G3 = 159-1; const byte P_G3s = 150-1; const byte P_A3 = 142-1; const byte P_A3s = 134-1; const byte P_H3 = 127-1; const byte P_C4 = 119-1; const byte P_C4s = 113-1; const byte P_D4 = 106-1; const byte P_D4s = 100-1; const byte P_E4 = 95-1; const byte P_F4s = 84-1; const byte P_G4s = 75-1; const byte P_A4 = 71-1; const byte P_H4 = 63-1; const byte P_C5 = 60-1; const byte P_C5s = 56-1; // //==============================================================
| レジスタビット | 設定 | 詳細 |
|---|---|---|
| PS | 0100 | 1/16プリスケーラを使用します。 |
| CLKS | 00 | MTIMのクロック源としてバスクロックを使用します。 このアプリケーションでは、バスクロックは4MHzです。 |
このアプリケーションでは、Modulo TIMer (MTIM) モジュールを使用して、 圧電スピーカからの発音、時間測定、およびチャタリング・キャンセルに 使用する周期的割り込みをさせています。 割り込み周期は、目的によって異なるので、その都度設定しなおしています。
プリフィックス"P_"の付いた定数は、音階を表現するときに使用します。 これらの定数を使って、楽譜を表現します。
//============================================================== // Constant parameters // // LED_COUNT : unit 1 // Number of LEDs // DEBOUNCE_PERIOD : unit 334usec // Time period to recognize button released. // 160*(334usec) = 53.4msec // BEEP_PERIOD : unit 334usec // Length of a BEEP in PRESSED & VALIDATED state. // 240*(334usec) = 80msec // IDLE_PERIOD : unit 8 msec // Idling time to enter STOP state. // 146*256*(8msec) = 299sec = 5min // 29*256*(8msec) = 59sec = 1min (QUICK mode) // PRESSED_PERIOD : unit 334usec // Time to recognize START button pressed long. // 19*256*(334usec) = 1.62sec // QSECOND_PERIOD : unit 1msec // Time period of 1/4 second. // 250*(1msec) = 250msec // MINUTE_PERIOD : unit 250msec // 240*(250msec) = 60sec = 1min // 40*(250msec) = 10sec = 1/6min (QUICK mode) //============================================================== const byte LED_COUNT = 6; // Number of LEDs. const byte DEBOUNCE_PERIOD = 160; // Period of debounce. const byte BEEP_PERIOD = 240; // Period of beep. #ifdef QUICK const word IDLE_PERIOD = 29*256; // Idling in QUICK. #else const word IDLE_PERIOD = 146*256; // Period of idling. #endif // QUICK const word PRESSED_PERIOD = 19*256; // Pressed long time. const word QSECOND_PERIOD = 250; // 1/4 second. #ifdef QUICK const byte MINUTE_PERIOD = 40; // 1/6 minute in QUICK. #else const byte MINUTE_PERIOD = 240; // 1 minute. #endif // QUICK
| 定数名 | 設定 | 詳細 |
|---|---|---|
| LED_COUNT | 6 | 装備しているLEDの個数をあらわします。 |
| DEBOUNCE_PERIOD | 160 | チャッタリング防止のための猶予時間を指定します。 単位は、334マイクロ秒です。 この時間が短かすぎるとチャッタリングを除去することができません。 また長すぎるとボタンを押してからの反応時間が長くなります。 |
| BEEP_PERIOD | 240 | ボタンを操作した時に鳴らす確認音の長さを指定します。 単位は、334マイクロ秒です。 |
| IDLE_PERIOD | 146*256(通常時) | 操作をしなくなってから、STOP状態に入るまでの待ち時間を指定します。 単位は、8ミリ秒です。 通常の待ち時間は数分程度に設定されます。 ところが、待ち時間が長くなると、デバッグが難しくなります。 このため、短縮動作時には早くSTOP状態に入るように切り替えられるようになっています。 |
| 29*256(短縮動作時) | ||
| PRESSED_PERIOD | 19*256 | 押しボタンの長押しを判定するための時間を指定します。 単位は、334マイクロ秒です。 このアプリケーションでは、ボタンの長押しを検出して動作を切り替えます。 ボタンの長押し時間には個人差があるので、微調整が必要です。 |
| QSECOND_PERIOD | 250 | 1/4秒の長さを指定します。 単位は、1ミリ秒です。 この定数は、時間を測定する際の基準となるため、時間の精度が必要です。 このアプリケーションでは、1ミリ秒ごとに割り込みを発生させ、 ソフトウェアカウンタを使って、時間の計測を行っています。 時間待ち状態では、1/4秒ごとにイベントを発生させます。 |
| MINUTE_PERIOD | 240(通常時) | 1分の長さを指定します。 単位は、1/4秒です。 この定数は、時間待ちの基準時間を示します。。 この定数もデバッグを容易にするため、 短縮動作時には早く時間を進められるように切り替えられるようになっています。 |
| 40(短縮動作時) |
ここでは、プログラムで使用する各種定数を定義しています。 一部の定数は、環境変数"QUICK"の定義の有無によって設定が変更されます。
このプログラムのステートマシンでは、 状態コードを数え上げ型(enumeration)で定義して使用しています。
//==============================================================
// State code declaration
//
// enum State indicates the state codes of the state
// machine.
//==============================================================
typedef enum {
ST_PROLOGUE = 0, // PROLOGUE
ST_WAIT = 1, // WAIT
ST_STOP = 2, // STOP
ST_PRESSED = 3, // PRESSED
ST_VALIDATED = 4, // VALIDATED
ST_ACTIVE = 5, // ACTIVE
ST_EPILOGUE = 6 // EPILOGUE
} State;
いずれの状態も先の状態遷移図で紹介した状態です。
数え上げ型は、コンパイラの初期状態では16ビットの数値として表現されるので、 RS08でプログラムを記述するときには無駄なコードが生成されます。 これを8ビットの数値で表現させるためには、 コンパイラのオプションで"enum"のサイズを8ビットに指定する必要があります。
もし、オプションを変更したくないのであれば、"enum"を単なる定数宣言として使用し、 状態コード変数に8ビットの"byte"を使用する方法もあります。
ここでは、楽譜を表現するための構造体を定義しています。
//==============================================================
// Music data structure
//
// Music is a list of Phrase pointer.
// Phrase is a list of Sono.
//==============================================================
typedef struct {
byte length; // Length of a SONO
byte period; // Period for MTIM
} Sono;
typedef const Sono * __paged SonoP;
typedef SonoP Phrase;
typedef const Phrase * __paged PhraseP;
typedef PhraseP Music;
RS08は、原則として64バイト以上の長さの配列を扱うことが出来ません。 そのため、楽譜などの長い配列をそのまま表現する時には工夫が必要です。 このプログラムでは、楽譜を三層構造の多重配列(配列へのポインタの配列)として表現しています。
まず、音階(period)と長さ(count)からなる、Sonoという構造体が宣言されています。 この構造体で楽譜の音符ひとつ分が表現できます。 Phraseは、Sono構造体へのポインタとして宣言されています。 実際には、Sono構造体の配列を指して使用します。 この時に使用されるポインタには、__paged指示子が付いているため、 Sono構造体の配列は、64バイト境界をまたがない所に配置しなくてはなりません。
Musicは、Phrase構造体へのポインタとして宣言されています。 これもPhrase配列を指して使用されます。 この時に使用されるポインタにも、__paged指示子が付いているため、 Phrase構造体の配列は、64バイト境界をまたがない所に配置しなくてはなりません。
Musicは、ポインタの配列であるため、 ひとつのPhraseをMusicの中に何度も使うことによって、 繰り返しの表現をすることもできます。
RS08の大域変数は、TINYアドレッシング領域($0000-$000F)に配置されるものと それ以外のDIRECTアドレッシング領域($0020-$00BF)に配置されるものに大別されます。 一切の宣言をせずに、全ての変数をDIRECT領域に配置することも可能ですが、 このプログラムではコード効率と処理速度の向上を狙って、 厳密に二つの領域を使い分けています。
TINY領域($0000-$000F)には、14バイト($0000-$000D)のRAMが存在しますが、 コンパイラはこのうち5バイト($0000-$0004)も作業領域に使用します。 そのため、ユーザが自由に使えるRAM領域は9バイト($0005-$000D)だけです。
//************************************************************** // Variable section // // Some variables are assigned to TINY area. //************************************************************** #pragma DATA_SEG TINY TINY_RAM_VARS byte led_index; // index for LED. byte debounce_count; // timer for debounce. word time_count; // Event timer counter. byte qsecond_count; // Quarter second counter. SonoP sono_point; // Pointer to SONOs. PhraseP phrase_point; // Phrase to be played. #pragma DATA_SEG DIRECT DIRECT_RAM_VARS byte minute_count; // Minute counter. byte minute_target; // Minute mode to be set. Flag flag; // user defined flag. State state; // State code. byte sono_count; // Sono counter. Music music; // Music to be played. #pragma DATA_SEG FAR DEFAULT
| 変数名 | 型 | 詳細 |
|---|---|---|
| led_index | byte | 点灯させるLEDの位置を記憶します。 |
| debounce_count | byte | 押しボタンスイッチのグリッチを取り除くためのタイマカウンタです。 |
| time_count | word | 時間を計測するための汎用ソフトウェア・タイマ・カウンタです。 |
| qsecond_count | byte | 待ち時間を計測するための1/4秒ごとに動作するソフトウェア・カウンタです。 |
| sono_point | SonoP | 音楽演奏中に音符を指し示すポインタ変数です。 |
| phrase_point | PhraseP | 音楽演奏中にPhrase構造体を指し示すポインタ変数です。 |
| minute_count | byte | 待ち時間を計測するための1分ごとに動作するソフトウェア・カウンタです。 |
| minute_target | byte | 待ち時間として設定された分単位の時間を記憶します。 |
| flag | Flag | 汎用ソフトウェア・フラグ群です。 |
| state | State | ステートマシンで使用される状態コードです。 |
| sono_count | byte | 音楽演奏中に使用される発音の残り時間を計測するソフトウェア・カウンタです。 |
| music | Music | 演奏中の楽譜を記憶します。 |
楽譜は、定数配列で宣言します。 今回準備したのは、3曲です。 一曲目は、不朽の名作、チャルメラ。 これは、単一のPhraseで出来ています。
#pragma CONST_SEG PAGED PAGED_ROM
//==============================================================
// Charumera
//==============================================================
const Sono phrase_noodle1[] = {
2*12, P_C4,
2*12, P_D4,
6*12, P_E4,
2*12, P_D4,
4*12, P_C4,
2*12, P_C4,
2*12, P_D4,
2*12, P_E4,
2*12, P_D4,
2*12, P_C4,
16*12, P_D4,
8*12, 0,
0, 0
};
const Phrase music_noodle[] = {
phrase_noodle1,
(Phrase)0
};
二曲目は、ムソルグスキーの「展覧会の絵」から「卵の殻をつけたひよこの踊り」から。 冒頭のPhraseを三番目のPhraseとして使いまわしています。
//==============================================================
// Ballett
//==============================================================
const Sono phrase_ballett1[] = {
4*6, P_C5,
4*6, 0,
4*6, P_H4,
4*6, 0,
4*6, P_C5,
4*6, 0,
4*6, P_G4s,
4*6, P_C5,
4*6, P_C5,
4*6, 0,
4*6, P_H4,
4*6, 0,
4*6, P_C5,
4*6, 0,
4*6, P_G4s,
4*6, 0,
0,0
};
const Sono phrase_ballett2[] = {
4*6, P_H2,
4*6, P_C3,
4*6, P_D3,
4*6, P_D3s,
4*6, P_E3,
4*6, P_F3,
4*6, P_G3,
4*6, P_G3s,
4*6, P_A3s,
4*6, P_H3,
4*6, P_C4,
4*6, P_C4s,
4*6, P_D4,
4*6, P_D4s,
4*6, P_E4,
4*6, P_C4,
0, 0
};
const Sono phrase_ballett3[] = {
4*6, P_C3s,
4*6, P_D3s,
4*6, P_E3,
4*6, P_F3,
4*6, P_F3s,
4*6, P_G3s,
4*6, P_A3,
4*6, P_H3,
4*6, P_D3s,
4*6, P_F3,
4*6, P_F3s,
4*6, P_G3,
4*6, P_G3s,
4*6, P_A3s,
4*6, P_H3,
4*6, P_C4,
0, 0
};
const Sono phrase_ballett4[] = {
4*6, P_C3,
4*6, P_E3,
4*6, P_C3s,
4*6, P_F3,
4*6, P_D3s,
4*6, P_F3s,
4*6, P_E3,
4*6, P_G3,
4*6, P_F3,
4*6, P_A3,
4*6, P_F3s,
4*6, P_A3s,
4*6, P_G3s,
4*6, P_H3,
4*6, P_A3s,
4*6, P_C4,
0, 0
};
const Sono phrase_ballett5[] = {
24*6, P_C5s,
4*6, 0,
4*6, P_C4,
4*6, 0,
0, 0
};
const Phrase music_ballett[] = {
phrase_ballett1,
phrase_ballett2,
phrase_ballett1,
phrase_ballett3,
phrase_ballett4,
phrase_ballett5,
(Phrase)0
};
三曲目は、ショパンの「別れの曲」から。 この曲も一部、Phraseの使い回しをしています。
//==============================================================
// L'Adieu
//==============================================================
const Sono phrase_adieu1[] = {
8*12, P_H2,
8*12, P_E3,
4*12, P_D3s,
4*12, P_E3,
0,0
};
const Sono phrase_adieu2[] = {
20*12, P_F3s, // 2;10
4*12, P_G3s,
4*12, P_G3s,
4*12, P_F3s,
20*12, P_G3s, // 3;11
4*12, P_A3,
4*12, P_A3,
4*12, P_G3s,
12*12, P_C4s,
4*12, P_H3,
4*12, P_A3, // 4;12
4*12, P_G3s,
4*12, P_D3s,
4*12, P_E3,
20*12, P_F3s, // 5;13
4*12, P_G3s,
4*12, P_G3s,
4*12, P_F3s,
16*12, P_E3,
0,0
};
const Sono phrase_adieu3[] = {
4*12, P_G3s, // 6
4*12, P_A3,
4*12, P_F3s,
4*12, P_G3s,
4*12, P_A3,
4*12, P_H3,
4*12, P_G3s,
4*12, P_A3,
8*12, P_C4s, // 7
16*12, P_F3s,
4*12, P_G3s,
20*12, P_F3s, // 8
4*12, P_G3s,
4*12, P_F3s,
16*12, P_H3,
8*12, P_G3s, // 9
4*12, P_D3s,
4*12, P_E3,
0,0
};
const Sono phrase_adieu4[] = {
4*12, P_H3, // 14
4*12, P_C4s,
4*12, P_C4s,
4*12, P_H3,
4*12, P_A3,
4*12, P_H3,
4*12, P_G3s,
4*12, P_A3,
4*12, P_D4s, // 15
4*12, P_E4,
4*12, P_E4,
4*12, P_D4s,
4*12, P_C4s,
4*12, P_D4s,
4*12, P_H3,
4*12, P_C4s,
4*12, P_E4, // 16
4*12, P_F4s,
4*12, P_D4s,
4*12, P_E4,
4*12, P_F4s,
4*12, P_G4s,
4*12, P_E4,
4*12, P_F4s,
0,0
};
const Sono phrase_adieu5[] = {
20*12, P_G4s, // 17
4*12, P_F4s,
4*12, P_E4,
4*12, P_C4s,
16*12, P_D4s, // 18
4*12, P_E4,
4*12, P_D4s,
4*12, P_C4s,
4*12, P_G4s,
16*12, P_H3, // 19
4*12, P_C4s,
4*12, P_H3,
4*12, P_A3,
4*12, P_E3,
16*12, P_G3s, // 20
8*12, 0,
0,0
};
const Phrase music_adieu[] = {
phrase_adieu1,
phrase_adieu2,
phrase_adieu3,
phrase_adieu2,
phrase_adieu4,
phrase_adieu5,
(Phrase)0
};
#pragma CONST_SEG DEFAULT
このアプリケーションでは、特定のLEDを点灯させるために、 PTADDレジスタとPTADレジスタの双方ともに適切に 設定する必要があります。 ここでは、これらのレジスタに設定すべき値を宣言しています。
#pragma CONST_SEG PAGED PAGED_ROM
//==============================================================
// PORTA drive pattern.
// Tables must be in a same page.
//==============================================================
const struct {
byte ptad;
byte ptadd;
} __paged ptad_pattern[6] = {
// PTAD PTADD
0b00010000, 0b00110001, // 0 4-5
0b00100000, 0b00110001, // 1 5-4
0b00000010, 0b00010011, // 2 1-4
0b00010000, 0b00010011, // 3 4-1
0b00100000, 0b00100011, // 4 5-1
0b00000010, 0b00100011 // 5 1-5
};
const byte PTAD_BLANK = 0b00000000;
const byte PTADD_BLANK = 0b00110011;
// |||||||+-- BEEPER as OUTPUT
// ||||||+--- SEG0 as OUTPUT
// |||||+---- START as INPUT
// ||||+----- BKGD
// ||++------ SEG1,SEG2 as OUTPUT
// ++-------- Reserved
#pragma CONST_SEG DEFAULT
さらに"PTAD_BLANK"と"PTADD_BLANK"は、STOP状態などのように LEDを消灯する際のレジスタの設定について定義しています。
Real Time Interrupt (RTI) を扱う際の便利な関数群を定義しています。
//==============================================================
//= Functions controlling RTI module.
//=
//= enable_rti :
//= Enable the RTI interrupt.
//= SRTISC is initialized to start RTI counter.
//=
//= software_rti :
//= Enable the RTI but interrupt.
//= SRTISC is initialized to start RTI counter.
//=
//= accept_rti :
//= Accept an RTI event.
//= RTIACK is set to clear the RTIF
//=
//= disable_rti
//= Disable the RTI interrupt.
//= Counter is stopped to reduce power.
//=
//==============================================================
void enable_rti(void) {
SRTISC = SRTISC_INIT;
SRTISC_RTIE = 1;
}
void software_rti(void) {
SRTISC = SRTISC_INIT;
}
byte detect_rti(void) {
return SRTISC & SRTISC_RTIF_MASK;
}
void accept_rti(void) {
SRTISC_RTIACK = 1;
}
void disable_rti(void) {
SRTISC = SRTISC_DISABLE;
}
| 関数名 | 効能 |
|---|---|
| enable_rti | RTI割り込みを受け付け可能にします。 |
| software_rti | RTI機能を有効にしますが、割り込みは発生させません。 |
| detect_rti | RTIイベントが発生した場合TRUEを返します。 |
| accept_rti | RTIイベントフラグをクリアします。 |
| disable_rti | RTI機能を停止します。 |
これらの関数を使ってリアルタイム割り込みに関する処理を抽象化しています。 RS08では、サブルーチンの呼び出しが深くなると不要な処理が増えるので、 一般にこのような粒度の低い関数は避けたほうが無難であると思われます。
ところが、このように粒度の極端に低い関数は、「インライン展開」と呼ばれる処理によって、 コンパイルの段階で呼び出し側のプログラムに埋め込む事が可能です。 そのため、これらの関数はサブルーチン呼び出しとしてではなく、 マクロがそうであるように単なる呼び出し側プログラムの一部として実装されることになります。
もし、コンパイラに「インライン展開」を行わせるのではなく、 「マクロ」として記述したい場合には以下のような記述も出来ます。
#define enable_rti() (SRTISC=SRTISC_INIT,SRTISC_RTIE=1) #define software_rti() (SRTISC=SRTISC_INIT) #define detect_rti() (SRTISC_RTIF) #define accept_rti() (SRTISC_RTIACK=1) #define disable_rti() (SRTISC=SRTISC_DISABLE)
これでも、同じプログラムが生成されますが、 私は「関数」として定義したほうが理解しやすいと考えています。
KeyBoard Interrupt (KBI) でも RTI と同じように関数群が定義されます。
//==============================================================
//= Functions controling KBI module.
//=
//= enable_kbi :
//= Enable the KBI interrupt.
//= KBACK is set to clear the KBF
//=
//= accept_kbi :
//= Accept an KBI event.
//= KBACK is set to clear the KBF
//=
//= disable_kbi
//= Disable the KBI interrupt.
//=
//==============================================================
void enable_kbi(void) {
KBISC_KBACK = 1;
KBISC_KBIE = 1;
}
byte detect_kbi(void) {
return KBISC & KBISC_KBF_MASK;
}
void accept_kbi(void) {
KBISC_KBACK = 1;
}
void disable_kbi(void) {
KBISC_KBIE = 0;
}
| 関数名 | 効能 |
|---|---|
| enable_kbi | キーボード割り込みを受け付け可能にします。 |
| detect_kbi | キーボード割り込みが発生した場合TRUEを返します。 |
| accept_kbi | キーボード割り込みフラグをクリアします。 |
| disable_kbi | キーボード割り込みを受け付け不可にします。 |
この関数の場合にも、以下のようなマクロ定義を使用することができます。
#define enable_kbi() (KBISC_KBACK=1, KBISC_KBIE=1) #define detect_kbi() (KBISC_KBF) #define accept_kbi() (KBISC_KBACK=1) #define disable_kbi() (KBISC_KBIE=0)
Modulo TIMer (MTIM) でも同様に関数群が定義されます。
//==============================================================
//= Functions controling MTIM module.
//=
//= enable_mtim :
//= Enable the TOF interrupt.
//= TRST is set to clear the TOF and MTIMCNT.
//= TSTP is controlled for safety enabling.
//=
//= accept_mtim :
//= Accept an TOF event.
//= TOF is reset to clear the TOF.
//=
//= disable_mtim
//= Disable the TOF interrupt.
//= TRST is set to reset the timer counter.
//= TSTP is set to stop the timer counter.
//=
//==============================================================
void enable_mtim(void) {
MTIMSC = MTIMSC_TOIE_MASK + MTIMSC_TRST_MASK + MTIMSC_TSTP_MASK;
MTIMSC_TSTP = 0;
}
byte detect_mtim(void) {
return MTIMSC & MTIMSC_TOF_MASK;
}
void accept_mtim(void) {
MTIMSC_TOF = 0;
}
void disable_mtim(void) {
MTIMSC = MTIMSC_TRST_MASK + MTIMSC_TSTP_MASK;
}
| 関数名 | 効能 |
|---|---|
| enable_mtim | モジュロ・タイマ割り込みを受け付け可能にします。 |
| detect_mtim | モジュロ・タイマ割り込みが発生した場合TRUEを返します。 |
| accept_mtim | モジュロ・タイマ割り込みフラグをクリアします。 |
| disable_mtim | モジュロ・タイマ割り込みを受け付け不可にします。 |
この関数の場合にも、以下のようなマクロ定義を使用することができます。
#define enable_mtim() (\ MTIMSC=MTIMSC_TOIE_MASK+MTIMSC_TRST_MASK + MTIMSC_TSTP_MASK,\ MTIMSC_TSTP = 0,\ MTIMMOD = MTIMMOD_3000\ ) #define detect_mtim() (MTIMSC_TOF) #define accept_mtim() (MTIMSC_TOF=0) #define disable_mtim() (MTIMSC=MTIMSC_TRST_MASK+MTIMSC_TSTP_MASK);
ここから、プログラムの本体が始まります。 最初の関数は、put_ledです。 この関数は、 大域変数led_indexとフラグblankの状態によって、 LEDを点灯・消灯します。
//**************************************************************
// code section
//**************************************************************
//-----------------------------------------------------
// Put on an LED at led_index.
//-----------------------------------------------------
#pragma NO_INLINE
void put_led(void) {
PTADD = BEEP_MASK;
if (flag.blank) {
PTAD = PTAD_BLANK;
PTADD = PTADD_BLANK;
} else {
PTAD = ptad_pattern[led_index].ptad;
PTADD = ptad_pattern[led_index].ptadd;
}
}
この関数には、"#pragma NO_INLINE"という行が追加されており、 インライン展開しないように指定しています。 この関数をインライン展開するとコードが大きくなるため、このような行を加えています。 この関数は、局所変数や作業領域を使用しない単純な構造になっているため、 サブルーチン呼び出しとされてもそれほどコストがかかりません。
この関数は、必要に応じて圧電スピーカ出力を反転させます。
//-----------------------------------------------------
// Make a BEEP if required.
//-----------------------------------------------------
void sono_tick(void) {
if (sono_count > 0) {
BEEP = ~BEEP;
sono_count--;
}
}
圧電スピーカからの発音時間は、変数sono_countで管理されています。 この変数は、関数が呼び出されるたびに減少していき、"0"になったときに音が停止します。
この関数では、アプリケーションで必要なモジュールなどの初期化を行っています。
//==============================================================
//= Genral initialization.
//=
//= Modules KBI, RTI and MTIM are initialized and ready to be
//= used.
//==============================================================
void general_init(void) {
// Initalize configuration.
SOPT = SOPT_INIT;
SPMSC1 = SPMSC1_INIT;
// Reload TRIM value.
ICSTRM_TRIM = ((const NV_ICSTRMSTR * __paged)CONVERT_TO_PAGED(0x00003FFA))->Bits.TRIM;
ICSSC_FTRIM = ((const NV_FTRIMSTR * __paged)CONVERT_TO_PAGED(0x00003FFB))->Bits.FTRIM;
// Specify initial position of LED.
led_index = 0;
// Enable KBI function on PTA2
KBIPE_KBIPE2 = 1;
PTAPE_PTAPE2 = 1;
KBISC_KBIMOD = 1;
// Initialize MTIM
MTIMCLK = MTIMCLK_INIT;
}
この中では、内部発振器のトリム・レジスタの値も設定していますが、 CodeWarriorが提供するヘッダファイルに不備があるため、 このような回りくどい書き方をする必要があります。 ヘッダファイルが修正されたら、以下のようなシンプルな書き方をすることができます。
ICSTRM_TRIM = NVICSTRM_TRIM; ICSSC_FTRIM = NVFTRIM_FTRIM;
この後、記述されているステートマシンでは、 それぞれの状態の初期化ルーチンを呼び出して状態遷移を表現しています。 ここでは、それら初期化ルーチンのプロトタイプ宣言を行っています。
//============================================================== // State machine declaration. // XXX_init() functions are used to make a transition. //============================================================== void prologue_init(void); void wait_init(void); void stop_init(void); void pressed_init(void); void validated_init(void); void active_init(void); void epilogue_init(void);
初期化ルーチンでは、 モジュロ・タイマ割り込みだけを許可して、 処理ルーチンに備えます。 モジュロ・割り込みの周期は、3000Hzに相当する周期です。
//==============================================================
//= Initialization for PROLOGUE state
//= KBI -- disabled not to capture KBI.
//= RTI -- disabled.
//= MTIM -- enabled to detect button released.
//==============================================================
void prologue_init(void) {
disable_kbi();
disable_rti();
enable_mtim();
MTIMMOD = MTIMMOD_3000;
sono_count = BEEP_PERIOD;
flag.blank = 0;
debounce_count = 0;
state = ST_PROLOGUE;
}
処理ルーチンでは、 モジュロ・タイマ割り込みにより定期的に押しボタンの状態を確認すると 同時にボタンを押したことを示す3000Hzの短い音を出します。 押しボタンが放された事を検出したときには、WAIT状態に遷移します。 押しボタンの監視には、デバウンス検出カウンタを使用し、 チャッタリングを防止しています。
//==============================================================
//= PROLOGUE state
//= Confirm START button released periodically.
//= Go to WAIT state if START released.
//==============================================================
void prologue_state(void) {
// Update LED position.
put_led();
wait();
if (detect_mtim()) {
accept_mtim();
sono_tick();
if (!START) {
// START still pressed.
debounce_count = 0;
} else {
// Count released period
if (++debounce_count >= DEBOUNCE_PERIOD) {
wait_init();
}
}
}
}
初期化ルーチンでは、 キーボード割り込みとリアルタイム割り込みを許可して、 処理ルーチンに備えます。
//==============================================================
//= Initialization for WAIT state
//= KBI -- enabled to wait START button.
//= RTI -- enabled to wait TIMEOUT event.
//= MTIM -- disabled to reduce power.
//==============================================================
void wait_init(void) {
enable_kbi();
enable_rti();
disable_mtim();
time_count = 0;
state = ST_WAIT;
}
処理ルーチンでは、キーボード割り込みとリアルタイム割り込みを待ちます。 キーボード割り込みが発生したら、 PRESSED状態に遷移して押しボタンを監視します。 リアルタイム割り込みが発生したら、 時間待ちカウンタを進めます。 この時、ほったらかし時間を経過していたらSTOP状態に遷移します。
//==============================================================
//= WAIT state
//= Wait for a START trigger and go to PRESSED state.
//= Go to PRESSED state if START pressed.
//= Go to STOP state if wait period expired.
//==============================================================
void wait_state(void) {
// Update LED position.
put_led();
wait();
if (detect_kbi()) {
// Detect START button pressed.
pressed_init();
} else if (detect_rti()) {
// Detect periodical event
accept_rti();
if (++time_count >= IDLE_PERIOD) {
stop_init();
}
}
}
初期化ルーチンでは、 キーボード割り込みを許可して、 処理ルーチンに備えます。
//==============================================================
//= Initialization for STOP state
//= KBI -- enabled to wake up by START button.
//= RTI -- disabled to reduce power.
//= MTIM -- disabled to reduce power.
//==============================================================
void stop_init(void) {
enable_kbi();
disable_rti();
disable_mtim();
flag.blank = 1;
state = ST_STOP;
}
処理ルーチンでは、LEDを消灯して省電力モードに遷移し、 キーボード割り込みを待ちます。 キーボード割り込みが発生したら、 PROLOGUE状態に遷移します。
//==============================================================
//= STOP state
//= Go to PROLOGUE state if START button pressed.
//==============================================================
void stop_state(void) {
// Update LED position.
put_led();
stop();
if (detect_kbi()) {
prologue_init();
}
}
初期化ルーチンでは、 モジュロ・タイマ割り込みを許可して、 処理ルーチンに備えます。 モジュロ・割り込みの周期は、3000Hzに相当する周期です。
//==============================================================
//= Initialization for PRESSED state.
//= KBI -- disabled.
//= RTI -- disabled.
//= MTIM -- enabled to detect RELEASED timing.
//==============================================================
void pressed_init(void) {
disable_kbi();
disable_rti();
enable_mtim();
MTIMMOD = MTIMMOD_3000;
debounce_count = 0;
time_count = 0;
sono_count = BEEP_PERIOD;
state = ST_PRESSED;
}
処理ルーチンでは、 モジュロ・タイマ割り込みにより定期的に押しボタンの状態を確認すると 同時にボタンを押したことを示す3000Hzの短い音を出します。 押しボタンが放された事を検出した時には、 押しボタンが短く押されたと判断して、 LEDの位置を進めてWAIT状態に遷移します。 押しボタンが長押しされた事を検出した時には、 VALIDATED状態に遷移します。
//==============================================================
//= PRESSED state
//= Wait for a START released.
//= Move LED and go to WAIT state if START released.
//==============================================================
void pressed_state(void) {
// Update LED position.
put_led();
wait();
if (detect_mtim()) {
accept_mtim();
sono_tick();
if (++time_count >= PRESSED_PERIOD) {
// Validated as START
validated_init();
}
if (!START) {
// START button still pressed
debounce_count = 0;
} else if (++debounce_count >= DEBOUNCE_PERIOD) {
// RELEASE recognized as MODE change
if (++led_index >= LED_COUNT) {
led_index = 0;
}
wait_init();
}
}
}
初期化ルーチンでは、 モジュロ・タイマ割り込みだけを許可して、 処理ルーチンに備えます。 モジュロ・割り込みの周期は、3000Hzに相当する周期です。
//==============================================================
//= Initialization for VALIDATED state.
//= KBI -- disabled.
//= RTI -- disabled.
//= MTIM -- enabled to detect RELEASED timing.
//==============================================================
void validated_init(void) {
disable_kbi();
disable_rti();
enable_mtim();
MTIMMOD = MTIMMOD_3000;
debounce_count = 0;
sono_count = BEEP_PERIOD;
state = ST_VALIDATED;
}
処理ルーチンでは、 モジュロ・タイマ割り込みにより定期的に押しボタンの状態を確認すると 同時にボタンを押したことを示す3000Hzの短い音を出します。 押しボタンが放された事を検出したら、 LEDの位置を記録してからACTIVE状態に遷移します。
//==============================================================
//= VALIDATED state
//= Wait for a START released.
//= Go to ACTIVE state if START button released.
//==============================================================
void validated_state(void) {
// Update LED position.
put_led();
wait();
if (detect_mtim()) {
accept_mtim();
sono_tick();
if (!START) {
// START still pressed
debounce_count = 0;
} else if (++debounce_count >= DEBOUNCE_PERIOD) {
// RELEASE recognized
minute_target = led_index + 2;
active_init();
}
}
}
初期化ルーチンでは、 キーボード割り込みと モジュロ・タイマ割り込みを許可して、 処理ルーチンに備えます。 モジュロ・割り込みの周期は、1000Hzに相当する周期です。
//==============================================================
//= Initialization for ACTIVE state.
//= KBI -- enabled to abort by START button.
//= RTI -- disabled.
//= MTIM -- enabled to detect TIMEOUT event.
//==============================================================
void active_init(void) {
enable_kbi();
disable_rti();
enable_mtim();
MTIMMOD = MTIMMOD_1000;
minute_count = minute_target;
time_count = 0;
qsecond_count = 0;
state = ST_ACTIVE;
}
処理ルーチンでは、 キーボード割り込みとモジュロ・タイマ割り込みを待ちます。 キーボード割り込みが発生したら、 LEDの点灯位置を修復してPROLOGUE状態に遷移します。 モジュロ・タイマ割り込みが発生したら、 時間待ちカウンタを進めます。 そして、設定時間が過ぎたら、EPILOGUE状態に遷移します。 ここでは、残り時間に従って、LEDの点灯状態を変更しています。
//==============================================================
//= ACTIVE state
//= Wait for a TIMEOUT and START pressed.
//= Go to PROLOGUE state if START button pressed.
//= Go to EPILOGUE state if TIMEOUT.
//==============================================================
void active_state(void) {
// Update LED position.
put_led();
wait();
if (detect_kbi()) {
// Process aborted by START button.
// Revert LED to last position.
led_index = minute_target - 2;
prologue_init();
} else if (detect_mtim()) {
accept_mtim();
// Occcurs every 1msec.
if (++time_count >= QSECOND_PERIOD) {
// Occurs every 1/4 second.
time_count = 0;
if (++qsecond_count >= MINUTE_PERIOD) {
// Occurs every 1 minute.
qsecond_count = 0;
if (--minute_count == 0) {
// Time-up
// Revert LED to last position.
led_index = minute_target - 2;
epilogue_init();
return;
}
}
// Update LED position.
if (minute_count <= 1) {
// LED running last 1 minute.
led_index = MINUTE_PERIOD - qsecond_count;
while (led_index >= 6) {
led_index -= 6;
}
flag.blank = 0;
} else {
// Blink in 2Hz at left minute LED.
led_index = minute_count - 2;
flag.blank = (qsecond_count & 2)?(1):(0);
}
}
}
}
初期化ルーチンでは、 モジュロ・タイマ割り込みだけを許可して、 処理ルーチンに備えます。 ここでは、処理ルーチンで使用するリアルタイム割り込みタイマも設定しますが、 割り込みは禁止します。 その後、待ち時間に従って、演奏する楽譜を選んで変数にセットします。
//==============================================================
//= Initialization for EPILOGUE state.
//= KBI -- disabled.
//= RTI -- enable flag only to handle SONO count.
//= MTIM -- enabled to detect TIMEOUT event.
//==============================================================
void epilogue_init(void) {
disable_kbi();
software_rti();
enable_mtim();
// Select a music
switch (minute_target) {
case 2:
music = music_ballett; break;
case 3:
music = music_noodle; break;
case 4:
music = music_adieu; break;
case 5:
music = music_noodle; break;
case 6:
music = music_noodle; break;
case 7:
music = music_noodle; break;
default:
music = music_noodle; break;
}
phrase_point = music;
sono_point = *phrase_point++;
sono_count = 1;
flag.quiet = 1;
state = ST_EPILOGUE;
}
処理ルーチンでは、 モジュロ・タイマ割り込みにより音の半周期ごとに処理を行います。 また、音を出すためにポートを反転しています。 押しボタンが押されたら、音楽の演奏を止めるためにPROLOGUE状態に遷移します。 もし、リアルタイムイベントが発生していたら、 音の残り時間を確認し、楽譜から新たな音符を選んできます。
//==============================================================
//= EPILOGUE state
//= Play MUSIC and wait for START button pressed.
//= Go to PROLOGUE state if START button pressed.
//= Play music with RTI and MTIM.
//= All behavior is implemented in MTIM event handler
//= to make a clear MUSIC sound.
//==============================================================
void epilogue_state(void) {
// Update LED position.
put_led();
wait();
if (detect_mtim()) {
accept_mtim();
// Reverse BEEP if required.
if (!flag.quiet) {
BEEP = ~BEEP;
}
// Abort if START pressed.
if (!START) {
prologue_init();
}
// Update SONO every 8msec
if (detect_rti()) {
accept_rti();
if (--sono_count == 0) {
// SONO time exhausted.
if (sono_point->length == 0) {
// a phrase exhausted.
if (*phrase_point == 0) {
// restart when reached to end of MUSIC
phrase_point = music;
}
// Get a phrase.
sono_point = *phrase_point++;
}
if (sono_point->period) {
// Update MTIM timer setting.
MTIMMOD = sono_point->period;
flag.quiet = 0;
sono_count = sono_point->length;
} else {
MTIMMOD = MTIMMOD_1000;
flag.quiet = 1;
sono_count = sono_point->length;
}
sono_point++;
}
// Stop last 3*8msec
if (sono_count < 3) {
flag.quiet = 1;
}
}
}
}
メイン関数では、システム全体の初期化を行った後、 状態遷移を行うための無限ループに入ります。
//==============================================================
//= MAIN loop.
//==============================================================
void main(void) {
// General Initialization
general_init();
// Specify the first state
prologue_init();
// State machine engine.
for(;;) {
switch (state) {
case ST_PROLOGUE: prologue_state(); break;
case ST_WAIT: wait_state(); break;
case ST_STOP: stop_state(); break;
case ST_PRESSED: pressed_state(); break;
case ST_VALIDATED: validated_state(); break;
case ST_ACTIVE: active_state(); break;
case ST_EPILOGUE: epilogue_state(); break;
default: prologue_init();
}
} /* loop forever */
/* please make sure that you never leave main */
}
このプログラムをリンクするためには、 通称PRMファイルと呼ばれるファイルを用意しなくてはなりません。
CodeWarriorが提供するひながたファイルでは、 ROM領域が0x3800から0x3FF7まで確保されていたのですが、 このうち0x3800から0x3AFFまでを楽譜などの定数配列を置く PAGED_ROM領域として宣言しました。 従って、ROM領域は0x3B00から0x3FF7までになっています。
/* This is a linker parameter file for the MC9RS08KA2 */
NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */
SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */
RESERVED_RAM = NO_INIT 0x0000 TO 0x0004;
TINY_RAM = READ_WRITE 0x0005 TO 0x000D;
DIRECT_RAM = READ_WRITE 0x0020 TO 0x004F;
ROM_PAGE0 = READ_ONLY 0x3800 TO 0x383F;
ROM_PAGE1 = READ_ONLY 0x3840 TO 0x387F;
ROM_PAGE2 = READ_ONLY 0x3880 TO 0x38BF;
ROM_PAGE3 = READ_ONLY 0x38C0 TO 0x38FF;
ROM_PAGE4 = READ_ONLY 0x3900 TO 0x393F;
ROM_PAGE5 = READ_ONLY 0x3940 TO 0x397F;
ROM_PAGE6 = READ_ONLY 0x3980 TO 0x39BF;
ROM_PAGE7 = READ_ONLY 0x39C0 TO 0x39FF;
ROM_PAGE8 = READ_ONLY 0x3A00 TO 0x3A3F;
ROM_PAGE9 = READ_ONLY 0x3A40 TO 0x3A7F;
ROM_PAGE10 = READ_ONLY 0x3A80 TO 0x3ABF;
ROM_PAGE11 = READ_ONLY 0x3AC0 TO 0x3AFF;
ROM = READ_ONLY 0x3B00 TO 0x3FF7;
// RESET_JMP_AREA = READ_ONLY 0x3FFD TO 0x3FFF; // area defined by RESET 0 below.
END
PLACEMENT /* Here all predefined and user segments are placed into the SEGMENTS defined above. */
RESERVED INTO RESERVED_RAM;
TINY_RAM_VARS INTO TINY_RAM;
DIRECT_RAM_VARS,
DEFAULT_RAM INTO DIRECT_RAM, TINY_RAM;
DEFAULT_ROM INTO ROM;
PAGED_ROM INTO
ROM_PAGE0, ROM_PAGE1, ROM_PAGE2, ROM_PAGE3,
ROM_PAGE4, ROM_PAGE5, ROM_PAGE6, ROM_PAGE7,
ROM_PAGE8, ROM_PAGE9, ROM_PAGE10, ROM_PAGE11;
END
STACKSIZE 0x00 /* no stack for RS08 */
VECTOR 0 _Startup /* Reset vector: this is the default entry point for an application. */
2007-10-24 正式公開
Updated: $Date: 2007/10/24 12:32:25 $
Copyright (C) 2007 noritan.org ■