Clock_Start() メソッドでクロックが起動しない [PSoC]
PSoC 4200 を使って、ブザーをボタンにより操作しようとしているのだけど、うまく動かないと相談を受けました。 さて、何が原因でしょうね。
ソフトウェアでで Clock を制御する
使われた回路は、上にある通りです。 ソフトウェアで入力端子の状態を読み出し、それにしたがって、 Clock コンポーネントを動かしたり止めたりするというコンセプトです。 ソフトウェアでの判断は、以下のようになっています。
#include <project.h> uint8 SW2_state; int main() { CyGlobalIntEnable; /* Enable global interrupts. */ /* Place your initialization/startup code here (e.g. MyInst_Start()) */ for (;;) { /* Place your application code here. */ SW2_state = Pin_SW2_Read(); if (SW2_state) { // SW2 OFF Clock_880Hz_Stop(); } else { // SW2 ON Clock_880Hz_Start(); } } }
メインループの中で入力端子の状態を読み取り、状態にしたがって Stop() メソッドまたは Start() メソッドを呼び出すという仕組みです。 ボタンを押し続けた場合、このループでは繰り返し Start() メソッドが呼び出されます。 「 Start() メソッドにより、 Clock コンポーネントが起動される」という目的で作られているのであれは、これでも問題ないのですが、どうも起動する以外の副作用が有りそうです。
実験開始
そこで、実験回路を作成しました。
左半分の回路では、 Pwm_Start タイマにより周期的な割り込み int_Start を発生させて Pin_LED 端子に接続されている Clock コンポーネントの Start() メソッドを発行しています。 LED は、 Clock コンポーネントの周期 200ms で点滅を行います。 タイマに PWM コンポーネントを使用しているのは、出力を波形で見るためです。
一方、右側の回路は、左の回路で生成された信号を取りこむロジック・アナライザとして機能します。 信号を取りこむタイミングは、割り込み int_Sample で決定します。 Status Register で取りこんだ信号は、 UART コンポーネントで送り出します。 UART で送信されたデータは Bridge Control Panel で波形として表示します。 この動作は、 CY8CKIT-042 でロジアナを作った ~UART編~ で紹介した通りです。
// Re-start Clock component ISR CY_ISR(int_Start_isr) { Clock_5Hz_Start(); } // Logic sampling ISR CYBIT int_Sample_flag = 0; uint8 probe = 0; CY_ISR(int_Sample_isr) { probe = SR_Probe_Read(); int_Sample_flag = 1; } int main() { CyGlobalIntEnable; /* Enable global interrupts. */ // Start method calling timing Pwm_Start_Start(); Pwm_Start_WritePeriod(489); int_Start_StartEx(int_Start_isr); // Logic sampling timing int_Sample_StartEx(int_Sample_isr); // Logic analyzer output UART_Start(); // Main loop for(;;) { // Sample logic level periodically if (int_Sample_flag) { UART_UartPutChar(probe); int_Sample_flag = 0; } } }
ソフトウェアは、上記のとおりです。 Start() メソッドを発行する周期を Pwm_Start タイマで規定しています。 この実験では、周期を変化させて動作を確認するために、周期をソフトウェアで決定しています。 こうすると、プロジェクトを Build した時に回路の配置配線を行う必要が無いため、実験に必要な時間を減らす事ができます。
実験の結果
まず、 Start() メソッドの周期を 490 とした場合、このような波形になりました。 Start() メソッドを発行してから、ちょうど 100ms 後に LED 出力が立ち下がり、さらに 100ms 後に立ちあがっているのがわかります。 100ms は、 LED を駆動する Clock コンポーネントの周期の半分です。 この様子から、 Start() メソッドの発行により、 Clock コンポーネントの内部カウンタがリセットされたと考えられます。 内部カウンタがリセットされたのに Clock 出力そのものがリセットされないのは妙です。 そこで、 Start() メソッドの周期を 390ms としてみました。
すると、 Start() メソッド発行後 100ms の位置にあった立ち下がりエッジが無くなりました。 さらに 100ms あと、つまり Start() メソッド発行後 200ms には立ち上がりエッジがあります。 これらの実験から以下の推論が導き出されます。
- Start() メソッドを発行すると Clock の内部カウンタがリセットされるが、クロック出力は以前の状態を保持する。
- Start() メソッドの発行からクロック半周期分の時間が経過すると、クロック出力が "0" に設定される。
- さらに半周期分の時間が経過するとクロック出力は "1" に設定される。
- 以降、クロック出力を "0" と "1" で交互に設定するとクロック出力が変化する。
以上の推測から、 Start() メソッドの発行周期を 200ms よりも短くすると、クロック出力は "0" に設定されたままで、クロックとしては機能しなくなると考えられます。
そこで、 Start() メソッドの周期を 199ms にしました。 すると、みごとに LED が点滅しなくなりました。 ブザーの音が出なくなったのは、 Start() メソッドの周期が短すぎて、クロック出力が出なくなってしまったものと推測されます。
解決策
ここからは、どうやったらブザー出力が出てくるかという解決策を探ります。 実験の結果から、 Start() メッソドの呼び出し周期が 1.2ms (880Hz) よりも短ければ、クロックは出力されます。 そこで、ボタンの判定の後、20ms ほど遅延を入れて Start() メッソドの呼び出しが頻繁に発生しないようにします。
for (;;) { /* Place your application code here. */ SW2_state = Pin_SW2_Read(); if (SW2_state) { // SW2 OFF Clock_880Hz_Stop(); } else { // SW2 ON Clock_880Hz_Start(); } CyDelay(20); }
これで、ボタンに連動してブザーが鳴るようになります。 しかし、音が「にごる」ようになってしまいました。 これは、追加した遅延時間が短すぎて、クロック出力が歪んでしまったのが原因です。
遅延時間を長くすると、音の濁りが少なくなりますが、ボタンを押してから音が出る・止まるまでの遅延も大きくなり、ボタンに対する反応が遅くなったように感じます。
#include <project.h> uint8 SW2_state; uint8 SW2_last_state = 1u; // OFF as default int main() { CyGlobalIntEnable; /* Enable global interrupts. */ /* Place your initialization/startup code here (e.g. MyInst_Start()) */ for (;;) { /* Place your application code here. */ SW2_state = Pin_SW2_Read(); if (SW2_state && !SW2_last_state) { // SW2 ON to OFF Clock_880Hz_Stop(); } else if (!SW2_state && SW2_last_state) { // SW2 OFF to ON Clock_880Hz_Start(); } SW2_last_state = SW2_state; } }
根本的な解決法として、以前のボタンの状態を記憶しておいて、状態が変化したら Clock コンポーネントの制御を行うようにしました。 ボタンの状態が変化した時だけ Start()/Stop() メソッドを呼び出すので、頻繁にメソッドの呼び出しが起こる事も無くなりました。 また、ボタンに対する反応も良くなりました。
コメント 0