- RXマイコンのソフト開発(1)クロック設定とLチカ
- RXマイコンのソフト開発(2)スイッチのチャタリング対策
- RXマイコンのソフト開発(3)スピーカーから音を出す
- RXマイコンのソフト開発(4)UART送信でHelloWorld
- RXマイコンのソフト開発(5)ADCでバッテリーの電圧測定
- RXマイコンのソフト開発(6)RSPIでMPU6000から角速度取得
- RXマイコンのソフト開発(7)モータドライバーDRV8836の動作
- RXマイコンのソフト開発(8)磁気式エンコーダーAS5047
RXマイコン(RX631)のソフト開発の3回目です。今回は、スピーカーから音を出したいと思います。MTUという機能を用いて、端子から矩形波を出力することで音を出します。この機能によって、いちいちプログラムで端子をLOW、HIGHに設定せずとも、自動で矩形波が出力されます。
環境
- パソコン: Windows10 64 bit
- ターゲットデバイス: R5F5631PDDFL(RX631 48ピン)
スピーカーの回路
作成したスピーカー部分の回路図は図1です。SPEAKERのポートは P14/MTIOC3A です。MTIOC3Aの機能を持った端子からは任意の周波数の矩形波を出力できるので、その機能を使って音を出力したいと思います。
プログラムフロー
プログラムの処理の流れは図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とコンペアマッチでカウントクリアするようにします。
あとは、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;
各値は以下の式で決定しています。
最後に、MTU.TSTR.BIT.CST3=1でMTU3を動作、CMT.CMSTR0.BIT.STR1 = 1でCMT1を動作させています。
プログラムの実行
プログラムを書き込むとスイッチを押すたびに以下のように音が変化します。
おわりに
今回でスピーカーから音が無事出力できました。次の記事では、デバッグを行いやすくするために、UARTで送信を行いたいと思います。