[H8]HEWでアセンブラ開発用のプロジェクトを作成し、シミュレータで実行してみる

今回は、H8シリーズのマイコンの中でも、H8/3069Fをターゲットとしたプロジェクトを作成し、シミュレータ上でプログラムを実行してみます。
インストール方法は、前回の記事を参考にしてください。

プロジェクトの作成

まずはHEWを起動し、ワークスペースの作成を選択します。


ワークスペースの作成です。
今回はアセンブラの環境を作りたいので、左のプロジェクトタイプからAssembly Applicationを選択します。ワークスペース名は、アルファベットで適当な名前を付けてください。(今回はasm_testにしました)



ターゲットとするCPUの選択です。
H8/3069Fのチップはマスクが変わってしまったため、最近ではH8/3069Rに型番が替わっているのですが、ソフト開発上は同等なので、これを選びます。


オプション指定です、デフォルトのまま次へをクリックします。


そのまま次へをクリックします。


割り込み発生時のベクタテーブル定義です。
デフォルトのままでOKをクリックします。


シミュレータの選択では、H8/300HAを選択します。


シミュレータの選択肢にあるHAとHNの違いは、ルネサスのページより確認できますが、CPUのモードです。今回はアドバンスモードで実行したいのでHAを選択しました。
H8SX,H8S,H8ファミリ用シミュレータデバッガ シミュレーション範囲 | ルネサス エレクトロニクス



デバッガオプションはデフォルトのままで次へをクリックします。



自動生成されるソースの確認です。
完了をクリックします。


-------- PROJECT GENERATOR --------
PROJECT NAME :	asm_test
PROJECT DIRECTORY :	C:\WorkSpace\asm_test\asm_test
CPU SERIES :	300H
CPU TYPE :	3069R
TOOLCHAIN NAME :	Renesas H8S,H8/300 Standard Toolchain
TOOLCHAIN VERSION :	7.0.0.0
GENERATION FILES :
    C:\WorkSpace\asm_test\asm_test\intprg.src
        Interrupt Program
    C:\WorkSpace\asm_test\asm_test\vecttbl.src
        Initialize of Vector Table
    C:\WorkSpace\asm_test\asm_test\vect.inc
        Definition of Vector
    C:\WorkSpace\asm_test\asm_test\resetprg.src
        Reset Program
    C:\WorkSpace\asm_test\asm_test\asm_test.src
        Main Program
    C:\WorkSpace\asm_test\asm_test\stacksct.src
        Setting of Stack area
START ADDRESS OF SECTION :
 H'000000000	VECTTBL,INTTBL
 H'000000400	ResetPRG,IntPRG
 H'000000800	P
 H'000FFFD00	Stack
 
* When the user program is executed,
* the interrupt mask has been masked.
* 
* **** H8/3069R Advanced ****
 
SELECT TARGET :
    H8/300HA Simulator




プロジェクトのツリーに、6つのファイルがある事を確認してください。
asm_test.src(プロジェクト名+”.src”)が、C言語でいうmain関数相当です。


これでプロジェクトの作成は完了です。

動作確認用のプログラムの作成とビルド


プロジェクトが正しく作られたのを確認するため、一旦このままビルドしてみます(F7でビルドできます)。


画面右下のBulidタブに、ビルド結果が表示されます。
まだ何も作っていないので、0 Errors, 0 Warningsで、ビルドが完了するはずです。


Build Finished
0 Errors, 0 Warnings



またリンカのメッセージで、評価版の残り日数が表示されます。

License expires in 60 days




ここまで出来たら、次は適当に動作確認用のプログラムを作ってみます。
asm_test.srcを以下のように書き換えます(長くなるのでファイル先頭のコメントは省略です)。

		.export		_main
_main:
		MOV.B	#H'FF, R0L
		MOV.B	R0L, @H'FFFF00
		MOV.B	#H'AA, R0L
		MOV.B	R0L, @H'FFFF00
 
		rts
		.end





このプログラムは、0xFFの値をメモリの0xFFFF00番地に書いた後、0xAAに書き換えています。
0xFFFF00番地を指定したのは特に意味がありませんが、強いてあげるなら以下の理由からです。

1.H8/3069Fのアドバンスモードはメモリ空間が0xFFFFFFまでなので、大きいアドレスにしといた方がメモリダンプウィンドウ上確認しやすい(一番下までスクロールさせて、ちょっと上に戻せばよい)。

2.とは言うものの、最初と最後の辺りのアドレスには特別な意味が有るので、端っこのアドレスは使用したくない(それぞれ、割り込みベクタとI/Oマッピングがアサインされている)。


ソースを書いたら再度F7でビルドします。エラーがないことを確認してください。



シミュレータでの実行


次に、作ったプログラムをシミュレータ上で実行します。

プロジェクトのセッションをシミュレータに変更します。
セッションの変更は、ツールバー右端のDefaultSessionというのを SimSesseionH8-300HAに変更することで行えます。


ファイル保存の確認が出ますが、OKをクリックします。



ここで、シミュレータで実行する前にもうすこし環境を整えます。

表示->CPUから、レジスタとメモリを選択してください。


メモリを選択した時は、追加でウィンドウが表示されますが、そのままOKで良いです。


画面右にレジスタ、下にメモリダンプが表示されます。
H8はレジスタがER0~ER7,PC,CCRの10本のレジスタがある事が確認できます。


メモリはウィンドウ右側をスクロールさせて、FFFF00番地が見えるようにしてください。



再度F7を押してビルドします。
以下のウィンドウが表示されるので、”すべてはい”をクリックします。
ここでダウンロードというのは、マイコンへのプログラム書き込みを意味します。
実機を持っている場合はここで書き込みが行われるのですが、今回はシミュレータなのでシミュレータ上へのロードが行われます。




ダウンロードが終わると、ソースウィンドウの表示が変わります。
行番号の右に、ロードされたメモリアドレスが表示されました。


ちなみに、ソースウィンドウの上にあるボタンの真ん中をクリックすると、アセンブルした機械語命令が表示されます。




S…と記載された列は、ブレークポイントです(タイトルのところにカーソルを合わせるとタイトルが表示されます)。
最初の命令のところをダブルクリックして、1行目で止まるようにしておきます。



ここまで準備できたら、いよいよプログラムを実行します。
下の赤枠で囲んだアイコン(リセット後実行)をクリックしてください。リセット後実行はShift+F5がショートカットになっています。



ブレークポイントを設定した行が黄色くなり、ここで処理がとまっています。
デバッグ->ステップオーバー(またはF10キー)をクリックし、プログラムを1行進めてください。


レジスタウィンドウのER0が赤くなり、”00000000″->”000000FF”に変わった事が分かります。
まだ説明していませんが、MOV命令で指定したR0Lというレジスタは、ER0レジスタの下位8ビットを意味します。


もう一度F10を押すと、今度はメモリのFFFF00番地の値がFFに変わる事が確認できます。


さらにF10を押していくと、ER0がAAに変化し、メモリのFFFF00番地がAAに変わっていきます。






これで、プロジェクトの作成とシミュレータでの実行確認はおしまいです。

[H8]ルネサスH8シリーズ用IDEのHEWをインストールする

H8マイコン向けのプログラムについて書かれているサイトを見ると,コンパイラにgccを使用していることが多いです。gccだとフリーで使用できるためお手軽ですが、学んだ事を仕事に生かそうと考えると、やはり業務で良く使用されているHEWを使用したくなります。
HEWというのはH8マイコンの製造元であるルネサス(Renesas)が販売している統合開発環境で、アセンブラ/C/C++の開発が可能です。

評価版だと、無制限での試用期間が60日、その後は生成されるコードが64kbyteに制限されてしまいますが、機能評価が目的なら問題有りませんし、勉強目的でミドルエンド・ローエンド向けのマイコンをする場合だと、そもそもメモリ空間が64kしかないので生成できるオブジェクトサイズを気にする必要がないです。

そこで、今回はHEW評価版のインストールを行ってみます。


HEW評価版のダウンロード


ルネサスのダウンロードページに移動します。
http://japan.renesas.com/products/tools/evaluation_software/index.jsp



ページをスクロールさせ、”コンパイラ/アセンブラパッケージ”一覧に有る「H8SX, H8S, H8ファミリ用 C/C++コンパイラパッケージ」の評価版ダウンロードをクリックします。


一覧から、以下のリンクをクリックします。
※この記事を書いている時は2ページ目に有りました
※最新版しか落とせないので、バージョン,Releaseは異なっているかもしれません。

【無償評価版】H8SX,H8S,H8ファミリ用C/C++コンパイラパッケージ V.7.00 Release 00


ダウロード検索結果という画面が出ます。
注意の文章が出るので、画面下の”同意する”をクリックします。




MyRenesasのユーザIDとパスワードを入力します。登録していない場合は、こちらのページを参考に登録をしておいてください。
送信ボタンをクリックすると、ダウンロードが始まります。



※ちなみにユーザ登録では、以下の情報を入力する必要がありました。

登録地域               
姓名                   
ふりがな               
メールアドレス         
パスワード             
会社名/学校名          
部署名/学部名          
役職                   
郵便番号               
都道府県               
市区郡                 
住所1                  
住所2                  
国                     
電話番号               
FAX番号                
アプリケーション分野   
アプリケーション製品名 
個人情報提供可否       
メールアドレス利用可否 
登録への同意           
登録情報の配信





HEWのインストール


ダウンロードしたプログラムを実行すると、セットアッププログラムの展開先指定ウィンドウが表示されます。デフォルトのままNextをクリックします(プログラムのインストール先は別途指定するので、ここは通常デフォルトでOKです)。


標準インストールをクリックします。
ちなみに、マルチインストールというのは1台のPCに複数のHEWをインストールするときに使用します。
マルチインストールを行うと、複数の開発環境を1台で構築できます(今回はマルチインストールしません)。


インストール先を指定して次へをクリックします


コンパイラにチェックが入っている事を確認し、そのままインストールをクリックします。


しばらくまちます…


そのまま次へ


内容を確認し、はいをクリックします。


その他の地域を選択し、次へ


インストールをクリックします。


暫く待つと、ファイルのコピーが完了します。
完了ボタンをクリックします。



完了ボタンをクリックします。


スタートメニューより、High-performance Embedded Workshopを選択します。



以下のように、ようこそウィンドウが表示されれば、インストールは完了です。
評価版なので、起動時にCDキーを聞かれるといった事は有りません。

[PIC]HI-TECH Cで7セグ4桁のLEDをダイナミック点灯させる

7セグ4桁のLEDを、HI-TECH Cでダイナミック点灯させるプログラムを作成しました。

ダイナミック点灯の仕組みですが、簡単で短い周期で順番に1桁づつ点灯させていくだけです。
どの桁を点灯させるかは7セグのコモン側のピンを操作する事で決定します。BQ-N516RDはカソードコモンなので、点灯したい桁のコモンをLowにすればOKです。

表示の周期ですが、60Hzぐらい(毎秒60回)まで速くしてあげれば、人間の目だと残像で全桁がずっとつきっぱなしになっていると錯覚してしまうようです。低めの周波数だとチラツキを感じる事も有るので、可能なら気持ち速めほうが良いかもしれません。

ネットで見た情報だと、表示桁を移る前に一瞬、全桁非表示になるタイミングを作ったほうが良いような内容もありましたが、そのような細工をしなくても問題なく表示されていました。


PICの周辺回路についてはこちらの記事を参考にしてください。



今回作ったプログラムは以下の通りです。

#include <stdio.h>
#include <htc.h>
 
#define TMR0_INTERVAL (255-250) // TMRモジュールのカウント初期値
#define PRESCALER 4
int data = 0;
 
void main( void )
{
    __CONFIG ( FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF );
    TRISB = 0;
 
    TMR0   = TMR0_INTERVAL;     // カウントアップタイマー値を初期化
 
    OPTION_REGbits.T0CS  = 0;           // タイマモードとして実行
    OPTION_REGbits.PS    = PRESCALER;   // プリスケーラ値(4:32倍, 6:128倍)
    OPTION_REGbits.PSA   = 0;           // プリスケーラをTMRモジュールで使用する
 
    INTCONbits.T0IE   = 1;      // TMR0   interruptを有効にする
    INTCONbits.GIE    = 1;      // Grobal interruptを有効にする
 
    // 無限ループに入る
    while ( 1 ) {
        NOP();
    }
}
 
// 割り込み処理(10msec周期)
static void interrupt intr( void )
{
    static int i;
    static int intr_counter;
 
    //--------------------------------
    // 一定周期でインクリメントする
    //--------------------------------
    intr_counter++;
    if ( intr_counter >= 50 ) {
        data++;         // 300msec周期で加算する
        intr_counter = 0;
    }
 
 
    //--------------------------------
    // 各桁の値をダイナミック点灯する
    //--------------------------------
    i++;
    switch ( i % 4 ) { 
        case 0:
            PORTB = data % 10;
            PORTB |= 0b11100000;
            break;
        case 1:
            PORTB = ( data / 10 ) % 10;
            PORTB |= 0b11010000;
            break;
        case 2:
            PORTB = ( data / 100 ) % 10;
            PORTB |= 0b10110000;
            break;
        case 3:
            PORTB = ( data / 1000 ) % 10;
            PORTB |= 0b01110000;
            break;
        default:
            PORTB = 0x00;
            break;
    }
 
 
    if ( data > 9999 ) {
        data = 0;
    }
 
    // 割り込みの再設定
    TMR0 = TMR0_INTERVAL;
    T0IF = 0;
}



main()関数は、割り込み処理の定義だけして直ぐに無限ループに入ってしまいます。
※割り込みについては以前書いたので、詳しく知りたい人はこちらの記事を参考にしてください。
今回の動作確認はPICを8MHzのクロックで動作させており、計算すると約5msec周期で割り込みが入る事になります。


一定周期でやってくる割り込み処理が、intr()関数です。
ここでは、表示すべきデータを変数”data”で管理しているのですが、割り込みが入るたびに加算していると分かり辛いので50回ごと(0.25秒毎)にインクリメントします。

次にLEDの出力処理で、これはswitch文のところで行っています。

    i++;
    switch ( i % 4 ) { 
        case 0:
            PORTB = data % 10;
            PORTB |= 0b11100000;
            break;
        case 1:
            PORTB = ( data / 10 ) % 10;
            PORTB |= 0b11010000;
            break;
        case 2:
            PORTB = ( data / 100 ) % 10;
            PORTB |= 0b10110000;
            break;
        case 3:
            PORTB = ( data / 1000 ) % 10;
            PORTB |= 0b01110000;
            break;
        default:
            PORTB = 0x00;
            break;
    }


今回はpicのPORTBに、LEDからの回路を接続しました。下位4ビットが表示すべき値で、上位4ビットが表示桁になっています。この為、割り込みが来るたびに上位ビットが”0001″, “0010”, “0100”, “1000”と、毎回1桁ずれるようにしています。
これで一回20msec、50Hzでの表示でしたが、見た感じではチラツキは特に気にならなかったです。

また、そのうちiがオーバーフローするのでは?と懸念されるかもしれませんが、仮にオーバーフローしても8bitのintでは128から-127に代わった時でもi%4の剰余は周期が代わるわけではないので特に問題ないです(確信犯的にクリアせずほったらかしにしています)。


作成したプログラムを実行した結果です。

4桁7セグLED点灯の回路を74HC4511/BQ-N516RD使用してブレッドボードで組む

前回作った回路図を参考にしつつ、一旦ブレッドボードで組んでみました。

前回描いた回路図はこんな感じでした。


回路で使用した74HC4511とBQ-N516RDの仕様と、配線の考え方はこちらで書いているので参考にしてください。
74HC4511とBQ-N516RDで,4桁の7セグLEDをコントロールする


以下が、回路図を参考に組んだブレッドボード上の回路です。結構大きくなってしまいました。



一部7セグLEDの下にも配線をしており分かり辛いので、7セグLEDをはずした状態での写真です。
薄い四角が7セグで覆われている領域です。


上下にワイヤーが出ていますが、これがマイコンへの出力ピン(計8本)になります。
上側が桁指定で、下がBCDによる入力値です。


実際に点灯させるとこんな感じになります。部屋が明るいと分かり辛いので暗くしてみました。

上記の点灯プログラムは、こちらで説明しています。


[読書メモ]12ステップで作る組込みOS自作入門: 1stステップ 開発環境の作成(と序章)

この本は、H8マイコンで動く独自OSを作成する。
ソースコードは2000行弱しかないので、シンプルだが全貌が分かりやすい。
作成するのは、ブートストラップ、ドライバ、スレッド、スケジューラ、メモリ管理、タスク間通信あたりで、逆にサポートしていないのは、ファイルシステム、タイマーサービス、LAN機能、リアルタイム性etcとなっている。

OSには汎用OSと組み込みOSがある。
汎用OSは、悪意のある人も含めいろんな人が書いたプログラムが動くので、基本的に性悪説になる。
組み込みは、たとえば家電製品だと追加でアプリの登録はNGなので、性善説で作られることが多い。
また組み込み向けはタイミングが大事だったりするので、優先度管理の機能が充実していたりする。

この書籍のOS開発はgccで行われる。
実行環境はlinux,cygwin,FreeBSD等、何でもよい。


プログラムが動くターゲットデバイスが異なるCPUの場合、クロスコンパイラが必要となる
GCCでH8向けのクロス開発環境を構築するには、以下のコマンドを実行する

1.binutilのインストール

tar xvzf binutils-2.19.1.tar.gz
cd binutils-2.19.1
./configure --target=h8300-elf --disable-nls
make
sudo make install



問題がなければ/usr/local/binに以下のようなコマンドができる
h8300-elf-as、h8300-elf-ld

また、/usr/local/h8300-elf/の下にもプログラムが作られる(シンボリックリンク?)
こちらは、コマンド名に接頭語がつかない。

2.gccのインストール

tar xvzf gcc-3.4.6.tar.gz
cd gcc-3.4.6
#setenv SHELL /usr/local/bin/bash
./configure --target=h8300-elf --diable-nls --disable-threads --disable-shared --enable-languages=c
# ./configure --target=h8300-elf --diable-nls --disable-threads --disable-shared --enable-languages=c --disable-werror
make
sudo make install




gccのビルド中に、gcc/collect2.cのopenシステムコール呼び出しでコンパイルエラーが出る場合がある。
この場合は、第三引数にパーミッション「0755」を追加する。



H8はフラッシュメモリにブートローダを持っていて、書き込みにはフリーソフトのh8writeを使用する。
(Windows環境だと、RenesasがFDTというツールを用意していて、これを使ってもよい)

h8write.cをwindowsで使う場合は、defineでWIN32を有効にしたうえで、使用するシリアルポートを定義する。


ここまで作ったら、Hello Worldのプログラムを作る。
表示はputsで指定するけど、H8マイコンはディスプレイを持おらずputs関数は使えないので自作する必要がある(シリアルに出力する)。一部の標準ライブラリ関数に関しては、glibcをリンクすれば使えるのだけど、コードサイズが大きくなるし、全部自作したほうが面白いので、この本では使用しないらしい。

ソースは下記のURLからDL可能。
(LICENSEを見るとコピーOKらしいので全部転記してもよいのですが、リンクだけの紹介にしておきます。)

http://kozos.jp/books/makeos/
http://kozos.jp/kozos/osbook/osbook_03/01/bootload/




で、今回作るソースは以下の8本。
各コードの詳細は2ndステップ以降らしいけど、普通にCの開発経験があればリンカオプション以外は簡単に理解できるレベルになっている

main.c     
    hello worldの本体
 
startup.s  
    スタートアップルーチン/ラベル_startが存在する
    アセンブラで記述されており、ここからmain()がコールされる
 
vector.c   
    割り込みベクタ
    今は、start()しかなく、これはstartup.sの_startラベルに対応している
 
lib.h/lib.c
    標準ライブラリ関数を入れるところ
    今はputs,putcしかない。
    putsはputcを使って実装されている
    (なので、今のところputcだけがHW依存になっている)
    putcはserial.cの関数を使っている
 
serial.h/serial.c
    シリアルデバイスドライバ
    0xffffb0,ffffb8,ffffc0のアドレスを直指定で、データのr/wを行っている。
    (シリアルは3つあるらしくプログラムでは0xffffb8から8byte分を使っている)
    クロックを元に9600bpsを算出してるっぽい。
 
defines.h 
    NULLとかuint8,uint16などのdefine
 
ld.scr
    リンカスクリプト
    ここで、各処理が何番地にマッピングされるかを決めているらしい。
    割り込みベクタはCPUの仕様として0番地から始まるので、開始アドレスを決めている。
    ここは、まだ良く分からないけど3章で説明されるらしい...
 
Makefile
    普通のmakefile



Makefileを見ると、クロスコンパイル用のgccを使ってELFのバイナリを出力している。
h8writeはモトローラのSレコードフォーマット(.mot)しか解釈できないので、その後”objcopy -O srec”で.motファイルに変換している。


4877832394
12ステップで作る組込みOS自作入門

「12ステップで作る組込みOS自作入門」を読んで、OSを作ってみたい

パタヘネ本、CPUの作り方を呼んで、CPU周りの仕組みはイメージがつかめてきたので、次はOSのレイヤに挑戦することにします。

Linuxはソースがあるのが良いけど、コードサイズが大きいので出来れば避けたい。
どうしようかと思いつつ入門者向けのOS作成はないかなと調べてたら、以下の本あたりが良さげらしい。

4839917639
MONA―2ちゃんねる発祥の手作りOS

4839919844
30日でできる! OS自作入門

4798022543
作りながら学ぶOSカーネル―保護モードプログラミングの基本と実践

4877832394
12ステップで作る組込みOS自作入門



Intel CPU向けの書籍だとVMWareで実行ができるから楽チンなんだけど、x86はアセンブラの命令が酷いのと、ブートストラップがめんどいというイメージがあります(偏見かも…)。個人的には、純粋にOS周りだけを勉強したく、かつ、ブートストラップから全部書きたいので、出来ればx86アーキテクチャは避けたいところです。

で、上記4冊の目次をチェックしてみると、上3つはx86向けで最後の1つだけはルネサスのH8がターゲットなので、「組込みOS自作入門」はその点でメリットがあります。また、組込みOS自作入門は、ソースも2000行未満でかつ改変・再配布もOKなので、全容が把握しやすくblogでネタにするにも都合が良いです。

というわけで、今回は「12ステップで作る組込みOS自作入門」を題材に勉強していく事にします。

パタヘネ本のときと同様、今回も読書メモを取りながら読み進めていきます。



目次:

第1部 ブート・ローダーの作成
  1stステップ: 開発環境の作成
      1.1 開発環境の構築
      1.2 「Hello World」のソース・コード
      1.3 「Hello World」を動かしてみよう!
      1.4 まとめ
  2ndステップ: シリアル通信
      2.1 メモリマップドI/O
      2.2 内蔵シリアル・コントローラ
      2.3 ライブラリ関数の追加
      2.4 プログラムの実行
      2.5 スタート・アップ
      2.6 まとめ
  3rdステップ: 静的変数の読み書き
      3.1 メモリ構成
      3.2 静的変数の書き換えの対応
      3.3 プログラムの実行
      3.4 まとめ
  4thステップ: シリアル経由でファイルを転送する
      4.1 ブート・ローダー
      4.2 シリアル経由でのファイル転送
      4.3 XMODEMを実装する
      4.4 プログラムの実行
      4.5 アセンブラ・プログラミング
      4.6 まとめ
  5thステップ: ELFフォーマットの展開
      5.1 オブジェクト・ファイル・フォーマット
      5.2 ELF形式
      5.3 プログラム・ヘッダによるメモリ展開
      5.4 プログラムの実行
      5.5 論理回路
      5.6 まとめ
  6thステップ: もう一度,Hello World
      6.1 プログラムのロード
      6.2 「Hello World」の作成
      6.3 プログラムの実行
      6.4 CPUの動作原理
      6.5 まとめ
 
第2部 OSの作成
  7thステップ: 割込み処理を実装する
      7.1 割込み処理
      7.2 H8/3069Fの割込み処理
      7.3 ブート・ローダーに割込みハンドラを実装する
      7.4 プログラムの実行
      7.5 まとめ
  8thステップ: スレッドを実装する
      8.1 OSの概要
      8.2 OSの実装
      8.3 プログラムの実行
      8.4 まとめ
  9thステップ: 優先度スケジューリング
      9.1 優先度ベースのスケジューリング
      9.2 優先度の実装
      9.3 プログラムの実行
      9.4 まとめ
  10thステップ: OSのメモリ管理
      10.1 OSの役割
      10.2 メモリ管理の概要
      10.3 メモリ管理の実装
      10.4 プログラムの実行
      10.5 まとめ
  11thステップ: タスク間通信を実装する
      11.1 タスク間通信
      11.2 関数の再入と排他
      11.3 メッセージ通信の実装
      11.4 プログラムの実行
      11.5 まとめ
  12thステップ: 外部割込みを実装する
      12.1 割込みとスレッド
      12.2 コマンド処理の実装
      12.3 プログラムの実行
      12.4 まとめ






あと、参考になりそうなサイトのリンク集を作っておきます。

Sakai Hiroaki’s Home Page
筆者オフィシャルのホームページ

独自OSを作ってみよう!
書籍で作るOSであるkozosのサイト

「H8マイコンボードで動作する組み込みOSを自作してみよう!」最新記事一覧 – ITmedia Keywords
ITmediaに連載されていた、筆者による記事
書籍のダイジェスト版のようなものなので、本を買う前に難易度を把握するにはお勧めです

Mac OS Lionで「12ステップで作る 組込みOS自作入門」1 – ひとりWEB開発日記
Mac OS Lionで「12ステップで作る 組込みOS自作入門」2 – ひとりWEB開発日記
タイトルどおり、Macでgccを使って環境を作ってみえます。
blogに載っているのは環境の構築までっぽいですが、Macでの取っ掛かりが欲しい人には良い情報です。
ボードにつなぐアダプタの電圧(と定格電流)に注意です。

会社帰りに近所の本屋で見つけたので立ち読みしてみると、基本的にはWin/Linuxでやれということだが、我が家はMacオンリー。
一度帰宅してから、ネットで情報を探すと、Macでやってる人もいるらしい。


久しぶりにH8の開発環境を構築:Pakri / Modki:So-netブログ
組込みOS自作入門 ? Hello, world!:Pakri / Modki:So-netブログ
こちらはubuntuで環境構築のヒントが有ります。

 Ubuntu上ではh8writeは最初の1回しか書込出来ないと本に書いてあったけど、実際、1回しか書込み出来なかった。この場合、本ではUbuntuを再起動するように指示があるが、killコマンドを使ってh8writeのプロセスを殺せば、再起動をしなくとも書込み出来る。


組み込みOS自作入門の入門 – tomの日記
こちらもUbuntu。9.10での環境構築が載ってます。

12ステップで作る組み込みOS自作入門 1ステップ目 – 不沈船金義丸
12ステップで作る組み込みOS自作入門 1.1ステップ目 – 不沈船金義丸
12ステップで作る組み込みOS自作入門 1.2ステップ目 – 不沈船金義丸
12ステップで作る組み込みOS自作入門 2ステップ目 – 不沈船金義丸
12ステップで作る組み込みOS自作入門 3ステップ目 – 不沈船金義丸
書籍序盤の作業メモがあります。

本:組込みOS自作入門(著:坂井弘亮) -天気晴朗ナレドモ浪高シ-
自作OS勉強会:”第12回もくもく会”に参加してきた件に関して -天気晴朗ナレドモ浪高シ-
メモ:”12ステップで作る 組込みOS自作入門”のステップ9でトラブル -天気晴朗ナレドモ浪高シ-
プログラム転送のUSBシリアルとして、以下の商品を紹介してくれています。
B000ANR7T4
ラトックシステム USB-Serial Converter REX-USB60F

ブログテーマ[組み込みOS]|embitのソフトな日常-または私は如何にして心配するのを止めてハードも学ぶようになったか
最新記事が2012/9なので、まさに今挑戦中のblogのようです。各記事は短いけど、はまったところと解決が簡潔に載っているので参考になりそうな感じ。

読書メモ/「12ステップで作る組込みOS自作入門」 – Glamenv-Septzen.net
書評です。書籍中でシリアルでのプログラム転送で上手くいかない場合があるとの注意書きが有るのですが、動作確認が取れている232cボードが紹介されています。

CuBeatSystems: XMODEM for KOZOS (12ステップで作る 組込みOS自作入門 KOZOS用ユティリティ kz_xmodem)
ブートローダ書き込み用のツールを開発&公開しています。

KOZOS友の会
12ステップ本もくもく会おつかれさまでした|KOZOSのブログ
第2回12ステップ本もくもく会おつかれさまでした|KOZOSのブログ
12ステップ組込みOS自作本の読書会をされいたようです。タイトルを「12ステップ組込みOS自作本」と略してるのだけど、この略称がスタンダードなんだろうか…

http://toragi.cqpub.co.jp/Portals/0/support/2010/H8/mihon/191_197_%2822%29.pdf
ルネサス提供のH8向けIDEであるHEWで、gccを使うための情報

みんなコマンドラインでgccなので、HEWで試してみたいのだけど、オブジェクトサイズの制限(128KB)に掛かるか、現段階では判断つかない。 あと、評価版のDLにMY RENESASというサービスのIDを作る必要が有るのも面倒だ…


4877832394
12ステップで作る組込みOS自作入門

[PIC] 16F84A->16F88で,ピンに追加された機能を確認する

PICの入門向け書籍でよく紹介されているPIC16F84Aですが、16F84Aとピン互換がありながらプログラムメモリが4倍に増えたPICに16F88が有ります。

16F88を単に84Aのプログラム容量が大きい版として使うのもよいのですが、機能も追加されています。
今回はデータシートのピンアサインを元に、16F88で増えた機能を確認します。

まず、こちらがPIC16F84Aのピンアサインです。


で、こちらが16F88です。


16F88を見ると分かるように、各ピンに対して様々な機能が追加されています。

慣れればピンの名称だけで機能が類推できますが、最初のうちは略称だけでは内容が分からないので、Pinout Descriptionを見ると概要が載っています。
(クリックで拡大します。)







RA0~4には、アナログ入出力が増えています。

OSC1,OSC2には、追加でRA6,7が割り当てられました。
16F84Aでは外部クロックが必須だったのに対して、16F88は内部のクロックを元に動作させることが可能なので、15,16pinもI/Oとして使用可能です。
内部クロックを使用するためには、コンフィギュレーションビットで以下の値を指定します。INTIO2の指定でI/Oとして使用可能となります。

INTIO1   Internal Oscillator with FOSC/4 output on RA6 and I/O on RA7
INTIO2   Internal Oscillator with I/O on RA6 and RA7



MCLRにもRA5が割り当たっています。
こちらも、コンフィギュレーションビットでMCLREを無効にすることで、RA5が使用可能になります

MCLRE: RA5/MCLR/VPP Pin Function Select bit
	1 = RA5/MCLR/VPP pin function is MCLR
 
	0 = RA5/MCLR/VPP pin function is digital I/O, 
	    MCLR internally tied to VDD


RA4~7はシュミットトリガ入力となっているため、ここにボタンを付ける場合チャタリング防止の回路を組みやすくなります。


pin6,9のCCPでPWM出力、pin7,10のSDA/SCLでI2C通信が行えます。


pin12,13のPGC/PGDはICD(インサーキットデバッガ)の機能です。
コンフィギュレーションビットでDEBUGを0にするとデバッガが使えます。

DEBUG: In-Circuit Debugger Mode bit
	1 = In-Circuit Debugger disabled, RB6 and RB7 are 
		general purpose I/O pins
 
	0 = In-Circuit Debugger enabled, RB6 and RB7 are 
		dedicated to the debugger


XBeeのピン配列

XBee(Series2)は、基板から20本の足が出ています。
ピン番号はICと同じで、上から見て左上から反時計回りに1-20番pinになります。


ピンアサインは以下の通りとなっています。


Pin 名称        I/O     I/Oデフォルト値      機能
--- ------------------  ------- -------------------  ------------------------------
1   VCC                 -                            電源(3.3v)
2   DOUT                出力                         UART出力(TX)
3   DIN/nCONFIG         入力                         UART入力(RX)
4   DIO12               双方向   無効                デジタルIO12
5   nRESET              双方向   オープンコレクタ    リセット(負論理)
6   RSSI/PWM0/DIO10     双方向   出力                信号強度出力/PWM/デジタルIO10
7   DIO11               双方向   入力                デジタルIO11
8   reserved            -                            予約済み(接続NG)
9   nDTR/SLEEP RQ/DIO8  双方向   入力                DTR/スリープ要求/デジタルIO8
10  GND                 -                            接地
11  DIO4                双方向   無効                デジタルIO4
12  CTS/DIO7            双方向   出力                CTS/デジタルIO7
13  ON/nSLEEP           出力                         スリープ表示
14  VREF                入力                         未使用
15  Associate/DIO5      双方向   出力                接続表示/デジタルIO5
16  nRTS/DIO6           双方向   入力                RTS/デジタルIO7
17  AD3/DIO3            双方向   無効                アナログ入力3/デジタルIO3
18  AD2/DIO2            双方向   無効                アナログ入力2/デジタルIO2
19  AD1/DIO1            双方向   無効                アナログ入力1/デジタルIO1
20  AD0/DIO0            双方向   無効                アナログ入力0/デジタルIO0





また、XBeeは基板上にSIFプログラミングヘッダのパターンがあり、これを使用することでカスタムのファームをアップロードできます。
こちらは、ピン番号の規則が足とは異なるので注意してください。


こちらピンアサインは以下の通りです。


Pin	機能
--- ---------
1	VBRD
2	SIF-MISO
3	GND
4	SIF-MOSI
5	GND
6	SIF-CLK
7	SIF-LOAD
8	Reset
9	PTI-EN
10	PTI-DATA

XBeeを始めて買うとき最低限知っておきたい6つのこと

B007V8I98O

XBeeを使うと、PICやArduinoと連携して無線データ通信を簡単に行えます。
今回はXBeeを新しく買いたい人向けに、基礎的な情報をまとめておきます。


XBeeとZigBeeの関係


XBeeについて調べていると、”ZigBee”というキーワードが出てきます。
ZigBeeというのは、Bluetoothや、wifiのような無線通信の規格を意味しています。
ZigBee物理層はIEEE 802.15.4として規格化されています。

XBeeは、ZigBeeプロトコルを使ってデータの送受信を行います。
ですので、XBeeを無線LANのルータや、Bluetoothイヤホンと通信させる事は出来ません。
ZigBeeプロトコルを使うデバイスはXBeeだけでは有りません。例えば国内メーカのルネサスもZigBee向けデバイスを発表しています。

無線による通信を行っているのですが、XBeeは日本版以外のモノであっても技適取得済なので国内で安心して合法的に使用できます。

XBeeのシリーズ


XBeeには、シリーズ1とシリーズ2というものが有ります。
シリーズ1とシリーズ2のデバイスは仕様が異なるため、相互に通信が出来ないので、特定のシリーズのもので統一する必要がります。
シリーズ2の方が新しいし、より消費電力が少なくて、かつ、遠くまで電波を飛ばせるため、新しく買うならシリーズ2の方が良いです。

シリーズ2は、”XBee ZB”という表記になっている場合もあります。これは、シリーズ2のファームがZB Zigbee meshと呼ばれている事に由来しています。


XBeeシリーズ2のスペック


シリーズ2の主なスペックは以下の通りです。(アンテナの形など、型番によって若干変わります)

電源                2.1~3.6 V DC
 
室内通信距離        40m
見通し通信距離      120m
 
周波数帯域          2.4GHz
通信速度            250 Kbps
デジタルI/O         11
アナログI/O         4
PWM出力             なし
通信時消費電流      40mA
スリープ時消費電流  1uA以下




XBeeとXBee Pro


XBeeには、ハイスペック版のXBee Proというものが有ります。

XBee
B007V99PFY

XBee Pro
B007V8I98O


XBee Proは5センチ程度奥行きが長くなりますが、データの通信距離が長くなります。

室内通信距離        40m   -> 60m
見通し通信距離      120m  -> 1500m



XBee Proのほうが高機能で価格差も少ないので、出来ればXBee Proを購入する方がお勧めです。


XBeeのアンテナ形状


XBeeはアンテナ形状が異なるものが、ワイヤアンテナ型、U.FLコネクタ型、PCBアンテナ型、RPSMAコネクタ型の4つあります。
ワイヤアンテナ型だと指向性が無く遠くまで電波が飛ぶので、これを選んでおけば確実です。

B007V8I98O
XBee-PRO ZB / ワイヤアンテナ型

B007V9YS9M
XBee-PRO ZB / U.FLコネクタ型

B0079TC396
Programmable XBee-PRO ZB / PCBアンテナ型


XBeeと一緒に買うべきもの


どの商品を買っても、ピンのピッチが2mmなので、ブレッドボードに直接刺せません。
ですのでXBee購入時は、以下の様なピンピッチ変換の基板を同時に入手しておく必要があります。
このピッチ変換基板の事は、ブレークアウトボードと呼ばれることが多いです。
B0054O1V6Y
XBeeピッチ変換基板とソケットのセット

また、最初の1個目は、USBコネクタ付きのXBeeエクスプローラを1つ買っておいてください。
これは、ブレークアウトボードに加えて、USB経由でXBeeの初期設定が行えるため楽チンです。
B0079DM572
XBeeエクスプローラUSB


U.FLコネクタ型のXBeeを飼った場合は、外部アンテナも必要です。
B007VAH78A
XBee用外部アンテナU.FLコネクタ型


Arduinoにつなげて使いたい場合は、専用のシールド(連結基板)も販売されています。
B0081X86HM
Arduino ワイヤレスプロトシールド

ArduinoシールドはSDコネクタ付きのモノも有ります。
データロガーとして使いたい場合は、こちらもお勧めです。
B0081XAGLG
Arduino ワイヤレスSDシールド


XBeeの周りに、小さな周辺回路を組みたい場合はこのような商品も安価でお勧めです。
B0085SU3MO
XBeeの載る「C基板」ソケット付き


電子工作が初めての場合、便利なセット品もあります
B0085SWE26
超お手軽無線モジュールXBee部品セット


ちなみに、XBeeは1台だけだと意味がない(通信相手がいない)ので、必ず2個買ってください。



というわけでまとめると、最小構成で購入する場合のお勧めは、以下のパターンです。
XBee-PRO ZB / ワイヤアンテナ型 x 2個
XBeeエクスプローラUSB x 1個
XBeeピッチ変換基板とソケットのセット x 1個
Arduino ワイヤレスプロトシールド x 1個 (Arduinoで使う場合のみ)


XBeeの購入方法

ネットだと、amazonからの購入が便利です。



デバイスだけでなく解説書つきのモノも有るので、何も分からないところからはじめる場合は、このような商品もお勧めです。

[XBee 2個+書込基板+解説書]キット付き 超お手軽無線モジュールXBee


Arduinoとの組み合わてのプログラムに関する書籍も出ています。

ZigBee/Wi-Fi/Bluetooth無線用Arduinoプログラム全集


東京・大阪・名古屋辺りの大都市なら、電気街でも購入できます(マルツや秋月で買えます)。

他にもスイッチサイエンスや、ストロベリーリナックスからも購入できるようです。


4873115302
XBeeで作るワイヤレスセンサーネットワーク (Make: PROJECTS)


最近はIoTがブームですが、このようなパーツを利用すると安価に試作できるのが良いですね。

回路図作成にBSch3Vを使ってみた

ユニバーサル基板で回路を組むために回路図を描きたかったのですが、手書きだと面倒なのでフリーソフトのBSch3Vを使ってみました。

今回、使い勝手を調べるために作った回路図は、以前調べた74HC4511/BQ-N516RDの組合せです。
74HC4511とBQ-N516RDで,4桁の7セグLEDをコントロールする


BSch3VにBQ-N516RDの部品は登録されていないので、部品から作る必要があります。
部品は、BSch3Vに同梱しているLCoV.exeというツールで作成します。



かなり使いやすいツールで、さくっと作れました。


ピン配置もこのような感じで、直感的に定義できます。



74HC4511の方は最初から入っているのだけど、ピン配列が実物と異なる順番なので、今回はこっちもパーツを作成しました。

ちなみに、予め用意されているのは、こんな感じのものです。




で、試しに作ってみた回路図です。
回路作成の専用ツールなので、Excelなどで描くよりも楽チンです。



上記の回路図だと交差が多いので、ユニバーサルで実装する事を考えると半田作業が面倒な事になります。
また、BQ-N516RDはピンの間が広くて配線スペースがあるので、その点を考慮して書き直したのが下の図です。ついでにVccやGNDも付けてみました。


立体交差が3つまで減りました。
出来上がりの絵を見るとfのラインも内側から配線できそうですが、実際のBQ-N516RDはピン間が5ライン分しか開いてないので、実際はこれが限界です。





で、1時間ほど使ってみた感想です。

1.Excelで作るよりは断然楽チン
Excelだと線幅をあわせたりするのが面倒なので、やはり専用ツールは便利です。
自動配線機能は有りませんが、配線を考えるところも含めて勉強したいので、個人的にはこれでOKです(シンプルが一番)。


2.実体配線図のシミュレーション目的にはちょっと使い辛い
元々回路図を作るためのソフトなので、ピン配列が実物と異なる順番なのは当たり前なのですが…


3.部品間の幅を調整したい時に、配線の幅を返るのが面倒
先ほどの2つ目の回路図で、2つのパーツの幅をもう少し縮めたり、間に抵抗を挟みたかったのですが、線の長さを纏めて変更できないっぽいので配線しなおしが面倒です。




やっぱり実体配線図向けには、別のソフトを使ったほうがよいのかな…、と思ったりしますが、もう少し使い込んでみます。


また、他のフリーな回路図エディタにはPCBEというものも有るらしいので、PCBEもそのうち評価してみたいです。

“MSVBVM50.DLLがないため、プログラムを開始できません。”エラー発生時にすべき事

プログラムを起動しようとすると、以下のエラーが発生して実行できない場合があります。


コンピューターに MSVBVM50.DLL がないため、プログラムを開始できません。
この問題を解決するには、プログラムを再インストールしてみてください。




これは、プログラムがVisual Basic5.0(VB5.0)という開発環境で作成されているのが原因です。
VB5.0用のプログラムを実行するためには、事前にMicrosoftのサイトから「ランタイムライブラリ」をダウンロードして、インストールしておく必要があります。

ダウンロード&インストールの手順は、以下の通りです。



ダウンロード&インストールの方法

下記のページに移動します。
FILE: Msvbvm50.exe Installs Visual Basic 5.0 Run-Time Files


ページをスクロールさせていくと、MORE INFORMATIONという欄があります。このすぐ下にある、Msvbvm50.exeのリンクをクリックするとランタイムがダウンロードできます。


ダウンロードしたMsvbvm50.exeファイルをダブルクリックします。


「ランタイムをインストールしますか?」のダイアログが出るので、はいをクリックします。


プログラム互換アシスタントが表示された場合は、一旦”正しくインストールされました”を選んでおきます。



これで、再度問題があったプログラムを実行してみて下さい。
今度は正しく起動できるはずです。




ちなみにインストール先ですが、Windows7の64bit版だとC:\Windows\SysWOW64にインストールされました。


このラインタイムによって以下のファイルが入るはずです。

Msvbvm50.dll 
Oleaut32.dll 
Olepro32.dll 
Stdole2.tlb  
Asycfilt.dll 
Comcat.dll




[PIC]HI-TECH Cで、タイマー割り込みを行う(PIC16F84A)

PIC16F84Aには、TMRというタイマーモジュールがあり、これを利用と一定周期でタイマ割り込みをかけることが出来ます。

今回は、HI-TECH Cでタイマ割り込み処理を作成してみます。


※アセンブラ(MPASM)でタイマー割り込み処理を行いたい場合は、こちらの記事を参考にして下さい。
[PIC]TMRモジュールを使用する(タイマー割り込み)


TMRを使用したサンプルコードです。
これは,8Mhzで動作しているPICに対して,約10ミリ秒周期でタイマー割り込みをかけています。

#include <stdio.h>
#include <htc.h>
 
#define TMR0_INTERVAL (255-150) // TMRモジュールのカウント初期値
                                // 8Mhz, PS=6, TMR0=150サイクル ≒ 10msec
 
int intr_counter = 0;
int data = 0;
 
//--------------------------------
// メイン処理
//--------------------------------
void main( void )
{
    __CONFIG ( FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF );
 
    // PortBを出力モードにする
    TRISB = 0;
 
 
    TMR0   = TMR0_INTERVAL;     // カウントアップタイマー値を初期化
 
    INTCONbits.T0IE   = 1;      // TMR0   interruptを有効にする
    INTCONbits.GIE    = 1;      // Grobal interruptを有効にする
 
    OPTION_REGbits.T0CS  = 0;   // タイマモードとして実行
    OPTION_REGbits.PS    = 6;   // プリスケーラ値(6:128倍)
    OPTION_REGbits.PSA   = 0;   // プリスケーラをTMRモジュールで使用する
 
    // 無限ループに入る
    while ( 1 ) {
        NOP();
    }
}
 
//--------------------------------
// 割り込み処理(10msec周期)
//--------------------------------
static void interrupt intr( void )
{
    intr_counter++;
    if ( intr_counter >= 10 ) {
        intr_counter = 0;
 
        //-------------------------------
        // 10回割り込みが来た
        //  -> 100msec周期で値を加算する
        //-------------------------------
        data++;
    }
 
    // 加算値をPortBに出力
    PORTB = data;
 
    //-------------------
    // 割り込みの再設定
    //-------------------
    TMR0 = TMR0_INTERVAL;
    T0IF = 0;
}



サンプルコードを元に、タイマー割り込みの使用方法を順に見ていきます。


割り込み処理を有効にする


まずはmain関数側で、main()では大きく3つの事を行っています。

まずは、タイマー値を初期化します。
タイマーはTMR0に指定した値からカウントアップが行われ、オーバーフローすると割り込み処理が走ります。レジスタ値は8bitなので、オーバーフローは値が255を超えたときに発生します。

    TMR0   = TMR0_INTERVAL;     // カウントアップタイマー値を初期化



今回のタイマー値はdefineされており、150回カウントアップすると割り込みが走るようにしました。
150を設定した理由は後で説明します。

#define TMR0_INTERVAL (255-150)





次に、タイマー割り込み処理を有効にします。タイマー割り込みはINTCONレジスタのT0IE(Tmr0 Interuupt Enabled)ビットを立てることで有効になります。割り込みを起こすには、T0IEを有効にした上で、さらにGIE(Grobal Interrupt Enabled)ビットを立てる必要があります。

    INTCONbits.T0IE   = 1;      // TMR0   interruptを有効にする
    INTCONbits.GIE    = 1;      // Grobal interruptを有効にする



INTCONレジスタの値を変更するには、INTCONbits共用体のT0IEフィールドを操作します。このフィールドはビットフィールドで実装されているので、メンバを指定する事でレジスタ内の該当ビットが操作できます。今回はpic15f84aを使用しているので、この構造体はHI-TECH Cをインストールした先のinclude\pic15f84a.hに定義されています。

ヘッダファイルを見ると分かるのですが、INTCONレジスタについては以下の定義がなされています。

// Register: INTCON
volatile unsigned char           INTCON              @ 0x00B;
// bit and bitfield definitions
volatile bit RBIF                @ ((unsigned)&INTCON*8)+0;
volatile bit INTF                @ ((unsigned)&INTCON*8)+1;
volatile bit T0IF                @ ((unsigned)&INTCON*8)+2;
volatile bit RBIE                @ ((unsigned)&INTCON*8)+3;
volatile bit INTE                @ ((unsigned)&INTCON*8)+4;
volatile bit T0IE                @ ((unsigned)&INTCON*8)+5;
volatile bit EEIE                @ ((unsigned)&INTCON*8)+6;
volatile bit GIE                 @ ((unsigned)&INTCON*8)+7;
volatile bit TMR0IF              @ ((unsigned)&INTCON*8)+2;
volatile bit TMR0IE              @ ((unsigned)&INTCON*8)+5;
#ifndef _LIB_BUILD
volatile union {
    struct {
        unsigned    RBIF                : 1;
        unsigned    INTF                : 1;
        unsigned    T0IF                : 1;
        unsigned    RBIE                : 1;
        unsigned    INTE                : 1;
        unsigned    T0IE                : 1;
        unsigned    EEIE                : 1;
        unsigned    GIE                 : 1;
    };
    struct {
        unsigned                        : 2;
        unsigned    TMR0IF              : 1;
        unsigned    : 2;
        unsigned    TMR0IE              : 1;
    };
} INTCONbits @ 0x00B;
#endif




INTCONbits構造体以外にも、各ビットを指すT0IE,GIE変数も定義されています。
ですので、下記のコードで指定する事も可能です。

    T0IE   = 1;
    GIE    = 1;



また、INTCONという変数自体も定義されている為、下記のコードでもOKです。

    INTCON |= 0x20;  // T0IEを有効にする
    INTCON |= 0x80;  // GIEを有効にする




割り込みを指定したら、OPTION_REGレジスタでTMRモジュールの加算トリガを決定します。
T0CSビットを0にするとクロックサイクルを元にタイマーが加算されるようになります。
結果として、アセンブラ命令が1つ実行する毎にTMR0レジスタが1づつ加算されます。

    OPTION_REGbits.T0CS  = 0;   // タイマモードとして実行



1クロックサイクル単位で1つ加算されると、仮にTMR0=0としても255命令毎に割り込みが掛かってしまうため、非常に短周期での割り込みとなってしまいます。

この周期を長くするためには、プリスケーラを使用します。プリスケーラを有効にした上で,プリスケーラ値を1~7の値にすることで、TMR0が加算されるタイミングを変更する事が出来ます。
サンプルでは、プリスケーラ値を6にしています。

    OPTION_REGbits.PS    = 6;   // プリスケーラ値(6:128倍)
    OPTION_REGbits.PSA   = 0;   // プリスケーラをTMRモジュールで使用する



プリスケーラ値が1増える毎にTMR0が加算されるタイミングが2倍遅くなります。
例えば2を指定すると、アセンブラを4命令実行する毎にTMR0が加算されます。


割り込み周期を算出する

今回は、プリスケーラ=6、TMR0=255-150で設定したので、タイマー割り込みは以下の周期となります。

150*128=19200クロックサイクル



PICは4クロックで1命令実行するので、以下のクロック周期で割り込みが起きます。

19200 * 4 = 76800クロック



今回のサンプルは、PICを8Mhzのセラロックで動作させているので、約10mSec周期での割り込みとなります。

76800 / 8000000 = 0.0096(秒)
                = 9.6(ミリ秒)



これで、割り込みの指定は完了です。



割り込みルーチン

割り込みルーチンですが、”static void interrupt”な関数を定義します。関数名は何でも良く、この修飾子を持つ関数を用意しておくと、HI-TECH Cはタイマー割り込みのハンドラ関数と見なしてくれます。

static void interrupt intr( void )




割り込み処理は、今回のサンプルでは以下のロジックになっています。
プリスケーラを使用して10ミリ秒周期まで落としましたが、これでも人間にとってはまだ速いので、さらに10倍して100ミリ秒単位で値を加算し、その値をPortBに出力しています。これで、PortBにLEDをつないでおけば割り込みが周期的に入っている事が目視で確認できます。

    intr_counter++;
    if ( intr_counter >= 10 ) {
        intr_counter = 0;
 
        //-------------------------------
        // 10回割り込みが来た
        //  -> 100msec周期で値を加算する
        //-------------------------------
        data++;
    }
 
    // 加算値をPortBに出力
    PORTB = data;



割り込み処理が終わったら、再度TMR0に値をセットした後、T0IFをクリアします。
T0IFは”TMR0 Overflow Interrupt Flag bit”で、割り込みのクリア用フラグです。これをクリアし忘れると、割り込み関数が終わっても元の処理に戻ってくれず割り込み関数が無限ループで動作する事になってしまいます。

    //-------------------
    // 割り込みの再設定
    //-------------------
    TMR0 = TMR0_INTERVAL;
    T0IF = 0;


ESET Smart Securityで、メールの末尾にメッセージを出さないようにする方法

ウィルスチェックソフトのESET Smart Securityを使用して、メール送信を行うと、自動で以下のメッセージが付きます。

__________  ESET Smart Security からの情報, ウイルス定義データベースのバージョン 9999 (20120831) __________
このメッセージは ESET Smart Security によって検査済みです。
http://canon-its.jp



企業間でのやり取りなら付いていても問題ないのでしょうが、B2Cで顧客向けに配信するメールに、このメッセージがついていると具合が悪い場合もあります。


このメッセージは、以下の操作をすることで付けないようにすることが出来ます。

画面左下のタスクトレイより、ESET Smart Securityのアイコン(目玉っぽいアイコン)をクリックします。


ESET Smart Securityのウィンドウが表示されます。
ウィンドウ右下の表示が、標準モードになっている場合は、”変更…”をクリックします。
(詳細になっている場合はクリックしなくて良いです)


ダイアログが出るので、”はい”をクリックします。


画面左にある”設定”をクリックし、”環境設定で詳細な設定をする…”をクリックします。


ウィンドウ左のメニューより、”ウイルス・スパイウェア対策” ->”電子メールクライアント保護”をクリックします。


警告と通知にあるコンボボックスを、”追加しない”に変更します(2箇所)。


これで、メール本文の末尾にメッセージが追加されなくなります。

アマゾンマケプレで、配送業者を自動選択するスクリプトを変更

以前、アマゾンのマーケットプレース出品者向けに、Greasemonkeyのスクリプトを書きました。
アマゾンマーケットプレースの「複数商品の出荷通知」で、配送業者を自動選択する


アマゾンのhtmlソースが変わったせいで、このスクリプトが動かなくなっており、今回対応版を作成したので公開しておきます。

ソースは以下のような感じで、プログラムで変わったのはElementのID名称だけです。

// ==UserScript==
// @name           Amazon_MarketPlace_ShippingConfirm
// @namespace      http://nanoappli.com/
// @include        https://sellercentral.amazon.co.jp/gp/orders-v2/bulk-confirm-shipment*
// @grant          GM_getValue
// @grant          GM_setValue
// ==/UserScript==
 
(function(){
    var onLoadScript = function() {
        var selCarrierName = window.document.getElementById( "carrierNameDropDown_UNSHIPPEDITEMS" );
        var txtShipMethod  = window.document.getElementById( "shippingMethod_UNSHIPPEDITEMS" );
 
        selCarrierName.selectedIndex = 1;          // 配送業者 1:ヤマト, 2:佐川, 3:日通 4:西濃 5:日本郵便
        txtShipMethod.value          = "メール便"; // 配送方法
    };
 
    window.addEventListener("load", function(e){
        onLoadScript();
    }, false);
 
})();



使い方の詳細は、前回の記事を見てください。


改変後バージョンのダウンロードはこちら
Amazon_MarketPlace_ShippingConfirm_v1_1.user.js

[PIC]HI-TECH Cでコンフィギュレーションビットを指定する

PICのプログラムをアセンブリ(MPASM)で作成する場合は、CONFIG命令でコンフィギュレーションビットを指定するのですが、HI-TECH Cではこの設定は、__CONFIG()マクロで行います。

下記のプログラムは、__CONFIG()マクロの使用を使用したサンプルです。

#include <stdio.h>
#include <htc.h>
 
void main( void )
{
    __CONFIG( FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF );
}




コンフィギュレーションビットとして指定可能な値はPICの型番によって異なります。
自分が使用しているチップで何の設定が可能かは、HI-TECH Cのincludeフォルダにあるヘッダをチェックすれば確認できます。コンパイラをデフォルトのフォルダにインストールした場合、includeファイルは”C:\Program Files\HI-TECH Software\PICC\9.83\include”辺りに置いてあります。

例えば、pic16f84a.hでは以下の定義になっています。

// Oscillator Selection bits
// RC oscillator
#define FOSC_EXTRC           0xFFFF
// HS oscillator
#define FOSC_HS              0xFFFE
// XT oscillator
#define FOSC_XT              0xFFFD
// LP oscillator
#define FOSC_LP              0xFFFC
// Watchdog Timer
// WDT enabled
#define WDTE_ON              0xFFFF
// WDT disabled
#define WDTE_OFF             0xFFFB
// Power-up Timer Enable bit
// Power-up Timer is disabled
#define PWRTE_OFF            0xFFFF
// Power-up Timer is enabled
#define PWRTE_ON             0xFFF7
// Code Protection bit
// Code protection disabled
#define CP_OFF               0xFFFF
// All program memory is code protected
#define CP_ON                0xC00F




C言語で、ファイル操作系の関数(fopen等)に慣れている場合、各設定値を論理和(“|”記号)でつなぎたくなるところですが、HI-TECH Cの__CONFIG()マクロは、論理積(“&”記号)でつなげるのが正しいです。

この理由は、実際の各設定値がどんな値にdefineされているかを見れば分かりやすいです。設定したい項目だけビットが0になるという、ビットを反転した形で定義されているからです。

また、ここで指定するdefine値はMPASMのCONFIG命令と一部表記が異なっているので注意が必要です。



…と書くと、自分のプログラムからpic16f84a.hを直接includeしたくなるところですが、そうではなく、htc.hをincludeしてください。
このことは、各チップのヘッダの先頭に以下の警告がある事からも分かります。

#warning Header file pic16f84a.h included directly. Use #include <htc.h> instead.





htc.hさえインクルードしておけば、自動で開発しているチップに応じたヘッダが読み込まれるのですが、この仕組みは以下のようになっています。

htc.hを見ると、Liteバージョンのコンパイラを使っている場合はpic.hをincludeしています。

/* HI-TECH PICC / PICC-Lite compiler */
#if defined(__PICC__) || defined(__PICCLITE__)
#include <pic.h>
#endif



pic.hの中では、ターゲットとなるチップに応じたヘッダをincludeする為のファイルであるchip_select.hを読み込んでいます。

#include <chip_select.h>



chip_select.hで、チップの型番に応じたヘッダが読み込まれています。
下記の定義はpic16f84a用のものを抜粋しましたが、これたチップの型番の数だけ羅列されています。

#ifdef _16F84A
#ifdef _LEGACY_HEADERS
#include <legacy/pic1684.h>
#else
#include <pic16f84a.h>
#endif
#endif





ちなみにCONFIGマクロは、pic.hで以下のように定義されており、これはプリプロセッサによってインラインアセンブラに展開されます。

#define __CONFIG(x) asm("\tpsect config,class=CONFIG,delta=2");\
            asm("\tdw "___mkstr(x))


[PIC]HI-TECH Cで一定時間スリープさせる

PICでは、HI-TECH Cコンパイラをを使用した、C言語による開発を行う事ができます。
C言語で開発を行う場合、プログラム中で一定時間処理をスリープさせるにはどうしたら良いでしょうか?


HI-TECH Cの環境では、主に以下の4つの手法があります。

1. 空ループを回す
2. _delay()マクロを使用する
3. __delay_ms()マクロを使用する
4. __delay_us()マクロを使用する



上記のうち2,3,4のマクロはHI-TECH Cがあらかじめ用意してくれているものです。
__delay_ms()と__delay_us()は先頭のアンダーバーが2つですが、_delay()は1つで有ることに注意してください。また、これらのマクロはhtc.hで定義されているため、ファイルの先頭に「#include <htc.h>」の記述が必要です。


それでは、各パターンの具体的な方法について確認していきます。

1. 空ループを回す


空ループを回すパターンは、MPASMのアセンブラでコードを書いた経験がある人にとってはわかりやすい方法です。ループ内の処理は、NOP()関数を呼んでおけばアセンブラのNOP命令が1つ実行できます。

#include <htc.h>
...
 
int loop = 100;
while( loop-- > 0 ) {
    NOP();
}



上記のコードをコンパイルすると、このようなアセンブリになりました。

12:                 int loop = 100;
   3C9    3064     MOVLW 0x64
   3CA    1283     BCF 0x3, 0x5
   3CB    008F     MOVWF 0xf
   3CC    3000     MOVLW 0
   3CD    0090     MOVWF 0x10
13:                 while( loop-- > 0 ) {
   3CE    2BD1     GOTO 0x3d1
   3D1    30FF     MOVLW 0xff
   3D2    1283     BCF 0x3, 0x5
   3D3    078F     ADDWF 0xf, F
   3D4    1803     BTFSC 0x3, 0
   3D5    0A90     INCF 0x10, F
   3D6    30FF     MOVLW 0xff
   3D7    0790     ADDWF 0x10, F
   3D8    1F90     BTFSS 0x10, 0x7
   3D9    2BDB     GOTO 0x3db
   3DA    2BDC     GOTO 0x3dc
   3DB    2BCF     GOTO 0x3cf
14:                     NOP();
   3CF    0000     NOP
   3D0    2BD1     GOTO 0x3d1
15:                 }



空ループで対応する場合は、生成されるコードとクロック周波数から待たせたい時間を逆算する必要があるので、待機時間が決まっている場合は面倒になります。

2. _delay()マクロを使用する


_delay()マクロを使用する場合も、基本的には空ループと同様なのですが、引数を指定できる点が違います。例えば以下のコードで100命令分の待ちを作ることが出来るので、空ループの時に考慮が必要だったループ判定はNOPで待機時間の計算についての面倒さから開放されます。

void main( void )
{
    _delay(100);
}





_delay()関数は、大きすぎる値を指定すると以下のエラーが発生する場合があるので注意が必要です。
例えば、下記のコードをコンパイルすると…

void main( void )
{
    _delay(50462463);  // ok
    _delay(50462464);   // ng
}



下側の_delay()命令でエラーになります。

Error   [1274] test\main.c; 11. delay exceeds maximum limit of 50462464 cycles


エラーメッセージにも表示されていますが、引数に50462464以上の値(16進数だと0x301FF00)を指定するとコンパイルが出来ません。

マイコンでこれほどの時間をスリープさせる事はあまり無いかもしれませんが、もし多くの時間をスリープさせたい場合は以下のようにループにすれば対処できます。

    // これはNG
    _delay(50462464);
 
    // これはOK
    int loop = 0;
    for ( loop = 0; loop < 1000; loop++ ) {
        _delay(50000);
    }




また、_delay()マクロに指定する値には定数での指定が必要です。
変数を使用するとエラーになります。

int waitTime = 100;
_delay(waitTime);



上記のコードをコンパイルした時に出力されるエラーメッセージです。

Error   [1387] test\main.c; 13. inline delay argument must be constantError   
         [712] test\main.c; 13. can't generate code for this expression



定数なら大丈夫なので、以下のようにdefineにしておけばエラーになりません。

#define WAIT_TIME 100
_delay(WAIT_TIME);




3. __delay_ms()マクロを使用する


_delay()命令をさらに便利にしたものが__delay_ms()です。
これは時間指定(ミリ秒)でスリープさせる事が可能です。

__delay_ms(1000); // 1秒待機



時間指定で待たせる場合、PICは時間をクロックから算出します。
PICの動作は、範囲内であれば任意のクロックが入力可能なので、プログラムでクロック周波数を定義する必要があり、具体的には_XTAL_FREQをdefineします。値はhzで指定するので、例えば8Mhzの場合、以下の定義となります。

#define _XTAL_FREQ 8000000



もし、この定義を書き忘れると以下のエラーが出力されます。

Error   [192] test\main.c; 22.32 undefined identifier "_XTAL_FREQ"




また、_delay_ms()も内部的に_delay()をコールしているため、同様の制限を受けます。
手元の環境だと、_delay_ms()に対して10000(10秒)を与えた時はOKでしたが、100秒だとエラーになりました。__delay_ms()の場合も、多くの時間をスリープさせたい場合は以下のようにループにすれば対処できます。

// これはNG
__delay_ms(100000); 
 
// これはOK
int loop = 0;
for ( loop = 0; loop < 10; loop++ ) {
    __delay_ms(10000); 
}



4. __delay_us()マクロを使用する

__delay_us()は__delay_ms()と基本的には同じです。
異なるのは、待ち時間の単位がマイクロ秒である点だけで、制限も同様です。




今回は、PICのプログラムをC言語で開発する際に、一定時間スリープさせる方法を紹介しました。
空ループによる手法もありますが、待ち時間が決まっている場合は、提供されている__delay_ms()や__delay_us()マクロを使用することで、簡単に処理を記述する事が出来ます。

PIC16F84AをC言語で,MPLAB IDE環境で開発する

PIC16シリーズでは、入門者向けとしてよく使用されているPIC16F84Aですが、プログラムの格納エリアが小さいため通常はMPASMというアセンブラを使用してプログラムを行う事が多いです。

PICの製造メーカであるMicroChipが提供している統合開発環境のMPLABでも、標準ではアセンブラしか用意されていません。ですが、実はMicroChipはCのコンパイラも用意してくれているので、追加でコンパイラをインストールすれば、Cで開発する事も可能です。


今回はMPLABで、PIC16F84AのプログラムをC言語で開発する方法を説明します。

C言語によるPICプログラミング入門


MicroChip提供のPIC向けのコンパイラを確認する


まずはMicroChipのサイトより、コンパイラのページへ移動します。
Microchip C Compilers



このページを見ると分かるのですが、MicroChip純正のCコンパイラには”MPLAB C Compilers”と、”HI-TECH C Compilers”という2つのモノがあります。


どちらを使用すればよいかは、すぐ下の一覧表から確認できます。



今回使用するPIC16F84AはPIC16シリーズなので…
縦軸を見るとHI-TECH Cのコンパイラしか選択肢が無いことが分かります。
横軸はエディションで、HI-TECH Cには、PRO,Standard,Liteという3つのエディションがあります(Evaluationは評価版です)

PROとStandardのエディションは有償なので、今回は無償で利用できるLite版を使用します。
Lite版でもCコンパイラとしては問題なく使用できるのですが、Omniscient Code Generation(OCG)機能が使用できません。OCGというのはいわゆるオプティマイザで、OCGがあると生成されるコードサイズを小さくさせることが可能となります。

ちなみに、PRO、Standard版の価格ですが、定価ベースでそれぞれ$1195、$495となっています。業務向けならともかく、個人が遊び目的で使用するにはかなりお高めの価格設定ですね…

※余談ですが、今回はHI-TECH CをMPLABのIDEで使用する方法を説明しています。HI-TECH C自体はEclipseをIDEとして使用することも出来るようです(未検証です)。


コンパイラのインストール


使用するコンパイラが決まったので、早速コンパイラのインストールを行います。
MPLABをまだインストールして無い場合は、先にインストールしといて下さい。
PIC用IDEのMPLABをインストールする « nanoblog


先ほどのページより、HI-TECH C Liteのリンクをクリックします。


遷移先ページの下のほうにあるDownloadsリンクよりダウンロードを行います。
今回はWindows版をダウンロードしましたが、Linux/Mac版も用意されているようです。
本記事の作成時点で、バージョンはv9.83でした。


ダウンロードが完了したら,セットアップのプログラムを実行します。



Nextをクリックします。


インストールするエディションの指定です。一番下のLite版を選択します。


ライセンスを確認後、I accept…のチェックを入れます。


インストール先フォルダの指定です。今回はデフォルトの場所にインストールしました。
基本的にはどこでもOKですが、日本語を含むフォルダは避けてください。


言語の選択です。残念ながら日本語は無いのでEnglishにしておきます。


Finishをクリックします。


しばらく待つとインストールが完了します。


MPLABで開発を行う


Cコンパイラがインストールできたら、MPLABでプロジェクトの作成を行います。

MPLABを起動し、Project->Project Wizardを選択します。


次へをクリックします。


デバイスの指定です。今回はPIC16F84Aを選択します。


Active Toolsuiteのコンボボックスを開くとHI-TECH Universal ToolSuiteが有るはずなので、これを選択します。
(もし選択肢が出ない場合は、コンパイラのインストールに失敗しているので、再インストール等してみて下さい)


プロジェクトのフォルダを指定します。


そのまま、次へをクリックします。


完了をクリックします。


プロジェクトが作成されたら、*.cファイルをプロジェクトに追加します。
予め空のファイルを作っておいて、Source Filesを右クリックしてAdd Filesを選択します。


作ったファイルを選択します。




Cのソースを作成します。
今回は動作確認としてPortBに接続したLEDを1秒周期で点滅させてみます。

#include <stdio.h>
#include <htc.h>            // for __delay_ms
#include <pic.h>
#include <pic16f84a.h>
 
#define _XTAL_FREQ 8000000 // 8Mhzの外部オシレータを使用する
 
void main( void )
{
    __CONFIG ( FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF );
    TRISB = 0x00;
 
    while ( 1 ) {
        PORTB = 0xff;
        __delay_ms(1000); 
 
        PORTB = 0x00;
        __delay_ms(1000); 
    }
}



ソースを作成後,F10でコンパイルします。
“Build successful!”が出ていればOKです。


Build C:\home\project\pic\16F84A_Ctest\16F84A_Ctest for device 16F84A
Using driver C:\Program Files\HI-TECH Software\PICC\9.83\bin\picc.exe
 
Make: The target "C:\home\project\pic\16F84A_Ctest\main.p1" is out of date.
Executing: "C:\Program Files\HI-TECH Software\PICC\9.83\bin\picc.exe" --pass1 C:\home\project\pic\16F84A_Ctest\main.c -q --chip=16F84A -P --runtime=default,+clear,+init,-keep,+osccal,-download,-resetbits,-stackcall,+clib --opt=default,+asm,-debug,-speed,+space,9 --warn=0 -D__DEBUG=1 --double=24 --float=24 --addrqual=ignore -g --asmlist "--errformat=Error   [%n] %f; %l.%c %s" "--msgformat=Advisory[%n] %s" "--warnformat=Warning [%n] %f; %l.%c %s" 
Executing: "C:\Program Files\HI-TECH Software\PICC\9.83\bin\picc.exe" -o16F84A_Ctest.cof -m16F84A_Ctest.map --summary=default,-psect,-class,+mem,-hex --output=default,-inhx032 main.p1 --chip=16F84A -P --runtime=default,+clear,+init,-keep,+osccal,-download,-resetbits,-stackcall,+clib --opt=default,+asm,-debug,-speed,+space,9 --warn=0 -D__DEBUG=1 --double=24 --float=24 --addrqual=ignore -g --asmlist "--errformat=Error   [%n] %f; %l.%c %s" "--msgformat=Advisory[%n] %s" "--warnformat=Warning [%n] %f; %l.%c %s" 
 
HI-TECH C Compiler for PIC10/12/16 MCUs (Lite Mode)  V9.83
Copyright (C) 2011 Microchip Technology Inc.
(1273) Omniscient Code Generation not available in Lite mode (warning)
Memory Summary:
    Program space        used    28h (    40) of   400h words   (  3.9%)
    Data space           used     5h (     5) of    44h bytes   (  7.4%)    
    EEPROM space         used     0h (     0) of    40h bytes   (  0.0%)    
    Configuration bits   used     1h (     1) of     1h word    (100.0%)    
    ID Location space    used     0h (     0) of     4h bytes   (  0.0%)
 
Running this compiler in PRO mode, with Omniscient Code Generation enabled,
produces code which is typically 40% smaller than in Lite mode.
See http://microchip.htsoft.com/portal/pic_pro for more information.
 
Loaded C:\home\project\pic\16F84A_Ctest\16F84A_Ctest.cof.
 
********** Build successful! **********



このプログラムをPICにロードして実行し、正しく動作することを確認してみて下さい。


今回は動作確認のため、プログラムの詳細は説明しません。
細かいところが知りたい場合は、以下の記事を参考にしてください。



コンパイラ結果をかくにんしてみる

メニューよりViewのDisassembly Listingを選択すると、アセンブリの出力を確認できます。


---  C:\home\project\pic\16F84A_Ctest\main.c  ----------------------------------------------------
1:                 #include <stdio.h>
2:                 #include <htc.h> // for __delay_ms
3:                 #include <pic.h>
4:                 #include <pic16f84a.h>
5:                 #define _XTAL_FREQ 8000000
6:                 
7:                 void main( void )
8:                 {
9:                  __CONFIG ( FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF );
10:                 TRISB = 0x00;
   3DB    1683     BSF 0x3, 0x5
   3DC    0186     CLRF 0x6
   3DD    2BDE     GOTO 0x3de
11:                
12:                 while ( 1 ) {
   3FE    2BDE     GOTO 0x3de
13:                     PORTB = 0xff;
   3DE    30FF     MOVLW 0xff
   3DF    1283     BCF 0x3, 0x5
   3E0    0086     MOVWF 0x6
14:                     __delay_ms(1000); 
   3E1    300B     MOVLW 0xb
   3E2    008E     MOVWF 0xe
   3E3    3026     MOVLW 0x26
   3E4    008D     MOVWF 0xd
   3E5    3066     MOVLW 0x66
   3E6    008C     MOVWF 0xc
   3E7    0B8C     DECFSZ 0xc, F
   3E8    2BE7     GOTO 0x3e7
   3E9    0B8D     DECFSZ 0xd, F
   3EA    2BE7     GOTO 0x3e7
   3EB    0B8E     DECFSZ 0xe, F
   3EC    2BE7     GOTO 0x3e7
   3ED    2BEE     GOTO 0x3ee
15:                
16:                     PORTB = 0x00;
   3EE    1283     BCF 0x3, 0x5
   3EF    0186     CLRF 0x6
17:                     __delay_ms(1000); 
   3F0    300B     MOVLW 0xb
   3F1    008E     MOVWF 0xe
   3F2    3026     MOVLW 0x26
   3F3    008D     MOVWF 0xd
   3F4    3066     MOVLW 0x66
   3F5    008C     MOVWF 0xc
   3F6    0B8C     DECFSZ 0xc, F
   3F7    2BF6     GOTO 0x3f6
   3F8    0B8D     DECFSZ 0xd, F
   3F9    2BF6     GOTO 0x3f6
   3FA    0B8E     DECFSZ 0xe, F
   3FB    2BF6     GOTO 0x3f6
   3FC    2BFD     GOTO 0x3fd
   3FD    2BDE     GOTO 0x3de
18:                 }
19:                }
   3FF    2800     GOTO 0



今回のテストプログラムの出力を見ると、アセンブラのコードで37ステップになりました。プログラムの格納先は3DB~3FFと、上位アドレスに格納されています。使用しているプログラムメモリのサイズが知りたいだけなら、Disassembly Listingで確認しなくても、コンパイル結果の以下の行で確認できます。

Memory Summary:
    Program space        used    28h (    40) of   400h words   (  3.9%)


最初の括弧の中が命令数(10進表記)です。括弧の前の28hは40を16進表記したもので、400hはプログラムエリアの全容量(10進表記で1024)です。


プログラムのサイズが37ではなく40となっている理由は、スタートアップルーチンとして3命令使用しているためです。この3命令はView->Program Memoryを見ると、0x000, 0x3D9, 0x3DAの位置に存在している事が確認できます(実質は、STATUSレジスタをクリアしてるだけですね)。




この結果より、PIC16F84Aはプログラムの格納領域として1024ワード(2kbyte)しかない為、たったLED点滅のプログラムだけでも3.9%もメモリを使用してしまっていることが分かります。

容量的が1024ワードというのは、本格的なプログラムを作成するにはちょっと厳しいですが、学習目的なら問題なく使用することが出来ます。
また、同じPIC16シリーズでもPIC16F84Aでは無くPIC16F88を使用すれば、4096ワード(8kbyte)と4倍もあるのでもう少し大きなプログラムがかけます。PIC16F88は価格も200円程度と安価で、機能もA/Dコンバータ/PWMモジュール/シリアル通信などが付いており高機能なのでお勧めです。

C言語によるPICプログラミング入門

[C#]ネットワーク(NIC)の情報をプログラムで取得する

C#(.Net Framework)では、System.Net.NetworkInformationクラスを使用することで、NICの情報を取得する事が可能です。

下記のコードはIPCONFIGで出力されるような、IPアドレス/MACアドレス等の情報を出力しています。

送受信速度は取得できませんが、これは一定時間周期でBytesSentやBytesReceivedの値を取得し、一定時間内にどれだけ値が増えたかを元に速度を算出します。
例えば1秒周期で値を取得すれば、前回取得値との差分が、Byte/Secの単位として速度が算出できます。

using System.Net.NetworkInformation;
...
 
private void button1_Click( object sender, EventArgs e ) {
 
    textBox1.Clear();
 
    //---------------------------------------
    // PCに搭載されている全てのNIC情報を取得
    //---------------------------------------
    NetworkInterface[] nicList = NetworkInterface.GetAllNetworkInterfaces();
 
 
    //-----------------------------------------
    // 取得した全NIC情報を出力するまで繰り返し
    //-----------------------------------------
    foreach( NetworkInterface nic in nicList ) {
        IPInterfaceProperties ipInfo = nic.GetIPProperties();
 
        textBox1.AppendText( "-----------------------------------" + Environment.NewLine );
        textBox1.AppendText( "名称       : " + nic.Name                                     + Environment.NewLine );
        textBox1.AppendText( "説明       : " + nic.Description                              + Environment.NewLine );
        textBox1.AppendText( "I/F種類    : " + nic.NetworkInterfaceType.ToString()          + Environment.NewLine );
        textBox1.AppendText( "最大速度   : " + ( nic.Speed / 1000000 ).ToString() + "Mbps"  + Environment.NewLine );
        textBox1.AppendText( "macアドレス: " +  nic.GetPhysicalAddress().ToString()         + Environment.NewLine );
        textBox1.AppendText( "送信byte数 : " + nic.GetIPv4Statistics().BytesSent.ToString() + Environment.NewLine );
        textBox1.AppendText( "受信byte数 : " + nic.GetIPv4Statistics().BytesReceived.ToString() + Environment.NewLine );
        if ( ipInfo.UnicastAddresses.Count > 0 ) {
            textBox1.AppendText( "IPアドレス : " +  ipInfo.UnicastAddresses[0].Address.ToString() + Environment.NewLine );
        } else {
            textBox1.AppendText( "IPアドレス : none" );
        }
    }
}



このコードを手元の環境で実行してみた結果です。

[パタヘネ:読書メモ]付録B アセンブラ,リンカ,SPIMシミュレータ その2

B.6 手続き呼出し規約

分割コンパイルする場合は、各コンパイル単位で関数の引数・戻り値をどうやって(=どのレジスタに)セットするかのルールが必要でそのルール手続き呼び出し規約(calling convention)と呼ぶ。

例えばIntel CPUの場合だけでも、以下のようなものがある。

cdecl
fastcall
stdcall
safecall
thiscall
Pascal
 
Intel ABI
x86-64



呼び出し規約が異なるobjやlibファイルを、リンカは通常リンクする事ができない。
例えばBorlandのコンパイラにはcoff2omf.exeというツールが添付されており、このツールを使う事で、オプションを指定する事でlibファイルをstdcallやcdecl方式に変換する事が出来る。

規約では主にレジスタの使い方や、スタック領域(スタックフレーム)の使い方が規定される。Intel CPUの場合、fastcallでは出来る限りレジスタを使用して引数を渡そうとするのに対して、cdeclでは全てスタックに積まれるといった違いがある。

B.7 例外と割込み

省略

B.8 入力と出力

MIPSのシミュレータであるSPIMでは、キーボード入力、ディスプレイ出力に対応している。
入出力を行うためには、メモリマップされたIOポートである、レシーバ(0xffff0004)と、トランスミッタ(0xffff000c)の下位8bitに値を読み書きする事で実現できる。それぞれのデータを制御するためのレジスタ(制御bit)が有り、それぞれ0xffff0000と0xffff0008の下位2ビットにマッピングされている。


B.9 SPIM

SPIMはLinuxやWindowsでMIPSのCPUをシミュレートする事が出来る。
シミュレータなので、かなり遅い(100倍ぐらい)けど、MIPS環境を手軽に手に入れることが出来るし、レジスタの振る舞いなどを見る事も出来るので学習目的には役立つ環境となっている。

ただ、所詮シミュレータなので、タイミングは実機と同じでは無い。例えば遅延分岐やパイプラインハザードに伴う遅延、メモリアクセスのレイテンシ等までは実機と同じ振る舞いにはなっていない。


システムコールは、最低限の入出力(キーボード、コンソール、ファイルI/O)と、メモリの動的確保(sbrk)関するものが実装されており、呼び出したいシステムコールの番号を$v0レジスタにセットしてsyscall命令を発行することで実行できる。


B.10 MIPS R2000のアセンブリ言語

省略

B.11 おわりに

省略

B.12 演習問題

省略



[パタヘネ:読書メモ]付録B アセンブラ,リンカ,SPIMシミュレータ

B.1 はじめに

コンピュータが理解できるのは0/1の羅列である機械語のみだけど、機械語だと人が読みづらいので、アセンブリ言語を用意した。アセンブリ言語は、機械語と1:1に対応する名前を付けることで、人が読みやすくなる。この名前(命令)をニーモニックと呼ぶ。

アセンブリ言語を機械語に置き換えるために、アセンブラというプログラムを使用する。
素のアセンブリ言語だけだと、表現できる幅が狭いので、マクロ機能を設ける事が多い。

アセンブリのプログラム(.asm)は、アセンブリ->リンクという経緯を経て実行ファイルが作成される。

また、.asmファイルはニーモニック以外にも情報を記述できる場合が多い。ありがちなのは、.align .textや.grobalなど、ドットから始まるものでこれを、アセンブラディレクティブと呼ぶ。

ディレクティブ(指令)は、アセンブラプログラムに対する命令となる。例えば.alignは、実行ファイルが使用するメモリのアライメント調整を、アセンブラプログラムに指示している。


ニーモニックやマクロを使用しても、(機械語よりはましだけど)まだ人間にとっては読み書きしづらい。
これは、コンピュータが出来る事がプリミティブすぎるから。

この為、C言語などの、さらに抽象度が高い言語を用意する事になった。高水準の言語は、ループ(for/while)や分岐処理(if/switch)を、より分かりやすく記述する事ができ、高水準の言語はコンパイラを使用することでアセンブラのプログラムに翻訳される。

このように、コンパイラはある言語を別の言語に置き換えるフィルタプログラムになる。
ここで、置き換え元言語(.c等)をソース言語、置き換え先(.asm等)をターゲット言語と呼ぶ。


最近はアセンブリで直接プログラムを書く頻度は少ないが、ゼロというわけでは無い。
例えば、組み込み向けのプログラムでは、入力に対してレスポンスまでの遅延の猶予が少ない場合が多い(10ミリ秒以内のレベル)ため、生成コードが見えないC言語より、アセンブラのほうが動作に掛かる時間を試算しやすくなる。


実際のところは、大半をC言語で作り、一部をインラインアセンブラで書くという方法もある。
直接アセンブラでコーディングするのが一番実行効率がよいと思われがちだけど、近年のハードはキャッシュやパイプラインなどが発達しているので、素人(?)のアセンブラプログラマが作ったコードより、コンパイラが最適化したコードのほうが効率が良いことが多い。
但し、それでもプログラマは処理するデータの特性を知っているといったアドバンテージがあるので、その辺を考慮したコードが書ければ、コンパイラが生成したコードより高効率で処理する事も可能となってくる。

ただ…本当の実際のところは、低遅延が要求される部分でもC言語でコーディングする事は多い。
これは遅延時間が読めることよりも、”バグが少ないこと”が優先度が高いからで、アセンブリのコードは分かりにくいためバグが混入しやすい。


アセンブリの欠点としては、以下のものがあげられる。

抽象度が低いのでコーディングし辛い
  これは説明済み。
  また、C言語でコーディングするより、アセンブラのほうがコード量が
  長くなる為、開発の生産性は低くなってしまう。
 
マシン(アーキテクチャ)依存のコードになってしまう。
  作った資産を流用しにくい




B.2 アセンブラ

アセンブラプログラムは、.asm(アセンブラソース)を元に、.obj(オブジェクトファイル)を出力する。

アセンブルは大きく見ると以下の流れて行われる。

ラベルを見つけて、それに対応する命令を対応付ける。
各ニーモニックを翻訳する。
ラベル情報、翻訳結果、オペランド等の情報を元に、機械語に置き換える。



ラベルには、単一のasmファイルで完結するものと、他ファイルからも参照されるものがある。前者(ローカルラベル)はアセンブルの過程で解決されるが、後者(グローバルラベル/外部ラベル)は解決されないのでそのことをobjファイルに記録しておく必要がある。


アセンブラの場合、プログラムの位置(実際の行番号的な意味で)的に見て、ラベルの定義場所よりも、参照している場所のほうが上にくることがある。これは、例えば後ろのほうの場所へのジャンプ命令などが相当する。このような状態を前方参照と呼ぶ。
という事は、アセンブラプログラムはファイルを1回読むだけではアセンブルを確定する事が出来ない。
まず、1回目の読み込みでラベルを調べラベル一覧を作る。その後、改めて2回目の読み込みで翻訳作業をする事になる。
前方参照が発生する言語では、複数回の読み込みが必要となり、これをマルチパスの処理と呼ぶ。
一方、C言語等の場合では、使用するモノは事前に宣言が必要というルールになっているのでこのようなことは発生しない。このようなものを(マルチパスではなく)ワンパスのコンパイラと呼んだりする。


マルチパスの場合、最初のパスではプログラムのparseを行う。
parseの途中でラベルが出てきた場合は、シンボルテーブルにその情報を格納する。
同時にこれまでに出てきた各命令の長さを調べておき、ラベルのアドレスを特定する。

2回目のパスでは、ファイルを再度上から読んでいくが、ラベルが出てきた時はシンボルテーブルより、具体的な相対アドレス値に置き換える。分割アセンブルでリンク時に確定するシンボル情報は、ここでは分からないので未解決としてシンボルテーブルに残ったままになる。


UNIXの場合、objファイルの形式は以下のようになる。

オブジェクトファイルヘッダ
  各セグメントの大きさ情報等を記録する
 
テキストセグメント
  プログラムの機械語命令が入る
 
データセグメント
 
リロケーション情報
  絶対アドレスに依存する情報を記録する。
 
シンボルテーブル
  未解決の参照情報が記録される。
 
デバッグ情報
  ソースプログラムの行番号やソースファイル名等の、コンパイル時の情報が記録される



アセンブラは、プログラムが何番地にロードされるか分からないので、.objファイル上では0番地から始まるなどの仮定を置く事が多い。この情報はリンク時に変更される場合があるが、分岐命令などで分岐先アドレスを相対値で指定するようなアーキテクチャの場合は、変更の必要が無いので楽チンになる。


B.3 リンカ


リンカは、1つ以上の.objを結合させ、未解決のアドレスを解消するのと、objで決まっていた相対アドレスを絶対アドレスに調整するのが主な目的となる。

外部参照先は、自分が作ったほかのプログラムだけとは限らず、用意されているライブラリ関数の場合も有りうる。ライブラリ関数をコールしている場合は、実行ファイルに該当関数のコードが埋め込まれる。

リンカの生成物は実行ファイル(windowsの場合は.exe)となり、実行可能となる。


B.4 ロード


実行ファイルはOSによってメモリにロードされ実行することが出来る。
(OS lessの場合は、CPU起動と同時に実行される)

UNIXの場合、実行ファイルは一般的に以下のような流れで実行される。

実行ファイルより、テキストセグメント/データセグメントの領域を求め、メモリ上にエリアを確保する。
エリアは他にスタック領域などのデータ領域を含む。
 
テキストセグメントの情報(プログラム)をメモリにロードし、スタックに起動引数(argv)を積む。
 
レジスタをクリアする。
 
プログラムカウンタをスタートアップルーチンにセットする。
 
スタートアップルーチンからmain()がコールされる。




B.5 主記憶領域の使用法

この節では、MIPSで上で動作するプログラムがメインメモリをどのように使用するかについてのガイドラインを示す。ガイドラインは、ハード的な制約ではなく、単なる取り決め(慣習)という位置づけなので破る事は(理論上は)可能となっている。

MIPSではメモリの使い道を大きく4つに分ける。

予備(0x00900000~0x00400000)
 
テキストセグメント(0x00400000~0x10000000)
	プログラムのコードが入る
 
データセグメント(0x10000000から上位方向へ)
	0x10000000から上位方向に向かって、使用する
	C言語で言うところの、文字列リテラル、static変数、グローバル変数などの"静的データ"が格納される。
	"静的データ"の直ぐ後ろに、他にmallocで動的に確保した領域が確保される。
 
スタックセグメント(0x7fffffffから下位方向へ)
	0x7fffffffから下位方向に向かって使用する
	ここには関数のコールスタックが格納される



データセグメントと、スタックセグメントは同じ空間を、両端からそれぞれ相手の方向に向かって確保していく。なので、一方が領域を使いすぎると、もう一方が十分なメモリを使えないという場合があり得る。