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 部品追加例

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

U19 部品追加例

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

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

[UMEHOSHI ITA]で、関数をEEPROMに配置する(PORTB5のパルス変調の再生関数をROMに配置する)例

[UMEHOSHI ITA]このプロジェクトに必要な部品の追加例です。


[UMEHOSHI ITA]のPORTB5の端子をON・OFFすることで、音を鳴らすことができます。
(PORTB5の端子の端子には、別途にLED D2のON・OFFも兼用しています。)

このページは、こちらのリンク先で行ったpb5_pwd関数に相当する「PWDで鳴らす機能」を、 「ウメ・エディットプログラム」で再利用できるようにEEPROMに配置する過程を示しています。



「ウメ・エディットプログラム」で利用できるEEPROM配置用の関数定義

C言語を使う場合の「ウメ・エディットプログラム」は、「umehoshiEdit」ツールを 使います。
このツールは「MPLAB XC32 (Free C compilers)」を利用しているのですが、 ビルド時はソースと同じフォルダ内にある「mylinkerscript.ld」を参照して コードのバイナリ位置を確定します。
「mylinkerscript.ld」の内容の一部を下記に示します。
MEMORY
{
  kseg0_program_mem     (rx)  : ORIGIN = 0x80005000, LENGTH = 0x4fc0
  exception_mem               : ORIGIN = 0x9D03F000, LENGTH = 0x1000
  debug_exec_mem              : ORIGIN = 0x9FC00490, LENGTH = 0x760
  kseg0_boot_mem              : ORIGIN = 0x9FC00490, LENGTH = 0x0
  kseg1_boot_mem              : ORIGIN = 0xBFC00000, LENGTH = 0x490
  config3                     : ORIGIN = 0xBFC00BF0, LENGTH = 0x4
  config2                     : ORIGIN = 0xBFC00BF4, LENGTH = 0x4
  config1                     : ORIGIN = 0xBFC00BF8, LENGTH = 0x4
  config0                     : ORIGIN = 0xBFC00BFC, LENGTH = 0x4
  kseg1_data_mem       (w!x)  : ORIGIN = 0x80008000, LENGTH = 0x1000
  sfrs                        : ORIGIN = 0xBF800000, LENGTH = 0x100000
  configsfrs                  : ORIGIN = 0xBFC00BF0, LENGTH = 0x10
}
上記のORIGIN = 0x80008000, LENGTH = 0x1000が静的な変数で使う記憶領域の位置と長さです。
つまり、このツールを使って「mylinkerscript.ld」の指示でビルドした作品のグローバル変数などは、同じ記憶域を使うので、 同じ「mylinkerscript.ld」の内容でビルドした複数のコードは共存できないことを意味します。
よって、このページの目標のように他の「ウメ・エディットプログラム」から利用するEEPROMに配置する関数を作る場合、
グローバル変数を定義しないで、動作するように変更する必要があります。
「ウメ・エディットプログラム」では、 _MEMO3(0x80009000番地)を先頭アドレスとする0x0F00byte(3840byte)記憶域があり、これをポインタ変数で管理することで、 グローバル変数の代わりとします。


この手法に従って、pb5_pwd関数のソースを変更します。
この場合、これまで使っていたグローバル変数や配列を_MEMO3から始まる領域に設定する必要があり、 その管理のためアドレスを決めておきます。
それを、次のように決めました。なお、前の識別名で配列でないものは"_P3"の接尾辞を付けた名前にしました。
_MEMO3の領域(0から0x0F00byteの範囲内を最後より前方に順番に配列や変数を割り当てました。
変数名_MEMO3のポインタ表現絶対アドレス備考
pwd_base (char*)_MEMO3+3808 0x80009EE0 =f"0x{0x80009000+0x0F00-2*16:8X}" 2byteのshort intが16個の配列
idx_base_P3 (char*)_MEMO3+3804 0x80009EDC =f"0x{0x80009EE0-4:8X}" 上記の配列領域より4byte前方の記憶域を使う
pwd_datas_P3 (char*)_MEMO3+3800 0x80009ED8 =f"0x{0x80009EDC-4:8X}"
pwd_size_P3 (char*)_MEMO3+3796 0x80009ED4 =f"0x{0x80009ED8-4:8X}"
pwd_idx_P3 (char*)_MEMO3+3792 0x80009ED0 =f"0x{0x80009ED4-4:8X}"
pwd_count_P3 (char*)_MEMO3+3788 0x80009ECC =f"0x{0x80009ED0-4:8X}"
change_count_P3 (char*)_MEMO3+3784 0x80009EC8 =f"0x{0x80009ECC-4:8X}"
この手法に従って、pb5_pwd関数のソースを関数ごとに分けたソースを定義しなおしてROM化を検討します。
最終的にchange関数の定義を、以下のchange.cのように定義しました。
(グローバル変数を無くして、  の箇所でローカルのポインタ変数に変更しています)
#include <xc.h> //change.c
#include "common.h"

#define NREP 6 // 1つ(4bit)のデータを、約(1/4410)秒周期で繰り返し使う回数(水平分解能指定パラメタ)

//__attribute__((address( 0x80005000 ))) void change(void);// 【1】サイスを調べるために一時的に使う
__attribute__((address( 0x9D03E620 ))) void change(void);// 【2】上記検討を経て決めた配置アドレス
void change(void) // OC2割り込み関数 (PWMのデューティ サイクルの変更
{
	short int *pwd_base = (short int *)((char *)_MEMO3+3808);// PWDのデータテーブル
	int *idx_base_P3 = (int *)((char *)_MEMO3+3804);// 上記のPWDのデータテーブルのアクセス用添え字
	char **pwd_datas_P3=(char **)((char*)_MEMO3+3800);// PWDのデータ添え字群の記憶領域の先頭アドレス
	int *pwd_size_P3 =    (int *)((char*)_MEMO3+3796);// 上記ポインタから始まったデータのサイズ
	int *pwd_idx_P3 =     (int *)((char*)_MEMO3+3792);// 上記アクセス用添え字
	int *pwd_count_P3 =   (int *)((char*)_MEMO3+3788);//(NREP+NREP)までのカウンタ
	int *change_count_P3 =(int *)((char*)_MEMO3+3784); // change()の割り込み回数計数で検証用

	(*change_count_P3)++;//割り込み回数計測用 (約44100Hzの周期でカウント)
	if(*pwd_count_P3 == 0){		// 下位4ビットの周期へ
		*idx_base_P3 = (*pwd_datas_P3)[*pwd_idx_P3] & 0x0f;
		OC2RS = pwd_base[*idx_base_P3];
	}else if(*pwd_count_P3 == NREP){	// 上位4ビットの周期へ
		*idx_base_P3 = ((*pwd_datas_P3)[*pwd_idx_P3] >> 4) & 0x0f;
		OC2RS = pwd_base[*idx_base_P3];
	}
	if(++(*pwd_count_P3) == (NREP+NREP)){ //上位4ビットが終わって次の要素周期へ
		*pwd_count_P3=0;
		(*pwd_idx_P3)++;
		if( *pwd_idx_P3 >= *pwd_size_P3 ) {//周期要素の配列を使い終わった
			*pwd_idx_P3 = 0;
			OC2RS=0;
			OC2CONbits.ON =0;	// OC2の動作OFF
		}
	}
	IFS0CLR = _IFS0_OC2IF_MASK;	// Clear the OC2 interrupt flag
}
まず、上記change関数の定義の【1】の行のコメント外して有用行とし、 【2】の行をコメントにして「umehoshiEdit」ツールでビルドします。
これで出来たchange.c.hexの一部は次のようになりました。
S108000500000D8FFBD272400BEAF21F0A00300A0023C3E
S108000501000E09E42340000C2AF00A0023CDC9E423467
・・・省略・・・
S10800051D0002400BE8F2800BD270800E00300000000A6
以上がプログラムコードを[UMEHOSHI ITA]のメモリに設定するコマンドコードとなる訳ですが、それは以下の範囲です。
0x80005000番地〜0x800051D0+0x10=800051E0番地の範囲です。
これは、0x800051E0-0x80005000=0x000001E0=480バイトです。
この範囲をROM領域(0x9D020000〜0x9D03E800)のどこに配置するかです。
配置位置は自由ですが、 消去する場合、(1024byte)バイト単位で行う必要があることや、 後から別つコード埋め込む可能がる場合などを考慮すると、アドレス値の大きい領域から使うとよいでしょう。
上記プログラムを最後に配置する場合、0x0x9D03E800-480=f"0x{0x9D03E800-480:08X}"=0x9D03E620番地に配置すればよいことになります。
関数の定義位置(アドレス)は、ワード単位で指定する必要する必要があり、上記のchange関数は0x9D03E620のアドレスに定義し直してしています。
なお、以前にこのEEPROM記憶域を使っている場合は、次のコードの実行で消去しておく必要があります。
EEPROM領域を削除するサンプルコードはこのリンク(erase.c)にありますが、 削除は、(1024byte)バイト単位です。
#include <xc.h>
#include "common.h"
__attribute__((address( 0x80005000 ))) void main(void);
void main()
{
	PORTBCLR = 0x8000;// LED1を消去
	_nvm_erase_page(0x9D03E000);// このアドレスから0x400(1024)バイトを消去
	_nvm_erase_page(0x9D03E400);// このアドレスから0x400(1024)バイトを消去
	PORTBSET = 0x8000;// LED1を点灯
	_send_string("END program\r\n");
}
この2048byteの消去範囲が前述のchange関数範囲より大きいのは、後述の関数pb5_pwdの定義領域を兼ねて消去するためです。
この消去プログラムの実行後に、前述のchange.c.hexを[UMEHOSHI ITA]へ転送します。
(この転送だけで、EEPROM領域に書き込みされます。)


次に前述の割り込みで使う0x9D03E620番地のchange関数を指定し初期化する関数pb5_pwdを次のpb5_pwd.cで作成します。
  の箇所で上記割り込み関数を登録しています。
#include <xc.h> //pb5_pwd.c
#include "common.h"

#define NREP 6 // 1つ(4bit)のデータを、約(1/4410)秒周期で繰り返し使う回数(水平分解能指定パラメタ)

//__attribute__((address( 0x80005000 ))) void pb5_pwd(char *pwd_adr, int size);// 【1】サイスを調べるために一時的に使う
__attribute__((address( 0x9D03E000 ))) void pb5_pwd(char *pwd_adr, int size);// 【2】上記検討を経て決めた配置アドレス

// pwd_adrの先頭アドレスからsizeバイトのサウンドデータの再生を割り込みで行わせる初期化関数
void pb5_pwd(char *pwd_adr, int size)
{
	short int *pwd_base = (short int *)((char *)_MEMO3+3808);// PWDのデータテーブル
	pwd_base[0]=2; //{2, 62, 122, 182, 242, 303, 363, 423, 483, 543, 604, 664, 724, 784, 844, 905};の代わり
	pwd_base[1]=62;
	pwd_base[2]=122;
	pwd_base[3]=182;
	pwd_base[4]=242;
	pwd_base[5]=303;
	pwd_base[6]=363;
	pwd_base[7]=423;
	pwd_base[8]=483;
	pwd_base[9]=543;
	pwd_base[10]=604;
	pwd_base[11]=664;
	pwd_base[12]=724;
	pwd_base[13]=784;
	pwd_base[14]=844;
	pwd_base[15]=905;
	int *idx_base_P3 = (int *)((char *)_MEMO3+3804);// 上記のPWDのデータテーブルのアクセス用添え字
	*idx_base_P3 = 0;		// PWDのデータテーブルのアクセス用添え字

	char **pwd_datas_P3=(char **)((char*)_MEMO3+3800);// PWDのデータ添え字群の記憶領域の先頭アドレス
	int *pwd_size_P3 =    (int *)((char*)_MEMO3+3796);// 上記ポインタから始まったデータのサイズ
	int *pwd_idx_P3 =     (int *)((char*)_MEMO3+3792);// 上記アクセス用添え字
	int *pwd_count_P3 =   (int *)((char*)_MEMO3+3788);//(NREP+NREP)までのカウンタ

	*pwd_datas_P3=pwd_adr;	// PWDのデータ記憶領域の先頭アドレス
	*pwd_size_P3 = size;		// PWDのデータサイズ
	*pwd_idx_P3 = 0;
	*pwd_count_P3 = 0;		// PWDのデータアクセス用添え字

	// Timer2の初期化------------------
	T2CON =0x00000000;//typeB,16bit, [1:1]プリスケール これで(1/40e6)の周期で、TMR2をカウントアップ
	T2CONbits.TCKPS = 0;// 1:1プリスケール値
	TMR2=0x0000; //16bitタイマの設初期値(上記設定周期でカウントして、PR2と一致するとクリアして割り込み)
	PR2=906; //16bitタイマの設定値(周期設定用  =int((1/44100)/(1/40e6)-1)
	IEC0CLR = 0x00000200;//T2IE Timer2 desable(割込み不許可)

	// Output Compare module 2の初期化-(割り込み指定を含む)-----------------
	OC2CON = 0x0000;// Turn off OC2 while doing setup. 
	RPB5Rbits.RPB5R = 0x5; // RB5をOC2の出力にする。
	OC2CONbits.SIDL = 0; // アイドルモード中も動作を継続する
	OC2CONbits.OC32 = 0; //OC2R<15:0> およびOC2RS<15:0> を使って16 ビットタイマ源と比較する
	OC2CONbits.OCTSEL = 0; // Timer2 をこの出力コンペア モジュールのクロック源として使う
	OC2CONbits.OCM = 5; // OC2 をPWM モードにし、フォルトピンを無効にする

	_HANDLES[_IDX_OUTPUT_COMPARE_2_VECTOR] = (void*)0x9D03E620; // OC2割り込みchange関数を登録
	IFS0CLR = _IFS0_OC2IF_MASK;	// Clear the OC2 interrupt flag
	IEC0SET = _IEC0_OC2IE_MASK;	// Enable OC2 interrupt 
	IPC2bits.OC2IP = 7;// Set OC1 interrupt priority to 7
	IPC2bits.OC2IS = 3;// Set Subpriority to 3, maximum
	//OC2R = 0;//出力コンペア1 コンペアレジスタの初期値
	//OC2RS = 905;//出力コンペア1 セカンダリコンペアレジスタ(デューティー比設定用で上記設定の最大値 )

	T2CONbits.ON = 1; // Timer2の動作スタート
	OC2CONbits.ON =1;	// OC2の動作スタート
}
上記をビルドした後に、[UMEHOSHI ITA]に転送する前に、得られたpb5_pwd.c.hexの「UME専用Hexコマンド」ファイル内容から、 0x9D03E000〜「0x9D03E2A0+0x10=0x9D03E2B0」の944バイトの範囲と確認します。
この範囲が、以前に書き込んだ範囲と重ならないことを確認してから転送します。
(前回転送したchange関数の先頭が0x9D03E620なので、重なっていないのが分かります)

pb5_pwd.c.hexの転送で、0x9D03E000から始まるpb5_pwd関数を利用すれば、指定アドレスから始まる指定サイズのサウンドデータを再生できることになります。
以下にその例を示します。
なおこの例は、このページで作ったdata.bin.hexの転送で設定される記憶域(0x80009000から始まる3451バイト)のデータを使って、「え、何?」の音を再生するコード例です。
#include "common.h"

#define NREP 6 // 1つ(4bit)のデータを、約(1/4410)秒周期で繰り返し使う回数(水平分解能指定パラメタ)

__attribute__((address( 0x80005000 ))) void main(void);
void main()
{
	void (*pb5_pwd)()=(void*)0x9D03E000;// 前述のpb5_pwd関数を配置したEEPOROMのアドレス

	int *change_count_P3 =(int *)((char*)_MEMO3+3784); // change()の割り込み回数計数で検証用 
	*change_count_P3 = 0;	// 検証用
	PORTBINV = 0x8000;	// LED1を反転
	unsigned long count_now = _CP0_GET_COUNT();//(1/40e6)*2 =50ナノ秒でカウントするコアタイマーの値を取得
	unsigned long count_end = count_now  + 200000000;//の追加が10秒後のカウント値(10/50e-9=200000000)

	pb5_pwd((char *) 0x80009000, 3451);// 0x80009000番地より3451byteサイズのデータを鳴らす

	while(count_now < count_end && OC2CONbits.ON == 1){ // 10秒間、または再生中の実行
		count_now = _CP0_GET_COUNT();//50ナノでカウントするコアタイマーの値を取得
	}
	PORTBINV = 0x8000;// LED1を反転

	_send_decimal(*change_count_P3,10);
	_send_string(" <==change_count\r\n");
}
この実行の再生音は、このページの実験でで示した音と同じです。
ファイル("え_何.wav")の16bitを、積分、微分、増幅、後に分解能4ビットに変換ししたデータを使って手持ちのスピーカーで鳴らした音です。

このスピーカー使用時の制限抵抗は、R9の2.2Kを47Ωに変更して実験した結果です。

上記で再生しているサウンドデータは、RAM領域の0x80009000番地より3451byteサイズです。

この時点で使っている[UMEHOSHI ITA]内メモリ利用状態を以下に示します。

アドレス 0x80005000〜 0x80009000〜0x80009D7B 0x80009EC8〜0x80009F00 0x9D03E000〜のROM領域 0x9D03E620〜のROM領域
内容 main関数定義域 音データ記憶域
「え、何?」の音
change関数と
pb5_pwd関数で使う
グローバル記憶域
上記pb5_pwd関数定義域
アドレスが示す音データを
鳴らす指定関数
change割り込み関数定義域
PWDの周期を変更する

この続きとして、 このようなサウンドデータを、ROM領域に複数配置して使う方法を、次のこのページで示します。