H8マイコンには何種類かありますが、マイコンボードの形で手に入るものの一つとしてH8/3694Fがあります。
左の写真に示すのは、秋月電子通商で販売しているマイコンボードAKI-H8/3694Fです。DIP変換基板になっているので趣味の工作には便利です。
また、プログラムを書き込むときに必要になる、RS-232トランシーバICもあらかじめ実装されています。
H8/3694Fは、H8/300HというCPUをコアに持つ16ビットマイコンですが、タイニーマイコンに分類されるもので、使用できるアドレス空間は64KBに限られます。
2.H8/3694Fの特徴左の写真に示すのは、秋月電子通商で販売しているマイコンボードAKI-H8/3694Fです。DIP変換基板になっているので趣味の工作には便利です。
また、プログラムを書き込むときに必要になる、RS-232トランシーバICもあらかじめ実装されています。
H8/3694Fは、H8/300HというCPUをコアに持つ16ビットマイコンですが、タイニーマイコンに分類されるもので、使用できるアドレス空間は64KBに限られます。
H8/3694Fの詳細なマニュアルは、ルネサスエレクトロニクスのサイトからダウンロードしていただくとして、ここではごく簡単に紹介したいと思います。
1)マイコンH8/3694Fの特徴
- ROM 32KB(H8/3694Fの場合フラッシュメモリ)
- RAM 2KB(マスクROM版は1KB)
- 20MHz動作
- タイマA(時計用タイムベースとして使用可能)
- タイマV(8ビットタイマ)
- タイマW(16ビットタイマ)
- ウォッチドッグタイマ
- SCI(調歩同期式またはクロック同期式シリアルコミュニケーションインタフェース)
- I2Cバスインタフェース※
- 10ビットA/D変換器(8チャンネル)
※ここでは、IICバス(Inter IC Bus)をI2Cと表記します。
3.CPUレジスタセット
下図はH8/300HシリーズのCPUレジスタセットです。図のように、32ビットの汎用レジスタとプログラムカウンタ(PC),コンディションコードレジスタ(CCR)で構成されています。
32ビットの汎用レジスタは8本(ER0~ER7)あり、それらの上下を分割して16ビットの汎用レジスタ16本(E0~E7,R0~R7)として使用することもできます。またさらに、そのうち8本(R0~R7)は上下を分割して8ビットの汎用レジスタ16本(R0H~R7H,R0L~R7L)としても使用できます。8ビット/16ビットデータを多用する組み込み用途では、効率の良いプログラミングが可能です。
なお、汎用レジスタのER7はスタックポインタとして使用されるので、事実上E7,R7(R7H,R7L)は汎用レジスタとして使用しません。
汎用レジスタER0~ER7は32ビットの幅がありますが、アドレスレジスタとして使用する場合はb23~b0の24ビットのみが有効です。ただし、H8/3694Fの有効なアドレス空間は64Kバイトなので、b16以上のアドレスビットは無視されます。
プログラムは、アセンブラではなくC言語などで書くことが一般的なので、レジスタの構成をあまり意識しないと思いますが、効率の良いプログラムあるいはデバッグを行うためには知っておいた方が良いです。
4.割り込みベクタ
割り込みベクタの製作例を下記に示します。
配列VectorTable[~]がベクタテーブルで、その内容は割り込み処理関数の関数名です。したがって、使用する割り込みによって異なります。
H8/300Hシリーズの割り込みベクタはROM(フラッシュメモリ)領域の先頭、つまり0x000000番地から作成されている必要があります。下記のリストでいうと、"section VECTOR"とあるところがアドレスを指定するところで、リンカでVECTORというセクションに0x000000を指定します。このセクション名VECTORは他の名前でもかまいませんが、ベクタテーブル専用の名前にしておきます。
上記の例では、割り込みベクタをセクションVECTORに配列VectorTable[~]として定義していますが、
#pragma interrupt (func(vect=XXX))
void func(void){ ... }
または
__interrupt(vect=XXX) void func(void){ ... }
このように割り込み処理関数を書くと、ベクタ番号XXXの割り込みベクタを自動生成させることもできます。その場合、割り込みベクタのセクション名は IntPRG になります。
関数PowOnResetは、CPUリセット後の最初に実行される関数で、プログラムの実行開始部分(entry)です。ただし、上記の例は統合開発環境HEWで自動生成されるものと若干異なり簡略化しています。また、この中でCCRのUビットをセットしていますが、H8/300HのUビットは割り込みと関係ありません。
関数HardwareSetupは、システムレジスタやI/Oレジスタの初期化を行うもので、HEWで自動生成できますが空の関数なので、内容は作成する必要があります。
関数_INITSCTは、セクションの初期化を行うものですが、HEWを使うと標準ライブラリからリンクされるので、作成する必要ありません。ただし、独自のセクションを追加した場合は、ファイルdbsct.cに初期化セクション名を追記する必要があります。
セクションの初期化とは、静的変数のうち初期値がない変数は0クリアし、初期値がある変数は初期値をセットすることです。具体的には、例えばセクションBには全領域に0x00を書き込み、セクションRにはセクションDから初期値をコピーするなどの処理を行います。
【参考】
セクションP=プログラム領域(ROM)
セクションC=定数格納領域、つまりconst型データの領域(ROM
セクションB=未初期化データ領域、つまり初期値のない変数の領域(RAM)
セクションR=初期化が必要なデータ領域、つまり初期値のある変数の領域(RAM)
セクションD=初期化データ領域、つまり初期値が必要な変数用の初期データの領域(ROM)
※セクションは他にもあり、独自に作ることもできます。詳細は「H8S,H8/300 Series C/C++コンパイラ、アセンブラ、最適化リンケージエディタ」のユーザーズマニュアルを参照してください。
セクションP=プログラム領域(ROM)
セクションC=定数格納領域、つまりconst型データの領域(ROM
セクションB=未初期化データ領域、つまり初期値のない変数の領域(RAM)
セクションR=初期化が必要なデータ領域、つまり初期値のある変数の領域(RAM)
セクションD=初期化データ領域、つまり初期値が必要な変数用の初期データの領域(ROM)
※セクションは他にもあり、独自に作ることもできます。詳細は「H8S,H8/300 Series C/C++コンパイラ、アセンブラ、最適化リンケージエディタ」のユーザーズマニュアルを参照してください。
関数mainもHEWにより生成される空の関数で、ユーザープログラムの開始部分です。この関数mainにメインとなる処理内容を記述することになります。
ベクタテーブルの未使用部分に書かれている割り込み処理関数SysFailは、未定義の割り込みの発生を検知したとき呼ばれるダミー関数ですが、未定義割り込みの発生という異常動作の検知として利用することも可能です。
5.ハードウェアの設定
5-1.システムコントロールの設定
下記に初期化関数HardwareSetupの作成例を示します。
H8/3694Fでは、マイコン自体の設定はほとんどなく、下記の例でも明示的に初期値を設定しているのがほとんどです。割り込みコントロールは、ここではなく関連する各I/Oの初期化関数で設定しますが、I/Oポートはここで一括して初期化した方がわかりやすいと思います。
低電圧検知回路の設定は、H8/3694Fでは無効なのですが下記の例には入れています。H8/3694Fでこの設定があっても問題ありません。
5-2.I/Oポートの設定
I/Oポートの設定例が前記の関数HardwareSetupにもありますが、改めて下記に例を示します。
下記の例は、わかりやすくするために全ビットを入力または出力としていますが、ビット単位で入力/出力を設定することができます。
ポートモードレジスタPMRは、I/Oポートと同じ端子を他の機能のために使用できる場合、その切り替えのために存在します。したがって、すべてのI/OポートにPMRがあるわけではありません。
プルアップコントロールレジスタPUCRは、I/Oポートを入力として設定する場合に内蔵のプルアップ抵抗を使うかどうか設定するレジスタです。PUCRもすべてのI/Oポートにあるわけではありません。
// ポートnを全ビット入力ポートにする場合
IO.PMRn.BYTE = 0x00;
IO.PCRn = 0x00;
IO.PUCRn.BYTE = 0xFF;
value = IO.PDRn.BYTE;
IO.PCRn = 0x00;
IO.PUCRn.BYTE = 0xFF;
value = IO.PDRn.BYTE;
IO.PMRm.BYTE = 0x00;
IO.PDRm.BYTE = 0x00;
IO.PCRm = 0xFF;
IO.PDRm.BYTE = 0x00;
IO.PCRm = 0xFF;
/* PMRmがある場合は'0'を書き込んで汎用入出力ポートを選択します */
/* 出力ポートの初期状態('0'または'1')をセットしておきます */
/* '1'を書き込んだビットが出力ポートになります */
設定後、出力ポートに出力するには次のように行います。/* 出力ポートの初期状態('0'または'1')をセットしておきます */
/* '1'を書き込んだビットが出力ポートになります */
// ポートmの全ビットに“H”を出力する場合
IO.PDRm.BYTE = 0xFF;
/* 出力ポートの全ビットに"H"('1')をセット */
// ポートmのビット3に“H”を出力する場合
IO.PDRm.BIT.B3 = 1;
または
IO.PDRm.BYTE |= 0x08;
または
IO.PDRm.BYTE |= 0x08;
/* ビット3に'1'をセット(ビット操作命令のBSET命令が生成されます) */
/* 通常は論理演算命令のOR命令が生成されますが、コンパイラの最適化によりビット操作命令のBSET命令が生成される可能性があります */
// ポートmのビット3に“L”を出力する場合/* 通常は論理演算命令のOR命令が生成されますが、コンパイラの最適化によりビット操作命令のBSET命令が生成される可能性があります */
IO.PDRm.BIT.B3 = 0;
または
IO.PDRm.BYTE &= 0xF7;
または
IO.PDRm.BYTE &= 0xF7;
/* ビット3に'0'をセット(ビット操作命令のBCLR命令が生成されます) */
/* 通常は論理演算命令のAND命令が生成されますが、コンパイラの最適化によりビット操作命令のBCLR命令が生成される可能性があります */
/* 通常は論理演算命令のAND命令が生成されますが、コンパイラの最適化によりビット操作命令のBCLR命令が生成される可能性があります */
- 注意1:
- 出力ポートは、PDRn(Port Data Register n)に先に初期出力データをセットしてから、PCRn(Port Control Register n)※で出力に設定してください。PDRnの初期値は全ビット’0’なので、そのままPCRnで出力ポートに設定すると’0’が出力されてしまいます。もし、出力ポートの初期出力を’1’にしたい場合は先にPDRnの目的のビットに’1’をセットしてください。
- 注意2:
- PCRnは書き込み専用レジスタです。従って、次のようにPCRnを読み出して何かANDやORしてPCRnに書き戻すというようなことはできません。
- IO.PCRn |= 0x01; ←できない
- IO.PCRn &= 0xFE; ←できない
- IO.PCRn |= 0x01; ←できない
- 注意3:
- H8マイコンもシリーズによってI/Oレジスタ名称や機能が異なる場合があります。使用する場合は、ハードウェアマニュアルで確認してください。
- 注意4:
- 例えばビット1を通常処理で操作、ビット0を割り込み処理で操作というように、同一の出力ポートを通常処理と割り込み処理の両方で操作するプログラムの場合で、通常処理の方がビット操作命令以外の命令(例えばANDやORなど)で出力するオブジェクトとなっている場合は注意が必要です。ANDやORなどの命令を使った出力では複数の命令コードとなるため、その命令間に割り込まれる可能性があります。その割り込みが同一出力ポートを操作する割り込みだった場合、出力ポートが意図しない状態になることがあります。対応策として、通常処理側で出力ポートを操作中は割り込みをマスクすることで回避できます。
※PCRは他のマイコンではDDR(Data Direction Register)という名称が多いようです。
- 備考:
- プログラムの記述方法や生成される関数などについては、ルネサスエレクトロニクスの純正コンパイラを前提にしています。コンパイラの詳細は、「H8S,H8/300 Series C/C++コンパイラ、アセンブラ、最適化リンケージエディタユーザーズマニュアル」をご覧ください。
/* '0'を書き込んだビットが入力ポートになります */
/* プルアップが必要なビットに'1'を書き込みます */
/* 入力ポートの状態を読み込むときはPDRnを読み込みます */