UMEHOSHI ITA TOP PAGE

アセンブリ言語で作る「 EEPROM上で動作するLチカ プログラム」(起動後もUSB通信を可能にする

D1のLEDは、下記回路図の赤の接続線で示しています。赤マルのRB15に繋がっていますが、 これは制御チップで[PORTB REGISTER]のBit15をを意味しています(資料:DS60001168K TABLE 11-4: PORTB REGISTER MAP)。
このRB15の出力端子がLowで消え、HiにすればLEDに電流が流れて点灯します。

これまで紹介したLED点滅処理の 1,2,3などは、 無限のループの中で、「待ち」、「LED点灯」、「LED消灯」、「待ち」を行うことで作っていました。
そのような作り方で、この無限ループを実行をしていまうと、USB通信が出来なくなってしまいます。
無限ループでなくても、下記のように作った  のプログラムの処理が長すぎて、 下記赤矢印のリターンが遅れると、正しく動作できなくなります。


UME専用Hexコマンドが受付可能なUSB通信も正しく動作する作品を 実現するためには、その利用者のプログラム(左記  )で、 速やかに終わらせて赤矢印のリターンが行われるように作り方にします。

このためには、上記の  で示す部分で、時間がかかる繰り返しを使わないで、 代わりにタイマーの割り込みで、  を次のように動作させます。

タイマーの割り込みの周期を、上手く調整すれば  の部分で、
繰り返しの必要が無くなります。

青の流れで実行する関数を、
 _HANDLES[_IDX_TIMER_4_FUNC] に登録します。
(この関数内で、赤の流れのタイマー割り込み処理を登録します)

上記で青の流れが起動処理が、リセット時の呼び出しです。
ここでタイマー処理の準備と、割込み許可を行います。
この許可により、赤の流れのタイマー割り込みが一定周期で実行するするように作ります。

以下では、この具体的な例(D1のLEDでの「 Lチカ 」無限ループ)をC言語で示し、 この逆アセンブルしたコードを参考に、アセンブリリストを示しています。
(下記C言語ソースは、「umehoshiEdit」ツールで、ビルド実行できます。)

上記の赤の流れLED点滅処理で、この関数をmain_loopの名前で0x9D020600番地に作ります。
そして、この関数をタイマー処理に登録する処理などのタイマー初期設定の起動関数(青の流れ)を、 0x9D020000番地に、init_timerの名前で作っています。


#include <xc.h>	// test.c
#include "common.h"

int flag=0;	// 0で点灯して1に変更
int count=0;// 点滅周期カウント用

//__attribute__((address( 0x80005100 ))) void main_loop (void);
// 上記はRAM配置で、下記はROM配置用
__attribute__((address( 0x9D020600 ))) void main_loop (void);
void main_loop(){
	count++;
	//if( count <= 4000 ) return; // 0.2秒まで、カウント
	if( count <= 20000 ) return; // 1秒まで、カウント
	count=0;
	if(flag == 0)
	{
		PORTBSET = 0x8000;	// D1 LED 点灯(RB15を1):0xbf886128番地への設定です.
		flag = 1;
	}
	else
	{
		PORTBCLR = 0x8000;	// D1 LED 消灯(RB15を0):0xbf886124番地への設定です.
		flag = 0;
	}
}

//__attribute__((address( 0x80005200 ))) void init_timer();
// タイマーの初期化処理;上記はRAM配置で、下記の0x9D020000はEEPROM領域のリセット起動用アドレス
__attribute__((address( 0x9D020000 ))) void init_timer();
void init_timer(){

	flag=0;	// 0で点灯して1に変更
	count=0;// 点滅周期カウント用

	T4CON =0x00000000;//typeB,16bit, [1:1]プリスケール
	TMR4=0x00000000; //16bitタイマの初期値(レジスタがカウントアップ)
	PR4=1999; //これは (1/40e6)*(1999+1) =0.05ミリ秒 
	IFS0bits.T4IF = 0;// Clear the timer interrupt status flag
	IEC0bits.T4IE = 1;// Timer4 Enable(割込み許可)
	_HANDLES[_IDX_TIMER_4_FUNC] = main_loop;// 割り込み関数のデフォルトを変更登録
	T4CONbits.ON = 1;// timer4を有効にすることでスタート	
}


/* // 以下はRAM配置時に使う起動関数登録当
__attribute__((address( 0x80005000 ))) void main();
void main()
{
	//_HANDLES[_IDX_HANDLE_USER_SET_FUNC] = init_timer; // HANDLES領域の初期化用
	//_handle_user_set_func();

	_HANDLES[_IDX_INIT_SUB_FUNC] = init_timer; // 起動登録
	//_init_sub_func();
}
*/

// 上記は、RAM配置用の登録関数で、『R00800050000061』の転送で実行するコードです。
// 上記で       の部分がコメントになって、
//       ROM領域で実行するプログラムになっています。
//  (このコメントを外し、ROM用の__attribute__指定行をコメントにすることで、RAM動作用にに変わります。)






/*
EEPROM領域におけるリセットで起動するプログラムは 0x9D020000番地からに決まっており、
 このアドレスのワード内容が0xffffffffでない場合だけ、このアドレス先の関数を実行する。
0x9D020000番地で、タイマー4の割り込みの初期化と、そのタイマー処理を _HANDLES[_IDX_TIMER_4_FUNC] = main_loop;で登録して、タイマーを起動する。 このinit_timer()をリセットで実行することで、main_loopが0.05ミリ秒ごとに実行する。 */
80008000 <flag>:
80008000:	00000000 	nop

80008004 <count>:
80008004:	00000000 	nop

9d020600 :
9d020600:	27bdfff8 	addiu	sp,sp,-8
9d020604:	afbe0004 	sw	s8,4(sp)
9d020608:	03a0f021 	move	s8,sp
9d02060c:	3c028001 	lui	v0,0x8001
9d020610:	8c428004 	lw	v0,-32764(v0)
9d020614:	24430001 	addiu	v1,v0,1
9d020618:	3c028001 	lui	v0,0x8001
9d02061c:	ac438004 	sw	v1,-32764(v0)
9d020620:	3c028001 	lui	v0,0x8001
9d020624:	8c428004 	lw	v0,-32764(v0)
9d020628:	28424e21 	slti	v0,v0,20001
9d02062c:	10400003 	beqz	v0,9d02063c 
9d020630:	00000000 	nop
9d020634:	0b4081a2 	j	9d020688 <.L1>
9d020638:	00000000 	nop
9d02063c:	3c028001 	lui	v0,0x8001
9d020640:	ac408004 	sw	zero,-32764(v0)
9d020644:	3c028001 	lui	v0,0x8001
9d020648:	8c428000 	lw	v0,-32768(v0)
9d02064c:	14400009 	bnez	v0,9d020674 
9d020650:	00000000 	nop
9d020654:	3c02bf88 	lui	v0,0xbf88
9d020658:	34038000 	li	v1,0x8000
9d02065c:	ac436128 	sw	v1,24872(v0)
9d020660:	3c028001 	lui	v0,0x8001
9d020664:	24030001 	li	v1,1
9d020668:	ac438000 	sw	v1,-32768(v0)
9d02066c:	0b4081a2 	j	9d020688 <.L1>
9d020670:	00000000 	nop
9d020674:	3c02bf88 	lui	v0,0xbf88
9d020678:	34038000 	li	v1,0x8000
9d02067c:	ac436124 	sw	v1,24868(v0)
9d020680:	3c028001 	lui	v0,0x8001
9d020684:	ac408000 	sw	zero,-32768(v0)

9d020688 <.L1>:
9d020688:	03c0e821 	move	sp,s8
9d02068c:	8fbe0004 	lw	s8,4(sp)
9d020690:	27bd0008 	addiu	sp,sp,8
9d020694:	03e00008 	jr	ra
9d020698:	00000000 	nop

9d020000 <init_timer>:
9d020000:	27bdfff8 	addiu	sp,sp,-8
9d020004:	afbe0004 	sw	s8,4(sp)
9d020008:	03a0f021 	move	s8,sp
9d02000c:	3c028001 	lui	v0,0x8001
9d020010:	ac408000 	sw	zero,-32768(v0)
9d020014:	3c028001 	lui	v0,0x8001
9d020018:	ac408004 	sw	zero,-32764(v0)
9d02001c:	3c02bf80 	lui	v0,0xbf80
9d020020:	ac400c00 	sw	zero,3072(v0)
9d020024:	3c02bf80 	lui	v0,0xbf80
9d020028:	ac400c10 	sw	zero,3088(v0)
9d02002c:	3c02bf80 	lui	v0,0xbf80
9d020030:	240307cf 	li	v1,1999
9d020034:	ac430c20 	sw	v1,3104(v0)
9d020038:	3c03bf88 	lui	v1,0xbf88
9d02003c:	8c621030 	lw	v0,4144(v1)
9d020040:	7c029cc4 	ins	v0,zero,0x13,0x1
9d020044:	ac621030 	sw	v0,4144(v1)
9d020048:	3c03bf88 	lui	v1,0xbf88
9d02004c:	8c621060 	lw	v0,4192(v1)
9d020050:	24040001 	li	a0,1
9d020054:	7c829cc4 	ins	v0,a0,0x13,0x1
9d020058:	ac621060 	sw	v0,4192(v1)
9d02005c:	3c02a000 	lui	v0,0xa000
9d020060:	34424094 	ori	v0,v0,0x4094
9d020064:	3c039d02 	lui	v1,0x9d02
9d020068:	24630600 	addiu	v1,v1,1536
9d02006c:	ac430000 	sw	v1,0(v0)
9d020070:	3c03bf80 	lui	v1,0xbf80
9d020074:	94620c00 	lhu	v0,3072(v1)
9d020078:	24040001 	li	a0,1
9d02007c:	7c827bc4 	ins	v0,a0,0xf,0x1
9d020080:	a4620c00 	sh	v0,3072(v1)
9d020084:	03c0e821 	move	sp,s8
9d020088:	8fbe0004 	lw	s8,4(sp)
9d02008c:	27bd0008 	addiu	sp,sp,8
9d020090:	03e00008 	jr	ra
9d020094:	00000000 	nop

  以下に、上記の実際に動作した、アセンブリリストを示します。上のC言語の逆アセンブルリストをコードにしてみました。

(UMEHOSHI ITA基板で使っている制御チップのPIC32MX270F256B用(MIPS32系)アセンブリに必要な情報の概要です。)


  上記で編集したアセンブリソースを、左のボタンで「アッセンブル」できます。
番地から実行するUME専用Hexコマンドを、 最後に追加埋め込みする場合にチェックする==>

上記で生成した[UME専用Hexコマンド]のテキストを コピー(CTRL+A CTRL+C)してUMEHOSHI ITAへ転送して動かすことができます。
上記のEEPROMへ書き込む[UME専用Hexコマンド]は、その領域byteが0xffになっていなければなりません。
つまり、書き込み領域を使っている場合は、その領域を消去してから書き込み処理する必要があります。
その場合は、次の、0x9D020000〜0x9D020400の1024byteを消去する処理をしてから行ってください。

// 0x9D020000 〜 0x9D0207FF の EEPROM 領域を消去

#include <xc.h>	// test.c
#include "common.h"


// RAMで動かす場合の絶対アドレス範囲は、(0x80005000〜0x80008000)とします。
__attribute__((address( 0x80005000 ))) void start_main (void);
void start_main(){
	_nvm_erase_page(0x9D020000);
	_nvm_erase_page(0x9D020400);
}

/*

EEPROMの0x9D020000番地から1024byteの領域を0xFFに消去する

nvm_erase_pageの引数のアドレスは
x9D020400,0x9D020800,0x9D020c00,0x9D021000,・・
	・・,9D03F800,9D03FC00 の何れかを指定する。

*/

80005000 <start_main>:
80005000:	27bdffe8 	addiu	sp,sp,-24
80005004:	afbf0014 	sw	ra,20(sp)
80005008:	afbe0010 	sw	s8,16(sp)
8000500c:	03a0f021 	move	s8,sp
80005010:	3c02a000 	lui	v0,0xa000
80005014:	344240f0 	ori	v0,v0,0x40f0
80005018:	8c420000 	lw	v0,0(v0)
8000501c:	3c049d02 	lui	a0,0x9d02
80005020:	0040f809 	jalr	v0
80005024:	00000000 	nop
80005028:	3c02a000 	lui	v0,0xa000
8000502c:	344240f0 	ori	v0,v0,0x40f0
80005030:	8c420000 	lw	v0,0(v0)
80005034:	3c039d02 	lui	v1,0x9d02
80005038:	34640400 	ori	a0,v1,0x0400
8000503c:	0040f809 	jalr	v0
80005040:	00000000 	nop
80005044:	03c0e821 	move	sp,s8
80005048:	8fbf0014 	lw	ra,20(sp)
8000504c:	8fbe0010 	lw	s8,16(sp)
80005050:	27bd0018 	addiu	sp,sp,24
80005054:	03e00008 	jr	ra
80005058:	00000000 	nop
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <xc.h>	// 0x9d020000番地のページ消去(0xffに設定)
	.set noreorder #アセンブラに命令の順序を自動変更させない。
	.section	start_main,address(0x80005000),code
	.ent	start_main
	addiu	sp,sp,-8
	sw	ra,4(sp)
	lui	v0,0xa000
	ori	v0,v0,0x40f0 # hex(0xA0004000+60*4)==>0xa00040f0より算出
	lw	v0,0(v0)
	lui	a0,0x9d02 # 消去対象のアドレス
	jalr	v0
	nop
	lui	v0,0xa000
	ori	v0,v0,0x40f0
	lw	v0,0(v0)
	lui	v1,0x9d02
	ori	a0,v1,0x0400 # 消去対象のアドレス
	jalr	v0
	nop
	lw	ra,4(sp)
	addiu	sp,sp,8
	jr	ra
	nop
左のコードで生成される[UME専用Hexコマンド]は、
0x9D020000 〜 0x9D0207FFのEEPROMを消去するアセンブラコードです。
実行するためには、「アッセンブル」のボタン操作前で、
[0x80005000]番地から起動するコード埋め込みのチェックをして、生成してください。
そして「command.txt」の名前でファイル化して、pythonで実行させます。
なお、「command.txt」の先頭行に、「G109D0200000059」の行を追加することで、
実行前の0x9d020000番地の16byteの内容を確認することができます。

上記コードを、Pythonで[UMEHOSHI ITA]基板で転送して実行する手順の紹介

予め、pythonが動作するターミナル(コマンドプロンプト)を、開いて準備して置くとよいでしょう。
以下では、R:\workを作業位置(カレントディレクトリ)にして説明しています。
また、pythonプログラムでは、serialモジュールを追加しておく必要があります。
「pip show serial」のコマンド操作で、インストールされているか確認できます。
インストールされていない場合は、「pip install pyserial」の操作で、インストールしておくと良いでしょう。

【1】 [UMEHOSHI ITA]基板で転送するテキストファイル「command.txt」を用意します。

上記の「アセンブラ編集部」の直下にある「アセンブル」ボタンをクリックします。
(このボタン右下の、指定アドレスから実行させるコード埋め込み用のチェックボックスがチェックされていること確認して行う)

「アセンブラ編集部」のソースにエラーが無ければ、[UME専用Hexコマンド]のテキストが、その下の TextArea に表示されます。
(エラーがあれば、エラーが無くHEXコードが生成されるまで、「アセンブラ編集部」の修正とアセンブル」を繰り返します)

メモ帳などで、「command.txt」のファイルを生成して開き、
上記操作で得られたTextAreaの[UME専用Hexコマンド]のテキストを、 コピー(CTRL+A CTRL+C)操作し、
  それを「command.txt」編集画面で貼り付け((CTRL+V)して、保存します。
保存位置は、pythonが動作するターミナルの作業位置(カレントディレクトリ)です。(後述例では、R:\workで示しています)

【2】 [UMEHOSHI ITA]基板へ[command.txt」を転送するpythonファイルを用意します。

「instruct.py」の名前のソースファイルを、[command.txt」と同じ位置に作ります。
(一度作れば、次回は使うだけで、作る必要はありません。(CTRL+A CTRL+C)操作で、コピー可能)


【3】 [UMEHOSHI ITA]基板へ[command.txt」を転送して実行させる。

上記で作った「instruct.py」と[command.txt」が存在する位置で、 pythonが動作するターミナル(コマンドプロンプト)を開きます。
以下では、この作業位置(カレントディレクトリ)が、R:\workである場合の例で説明しています。

まず [UMEHOSHI ITA]基板と、PCをUSBで接続します。

次にターミナルプロンプトを『powershell』にして、
『Get-CimInstance Win32_PnPEntity | Where-Object {$_ -like "*(COM*"} | Select-Object Caption』
のコマンド操作で、USBのシリアル デバイスのCOM番号を調べます。

実行例
R:\work>powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6

PS R:\work> Get-CimInstance Win32_PnPEntity | Where-Object {$_ -like "*(COM*"} | Select-Object Caption

Caption
-------
Bluetooth リンク経由の標準シリアル (COM10)
USB シリアル デバイス (COM4)
Bluetooth リンク経由の標準シリアル (COM11)
PS R:\work>

この実行例から、USB シリアル デバイス がCOM4が使える状態になっていることが分かります。
このリンクページで示すように、デバイスマネージャで確認することもできます)
この番号の4を記憶して次の転送プログラム実行に進むのですが、 そのUSB シリアル デバイス が見つからない場合、次の点が考えられます。

いずれにしても、接続でUSB シリアル デバイスCOM番号が得られないと、次に進めません。

続いて、以下の『python command.txt』の操作で、転送実行プログラム(上記のアセンブラ用の例)を実行します。
実行例 (最初に上記で得られた番号の4をキー入力しています。)
R:\work>python instruct.py
USB シリアル デバイスで、使用するCOMの番号を入力>>4
S0880008000000000000000000000  55チェックサム: 0
USB受信● b'S088000800000000000000000000055\r\n'
USB受信● b'SET:80008000\r\n'
S109D02060000F8FFBD270400BEAF21F0A0030180023C  2Eチェックサム: 0
USB受信● b'S109D02060000F8FFBD270400BEAF21F0A0030180023C2E\r\n'
USB受信● b'SET:9D020600\r\n'
S109D020610000480428C010043240180023C048043AC  B3チェックサム: 0
USB受信● b'S109D020610000480428C010043240180023C048043ACB3\r\n'
USB受信● b'SET:9D020610\r\n'
S109D020620000180023C0480428C214E422803004010  C3チェックサム: 0
USB受信● b'S109D020620000180023C0480428C214E422803004010C3\r\n'
USB受信● b'SET:9D020620\r\n'
S109D0206300000000000A281400B000000000180023C  F1チェックサム: 0
USB受信● b'S109D0206300000000000A281400B000000000180023CF1\r\n'
USB受信● b'SET:9D020630\r\n'
S109D02064000048040AC0180023C0080428C09004014  B3チェックサム: 0
USB受信● b'S109D02064000048040AC0180023C0080428C09004014B3\r\n'
USB受信● b'SET:9D020640\r\n'
S109D020650000000000088BF023C00800324286143AC  A5チェックサム: 0
USB受信● b'S109D020650000000000088BF023C00800324286143ACA5\r\n'
USB受信● b'SET:9D020650\r\n'
S109D020660000180023C01000324008043ACA281400B  B1チェックサム: 0
USB受信● b'S109D020660000180023C01000324008043ACA281400BB1\r\n'
USB受信● b'SET:9D020660\r\n'
S109D020670000000000088BF023C00800324246143AC  A7チェックサム: 0
USB受信● b'S109D020670000000000088BF023C00800324246143ACA7\r\n'
USB受信● b'SET:9D020670\r\n'
S109D020680000180023C008040AC21E8C0030400BE8F  6Fチェックサム: 0
USB受信● b'S109D020680000180023C008040AC21E8C0030400BE8F6F\r\n'
USB受信● b'SET:9D020680\r\n'
S0C9D020690000800BD270800E00300000000  55チェックサム: 0
USB受信● b'S0C9D020690000800BD270800E0030000000055\r\n'
USB受信● b'SET:9D020690\r\n'
S109D02000000F8FFBD270400BEAF21F0A00300000000  55チェックサム: 0
USB受信● b'S109D02000000F8FFBD270400BEAF21F0A0030000000055\r\n'
USB受信● b'SET:9D020000\r\n'
S109D0200100080BF023C000C40AC80BF023C100C40AC  45チェックサム: 0
USB受信● b'S109D0200100080BF023C000C40AC80BF023C100C40AC45\r\n'
USB受信● b'SET:9D020010\r\n'
S109D0200200080BF023CCF070324200C43AC88BF033C  39チェックサム: 0
USB受信● b'S109D0200200080BF023CCF070324200C43AC88BF033C39\r\n'
USB受信● b'SET:9D020020\r\n'
S109D020030003010628CC49C027C301062AC88BF033C  53チェックサム: 0
USB受信● b'S109D020030003010628CC49C027C301062AC88BF033C53\r\n'
USB受信● b'SET:9D020030\r\n'
S109D020040006010628C01000424C49C827C601062AC  8Aチェックサム: 0
USB受信● b'S109D020040006010628C01000424C49C827C601062AC8A\r\n'
USB受信● b'SET:9D020040\r\n'
S109D0200500000A0023C94404234029D033C00066324  B4チェックサム: 0
USB受信● b'S109D0200500000A0023C94404234029D033C00066324B4\r\n'
USB受信● b'SET:9D020050\r\n'
S109D02006000000043AC80BF033C000C629401000424  A0チェックサム: 0
USB受信● b'S109D02006000000043AC80BF033C000C629401000424A0\r\n'
USB受信● b'SET:9D020060\r\n'
S109D02007000C47B827C000C62A421E8C0030400BE8F  43チェックサム: 0
USB受信● b'S109D02007000C47B827C000C62A421E8C0030400BE8F43\r\n'
USB受信● b'SET:9D020070\r\n'
S0C9D020080000800BD270800E00300000000  5Cチェックサム: 0
USB受信● b'S0C9D020080000800BD270800E003000000005C\r\n'
USB受信● b'SET:9D020080\r\n'
終了確認の Enter >>>
R:\work>

[command.txt」の転送が終わって、終了確認の Enter >>>のプロンプトが出て、ENTERキーを 入力することで終了しますが、 まだ「 Lチカ 」は実行しません。
実行するには、パワーオンの後でリセットボタンを押すと、「 Lチカ 」が始まります。
(それは、0x9D020000に存在する関数の実行で、_HANDLES[_IDX_TIMER_4_FUNC]に登録した関数をタイマー4割り込みで 0.05ミリ秒ごとに実行して実現しています)
動作例の動画がこちらでで紹介しています。