UMEHOSHI ITA TOP PAGE    COMPUTER SHIEN LAB

[UMEHOSHI ITA]の制御で使っているIC「PIC32MX270F256B-I/SO」のフラッシュメモリには、テスト用プログラムが書き込まれいています。
以降では、このプログラムを「テスト・ウメ・フラッシュ」と呼ぶことにして解説します。
また、「テスト・ウメ・フラッシュ」を利用したユーザー用のプログラムを 「ウメ・エディットプログラム」と呼ぶことにします。
「ウメ・エディットプログラム」の開発では「umehoshiEditツール」が必要で、 その取得や最初の操作情報は、こちらを参照してください。
(「PICKit3などの書き込みツール」をお持ちの方で、「テスト・ウメ・フラッシュ」を利用しないで、 「MPLAB X IDE」の開発環境ですべてをプログラミングする場合の情報ではありません。)
「テスト・ウメ・フラッシュ」をどのように利用して、「ウメ・エディットプログラム」を 作るかの解説で、「umehoshiEditツール」の使用例を示しています。
各サンプルは、このWebページ上でドラック&コピーして、貼り付けしてご利用ください。

各種確認プログラム左記に必要な部品の追加例

USBで接続されるホストにメッセージ(文字列)を送信する。

とくに必要ありません。(D1のLEDはあるとよい)

モータ制御、PWM (Timer2利用)

PWM対応の部品追加例

ADC(A/Dコンバータ)を使う(Timer3利用)

ADC 対応の部品追加例

BEEP(ブザー音で、デフォルトのCORE Timer、Timer1を利用)

BEEP SWITCH 対応の部品追加例

[UMEHOSHI ITA]単体で動作させる

Reset SW, Type-A, CN2 部品追加例

赤外線制御

U20,D4,D5,NPN, D3 部品追加例

UART を介して、Bluetoothなどで制御

CN11,CN-12 ,Bluetoothなどの基板 の部品追加例

UART を介して、RN-42(Bluetooth)を制御

CN11,CN-12 部品追加例

UART を介して、ESP32-WROOM-32DをBASIC制御

U19 部品追加例

UART を介して、RN4020(BLE)で制御

U17にRN4020の部品を追加する例

BEEP音におけるコード音の出力

ここでは上記の赤マルの圧電スピーカを鳴らします。(ここで使うの部品追加例)
「テスト・ウメ・フラッシュ」では、「マイクロコントローラ (PIC32MX270F256B」内の「CORE TIMER」の周期を利用して、 ビープ音を鳴らすプログラムが組み込まれています。
利用する場合は「_set_beep_code(byteコード)」の呼び出しで行います。
このコードは、キューに登録されて、これを呼び出したプログラムの流れではなく、「CORE TIMER」の割込み処理で音を鳴らします。
音は引数のbyteコードの上位4bitの1と0を、それぞれ長音と短音に対応させて鳴らします。
これを使うと、モールス信号で情報知らせるようなアプリを簡単に作れるでしょう。
以下で、「110」「1011」の情報をループで生成するコード例を示します。

#include <xc.h>//beep_code.c
#include <stdlib.h>
#include "common.h"

#define AdrStart	0x80005000 //下記start関数の起動アドレス

__attribute__((address( AdrStart  ))) void start (void);
void start()
{
	//_set_switching_period( 90 );//短音の間隔
	_clear_beep_code(); // 登録を先頭に初期化
	_set_beep_code((uint8_t)0b11000011);//最初の音パターンの登録
	_set_beep_code((uint8_t)0b10110100);// 次の音パターンの登録
}
__attribute__((address( 0x80007000 ))) void clear (void);
void clear()
{
	_clear_beep_code();
}

上記の実行でブザーを付けていれば、「110」「1011」の コードに対応する音として 次のように聞こえるはずです。

ツー・ツー・ト   ツー・ト・ツー・ツー 

ツーが長音、が短音です。
そして、長音の長さは短音の3倍で、コード内の音と音の間隔は、短音と同じです。
また一つのパターンの音が終わって、次のパターンの音が始まるまで、「短音の3倍」の間隔があります。
またデフォルトでは、登録したパターンの音生成が終わると、少し待ってから再び始まるループ再生になっています。

最初の音パターンの登録で0b11000011を指定していますが、下位3ビットが生成音のビット数です。
これは、上位ビットの3個のbitを使う意味です。 この 110 で、ツー・ツー・トになります。
_set_beep_codeで指定する1byte引数は、[PTTERN:4bit][CB:1bit][LN:3bit]の構造のパターンになってます。

次の音パターンの登録の0b10110100は、下位3ビットが100より 4個のbitで、それが1011なので ツー・ト・ツー・ツーになります。
つまり1回の登録は、4bitの音パターンを登録することができます。これで16進の音パターンの登録ができます。
(元々はデバック用に作成したものです。
2回使えば1byte分の情報を音で知ることができます。 set_beep_codeによる最大登録数は42個までできます。)

「CORE TIMER」によるビープ処理は、所定の記憶域のコードで「ブザーON・OFF処理」を行うことで実現しています。
制御チップ内の「CORE Timer」割り込みは、デフォルトで動作しており、440Hz(0.00113635秒周期)でブザーの周期を制御しています。
上記の「_set_switching_period( 90 );」は短音の間隔を 90 に指定するコードです。
  (デフォルト値が90なので、上記ではコメントにしています。)
Timer割り込みでは、この値までカウントしたタイミングで音や無音の長さを制御しています。
デフォルトの短音の間隔の90は、(割り込み周期の0.00113635秒)*90=約0.1秒の時間です。

さて、モールス信号のように4ビットを超える場合は[CB:1bit]を1にすることで、連続パターンを指定できます。
例えば、訂正音は「ト・ト・ト・ト・ト・ト・ト・ト」の短音8で、数字の1は「ト・ツー・ツー・ツー・ツー」ですが、 そのコードは次のようになります。

#include <xc.h>
#include <stdlib.h>
#include "common.h"

#define AdrStart 0x80005000 //下記start関数の起動アドレス

__attribute__((address( AdrStart  ))) void start (void);
void start()
{
	//_set_switching_period( 90 );//短音の間隔
	_clear_beep_code(); // 登録を先頭に初期化
	_set_beep_code((uint8_t)0b00001100);//  パターンを次に続ける指定
	_set_beep_code((uint8_t)0b00000100);// 次の音パターンの登録(2行で、「ト・ト・ト・ト・ト・ト・ト・ト」の短音を登録)

	_set_beep_code((uint8_t)0b01111100);
	_set_beep_code((uint8_t)0b10000001);//前の1行と合わせて[・−−−−]を登録

	// _UM_PTR_GOTO_BEEP  = NULL;// ループをしない設定
}

なお、上記コードの最後の「_UM_PTR_GOTO_BEEP」の記憶をNULLにすると、ループ再生が無くなります。
「_UM_PTR_GOTO_BEEP」には、set_beep_code関数で登録されたコードの音再生がすべて終わった時に進むべきアドレスを記憶しており、 デフォルトでset_beep_code関数で登録する配列の先頭アドレスが記憶されています。


BEEP音における疑似サウンドの出力

次のコードで、「ドレミファソラシド」までを繰り返し鳴らすことができます。

#include <xc.h>
#include <stdlib.h>
#include "common.h"

#define AdrStart 0x80005000 //下記start関数の起動アドレス

__attribute__((address( AdrStart  ))) void start (void);
void start()
{
	int i=0;
	_set_switching_period( 125 );//テンポ(標準値)
	_set_beep_sound_node(i++,40,4,0);//ド(最初のこの呼び出しで、内部が初期化)〇
	_set_beep_sound_node(i++,42,4,0);//レ〇
	_set_beep_sound_node(i++,44,4,0);//ミ〇
	_set_beep_sound_node(i++,45,4,0);//ファ〇
	_set_beep_sound_node(i++,47,4,0);//ソ〇
	_set_beep_sound_node(i++,49,4,0);//ラ〇
	_set_beep_sound_node(i++,51,4,0);//シ〇
	_set_beep_sound_node(i++,52,8,4);//ド〇

	T1CONSET =0x8000; // Start timer1(この実行で、演奏がスタートします。)

	// _UM_PTR_GOTO_BEEP  = NULL;// ループをしない設定
}

set_beep_sound_nodeの引数は、『記憶位置の添え字、音色データ、音の長さ、音の後の無音の長さ』になっています。
音を出す振動(音色)は、「CORE TIMER 割込み処理」で行われ、引数はピアノ鍵盤番号です。各鍵盤番号は次のようになっています。

引用元
鍵盤番号音階周波数Hz 鍵盤番号音階周波数Hz 鍵盤番号音階周波数Hz 鍵盤番号音階周波数Hz
1ラ(A0)27.500 25ラ2(A2)110.000 49ラ4(A4)440.000 73ラ6(A6)1760.000
2ラ0(A#0)29.135 26ラ#2(A#2)116.541 50ラ#4(A#4)466.164 74ラ#6(A#6)1864.655
3シ0(B0)30.868 27シ2(B2)123.471 51シ4(B4)493.883 75シ6(B6)1975.533
4ド1(C1)32.703 28ド3(C3)130.813 52ド5(C5)523.251 76ド7(C7)2093.005
5ド1#(C#1)34.648 29ド#3(C#3)138.591 53ド#5(C#5)554.365 77ド#7(C#7)2217.461
6レ1(D1)36.708 30レ3(D3)146.832 54レ5(D5)587.330 78レ7(D7)2349.318
7レ1#(D#1)38.891 31レ#3(D#3)155.563 55レ#5(D#5)622.254 79レ#7(D#7)2489.016
8ミ1(E1)41.203 32ミ3(E3)164.814 56ミ5(E5)659.255 80ミ7(E7)2637.020
9ファ1(F1)43.654 33ファ3(F3)174.614 57ファ5(F5)698.456 81ファ7(F7)2793.826
10ファ1#(F#1)46.249 34ファ#3(F#3)184.997 58ファ#5(F#5)739.989 82ファ#7(F#7)2959.955
11ソ1(G1)48.999 35ソ3(G3)195.998 59ソ5(G5)783.991 83ソ7(G7)3135.963
12ソ1#(G#1)51.913 36ソ#3(G#3)207.652 60ソ#5(G#5)830.609 84ソ#7(G#7)3322.438
13ラ1(A1)55.000 37ラ3(A3)220.000 61ラ5(A5)880.000 85ラ7(A7)3520.000
14ラ#1(A#1)58.270 38ラ#3(A#3)233.082 62ラ#5(A#5)932.328 86ラ#7(A#7)3729.310
15シ1(B1)61.735 39シ3(B3)246.942 63シ5(B5)987.767 87シ7(B7)3951.066
16ド2(C2)65.406 40ド4(C4)261.626 64ド6(C6)1046.502 88ド8(C8)4186.009
17ド#2(C#2)69.296 41ド#4(C#4)277.183 65ド#6(C#6)1108.731
18レ2(D2)73.416 42レ4(D4)293.665 66レ6(D6)1174.659
19レ#2(D#2)77.782 43レ#4(D#4)311.127 67レ#6(D#6)1244.508
20ミ2(E2)82.407 44ミ4(E4)329.628 68ミ6(E6)1318.510
21ファ2(F2)87.307 45ファ4(F4)349.228 69ファ6(F6)1396.913
22ファ#2(F#2)92.499 46ファ#4(F#4)369.994 70ファ#6(F#6)1479.978
23ソ2(G2)97.999 47ソ4(G4)391.995 71ソ6(G6)1567.982
24ソ#2(G#2)103.826 48ソ#4(G#4)415.305 72ソ#6(G#6)1661.219
			

また、一つの音長さや無音間隔は、別途変数を参照してON/OFFの制御が行われます。
第3、4の引数の値は、32分音符も長さを1とした値です。
2なら16分音符、4なら8分音符、8なら4分音符(0.5秒)、16が2分音符、32が全音符になります。
4ビットコード音出力の「CORE TIMER 割込み処理」と違って、音符のON・OFFは「TIMER1 割込み処理」で行われます。
「TIMER1 割込み処理」はデフォルトでスタートしていないので「」で起動させる必要があります。 デフォルトのTimer1は、0.5mSで割り込みが発生するようになっています。
_set_switching_period関数で全体のテンポを変更することができます。
上記の125の設定が標準値です。この125の値のカウントで区切られる間隔が32分音符です。
この間隔(32分音符)は、0.5mS ×:125=0.0625秒です。

さて、自身で用意した音データ記憶域を利用することができます。 それは、記憶域を[_UM_PTR_BEEP_AREA] に設定することで可能です。
(ループの移動先[_UM_PTR_GOTO_BEEP]の設定も必要です。)以下に例を示します。

#include <xc.h>
#include <stdlib.h>
#include "common.h"

#define AdrStart 0x80005000 //下記start関数の起動アドレス

__attribute__((address( AdrStart  ))) void start (void);
void start()
{
	static uint8_t area[] = {
		28,4,0,  30,4,0,  32,4,0,  33,4,0,  35,4,0,  37,4,0,  39,4,0,  40,8,8,  
		40,4,0,  39,4,0,  37,4,0,  35,4,0,  33,4,0,  32,4,0,  30,4,0,  28,8,16,  
		28,4,0,  30,4,0,  32,4,0,  33,4,0,  35,4,0,  37,4,0,  39,4,0,   
		40,4,0,  42,4,0,  44,4,0,  45,4,0,  47,4,0,  49,4,0,  51,4,0,  
		52,4,0,  54,4,0,  56,4,0,  57,4,0,  59,4,0,  61,4,0,  63,4,0,  64,8,8,
  		
		0 //最後は終わりのマーク
	};
	_set_switching_period( 125 );//テンポ(標準値)
	_UM_PTR_BEEP_AREA  = area;
	_UM_PTR_GOTO_BEEP  = area;// ループをする指定(しない場合はNULLを設定)

	_set_beep_sound_node(0, 27,4,0);//先頭要素の設定で内部初期化を行う

	T1CONSET =0x8000; // Start timer1
}

デバック用のビープ関数 詳細


以下の関数群において、bが1で実行すると、cの16進の音が登録されて、そのパターンの音が繰り返しで鳴り始めます。
音のパターンはcの値の16進数で1が長音、0が単音で4つの音を出します。
_debug_hex4が4bit用、_debug_hex8が8ビット用・・・です。
第1引数のnの指定範囲は0から15です。その番号の音が既に鳴っている場合は何も何もせず戻ります。
 (_debug_hex4〜_debug_hex32のそれぞれでn番号の音の登録が可能です)
_debug_hex4(n,c,b)
_debug_hex8(n,c,b)
_debug_hex16(n,c,b)
_debug_hex32(n,c,b)
音が登録されるか、既に鳴っている時の戻り値が1になります。それを利用して
複数の箇所で使うことで、どちらのコードが先に行われるか判断でき、その例を以下に示します。
下記は、Aの位置を通過した時点で、0000のパターンで音をスタートさせて、
その後に、Bの位置に来た時点で、1111のパターンの音を追加させる場合の例です。
int bA = 0;
int bB = 0;	とグローバル変数を用意して、
Aの位置で、bA=_debug_hex4(1,0b0011, 1 ); と書いて
	・・・・・・
Bの位置に、bB=_debug_hex4(2,0b0101, bA ); と書きます。

プログラムがAの位置を通過していれば、・・― ― のパターンで音がします。
その後で、Bの位置を通過すれば、・・― ― ・―・― のパターンの音が追加して鳴ります。
Aの実行でbAが1になっていない時は、Bの位置を通過しても音が登録されません。
2回通過しているかの判定であれば次のような書き方をします。
int bT0=0,bT1=0; //グローバルで用意します。
判定箇所で、次を書きます。

bT1=_debug_hex4(1,0b0001, bT0 );	// 【C】
bT0=_debug_hex4(0,0b0000, 1 );		// 【D】

【C】、【D】を通過した時、bT0が0なので【C】で音は鳴らずに【D】の音が出てbT0が1に変わります。
再度【C】、【D】を通過した時、bT0が1なので【C】で音が追加登録されてなり始めます。

次の関数は、bが1であれば、この関数がc回通過した時点で、nのパターン(16進)音が出ます。
(nの指定範囲は0から15です)
既に鳴っている場合は、何もせずに1で戻ります。
なお、 音が登録、または鳴っている状態で1、鳴っていなければ0を返します。
_debug_count(n,c,b)

例えば、 _debug_count(5,10,1); と実行させれば、ここを10回実行した時点で、5に対応する・―・―のパターンで鳴りだします。

同じパターンのnの引数で、複数の箇所で使った場合、それぞれの箇所の通過総数がcに達した時点で鳴りだします。

異なるnで使った場合は、それぞれのcの回数だけ実行した時点で、それぞれのnのパターン音で鳴ります。
その場合、通過順で音のパターンが登録され、その順番で音が鳴るので、どちらが先に通過したか判断できます。

下記は、少し複雑ですが上記デバック関数を検証するコード例です。
●最初にsetting関数内で、「・・・・」の音が鳴ります。そしてrestart()が実行
●restart()でタイマ4のインターバルの割込み関数に、 timer()を指定し、「・・・―」の音を鳴らせます。
●timer()が0.05m秒ごとに実行しますが、100000回実行した時_debug_countで「・・― ―」の音を鳴らせます
 (ここで、変数t3が1に変わります)
●timer()の中で、t3が1なので300000回実行後にbが1になり、タイマ4の割込みを終わらせ、音も停止させます。
 (4の「・―・・」となる音が登録されますが、すぐ停止するので、この音は鳴りません。)
●SW1のリセットスイッチを付けている場合、このリセットでrestart()が起動する指定を、setting関数で行っています。
 つまり、リセット操作で上記の「・・・―」「・・― ―」の登録動作を再び確認できます。
#include <xc.h>// beep_debug.c
#include <stdlib.h>
#include "common.h"

int t3=0;
void timer()//「割り込みで目的のプログラム行うため処理: 0.00005秒ごとに呼び出される。」
{
	int b = _debug_count(4 ,300000 , t3);//t3が1であれば、300000回通過したら4のコード音
	t3  = _debug_count(3,100000,1);//100000回通過したら3のコード音で、1の戻り値
	if(b) {
		IEC0bits.T4IE = 0;// Timer4 Disable(割込み不許可)
		T4CONbits.ON = 0;// timer割込みOFF
		_send_string("Timer STOP\n");	
		_clear_beep_code();//通知音がクリア
	}
}

void restart(){
	_HANDLES[_IDX_TIMER_4_FUNC] = timer;//デフォルトで0.00005秒ごとに呼び出しに登録 	
	IEC0bits.T4IE = 1;// Timer4 Enable(割込み許可)
	T4CONbits.ON = 1;// timer割込みオン
	_debug_hex4(1,1,1);
	_send_string("Timer START\n");
}

__attribute__((address( 0x80005000 ))) void setting(void);
void setting()
{
	_HANDLES[_IDX_INIT_SUB_FUNC] = restart;// リセットで実行する関数の登録
	_clear_beep_code();//通知音がクリア
	_UME_CONFIG |= 1;//LED D2の変化をLED D1に連動させる
	_debug_hex4(0,0,1);
	restart();
}

「テスト・ウメ・フラッシュ」プログラムの内部情報

音の再生は、割り込み内の[beep_code_switching()関数]で行っているが、
_clear_beep_code()の実行は、この関数に同期しないで内部ポインタをNULLにすることを行っている。
よって、音の再生割り込みタイミングによって、_clear_beep_code()が働ないタイミングが存在し、
場合によっては、NULLの参照が生じて、予想外のコードに変わることも考えられる。(そうならない考慮はしているが・・)
よって、使う場所によっては、割込みとの排他制御が行う必要がある。
なお、_clear_beep_code()を同期させなかった理由は、同期させると、ブザー間隔のタイミングでウェイトがかかるためです。