RXマイコンのソフト開発(3)スピーカーから音を出す

RXマイコン(RX631)のソフト開発の3回目です。今回は、スピーカーから音を出したいと思います。MTUという機能を用いて、端子から矩形波を出力することで音を出します。この機能によって、いちいちプログラムで端子をLOW、HIGHに設定せずとも、自動で矩形波が出力されます。

環境

  • パソコン: Windows10 64 bit
    • 統合開発環境: e2studio 2021-01をインストール
    • コンパイラ: Renesas CCRX v3.03.00をインストール
    • 書き込みソフト: Renesas Flash Programmer V3.08.01(無償版) をインストール
  • ターゲットデバイス: R5F5631PDDFL(RX631 48ピン)

スピーカーの回路

図1:スピーカーの回路図
図1:スピーカーの回路図

作成したスピーカー部分の回路図は図1です。SPEAKERのポートは P14/MTIOC3A です。MTIOC3Aの機能を持った端子からは任意の周波数の矩形波を出力できるので、その機能を使って音を出力したいと思います。

プログラムフロー

プログラムの処理の流れは図2です。レジスタプロテクトの設定は省略しています。

図2:プログラムのフローチャート
図2:プログラムのフローチャート

まず、前回の記事と同様にクロックの初期化とI/Oポートの設定、コンペアマッチタイマー0(CMT0)の初期化を行っています。次に、CMT1の初期化とマルチファンクションパルスユニット2 のチャネル3 (MTU3)を初期化します。その後、メインルーチンに入り、スイッチの押し込みを検知したら音を出力します。音は0.5秒間だけ出力して、音階名でC5からC6を出力するようにします。

プログラム

重要なプログラム部分だけ示します。全てのソースコードGitHubの「3_speaker」フォルダの中にソースコードがありますので、詳細を知りたい場合はご覧ください。

メイン関数

main.c:メイン関数のソースコード

#include "define_wakaba.h"
#include "interface.h"

void main(void){
    unsigned char spk_note_num = 0;

    init_rx631();  // Overall Initialization

    while(1){ 
        if(g_sw_chg){   // Enter when SW is pressed
            g_sw_chg = 0;
            switch(spk_note_num){
            case 0:
                SPK_Soundout(C5, 50);
                break;
            case 1:
                SPK_Soundout(D5, 50);
                break;

            ~略~       

            case 7:
                SPK_Soundout(C6, 50);
                break;
            }
            spk_note_num++;
            spk_note_num = spk_note_num % 8;
        }
    }
}

メイン関数では、RX631の初期化を行い、割り込み関数によってスイッチの押し込みフラグ(g_sw_chg)が立ったら、フラグを下げて、SPK_Soundout で音を出力するようにしています。

CMT1とMTU3の初期化

初期化関数のソースコードが以下です。ここでは MTU3 の初期化について主に説明します。MTU に関してはユーザーズマニュアルの p.765「23. マルチファンクションタイマパルスユニット2(MTU2a)」に記述があります。

init_rx631.c:初期化関数のソースコード

#include "init_rx631.h"
#include "iodefine.h"

/*** Function Declaration ***/
static void init_clock(void);
static void init_cmt0(void);
static void init_cmt1(void);
static void init_mtu3(void);

/*---- RX631 Initialization ----*/
void init_rx631(void){
    SYSTEM.PRCR.WORD = 0xA503;  // Unprotect

    // MainCLK, SUBCLK and RTC Initialization
    init_clock();

    // CMT0 and CMT1 Initialization
    init_cmt0();
    init_cmt1();

    // MTU3 Initialization
    init_mtu3();

    SYSTEM.PRCR.WORD = 0xA500;  // Reprotect
}

~略~

/*---- CMT1 Initialization ----*/
void init_cmt1(void){

    MSTP(CMT1) = 0;          // CMT1 Module Stop Release

    CMT.CMSTR0.BIT.STR1 = 0; // CMT1 Stop
    CMT1.CMCR.WORD = 0x00C1; // interrupt enabled and PCLK/32
    CMT1.CMCOR = 15624;      // Set interrupt cycle to 10ms

    IPR(CMT1,CMI1) = 9;      // Priority Level 9
    IEN(CMT1,CMI1) = 1;      // Interrupt enabled
}

/*---- MTU3 Initialization ----*/
void init_mtu3(void){

    MSTP(MTU) = 0;              // MTU Module Stop Release

    MPC.PWPR.BIT.B0WI   = 0;    // PFSWE bit write enable
    MPC.PWPR.BIT.PFSWE  = 1;    // PmnPFS Register write enable
    MPC.P14PFS.BIT.PSEL = 0x01; // set to MTIOC3A
    MPC.PWPR.BYTE       = 0x80; // Reprotect
    PORT1.PMR.BIT.B4    = 1;    // set to Peripheral
    
    MTU3.TMDR.BIT.MD   = 0x2;   // PWM mode1
    MTU3.TIORH.BIT.IOA = 0x5;   // Initial output HIGH, Compare match LOW
    MTU3.TIORH.BIT.IOB = 0x6;   // Initial output HIGH, Compare match HIGH
    MTU3.TCR.BIT.TPSC  = 0x1;   // PCLK/4
    MTU3.TCR.BIT.CKEG  = 0x0;   // count by riging edge
    MTU3.TCR.BIT.CCLR  = 0x2;   // Clear by compare match TGRB
}

CMT1の初期化はCMT0の初期化とほとんど変わらないです。変化している点は割り込み周期が10msである点とカウンタを動作させていない点です。

MTU3の初期化では、まず、モジュールストップ機能によって MTU が停止しているため、モジュールストップ状態を解除します。

端子機能の変更

MPC.PWPR.BIT.B0WI   = 0;    // PFSWE bit write enable
MPC.PWPR.BIT.PFSWE  = 1;    // PmnPFS Register write enable
MPC.P14PFS.BIT.PSEL = 0x01; // set to MTIOC3A
MPC.PWPR.BYTE       = 0x80; // Reprotect
PORT1.PMR.BIT.B4    = 1;    // set to Peripheral

端子の機能を変更します。MTU3のPWMモード1で矩形波を出力したいので、P14端子の機能をMTIOC3Aに変更します。ただ、端子の機能を変更するレジスタがプロテクトされているので解除します。その解除は少し面倒で、MPC.PWPR.BIT.B0WI=0 で書き込みを可能にするビットの書き込みを可能にして、MPC.PWPR.BIT.PFSWE=1 で書き込みを可能にします。

P14 端子の選択できる機能は p.723「22.2.3 P1n 端子機能制御レジスタ(P1nPFS)(n=0 ~ 7)」に記述してあります。機能がMTIOC3Aとなるように、MPC.P14PFS.BIT.PSEL=0x01 とします。機能を選択できたら、MPC.PWPR.BYTE=0x80 で再度プロテクトします。また、端子を周辺機能として使用できるようにPORT1.PMR.BIT.B4=1でポートモードレジスタを変更します。

MTU3の設定

MTU3.TMDR.BIT.MD   = 0x2;   // PWM mode1
MTU3.TIORH.BIT.IOA = 0x5;   // Initial output HIGH, Compare match LOW
MTU3.TIORH.BIT.IOB = 0x6;   // Initial output HIGH, Compare match HIGH
MTU3.TCR.BIT.TPSC  = 0x1;   // PCLK/4
MTU3.TCR.BIT.CKEG  = 0x0;   // count by riging edge
MTU3.TCR.BIT.CCLR  = 0x2;   // Clear by compare match TGRB

MTU3の設定を変更します。今回は、PWMモード1で端子からPWM波形を出力したいので、MTU3.TMDR.BIT.MD=0x2を実行します。

図3のようにPWM波形を出力したいので、TIORH レジスタを変更して、初期出力HIGH、TGRAとコンペアマッチでLOW出力、TGRBとコンペアマッチでHIGH出力に設定します。

また、TCR レジスタの CCLR ビットを変更することで、TGRBとコンペアマッチでカウントクリアするようにします。

図3:PWMモード1の動作例
図3:PWMモード1の動作例

あとは、MTU3.TCR.BIT.TPSC = 0x1で分周比を4に設定して、MTU3.TCR.BIT.CKEG = 0x0でクロックの立ち上がりエッジでカウントするようにします。

音を出力する関数

interface.c:インターフェース用の関数のソースコード

#include "define_wakaba.h"
#include "interface.h"
#include "iodefine.h"

volatile unsigned char g_sw_chg;
static volatile unsigned char spk_cnt;

~略~

// CMT1 CMI1 Interrupt function
// Control soundout time
void SPK_CtrlCnt(void){

    // Reduce the count
    spk_cnt--;
    // Stop square wave output
    if(spk_cnt==0){
        MTU.TSTR.BIT.CST3 = 0;
        MTU3.TCNT = 0;
        CMT.CMSTR0.BIT.STR1 = 0;
        CMT1.CMCNT = 0;
    }
}

// Speaker Sound output
void SPK_Soundout(NOTE spk_note, unsigned char l_spk_cnt){
    spk_cnt = l_spk_cnt;
    MTU3.TGRA = spk_note>>2;
    MTU3.TGRB = spk_note;
    MTU.TSTR.BIT.CST3 = 1;
    CMT.CMSTR0.BIT.STR1 = 1;
}

SPK_CtrlCnt 関数は、割り込み関数の中に書かれている関数です。そのため、実質割り込み関数です。カウンタを減らして、カウンタがゼロになれば、MTU3を停止させて、カウンタTCNTをクリアさせています。また、CMT1も停止させて、カウンタCMCNTもクリアさせています。

SPK_Soundout 関数は、カウンタの初期値をまず設定してます。l_spk_cnt=50 とすれば、音を500 ms 間出力します。それから、TGRAやTGRBの値を設定して、周波数を設定します。spk_note の変数型がNOTEになっていますが、これはinterface.hに以下のように記述されたものです。

/*** type define ***/
typedef enum{
    C5 = 23900,  // 523Hz
    D5 = 21282,  // 587Hz
    E5 = 18960,  // 659Hz
    F5 = 17896,  // 698Hz
    G5 = 15943,  // 783Hz
    A5 = 14204,  // 880Hz
    B5 = 12654,  // 987Hz
    C6 = 11944   // 1046Hz
} NOTE;

各値は以下の式で決定しています。

\displaystyle{
TGRB=\frac{\rm PCLK}{分周比}\times \frac{1}{周波数} - 1
}

最後に、MTU.TSTR.BIT.CST3=1でMTU3を動作、CMT.CMSTR0.BIT.STR1 = 1でCMT1を動作させています。

プログラムの実行

プログラムを書き込むとスイッチを押すたびに以下のように音が変化します。

おわりに

今回でスピーカーから音が無事出力できました。次の記事では、デバッグを行いやすくするために、UARTで送信を行いたいと思います。

参考文献

お問い合わせフォーム プライバシーポリシー

© 2021 Setoti All rights reserved.