このページは、パワーONでないリセット操作で実行するRAM上で起動するプログラムの作り方です。(参考:EEPROM版の紹介)
この手法は、このページでも紹介しています。
リセットに関係なく、"R00〜"の実行アドレスを指定した起動用のコマンド文字列で実行させる方法のリンクはこちらです
if( パワーオンリセットか? ){ マクロ用の関数へのポインタなど各種変数の初期化や_HANDLES配列の初期化を行う。 「ウメ・エディットプログラム」で使われるマクロ関数が登録される。そして、 _HANDLES[_IDX_HANDLE_USER_SET_FUNC] = dummy_init_function; の関数が登録される。 (上記関数へのポインタは、_handle_user_set_func()のマクロの関数で実行できる) _HANDLES[_IDX_INIT_SUB_FUNC] = dummy_init_function; の関数が登録される。 (上記関数へのポインタは、_init_sub_func()のマクロの関数で実行できる) power_on_flag = 1; } _handle_user_set_func(); // 起動時のマクロ関数呼び出し(ここで、_HANDLESの要素の変更などを行う) SYS_Initialize ( NULL );// ここでUSBや指定IOの初期化が行われる _handle_user_set_func(); // 起動時のマクロ関数呼び出し この後で、『USB受信文字列処理用のループ』に進みます。
上記より、パワーオン時にマクロ関数用配列の_HANDLES[機能識別添え字] 群に関数を記憶しています。
ここで_HANDLES[_IDX_HANDLE_USER_SET_FUNC] や、
_HANDLES[_IDX_INIT_SUB_FUNC]に何もしないdummy_init_func関数が記憶しています。
それがリセット時に_handle_user_set_func()、
_handle_user_set_func()のマクロ呼び出しで実行されます。
リセットで実行プログラムは、このどちらかまたは両方を使います。
USBや指定IOの初期化前に行わせる関数であれば、_HANDLES[_IDX_HANDLE_USER_SET_FUNC]に登録し、
初期化後に行わせる関数であれば、_HANDLES[_IDX_HANDLE_USER_SET_FUNC]に登録します。
この登録を「希望する起動関数」に設定し直せば、
電源を切るまでリセットボタンで「希望する起動関数」を実行させることができます。
以下ではこの仕組みを利用して、D1のLEDでの「 Lチカ 」無限ループの関数を、
_HANDLES[_IDX_INIT_SUB_FUNC] に登録しています。
これでこの登録関数は、電源投入後、リセットボタンの操作で動作することになります。
下記C言語ソースは、「umehoshiEdit」ツールで、ビルド実行できます。
右リストは逆アセンブルしたコードで、これを参考に、後述のアセンブリリストを作っています。
#include <xc.h> // test.c #include "common.h" // RAMで動かす場合の絶対アドレス範囲は、(0x80005000〜0x80008000)です __attribute__((address( 0x80005000 ))) void start_boot (void); void start_boot(){ extern void start_main(); _HANDLES[_IDX_INIT_SUB_FUNC] = start_main;//起動時に動作する関数に、下記start_mainを登録 } // |
80005000 <start_boot>: 80005000: 27bdfff8 addiu sp,sp,-8 80005004: afbe0004 sw s8,4(sp) 80005008: 03a0f021 move s8,sp 8000500c: 3c02a000 lui v0,0xa000 80005010: 344240c8 ori v0,v0,0x40c8 80005014: 3c038000 lui v1,0x8000 80005018: 24635070 addiu v1,v1,0x5070 8000501c: ac430000 sw v1,0(v0) 80005020: 03c0e821 move sp,s8 80005024: 8fbe0004 lw s8,4(sp) 80005028: 27bd0008 addiu sp,sp,8 8000502c: 03e00008 jr ra 80005030: 00000000 nop 80005034 <wait>: 80005034: 27bdfff8 addiu sp,sp,-8 80005038: afbe0004 sw s8,4(sp) 8000503c: 03a0f021 move s8,sp 80005040: afc40008 sw a0,8(s8) 80005044: 00000000 nop 80005048: 8fc20008 lw v0,8(s8) 8000504c: 2443ffff addiu v1,v0,-1 80005050: afc30008 sw v1,8(s8) 80005054: 1440fffc bnez v0,80005048 <wait+0x14> 80005058: 00000000 nop 8000505c: 03c0e821 move sp,s8 80005060: 8fbe0004 lw s8,4(sp) 80005064: 27bd0008 addiu sp,sp,8 80005068: 03e00008 jr ra 8000506c: 00000000 nop 80005070 <start_main>: 80005070: 27bdffe8 addiu sp,sp,-24 80005074: afbf0014 sw ra,20(sp) 80005078: afbe0010 sw s8,16(sp) 8000507c: 03a0f021 move s8,sp 80005080: 00000000 nop 80005084: 3c02bf88 lui v0,0xbf88 80005088: 34038000 li v1,0x8000 8000508c: ac436128 sw v1,0x6128(v0) 80005090: 00000000 nop 80005094: 0c00140d jal 80005034 <wait> 80005098: 3c04003f lui a0,0x3f 8000509c: 3c02bf88 lui v0,0xbf88 800050a0: 34038000 li v1,0x8000 800050a4: ac436124 sw v1,0x6124(v0) 800050a8: 00000000 nop 800050ac: 0c00140d jal 80005034 <wait> 800050b0: 3c04003f lui a0,0x3f 800050b4: 08001421 j 80005084 <start_main+0x14> 800050b8: 00000000 nop |
以下に、上記の実際に動作した、アセンブリリストを示します。上のC言語の逆アセンブルリストをコードにしてみました。
無限ループなので、本来あるべきユーザープログラムのリターン処理がありません。(コメントアウトしています。)
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <xc.h> // test.S (Lチカの無限ループ) .set noreorder #アセンブラに命令の順序を自動変更させない。 .section start_boot,address(0x80005000),code .ent start_boot start_boot: ADDIU sp,sp,-8 LUI v0,0xa000 # 以下2行で、_HANDLES[_IDX_INIT_SUB_FUNC]要素のアドレスの0xa00040c8をv0に設定 ORI v0,v0,0x40c8 # 0xA0004000+50*4=0xa00040c8 の下位16bit LUI v1,%hi(start_main) ADDIU v1,v1,%lo(start_main) # v1のstart_main関数のアドレスを設定 SW v1,0(v0) # *v0 = v1 で「_HANDLES[_IDX_INIT_SUB_FUNC] = start_main;」を実現 ADDIU sp,sp,8 JR ra NOP start_main: .L4: LUI v0,0xbf88 # 以下の3行で、0xbf886128のPORTBSETに0x8000を設定することで、D1のLEDを点灯 li v1,0x8000 SW v1,0x6128(v0) # 以上の3行で1のLEDを点灯 NOP JAL wait # チョット待つwait関数呼び出し(次行のRaに戻り番地を記憶してジャンプ) LUI a0,0x3f NOP LUI v0,0xbf88 # 以下の3行で、0xbf886124のPORTBCLRに0x8000を設定することで、D1のLEDを消灯 li v1,0x8000 SW v1,0x6124(v0) # 以上の3行で1のLEDを消灯 JAL wait # チョット待つwait関数呼び出し(次行のRaに戻り番地を記憶してジャンプ) LUI a0,0x3f J .L4 # 無限ループのジャンプ NOP .end test_main wait: ADDIU sp,sp,-8 # 以下で使う24byteまでの退避サイスをスタックに作る。<====wait関数 SW ra,4(sp) # 現在の関数の戻り番地をスタックに退避 ADDU s7, a0, zero # 引数レジスタa0を、s7に記憶 Lwait1: ADDIU s7, s7,-1 # s7=s7+(-1) BNE s7, zero, Lwait1 NOP ADDIU sp,sp,8 # 関数実行前のスタックポインタに戻す JR ra NOP |
予め、pythonが動作するターミナル(コマンドプロンプト)を、開いて準備して置くとよいでしょう。
以下では、R:\workを作業位置(カレントディレクトリ)にして説明しています。
また、pythonプログラムでは、serialモジュールを追加しておく必要があります。
「pip show serial」のコマンド操作で、インストールされているか確認できます。
インストールされていない場合は、「pip install pyserial」の操作で、インストールしておくと良いでしょう。
上記の「アセンブラ編集部」の直下にある「アセンブル」ボタンをクリックします。
(このボタン右下の、指定アドレスから実行させるコード埋め込み用のチェックボックスがチェックされていること確認して行う)
「アセンブラ編集部」のソースにエラーが無ければ、[UME専用Hexコマンド]のテキストが、その下の TextArea に表示されます。
(エラーがあれば、エラーが無くHEXコードが生成されるまで、「アセンブラ編集部」の修正とアセンブル」を繰り返します)
メモ帳などで、「command.txt」のファイルを生成して開き、
上記操作で得られたTextAreaの[UME専用Hexコマンド]のテキストを、 コピー(CTRL+A CTRL+C)操作し、
それを「command.txt」編集画面で貼り付け((CTRL+V)して、保存します。
保存位置は、pythonが動作するターミナルの作業位置(カレントディレクトリ)です。(後述例では、R:\workで示しています)
上記で作った「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 シリアル デバイス が見つからない場合、次の点が考えられます。
R:\>python instruct.py USB シリアル デバイスで、使用するCOMの番号を入力>>4 S108000500000F8FFBD2700A0023CC84042340080033C 70チェックサム: 0 USB受信● b'S108000500000F8FFBD2700A0023CC84042340080033C70\r\n' USB受信● b'SET:80005000\r\n' S10800050100024506324000043AC0800BD270800E003 C2チェックサム: 0 USB受信● b'S10800050100024506324000043AC0800BD270800E003C2\r\n' USB受信● b'SET:80005010\r\n' S1080005020000000000088BF023C00800324286143AC C0チェックサム: 0 USB受信● b'S1080005020000000000088BF023C00800324286143ACC0\r\n' USB受信● b'SET:80005020\r\n' S108000503000000000001714000C3F00043C00000000 09チェックサム: 0 USB受信● b'S108000503000000000001714000C3F00043C0000000009\r\n' USB受信● b'SET:80005030\r\n' S10800050400088BF023C00800324246143AC1714000C A2チェックサム: 0 USB受信● b'S10800050400088BF023C00800324246143AC1714000CA2\r\n' USB受信● b'SET:80005040\r\n' S1080005050003F00043C0914000800000000F8FFBD27 98チェックサム: 0 USB受信● b'S1080005050003F00043C0914000800000000F8FFBD2798\r\n' USB受信● b'SET:80005050\r\n' S1080005060000400BFAF21B88000FFFFF726FEFFE016 F1チェックサム: 0 USB受信● b'S1080005060000400BFAF21B88000FFFFF726FEFFE016F1\r\n' USB受信● b'SET:80005060\r\n' S108000507000000000000800BD270800E00300000000 01チェックサム: 0 USB受信● b'S108000507000000000000800BD270800E0030000000001\r\n' USB受信● b'SET:80005070\r\n' R008000500000 61チェックサム: 0 実行スタートの Enter >> USB受信● b'R00800050000061\r\n' USB受信● b'START:80005000\r\n' 終了確認の Enter >>> R:\>
[command.txt」の中に"R008000500000"のような実行コマンドが存在すると、実行スタートの Enter >>の
プロンプトが出て、一時停止します。
上記例では、そこでENTERキーを入力することで、"R00800050000061"が送られ、UMEHOSHI ITA基板で「0x80005000」番地から実行しますが、
まだ「 Lチカ 」は実行しません。
実行するのは
_HANDLES[_IDX_INIT_SUB_FUNC] = start_main相当の登録処理です。
この登録の実行の後で、リセットボタンを押すと、登録関数実行で「 Lチカ 」が始まります。
リセットボタンの操作に関係なく、実行アドレスを指定したスタート用のコマンド文字列("R00〜")を送るこので 起動させるプログラムの作り方はこちらで示すページで示します。