KURUMIでキーボードピアノ(toneのお試し)

キーボードと圧電スピーカーとKURUMIでできる、ピアノ作りました。TeraTermを立ち上げてテンキーで演奏できます。ドレミに合わせてKURUMIも光ります。

スケッチを書きこむときは、Kurumi Writerの以下の赤枠部分にチェックを入れてください。

例えばターミナルソフトでTeraTermを使っている場合、TeraTerm起動後にポート指定するとプログラムが実行されるようになります。逆にポート指定前はプログラムは走りません。

以下、そのままコピービルドで動きます。

#include <RLduino78.h>
#define BLACK 0
#define RED 1
#define GREEN 2
#define YELLOW 3
#define BLUE 4
#define PINK 5
#define SKY 6
#define WHITE 7

#define PIN_BZ 2

#define LED_R 22
#define LED_G 23
#define LED_B 24

int melody[131952358765969878488098810471175};
void ledKurumi(int);

void setup()
{                
    Serial.begin(9600);
    pinMode(LED_R OUTPUT)//LED RED
    pinMode(LED_G OUTPUT)//LED GREEN
    pinMode(LED_B OUTPUT)//LED BLUE

    //DO RE MI
    tone(PIN_BZmelody[1]);
    delay(200);
    tone(PIN_BZmelody[2]);
    delay(200);
    tone(PIN_BZmelody[3]);
    delay(200);
    noTone(PIN_BZ);
}

void loop()
{
    int iNum;

    noTone(PIN_BZ);
    ledKurumi(BLACK);
    while(!Serial.available());
    char cNum Serial.read();
    Serial.println(cNum);
    if (cNum '0'|| (cNum '9'){  //from char to int (0-9)
        iNum 0//illegal
    }else{
        iNum (int)cNum '0' );
    }
    
    ledKurumi(iNum);
    tone(PIN_BZmelody[iNum]);
    delay(200);

}

void ledKurumi(int color)
{
    switch(color){
        case BLACK:
            digitalWrite(LED_RHIGH);
            digitalWrite(LED_GHIGH);
            digitalWrite(LED_BHIGH);
            break;
        case RED:
            digitalWrite(LED_RLOW);
            digitalWrite(LED_GHIGH);
            digitalWrite(LED_BHIGH);
            break;
        case GREEN:
            digitalWrite(LED_RHIGH);
            digitalWrite(LED_GLOW);
            digitalWrite(LED_BHIGH);
            break;
        case YELLOW:
            digitalWrite(LED_RLOW);
            digitalWrite(LED_GLOW);
            digitalWrite(LED_BHIGH);
            break;
        case BLUE:
            digitalWrite(LED_RHIGH);
            digitalWrite(LED_GHIGH);
            digitalWrite(LED_BLOW);
            break;
        case PINK:
            digitalWrite(LED_RLOW);
            digitalWrite(LED_GHIGH);
            digitalWrite(LED_BLOW);
            break;
        case SKY:
            digitalWrite(LED_RHIGH);
            digitalWrite(LED_GLOW);
            digitalWrite(LED_BLOW);
            break;
        case WHITE:
            digitalWrite(LED_RLOW);
            digitalWrite(LED_GLOW);
            digitalWrite(LED_BLOW);
            break;
        case 8:
            digitalWrite(LED_RLOW);
            digitalWrite(LED_GHIGH);
            digitalWrite(LED_BHIGH);
            break;
        case 9:
            digitalWrite(LED_RHIGH);
            digitalWrite(LED_GLOW);
            digitalWrite(LED_BHIGH);
            break;
    default:
            digitalWrite(LED_RHIGH);
            digitalWrite(LED_GHIGH);
            digitalWrite(LED_BHIGH);
            break;
    }
}

  • > ブザーと

    圧電スピーカーでは?

  • ナイス突っ込みです。直しました。

  • TONE関数の仕様についてご質問があります。

    PIN3を出力端子に変更すると、TONE利用時のパルスがその端子からも出力されます。

       Serial.begin(9600);

       pinMode(LED_R , OUTPUT); //LED RED

       pinMode(LED_G , OUTPUT); //LED GREEN

       pinMode(LED_B , OUTPUT); //LED BLUE

       pinMode(3 , OUTPUT);

  • あれ?コメントがきれてしましました。

    上記サンプルに、

    pinMode(3 , OUTPUT); <- この行を追加するだけで、

    PIN2とPIN3の両方から、TONEのパルスが出力されるようになります。

    ※PIN2をPIN3に変更するのではありません。説明不足ですみません。

    なんで切れたんだろう?

  • gr_common/include/RLduino78_mcu_depend.h の中で

    #define TONE_TIMER PWM_PIN_3

    と定義されてる通り tone 用のタイマーとして PIN3 に割り当て可能な TO01 を使用しており、

    gr_common/RLduino78/cores/RLduino78_basic.cpp

    の中の

    void tone(uint8_t u8Pin, unsigned int u16Frequency, unsigned long u32Duration)

    から呼ばれる

    static void _startTimerChannel(uint8_t u8TimerChannel, uint16_t u16TimerMode, uint16_t u16Interval, bool bPWM, bool bInterrupt)

    が、引数 bPWM の値に関係なくタイマー出力が常に行われるようコーディングされてるのが原因のようです。

    if (bPWM == true) {

    TOM0.tom0 |=  (1 << u8TimerChannel);// タイマ出力モードの設定

    } else {

    TOM0.tom0 &= ~(1 << u8TimerChannel);// タイマ出力モードの設定

    }

    TOL0.tol0 &= ~(1 << u8TimerChannel);// タイマ出力レベルの設定

    TO0.to0   &= ~(1 << u8TimerChannel);// タイマ出力の設定

    TOE0.toe0 |=  (1 << u8TimerChannel);// タイマ出力許可の設定

    TS0.ts0   |=  (1 << u8TimerChannel);// タイマ動作許可

  • Servo 用に タイマユニット0チャネル4が使われており、

    gr_common/include/RLduino78_mcu_depend.h:

    #define SERVO_CHANNEL 4 // TM04

    PWM_PIN_9 と被るので同様の問題がありそうなカンジ。

  • GR-KURUMI に搭載されているユニット0 の 8 つあるタイマの内、チャネル0は PWM の Mater チャネル(?)に使用され、あと 5 つのチャネルがハードウェア PWM の各ピン対応して使用されている。

    gr_common/include/RLduino78_mcu_depend.h:

    #define PWM_PIN_3 1 // TO1

    #define PWM_PIN_5 2 // TO2

    #define PWM_PIN_6 7 // TO7

    #define PWM_PIN_9 4 // TO4

    #define PWM_PIN_10 3 // TO3

    各ピンを PWM に使用しない限りは、対応したタイマのチャネルは別の用途に使用できる(筈)。現在のところ Tone 用と Servo 用に チャネル 1 と チャネル 4 が PWM と排他利用する前提で使用されてる。

    #define TONE_TIMER PWM_PIN_3

    #define SERVO_CHANNEL 4 // TM04

    他、ソフトウェア PWM にチャネル 6 が、使用され、

    #define SW_PWM_TIMER        6

    マイクロ秒タイマにチャネル 5 を使用するようハードコーディングされている。

    gr_common/RLduino78/cores/RLduino78_timer.c:

    TT0.tt0     |= 0x0020; // マイクロ秒タイマの動作停止

    TMR05.tmr05  = IT_CLOCK_MODE; // マイクロ秒タイマの動作モード設定

    TDR05.tdr05  = INTERVAL_MICRO; // マイクロ秒タイマの周期設定

    TO0.to0     &= ~0x0020; // マイクロ秒タイマの出力設定

    TOE0.toe0   &= ~0x0020; // マイクロ秒タイマの出力許可設定

    ハードウェア PWM は、使用できるピンと使用されるタイマのチャネルが一対一で対応しており、ハードウェア PWM を使用しないピンについては、対応したタイマのチャネルは別の用途に使用できる。

    Tone 用、Servo 用、ソフトウェア PWM 用、マイクロ秒タイマ用 に使用されるタイマは機能としては単なるインタバルタイマであり、使用するタイマのチャネルは本来自由に選択できる筈だが、現在のライブラリの実装では gr_common/ 以下を書き換える必要が発生する。

    もしこれが、使用するタイマのチャネルを自由に変更する機能/仕組みが提供されれば、ピン割り当ての自由度が高くなっていい感じになるのではなかろうか。

    例)

    SetToneTimer(6);    // ハードウェア PWM は 5 つ全部使用し、ソフトウェア PWM は使わないので空いてるタイマのチャネル 6 を Tone用に割り当てる

  • >PWM_PIN_9 と被るので同様の問題がありそうなカンジ。

    いや、ないか。

  • 誤)引数 bPWM の値に関係なくタイマー出力が常に行われるようコーディングされてるのが原因のようです。

    正)引数 bInterrupt の値に関係なくタイマー出力が常に行われるようコーディングされてるのが原因のようです。

  • ルネサスナイト直前に申し訳ありません。Fujitaさん、ありがとうございます。まだきちんと確認できていません。もうしばらくお時間をいただきたいです。

  • 「PIN3を出力端子に変更すると、TONE利用時のパルスがその端子からも出力されます。」への修正案

    gr_common/RLduino78/cores/RLduino78_basic.cpp の

    TOL0.tol0 &= ~(1 << u8TimerChannel);// タイマ出力レベルの設定

    TO0.to0   &= ~(1 << u8TimerChannel);// タイマ出力の設定

    TOE0.toe0 |=  (1 << u8TimerChannel);// タイマ出力許可の設定

    の箇所を

    if (bInterrupt == true) {

    TO0.to0   &= ~(1 << u8TimerChannel);// タイマ出力の設定

    TOE0.toe0 &= ~(1 << u8TimerChannel);// タイマ出力禁止の設定

    } else {

    TOL0.tol0 &= ~(1 << u8TimerChannel);// タイマ出力レベルの設定

    TO0.to0   &= ~(1 << u8TimerChannel);// タイマ出力の設定

    TOE0.toe0 |=  (1 << u8TimerChannel);// タイマ出力許可の設定

    }

    に変更

  • 藤田さんありがとうございます。

    設計した基板のLEDが不正に光る原因が、このTONEとの関連があることが分かったので良かったです。

    急いではいないので、ライブラリ側の対応有無の判断を待ちたいと思います。

    ※藤田さん中の人みたいですね。(笑)

  • 本当に恐縮してしまいます。toneが、D3のPWM出力を妨げるのは仕様としてありつつも、ポート出力に影響が出るとは。Fujitaさんの修正案、検討させていただきます。

  • 確認したところFujitaさんの修正で問題なそうでした。

    以下☆印の部分のみでもたぶん動作しますが、評価後にアップデートをかけていきたいと思います。

    ちなみに、carcon999さんのコメントが途中で切れたのは"<<"が制御文字と間違えられていたためですね。私もそうなりました。以下の<<は全角です。

    ☆if (bInterrupt == true) {

    TO0.to0   &= ~(1 <<u8TimerChannel);// タイマ出力の設定

    ☆ TOE0.toe0 &= ~(1 << u8TimerChannel);// タイマ出力禁止の設定

    ☆} else {

    TOL0.tol0 &= ~(1 << u8TimerChannel);// タイマ出力レベルの設定

    TO0.to0   &= ~(1 << u8TimerChannel);// タイマ出力の設定

    ☆ TOE0.toe0 |=  (1 << u8TimerChannel);// タイマ出力許可の設定

    ☆}

    原因は、トーン生成用タイマとしてTIO1を使用しており、TIO01はPIN2のPWM用としても割り当てらていて、トーン生成時にはタイマの出力をポートに出力しない設定にすべきところがポートに出力する設定になっていたため、該当のポートを有効にしたときにトーン生成タイマの出力がポートにも出てしまうことになっていました。

  • > "<<"が制御文字と間違えられていたためですね。

    &lt;&lt; と書けばおk

    # "&lt;" をシングルバイト文字で書けないものか…