AKI-H8(HitachiH8/3048F-ONE)の使い方
マザーボードSWとLEDによるプログラミング
Copyright(C) 2Sep2008
Copyright(C) 15Dec2001
coskx
0. はじめに
0.1 この文書の構成
この文書は
H8/3048F-ONEのCプログラムをコンパイルする方法,AKI-H8/3048foneにフラッシュメモリ書き込み(転送)する方法を修得した学
習者が,AKI-H8/3048F-ONEのマザーボードを用いてLEDをつけたりスイッチを取り込んだりするプログラムを学習するための文書です。
テンプレートフォルダなどのダウンロード
DownLoad |
0.2
AKI-H8(HitachiH8/3048F-ONE)
AKI-H8/3048F-ONE(図1)は日立製作所の製品であるマイクロコンピュータH8製品群のH8/3048foneFを用いて,秋月電子通商がCPUボード,マザーボードを製作販売している製品の商品名です。
購入して組み立てたばかりの状態では図の黄色部分のみでできています。
(LCD(液晶表示器)がついている場合もあります。)
マザーボード上には次の要素が搭載されています
(1)
フラッシュメモリ(ROM)書き込み回路(CPUカードの裏側に隠れているので見えない)
(2)
12V電源入力,5V出力3端子レギュレータ(12Vから安定した5Vを作るモジュール,図1の右上の黒い素子)
(3)
8ビットスイッチ
(4) プッシュスイッチ
(5) LED(発光ダイオード)
AKI-H8/3048foneでのプログラミングはパソコン上で次のように行ないます。
(1)PC上でプログラムを作成します。
(2)PC上でコンパイルして,実行形式プログラムをH8マイコンに転送します。
(3)H8マイコンを起動します。
もう少し丁寧に説明すると次のようになります。
(1)
PC上でC言語またはアッセンブリ言語でプログラムを開発
(2)
PC上でコンパイル・リンクを行ない実行プログラムファイルを作ります
(3)
PC上でロードモジュールに変換します
(4)
RS232C通信でロードモジュールをH8マイコンに転送(フラッシュメモリに書き込む)します。
(5)
H8マイコン上でプログラムを実行します。
|
図1 AKI-H8 マザーボード |
1. LEDの点滅のプログラム
図2のLEDが対象となるLEDです。
| |
図2 2つのLED |
CN3-31,32はここです |
1.1 LED点滅プログラム
マザーボード上の2つのLEDを点滅させるプログラムを作ります。
2つのLEDはH8/3048foneCPUのポート5の第0ビット,第1ビットの端子につけられています。
H8/3048foneCPUでポートというのは8ビットの出入り口のことです。ポート5には第0ビットから第7ビットまでの8ビットがあります。
ポートがCPUのどのピンに接続されているかはCPU設計者によって決められ,CPUのマニュアルに書いてあります。
またCPUのピンがCPUカードのどのピンに接続されているかは,CPUカードの設計者によって決められ,CPUカードのマニュアルに書いてあります。このWebページの「参考1」に接続一覧が書いてあります。
通常ポートは入出力兼用なので,この例のようにポート5の第0ビット,第1ビットの端子の先にLEDがついている場合は,CPUに対し,この2つのビットは出力に使用することを教えてあげなければなりません。(CPU起動後1回だけ設定しなければなりません。)
参考 電流制限
CPUの出力端子はレベルHの時5V,レベルLの時0Vであり,LED単体の電圧降下が1.5V程度であるため,保護抵抗1.5kΩには3.5Vがかかっていることになります。この抵抗に流れる電流は3.5[V]/1500[Ω] = 0.0023[A] = 2.3[mA]となります。LEDは10mA駆動が標準ですが,1mA駆動でも光らないないわけではありません。一方CPUの端子出力電流は最大2.0mAとされている(H8/3048foneの仕様)ので,AKI-H8はわずかに仕様違反となっています。
1.2
LED点滅プログラムの考え方
(1)マイコン上での駆動の手順
(1) ポート5の下位2ビットを出力に設定
0:入力 1:出力
下位2ビット(第0ビットと第1ビット)を出力に設定するには
2進数表現で00000011,10進数表現(16進表現でも同じ)で3を与えればよい。
(2) ポート5の下位2ビットに0または1を出力してLED点滅を行ないます。
0:OFF 1:ON
(3) 時間調整して1秒ごとに2つのLEDが交互に点滅するようにします。
(2)具体的なプログラミングの考え方
1.3 LED点滅プログラムポート5の下位2ビットを出力に設定
以下を無限ループ
ポート5の第0ビットに1を出力
ポート5の第1ビットに0を出力
1秒間時間待ち
ポート5の第0ビットに0を出力
ポート5の第1ビットに1を出力
1秒間時間待ち
/*P5の下位2ビットを出力に設定*/
P5.DDR =
0x3;
while(1)
{/*これは無限ループ*/
/*LED0をONにする*/
P5.DR.BIT.B0=1;
/*LED1をOFFにする*/
P5.DR.BIT.B1=0;
msecwait(1000);/*1000msecの間なにもしない*/
/*LED0をOFFにする*/
P5.DR.BIT.B0=0;
/*LED1をONにする*/
P5.DR.BIT.B1=1;
msecwait(1000);
}
参考 この部分は読み飛ばして,後でもう一度読んでください。
(1)ポート5はP5という構造体(構造体学習前なら,家の名前と考えればよい)で表されていると考えられる。
(2)P5 の各ビットを入出力のどちらに使うかの設定はP5.DDRという8ビットのレジスタ(P5家のDDR部屋と考えればよい)に設定する。ここでは下位2ビッ トのみを出力にするので2進数00000011に設定したいのでP5.DDRには3を設定する。もし,第2,第3ビットのみを出力にしたいのなら,2進数 00001100を設定すればよいので,P5.DDRには16進数で0xc(10進数では8+4=12)を設定する。またもし第0ビットと第3ビットのみ を出力にしたいのなら,2進数00001001を設定すればよいので,P5.DDRには9を設定する。別のポートも同様な記述を行なうことができる。例え ばポート1の8ビットすべてを出力に設定したいのなら,2進数11111111に設定すればよいのでP1.DDR=0xffのように設定する。
(3) ポート5に出力する値はP5.DR(P5家のDR部屋)に出力する。P5.DRはさらに細かい小部屋に分かれており,P5.DR.BIT.B0はポート5 の第0ビットを表す。同様にP5.DR.BIT.B1はポート5の第1ビットを,P5.DR.BIT.B2はポート5の第2ビットを,....のように使 う。ポート5の第2ビットに0を出力したければ,P5.DR.BIT.B2=0,ポート5の第2ビットに1を出力したけれ ば,P5.DR.BIT.B2=1のように使う。この表現は1ビットのみを表すため,代入できる値は0または1のみである。またP5.DR.BYTEの表 現では8ビットまとめて出力が可能である。例えばポート5の第2ビットに1を,その他のビットには0を出力したければ,00000100を出力することに なるので,P5.DR.BYTE=4のように表現できる。(ポート1の第0から第3ビットに1を,その他のビットには0を出力したけれ ば,00001111を出力することになるので,P1.DR.BYTE=0xf)
P5はDDRとDRの2つの8ビットのレジスタで構成されている
さらにDRはBYTEとBITの2つの方法で書き込みが可能である。参考図 レジスタP5の構造
さらに詳細な説明
勘どころ
ポート5のbit0とbit1にLEDをつけたのはマザーボードの設計者が決めたことなので,
回路がそのようにできている。
すでに取り付けられているLEDを別のポートを介して点滅させることは,ソフトウェア
の変更だけではできない。
別のポートを介してLEDを点滅させるには,そのポートがどの端子とつながっている
のか調べて,マザーボードの設計変更が必要になる。
1.4 実際のプログラム
led1st.c
/* msecwait関数で1秒ごとのLEDのON-OFFを行う */
#include <3048fone.h>
void msecwait(int msec)
/*msec間なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<4190;j++); /*4190は実測によって求めた値 25MHz駆動*/
}
}
main()
{
/*P5の下位2ビットを出力に設定*/
/* P5のDDRの下位2ビットに1を与えるとこの設定になる*/
/*DDRとはDataDirectionRegister(データ方向設定レジスタ)*/
P5.DDR = 0x3; /*0x3 = 00000011(二進数)*/
while(1) {/*これは無限ループ*/
/*LED0をONにする P5のDRの第0ビットを1にする*/
/*DRとはDataRegister(データレジスタ)*/
P5.DR.BIT.B0=1;
/*LED1をOFFにする P5のDRの第1ビットを0にする*/
P5.DR.BIT.B1=0;
msecwait(1000);/*1000msecの間なにもしない*/
/*LED0をOFFにする P5のDRの第0ビットを0にする*/
P5.DR.BIT.B0=0;
/*LED1をONにする P5のDRの第1ビットを1にする*/
P5.DR.BIT.B1=1;
msecwait(1000);
}
}
実用プログラムでは,ポートの記述はできるだけ隠すようにしています。h8_3048fone.hは
マザーボード上のハードウェア操作のプログラミングを関数呼び出しで行なえるようにしたものです。関数呼び出しを利用すると,どこのポートのどのビットを
どうするということを考えずに済み,専用コマンドを使うような感覚でプログラミングでき,便利です。
もう1つ重要なことがあります。将来別のCPUを使うことになって,このプログラムを移植することになった場合を想定しよう。全体の動作を記述するプログラムと,CPUのハードウェアを操作するプログラムを分離することにより,移植性が高くなることが想像できるでしょう。
関数の定義はh8_3048fone.h中にあります。本Webページの末尾に「h8_3048fone.h」がありますが,その先頭部分には関数の説明があるので読んでください。
led2nd.c
#include <3048fone.h>
#include "h8_3048fone.h"
void msecwait(int msec)
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<4190;j++); /*4190は実測によって求めた値 25MHz駆動*/
}
}
main()
{
initLed(); /*LED初期化*/
while(1) {
turnOnLed(0); /*LED0のON*/
turnOffLed(1); /*LED1のOFF*/
msecwait(1000);
turnOffLed(0); /*LED0のOFF*/
turnOnLed(1); /*LED1のON*/
msecwait(1000);
}
}
練習問題 led2nd.cを元にして次のプログラムを作りなさい。 (1)周期1秒,デューティ比50%で両方のLEDが同時に点滅するプログラム(mp1ex01.txt) |
練習問題 次の作業を行ないなさい。 |
LEDを高速にON-OFFを繰返し,ONになっている時間と周期との比(デューティ比)を変化させると,人間の目には点滅は見えず,デューティ比に応じて明るさが変化しているように見えます。
このような高速ON-OFFスイッチングで出力を制御する方法は「パルス幅変調(PWM)」「Pulse
Width
Modulation」と呼ばれます。
次のプログラムはLED0を点灯状態に保ち,LED1をPWM駆動します。3秒ごとに,デューティ比を90%,50%,10%と変化させています。
ledpwm.c
/**********************************************************
LEDのPWM(PulseWidthModulation)駆動
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"void msecwait(int msec)
/*msec間なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<4190;j++); /*4190は実測によって求めた値 25MHz駆動*/
}
}main()
{
int i;
initLed(); /*LED初期化*/
turnOnLed(0); /*LED0のON*/
while(1) {
for (i=0;i<300;i++) { /*ループ3秒間ループ デューティ比90%*/
turnOnLed(1); /*LED1のON*/
msecwait(9);
turnOffLed(1); /*LED1のOFF*/
msecwait(1);
}
for (i=0;i<300;i++) { /*ループ3秒間ループ デューティ比50%*/
turnOnLed(1); /*LED1のON*/
msecwait(5);
turnOffLed(1); /*LED1のOFF*/
msecwait(5);
}
for (i=0;i<300;i++) { /*ループ3秒間ループ デューティ比10%*/
turnOnLed(1); /*LED1のON*/
msecwait(1);
turnOffLed(1); /*LED1のOFF*/
msecwait(9);
}
}
}
次のプログラムは約1秒間隔でLED2が徐々に明るくなる動作を繰り返すプログラムです。
pは0から999まで変化するが,各pの値に対してiの値が0から999まで変化します。
pが10の時は,iが0から9の時LED1はONで10から999まではOFFとなります。すなわちpが10の時LED1がONになっている時間割合は1%程度です。
pが100の時は,iが0から99の時LED1はONで100から999まではOFFとなります。すなわちpが100の時LED1がONになっている時間割合は10%程度です。
pが900の時は,iが0から899の時LED1はONで900から999まではOFFとなります。すなわちpが900の時LED1がONになっている時間割合は90%程度です。
このように時間経過を考えると,LED1がONになっている時間とOFFになっている時間比が変化しています。しかし大変高速にLED1が点滅しているため,人間の目にはLED1の明るさが変化しているように見えます。
ledpwm1.c
/**********************************************************
LEDのPWM(PulseWidthModulation)駆動
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
main()
{
int p,i;
initLed(); /*LED初期化*/
turnOnLed(0); /*LED0のON*/
while(1) {
for (p=0;p<1000;p++) {
for (i=0;i<1000;i++) {
if (i<p) turnOnLed(1); /*LED1のON*/
else turnOffLed(1); /*LED1のOFF*/
}
}
}
}
練習問題 次の作業を行ないなさい。
(mp1ex10.txt) |
図3の8ビットスイッチの読み取りを行い,LEDを制御します。
8ビットスイッチのSW1がONならLED1をON。SW2がONならLED2をON,SW3がONならLED1・LED2ともにON,それ以外は2つのLEDはOFFにします。
8ビットスイッチはP2の8つのビットすべてにつながっています。
図3 8ビットスイッチ |
参考
通常用いられるのは,図3.1の回路である。スイッチがOFFの時CPUの入力ピンには5Vが与え られ,スイッチがONの時CPUの入力ピンには0Vが与えられる。この用途で用いられている抵抗のことをプルアップ抵抗と呼んでいる。通常この抵抗値は 10kΩから100kΩが用いられる。図3.2左はスイッチがOFFであるため,電流は流れず,抵抗で電圧降下が起こらないため,5Vが出力されていると ころを示している。図3.2右はスイッチがONであるため,電流が流れ,抵抗で電圧降下が起こり,0Vが出力されているところを示している。
H8CPU のポート2,4,5では,スイッチのON-OFF状態の取得等に都合の良い仕掛けがある。図3.3に示すように,図3.1のプルアップ抵抗をCPUユニッ トが内蔵しており,プルアップ抵抗を有効にするかどうかをソフトウェアで決めることができるようになっている。このプルアップ抵抗の有効無効を設定するの が,プルアップコントロールレジスタ(PCR)である。
AKI-H8のマザーボードでは,ディップスイッチ(8ビットスイッチ)がポート2の8つ のビットに,プッシュスイッチ(4つ)がポート4の上位4ビットにつながっており,P2.PCRとP4.PCRの対応するビットに1を書き込むことで,プ ルアップ抵抗を有効にして使用することができる。そのため,マザーボード上のこれらのスイッチにはプルアップ抵抗がついていない。
図3.1 スイッチ状態のCPUへの入力 図3.2 プルアップ抵抗の役割 図3.3 H8のプルアップコントロール 外部からのポートのビット入力が5Vの時,CPU内部では1としてとらえ,0Vの時は0としてとらえる。
その結果,ハードウェアの構成に依存して,スイッチの状態ONを0,状態OFFを1として,レジスタに取り込まれることになる。
スイッチの状態 ポートのビット入力端子の電圧 レジスタに取り込まれるビット状態 ON
0V
0
OFF
5V
1
2.1 8ビットSWでLED駆動
8ビットスイッチのON-OFFの状態によってLEDのON-OFFを制御するプログラムを作成します。
LED駆動部のみh8_3048fone.hを用いてプログラムを作ります。
8ビットスイッチの各端子はH8内部でプルアップされる設定なので,スイッチがONになるとポート2の対応するビットは0になります。OFFになると1になります。
「8bitSWの1(ポート2の第0ビット)がONの時では」というのは「if (P2.DR.BIT.B0==0) {」のようになり,
「8bitSWの1(ポート2の第0ビット)がOFFの時では」というのは「if (P2.DR.BIT.B0==1) {」のようになり,
通常の感覚と逆なので注意しなければなりません。
eightsw.c
/**********************************************************
8ビットスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
main()
{
initLed();
P2.DDR = 0x00;/*8bitSWのポートを入力に設定*/
P2.PCR.BYTE = 0xff;/*8bitSWのプルアップ設定*/
while(1) {
if (P2.DR.BIT.B0==0) { /*8bitSWの1がONの時*/
turnOnLed(0);
turnOnLed(1);
} else if (P2.DR.BIT.B1==0) { /*8bitSWの2がONの時*/
turnOnLed(0);
turnOffLed(1);
} else if (P2.DR.BIT.B2==0) { /*8bitSWの3がONの時*/
turnOffLed(0);
turnOnLed(1);
} else {
turnOffLed(0);
turnOffLed(1);
}
}
}
参考 「P2.DDR」
は「P2のDDR」,「P2.PCR.BYTE」は「P2のPCRをバイト単位で見た時のバイトデータ」,「P2.DR.BIT.B0」は「P2のDRを
ビットごと指定した時の第0ビット」と読めばよい。「P2.DDR」に指定できる値は0から255(0xff),「P2.PCR.BYTE」に入る値は0
から255(0xff),「P2.DR.BIT.B0」に入る値は0または1である。
この例ではでてこないが,P2.PCR.BIT.B0」は「P2のPCRをビットごと指定した時の第0ビット」と読み,この場合は入る値は0または1となる。
2.2
8ビットSWでLED駆動(「h8_3048fone.h」の利用)
eightswh.c
/**********************************************************
8ビットスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
main()
{
initLed();
init8BitSW();/*8bitSWの初期化*/
while(1) {
if (check8BitSW(0)) { /*8bitSWの1がONの時*/
turnOnLed(0);
turnOnLed(1);
} else if (check8BitSW(1)) { /*8bitSWの2がONの時*/
turnOnLed(0);
turnOffLed(1);
} else if (check8BitSW(2)) { /*8bitSWの3がONの時*/
turnOffLed(0);
turnOnLed(1);
} else {
turnOffLed(0);
turnOffLed(1);
}
}
}
練習問題 eightsw.cを元にして次のプログラムを作りなさい。 |
練習問題
|
3. プッシュスイッチ
図4 プッシュスイッチ 図4のプッシュスイッチの読み取りを行い,LEDを制御します。
|
3.1 PushSWでLED駆動(「h8_3048fone.h」の利用)
pushsw.c
/**********************************************************
プッシュスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
main()
{
initLed();
initPushSW();/*PushSWの初期化*/
while(1) {
if (checkPushSW(0)==1) { /*PushSWの1がONの時*/
turnOnLed(0);
turnOffLed(1);
} else if (checkPushSW(1)==1) { /*PushSWの2がONの時*/
turnOffLed(0);
turnOnLed(1);
} else {
turnOffLed(0);
turnOffLed(1);
}
}
}
練習問題 pushsw.cを元にして次のプログラムを作りなさい。 (3)プッシュスイッチ1を1回押すごとに,LEDの点灯,消灯の切り替えが起こり,
|
通常の関数は,関数がプログラム中の他の関数から呼び出されたときに作業を行ないます。これに対して,割り込み関数は何らかの割り込み要因によって呼び出される関数です。
タイマ割り込み関数は,タイマ割り込み初期設定によって設定された時間間隔で起動する割り込み関数です。「4.1」のプログラムでは,500msのタイマ割り込み初期設定が行なわれ,CPUの割り込み許可がなされ,タイマがスタートした後,プログラムの流れは
while(1);
と
なり,何もしない無限ループに突入します。しかし,500ms(0.5秒)ごとにタイマ割り込み関数「interrupt_cfunc()」が起動
し,LEDのON−OFFが継続して行なわれます。変数tickはstatic修飾されているので,関数が呼び出されたときに,前回呼び出しの時の値が
残っています。かつ0が代入されるのははじめの1回だけです。tick=1-tickの演算により,tickの値は0,1を繰り返します。
ロボットの制御には一定時間ごとに起動する定時間割り込み(タイマー割り込み)が良く用いられます。またPWMの生成にもこの定時間割り込みが用いられる場合があります。
関数void
msecwait(int msec)を用いた時間管理よりはるかに正確な時間管理ができます。(CPUクロックの精度依存)
4.1 割り込みでLED駆動(インターバルタイマによるタイマ割り込み)
プログラム中main()側で500msec(0.5秒)間隔でタイマ割り込みを設定します。
main()関数内でなにもしないループ動作をしている最中に,割り込み関数interrupt_cfunc()は0.5秒ごとに起動し,LEDのON-OFFを行ないます。
このタイマ割り込みはITUのch0,ch1が使われています。(割り込み周期を1秒近くにするにはITU2段重ねが必要)
割り込みを実現するには次のことが必要になる。
(1)割り込みベクトルの設定(割り込みが発生した時に起動すべき関数のアドレスを所定の領域に書いておく)
(2)割り込み要因となる機能の初期化
(3)CPUの割り込み許可
(4)割り込み関数は割り込み関数として定義され,レジスタの退避復帰,割り込みリターンなどの特殊な作り方が必要
これらの必要事項は次のように記述される。
(1)アセンブラでしか書くことが出来ないのでインラインアセンブリで記述
(インラインアセンブリ : C言語プログラム中にアセンブリ言語を記述すること)
タイマ割り込みの関数のアドレスは0x70に書くことになっている。(割り込みベクタテーブル領域)
アセンブラから見るとCの関数「TimerIntFunc」の名前は「_TimerIntFunc」に見える。
関数の名前は関数の先頭アドレスを意味する。
#pragma asm
.SECTION MYVEC, DATA, LOCATE=H'000070
.ORG H'000070 ;IMIA1
.DATA.L _TimerIntFunc
.SECTION P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm
ハードウェアマニュアル
http://tnct20.tokyo-ct.ac.jp/~kosaka/for_students/H8/j602093_h83048.pdf
において,割り込みベクタテーブルについての記述は4.1.3に記述されている。
ここで用いているのは,ITUch1を使用したIMIA1という割り込み要因である。
(2)main関数内で記述
(関数の定義はh8_3048fone.hにある)
(3)main関数内で記述
E_INT(); /*CPU割り込み許可*/
この関数の本体はアセンブリ言語でないと書けないので,スタートアップルーチンのソースの後ろに
ついている。次の説明を参照のこと
h8startup2.html
(4)割り込み関数独特の宣言を行なう TimerIntFuncは割り込み関数名
#pragma interrupt (TimerIntFunc)
この宣言が行われた関数は,プログラム内から関数呼び出ししてはいけない。
int1st.c /**********************************************************
時間割り込みによってLEDのON-OFFを行う
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
#define clearTimer1Flag() (ITU1.TSR.BIT.IMFA=0)
main()
{
initLed();
initTimer01Int(500); /*時間割り込み500msec ch0,ch1使用*/
E_INT(); /*CPU割り込み許可*/
startTimer01(); /*時間割り込みタイマスタートch0,ch1*/
while(1); /*なにもしないループ*/
}
#pragma asm
.SECTION MYVEC, DATA, LOCATE=H'000070
.ORG H'000070 ;IMIA1
.DATA.L _TimerIntFunc
.SECTION P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm
#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
static int tick=0;
clearTimer1Flag(); /*タイマステータスフラグのクリア 忘れないこと*/
if (tick==1) {
turnOnLed(0);
turnOffLed(1);
} else {
turnOffLed(0);
turnOnLed(1);
}
tick=1-tick;
}ここではアセンブリ言語には踏み込まないが簡単に解説
(深入りはしない。後にアセンブリ言語を学んだ後に読み返せばよい)
#pragma asm
ここの間はアセンブリ言語による記述ですという意味
#pragma endasm
.SECTION MYVEC, DATA, LOCATE=H'000070
MYVECという名前のセクションを0x70番地から始める
.ORG H'000070 ;IMIA1
0x70からこれ以降の内容を配置
.DATA.L _TimerIntFunc
関数TimerIntFuncの先頭アドレスをここに書く
DATA.L というのは,32ビット幅のデータの意味
.SECTION P,CODE,ALIGN=2 ;これを忘れてはいけない
これ以降はCODEというセクションの続きになるの意味
アセンブリ言語の記述スタイルについて
プログラムの内容は行の先頭にいくつかのスペースまたはタブの後ろに書く。
「;」以降はコメントとみなされる。
勘どころ |
ITU1の割り込みベクタ(割り込み関数の先頭番地)が0x70であることは,CPUの設計者が決めたことなので, プログラム作成者が変更することはできない。 割り込み関数の名前は,プログラム作成者が決めることができる。 |
練習問題 int1st.cを元にして次のプログラムを作りなさい。 |
割り込みを用いたPWM駆動でLEDの制御を行ないます。
このプログラムではITUのch1のみを用います。
PushSW
1 → LED 0 明るく点灯(10/10)
PushSW 2 → LED 0 暗く点灯(1/10)
PushSW 3 → LED 1
明るく点灯(10/10)
PushSW 4 → LED 1 暗く点灯(1/10)
volatile修飾子は,割り込み関数と通常の関数とで同じグローバル変数を用いるときに使います。
コ
ンパイラは通常コードの最適化を行ないます。例えば,変数は通常はメモリ上にあります。ある変数を一度レジスタに読み込んで,その変数の値を変化させてい
ない場合は,次にその変数の値が必要になったときには,メモリをもう一度読みに行くようなことはせず,レジスタの値を用います。しかし,その間に割り込み
関数がその変数の値を変化させることもあります。そこでvolatile修飾子をつけた変数にしておくと必ずメモリにある変数を使うコードになります。こ
の例の場合は,特に必要がありませんが,おまじないとしてつけておくようにしましょう。
このプログラムではLEDの暗い点灯時では1/10の時間割合で点灯している。LEDの暗い点灯時に5/10の時間割合で点灯するように変更しなさい。この点灯時間の割合は「デューティ比」と呼ばれる。
「h8_3048fone.h」にある関数initTimer1Int(),startTimer1()はITU1を使ったタイマ割り込みを可能にしている。
ハードウェアマニュアルのITUの基本動作(TCNT(カウンタ)がカウントアップ)を見なさい。
カウンタ(TCNT)がカウントアップしてあらかじめ設定した値(GRA)とコンペアマッチ(比較して一致)で割り込みが起こり,カウンタがクリアされ,この動作が継続される。カウンタは内部クロック(25MHz)の1/8で動作している。
このことにより,タイマ割り込みが行われている。
「1」で取り上げた関数msecwait()を使う方法と割り込みを使う方法とでは,時間精度が違う。高精度な時間管理には関数msecwait()を使うべきではない。タイマユニットが使えない場合に使用を検討するのがよい。
int2nd.c
/**********************************************************
プッシュスイッチと時間割り込みによってLEDのPWM制御を行う
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
#define clearTimer1Flag() (ITU1.TSR.BIT.IMFA=0)volatile int led0,led1;
const int period=10; /*周期10msec*/
const int low=1;main()
{
led0=led1=0;
initLed();
initPushSW();
initTimer1Int(1000); /*タイマ割り込み1msec */
/*単位はμsec ITUch1のみ使用*/
E_INT(); /*CPU割り込み許可*/
startTimer1(); /*時間割り込みタイマスタート*/
while(1){
if (checkPushSW(0)==1) { /*PushSWの1がONの時*/
led0=period;
} else if (checkPushSW(1)==1) { /*PushSWの2がONの時*/
led0=low;
} else {
led0=0;
}
if (checkPushSW(2)==1) { /*PushSWの3がONの時*/
led1=period;
} else if (checkPushSW(3)==1) { /*PushSWの4がONの時*/
led1=low;
} else {
led1=0;
}
}
}#pragma asm
.SECTION MYVEC, DATA, LOCATE=H'000070
.ORG H'000070 ;IMIA1
.DATA.L _TimerIntFunc
.SECTION P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
static int tick=0;
clearTimer1Flag(); /*タイマステータスフラグのクリア 忘れないこと*/
if (tick<led0) {
turnOnLed(0);
} else {
turnOffLed(0);
}
if (tick<led1) {
turnOnLed(1);
} else {
turnOffLed(1);
}
tick++;
if (tick==period) tick=0;
}
練習問題 int2nd.cを元にして次のプログラムを作りなさい。 |
5. パソコンとのシリアル通信
プログラム書き込み用通信回線はそのままプログラム実行時にも使えます。
実行時にはハイパーターミナルを起動しておいて下さい。
サンプルフォルダ中の次に示すアイコンで起動すると,設定済みのハイパーターミナルが起動します。
Async, 8bit, NoParity, stop1 ,38400baud,
(Backspace:Ctrl+H, Space, Ctrl+H)で設定されています。
ハイパーターミナルを利用した,ビットスイッチやプッシュスイッチのチェックおよびキーボードとの通信プログラムです。
ビットスイッチやプッシュスイッチのチェックの時はそれぞれのスイッチを動かしてみてください。
パソコン画面への出力(WindowsのHyperTerminal使用)
パソコン(キーボード)からの入力(WindowsのHyperTerminal使用)
sciout.c
/**********************************************************
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は 38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
void func1(void)
{
unsigned char sw,previous;
SCI1_printf("Printing 8-bitSW status...\n");
SCI1_printf("Change 8-bitSW and new status will appear.\n");
SCI1_printf("If any key on the keyboard, this test will quit.\n");
previous=sw=get8BitSW();
SCI1_printf("8-bitSW status= %2x[%08b]\n",sw,sw);
do {
sw=get8BitSW();
if (sw!=previous) {
SCI1_printf("8bitSW status= %2x[%08b]\n",sw,sw);
previous=sw;
}
} while (chkgetCharSCI1()<0);
}
void func2(void)
{
unsigned char sw,previous;
SCI1_printf("Printing PushSW status...\n");
SCI1_printf("Change PushSW and new status will appear.\n");
SCI1_printf("If any key on the keyboard, this test will quit.\n");
previous=sw=getPushSW();
SCI1_printf("PushSW status= %2x[%08b]\n",sw,sw);
do {
sw=getPushSW();
if (sw!=previous) {
SCI1_printf("PushSW status= %2x[%08b]\n",sw,sw);
previous=sw;
}
} while (chkgetCharSCI1()<0);
}
void func3(void)
{
int x;
x=getIntSCI1("Key in a decimal number >>>");
SCI1_printf("The number you keyed in is %d %x\n",x,x);
x=getIntSCI1("Key in a hexdecimal number (ex. 0x23ff) >>>");
SCI1_printf("The number you keyed in is %d %x\n",x,x);
}
main()
{
int menu;
initSCI1(); /*SCI-ch1の初期化*/
initPushSW(); /*押しボタンスイッチの初期化*/
init8BitSW(); /*8ビットスイッチの初期化*/
SCI1_printf("Hello. How are you?\n");
while (1) {
SCI1_printf("***************menu**********\n");
SCI1_printf("1: get 8-bit SW and print \n");
SCI1_printf("2: get Push SW and print \n");
SCI1_printf("3: get integer from SCI1 and print \n");
do {
menu=getCharSCI1(); /*menuには'1','2','3'が入るはず*/
} while (menu<'1'||'3'<menu);
SCI1_printf("\n");
switch (menu) {
case '1':
func1();
break;
case '2':
func2();
break;
case '3':
func3();
break;
default:
break;
}
}
}特別な関数の説明 これらの関数はh8_3048fone.h中に定義されている。
short int getCharSCI1()
SCI-ch1から1byte入力コードを得て関数の値として返す関数。通信エラーがあると-2が戻る。
SIC-ch1入力バッファを検査し,データがあれば持ち帰るが,データがない場合はデータが来るまで待ち続け,
データが来たら,それを持ち帰ってくる
PCのキーボードが押され,SCI-ch1経由でデータが転送されてくるまで,永久に待つ関数である。
ANSIの関数getchar()と同じ動作をするshort int chkgetCharSCI1()
SCI-ch1入力バッファを検査し,受信データがあれば1byte入力コードを得て関数の値として返す関数。
受信データガなければ-1が,通信エラーがあると-2が戻る。
SICバッファを検査し,データがあれば持ち帰るが,データがなくても-1をもってすぐに帰ってくる関数である。int get8BitSW()
8bitSWの状態をそのまま1バイトの値として読み込む関数。状態(00〜ff)は関数の値として返す。
ただし,値は反転しており,スイッチの状態がONのビットは1,OFFのビットは0で読み込む。int getIntSCI1(char prompt[])
SCI-ch1からプロンプト付で,short intの値を受け取り,関数の値として返す。
(引数で与えた文字列を表示してから,short intの値を受け取る。)
正負の10進数または16進数を受け付ける。「0x」で始めまる文字列は16進数として受け取る。
●画面への表示をそのままファイル化する方法(テキストキャプチャ)
SCI通信の時PC側の「Enterキー」入力では,文字キーの右側にある大きなenter-keyを使うこと。
enter-keyについて
キーボードには2つのenter-keyがあるが,実は文字コードが異なる。
文字キーの右側にある大きなenter-key \r\n (0x0d,0x0a)
数字キーの右側にある小さなenter-key \r (0x0d)
である。
\r(0x0d) はコンソール上で文字ポインタを左端に戻すコード
\n(0x0a) はコンソール上で文字ポインタを次の行に進めるコード
練習問題 次のプログラムを作りなさい。 |
「パソコンとのシリアル通信」,「タイマー割り込み」を用いた時計を作ります。
timer.c
/**********************************************************
割り込みを用いた時計 起動時からの経過時間[秒]を
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
#define clearTimer1Flag() (ITU1.TSR.BIT.IMFA=0)
volatile unsigned int counter;
main()
{
unsigned int counter1;
unsigned int t1,t2;
initSCI1(); /*SCI-ch1の初期化*/
initTimer01Int(100); /*時間割り込み100msec ch0,ch1使用*/
E_INT(); /*CPU割り込み許可*/
startTimer01(); /*時間割り込みタイマスタートch0,ch1*/
counter=0;
while(1) {
counter1=counter;
t1=counter1/10;
t2=counter1%10;
SCI1_printf("%10u.%1u\r",t1,t2);
}
}
#pragma asm
.SECTION MYVEC, DATA, LOCATE=H'000070
.ORG H'000070 ;IMIA1
.DATA.L _TimerIntFunc
.SECTION P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
clearTimer1Flag(); /*タイマステータスフラグのクリア 忘れないこと*/
counter++;
}
練習問題 int2nd.c,sciout.c,timer.cを元にして次のプログラムを作りなさい。 (3)シリアル通信速度は"h8_3048fone.hの中の関数initSCI1で設定されている。 |
7.独立した複数の作業の書き方
リアルタイムOSを使うと,もっと別な書き方になるが,ここではリアルタイムOSを使わない方法でプログラミングする
例題
プッシュスイッチ1(内部表現では0)を押しした瞬間から2秒間LED0が点灯し、消灯する。
プッシュスイッチ2(内部表現では1)を押しした瞬間から2秒間LED1が点灯し、消灯する。
ただし、LED0が点灯中にプッシュスイッチ1が押されたら、残り点灯時間には無関係に、その時点から新たに2秒間点灯するものとする。
同様に、LED1が点灯中にプッシュスイッチ2が押されたら、残り点灯時間には無関係に、その時点から新たに2秒間点灯するものとする。
また、LED0が点灯中にプッシュスイッチ2が押されたら、LED0の動作には影響を与えず、LED1は、その時点から2秒間点灯するものとする。
同様に、LED1が点灯中にプッシュスイッチ1が押されたら、LED1の動作には影響を与えず、LED2は、その時点から2秒間点灯するものとする。
このような2つの動作を独立動作と呼ぶ。
独立した2つの作業を行う
失敗作(プログラムはわかりやすいが失敗する)/**********************************************************
PushSW1でLED0をPushSW2でLED0を1秒間点灯させる
この2つの作業は独立に行われる
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
#define clearTimer1Flag() (ITU1.TSR.BIT.IMFA=0)volatile int count1=-1;/*LED0用カウンタ -1の時は休止中*/
volatile int count2=-1;/*LED1用カウンタ -1の時は休止中*/
main()
{
initLed();
initPushSW();/*PushSWの初期化*/
initTimer1Int(10000); /*時間割り込み10000μsec=10msec ch1使用*/
E_INT(); /*CPU割り込み許可*/
startTimer1(); /*時間割り込みタイマスタートch1*/
while(1) {
if (count1==-1 && checkPushSW(0)==1) {
count1=0;
turnOnLed(0);
while (count1<200); /*200カウントで2秒経過*/
count1=-1;
turnOffLed(0);
}
if (count2==-1 && checkPushSW(1)==1) {
count2=0;
turnOnLed(1);
while (count2<200); /*200カウントで2秒経過*/
count2=-1;
turnOffLed(1);
}
}
}#pragma asm
.SECTION MYVEC, DATA, LOCATE=H'000070
.ORG H'000070 ;IMIA1
.DATA.L _TimerIntFunc
.SECTION P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
static int tick=0;
clearTimer1Flag(); /*タイマステータスフラグのクリア 忘れないこと*/
if (count1!=-1) count1++;
if (count2!=-1) count2++;
}
独立した2つの作業を行う(成功例)
/**********************************************************
PushSW1でLED0をPushSW2でLED0を2秒間点灯させる
この2つの作業は独立に行われる
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
#define clearTimer1Flag() (ITU1.TSR.BIT.IMFA=0)volatile int count1=-1;/*LED0用カウンタ -1の時は休止中*/
volatile int count2=-1;/*LED1用カウンタ -1の時は休止中*/
main()
{
initLed();
initPushSW();/*PushSWの初期化*/
initTimer1Int(10000); /*時間割り込み10000μsec=10msec ch1使用*/
E_INT(); /*CPU割り込み許可*/
startTimer1(); /*時間割り込みタイマスタートch1*/
while(1) {
if ((count1==-1 || 50<count1) && checkPushSW(0)==1) {
count1=0;
turnOnLed(0);
} else if (200<count1) { /*200カウントで2秒経過*/
count1=-1;
turnOffLed(0);
}
if ((count2==-1 || 50<count2) && checkPushSW(1)==1) {
count2=0;
turnOnLed(1);
} else if (200<count2) { /*200カウントで2秒経過*/
count2=-1;
turnOffLed(1);
}
}
}#pragma asm
.SECTION MYVEC, DATA, LOCATE=H'000070
.ORG H'000070 ;IMIA1
.DATA.L _TimerIntFunc
.SECTION P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm#pragma interrupt (TimerIntFunc)
void TimerIntFunc() /*タイマ割り込みルーチン*/
{
static int tick=0;
clearTimer1Flag(); /*タイマステータスフラグのクリア 忘れないこと*/
if (count1!=-1) count1++;
if (count2!=-1) count2++;
}
練習問題 (mp7ex01.txt) プッシュスイッチ1(内部表現では0)を押した瞬間からLED0は1秒間点灯し、1秒間消灯し、 練習問題 (mp7ex02.txt) 前課題の2つプッシュスイッチにより起動する定型動作に加え、プッシュスイッチ3によって起動するも |
参考1 H8ピン配置
コネクタ1(CN1)ピン割り当て |
コネクタ2(CN2)ピン割り当て |
コネクタ3(CN3)ピン割り当て |
コネクタ4(CN4)ピン割り当て |
コネクタ5(CN5)ピン割り当て | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
AKI-H8付属のコネクタ番号表 |
参考2 h8_3048fone.h
h8_3048fone.h
/****************************************************************
h8_3048fone.h
Copyright (c) Kosaka Lab CS TNCTこのインクルードファイルは小坂研究室の代々の研究生が開発した
h8/3048用の有用な関数群を改良して小坂がまとめたものである。
01 Jun 2009 h8-3048.h 小坂 教材用にリファイン
28 Jun 2006 h8-3048.h 小坂 chkgetSCI1のタイミング修正
4 Dec 2003 h8-3048.h 小坂 printf更新,initLed更新,initDDR削除
08 Oct 2003 h8-3048.h 小坂 stopTimer追加,getIntSCI1でBS使用可
6 Jan 2003 h8_3048.h 小坂 getIntSCIバックスペイスに対応。
17 Apr 2002 h8-01.h 小坂 %uの使い方をansiにあわせた。
14 Dec 2001 h8-01.h 小坂,越智
15 Jly 2000 h8-00.h 小坂,藤原
22 Dec 1999 h8-99.h 小坂,高沢
29 Oct 1999 h8-99.h 小坂
05 Feb 1999 lib.h 笠井
【1】SCI
【1.1】 ch1 関係
void initSCI1()
SCI-ch1の初期化 38400baud, Async, 8bit , NoParity, stop1short int getCharSCI1()
SCI-ch1から1byte入力コード。エラーがあると-2が戻る。
short int chkgetCharSCI1()
SCI-ch1を検査し,受信データがあれば1byte入力コード。なければ-1が,失敗すると-2が戻る。
short int getIntSCI1(char prompt[])
SCI-ch1からプロンプト付で,short intの値を受け取る。
正負の10進数または16進数を受け付ける。16進数は0xで始まるvoid putCharSCI1(char c)
SCI-ch1に1バイト出力する。
void putStringSCI1(char *str)
SCI-ch1に文字列を出力する。
void SCI1_printf(char *format,...)
関数printfのSCI1版
軽量化のためエラー処理はないので桁数指定の場合は注意
対応書式
%d : [int] integer with sign. '%d','%4d','%-4d', and '%04d' are available
%ld : explicit [long int] '%ld','%9ld','%-9ld', and '%09ld' are available
%u : [unsigbed int] unsigned integer.
'%u','%4u','%-4u', and '%04u' are available
%lu : explicit [unsigned long int]
'%lu','%9lu','%-9lu', and '%09lu' are available
%x : [unsigned int] in Hex '%x','%4x','%-4x', and '%04x' are available
%lx : explicit [unsigned long int] in Hex
'%lx','%8lx','%-8lx', and '%08lx' are available
%o : [unsigned int] in Oct '%o','%4o','%-4o', and '%04o' are available
%lo : explicit [unsigned long int] in Oct
'%lo','%8lo','%-8lo', and '%08lo' are available
%b : [unsigned int] in Bin '%b','%8b','%-8b', and '%08b' are available
%lb : explicit [unsigned long int] in Bin
'%lb','%8lb','%-8lb', and '%08lb' are available
%c : char
%s : string %20s %-20s are available【1.2】SCI ch0 関係
void initSCI0()
SCI-ch0の初期化 38400baud, Async, 8bit , NoParity, stop1short int getCharSCI0()
SCI-ch0から1byte入力コード。エラーがあると-2が戻る。
short int chkgetCharSCI0()
SCI-ch0を検査し,受信データがあれば1byte入力コード。なければ-1が,失敗すると-2が戻る。
short int getIntSCI0(char prompt[])
SCI-ch0からプロンプト付で,short intの値を受け取る。
正負の10進数または16進数を受け付ける。16進数は0xで始まるvoid putCharSCI0(char c)
SCI-ch0に1バイト出力する。
void putStringSCI0(char *str)
SCI-ch0に文字列を出力する。
void SCI0_printf(char *format,...)
関数printfのSCI0版
仕様はvoid SCI1_printf(char *format,...)参照【2】LCD関係
void LCDputchar(char data)
LCDに向けた putchar()
void LCDputs(char *str)
LCDに向けた puts()
void initLCD( void )
LCDの初期化
void LCDgotoxy(unsigned x,unsigned y)
LCDに向けた gotoxy()
void LCDclrscr(void)
LCDに向けた clrscr() clear screen
void LCD_printf(char *format,...)
関数printfのLCD版
仕様はvoid SCI1_printf(char *format,...)参照
【3】AKI-H8マザーボード関係
void initLed()
LEDの初期化
void turnOnLed(short int number)
LEDの点灯 numberはLED番号で0または1を指定する
void turnOffLed(short int number)
LEDの消灯 numberはLED番号で0または1を指定するvoid initPushSW(void)
押しボタンスイッチの初期化
short int getPushSW(void)
押しボタンスイッチの取得 ただしポートを読み込み,ビット反転のみ。
押しボタンスイッチの状況は第4-第7ビットに現れる。
これはマクロ定義で実現されている
short int checkPushSW(short int number)
push sw 0,1,2,3の状態を調べる number:0,1,2,or 3
押されていたら1、そうでなかったら0を返すvoid init8BitSW(void)
8ビットスイッチの初期化
short int get8BitSW(void)
8ビットスイッチの取得 ただしポートを読み込み,ビット反転のみ。
8ビットスイッチの状況は第0-第7ビットに現れる。
これはマクロ定義で実現されている
short int check8BitSW(short int number)
8bitsw 0,1,2,3,4,5,6,7の状態を調べる number:0,1,2,3,4,5,6,or 7
ONなら1、そうでなかったら0を返す【3】タイミング割り込み
void initTimer1Int(unsigned short int period)
ITU1による割り込みタイマーの設定
割り込み間隔は引数peiodで単位はμsecである
値は20971以下でなければならない。20.971msecまで設定可能void startTimer1(void)
Timer CH1 スタート
これはマクロ定義で実現されている
void stopTimer1(void)
Timer CH1 ストップ
これはマクロ定義で実現されているvoid initTimer01Int(unsigned short int period)
ITU0とITU1による割り込みタイマーの設定
割り込み間隔は引数peiodで単位はmsecである
値は65535以下でなければならない。65.535secまで設定可能
ただしポートPAの第3ビットが使用できなくなるので注意
void startTimer01(void)
Timer CH0 CH1 同時スタート
これはマクロ定義で実現されている
void stopTimer01(void)
Timer CH0 CH1 同時ストップ
これはマクロ定義で実現されている****************************************************************/
#include<stdarg.h>
unsigned char P1DDR=0,P2DDR=0,P3DDR=0,P4DDR=0,P5DDR=0;
unsigned char P6DDR=0,P7DDR=0,P8DDR=0,P9DDR=0,PADDR=0,PBDDR=0;extern void E_INT();
extern void D_INT();
/*SCI関係の基本部分は笠井君(1998年度)藤原君(2000)の開発です*/
/* ------------------------------------------------- */
/* SCI1 INITIALIZATION fixed baud at 38400 */
/* ------------------------------------------------- */
void initSCI1()
{
short int i;
SCI1.SCR.BYTE = 0; /* clear all flags */
/* 2400-38400baud are available at n=0(cks1=0,cks2=0) */
SCI1.SMR.BYTE = 0; /* Async, 8bit , NoParity, stop1, 1/1 */
SCI1.BRR = 19; /* 38400baud (CPU=25MHz) */
for(i=0;i<1000;i++); /* wait more than 1bit time */
SCI1.SCR.BYTE = 0x30; /* scr = 0011 0000 (TE=1,RE=1) */
return;
}/* ------------------------------------------------- */
/* GET BYTE FROM SCI1 */
/* ------------------------------------------------- */
short int getCharSCI1()
/* return value 0x00-0xFF:received data */
/* -2(0xFFFE):error */
{
short int flags,recdata;
do {
flags = SCI1.SSR.BYTE;
if (flags&0x38) {/* error */
SCI1.SSR.BIT.RDRF = 0;
SCI1.SSR.BIT.ORER = 0;
SCI1.SSR.BIT.FER = 0;
SCI1.SSR.BIT.PER = 0;
return -2;
}
if (flags&0x40) {/* normally received one data */
SCI1.SSR.BIT.RDRF = 0;
recdata=SCI1.RDR;
return recdata;
}
} while (1);
}/* ------------------------------------------------- */
/* CHECK SCI BUFFER AND GET DATA */
/* ------------------------------------------------- */
short int chkgetCharSCI1()
/* return value -1(0xFFFF):no received data */
/* 0x00-0xFF:received data */
/* -2(0xFFFE):error */
{
short int flags,recdata;
flags = SCI1.SSR.BYTE;
if (flags&0x38) {/* error */
SCI1.SSR.BIT.RDRF = 0;
SCI1.SSR.BIT.ORER = 0;
SCI1.SSR.BIT.FER = 0;
SCI1.SSR.BIT.PER = 0;
return -2;
}
if (flags&0x40) {/* normally received one data */
recdata=SCI1.RDR;
SCI1.SSR.BIT.RDRF = 0;
return recdata;
} else {
return -1;
}
}void putStringSCI1(char *str);
/*SCI1より文字列入力[return]が終端だが,'\n'は取得されない*/
/*^Hでバックスペイス*/
int getStringSCI1(char *buff,int max)
{
int i,ch;
for (i=0;i<max-1;i++) {
ch=getCharSCI1(); /*1文字取得*/
*buff=(char)ch; /*1文字取得*/
if (*buff=='\r'||ch<0) {
*buff=0;
return i+1;
}
if (*buff==0x8) {
buff-=2;
i-=2;
}
if (*buff!='\n') buff++;
else i--;
}
*buff=0;
return i+1;
}/*SCI1へプロンプトを表示して,SCI1より整数値を入力*/
int getIntSCI1(char prompt[])
/*getting integer from serial port*/
/* format 123[ret] */
/* -123[ret] */
/* 0x1a[ret] */
/* -0x100[ret] */
{
int x=0,y,m=0,n=0,v=0,i=0;
char buff[16];
putStringSCI1(prompt);
getStringSCI1(buff,16);
y=buff[i];
while(y!=0){
if(y=='-') m=1;
if('a'<=y&&y<='z') y=y-'a'+'A';
if(y=='0') n=1;if(v==1){
if('0'<=y&&y<='9'){
y=y-'0';
}
else if('A'<=y&&y<='F'){
y=y-'A'+10;
}
x=16*x+y;
}if(n==1&&y=='X'){
v=1;
}
if(v==0&&'0'<=y&&y<='9'){
y=y-'0';
x=10*x+y;
}y=buff[++i];
}
if(m==1) x=-x;
return x;
}/* ------------------------------------------------- */
/* PUT BYTE TO SCI1 */
/* ------------------------------------------------- */
void putCharSCI1(char c)
{
unsigned char tmp;
if (c=='\n') putCharSCI1('\r');
do{
tmp = SCI1.SSR.BYTE;
} while((tmp & 0x80)==0);
SCI1.TDR = c;
SCI1.SSR.BIT.TDRE = 0;
return;
}void putStringSCI1(char *str)
{
while(*str){
putCharSCI1(*str);
str++;
}
}
/* ------------------------------------------------- */
/* SCI0 INITIALIZATION fixed baud at 38400 */
/* ------------------------------------------------- */
void initSCI0()
{
short int i;
SCI0.SCR.BYTE = 0; /* clear all flags */
/* 2400-38400baud are available at n=0(cks1=0,cks2=0) */
SCI0.SMR.BYTE = 0; /* Async, 8bit , NoParity, stop1, 1/1 */
SCI0.BRR = 19; /* 38400baud (CPU=25MHz) */
for(i=0;i<1000;i++); /* wait more than 1bit time */
SCI0.SCR.BYTE = 0x30; /* scr = 0011 0000 (TE=1,RE=1) */
return;
}/* ------------------------------------------------- */
/* GET BYTE FROM SCI0 */
/* ------------------------------------------------- */
short int getCharSCI0()
/* return value 0x00-0xFF:received data */
/* -2(0xFFFE):error */
{
short int flags,recdata;
do {
flags = SCI0.SSR.BYTE;
if (flags&0x38) {/* error */
SCI0.SSR.BIT.RDRF = 0;
SCI0.SSR.BIT.ORER = 0;
SCI0.SSR.BIT.FER = 0;
SCI0.SSR.BIT.PER = 0;
return -2;
}
if (flags&0x40) {/* normally received one data */
SCI0.SSR.BIT.RDRF = 0;
recdata=SCI0.RDR;
return recdata;
}
} while (1);
}/* ------------------------------------------------- */
/* CHECK SCI BUFFER AND GET DATA */
/* ------------------------------------------------- */
short int chkgetCharSCI0()
/* return value -1(0xFFFF):no received data */
/* 0x00-0xFF:received data */
/* -2(0xFFFE):error */
{
short int flags,recdata;
flags = SCI0.SSR.BYTE;
if (flags&0x38) {/* error */
SCI0.SSR.BIT.RDRF = 0;
SCI0.SSR.BIT.ORER = 0;
SCI0.SSR.BIT.FER = 0;
SCI0.SSR.BIT.PER = 0;
return -2;
}
if (flags&0x40) {/* normally received one data */
recdata=SCI0.RDR;
SCI0.SSR.BIT.RDRF = 0;
return recdata;
} else {
return -1;
}
}void putStringSCI0(char *str);
/*SCI0より文字列入力[return]が終端だが,'\n'は取得されない*/
/*^Hでバックスペイス*/
int getStringSCI0(char *buff,int max)
{
int i,ch;
for (i=0;i<max-1;i++) {
ch=getCharSCI0(); /*1文字取得*/
*buff=(char)ch; /*1文字取得*/
if (*buff=='\r'||ch<0) {
*buff=0;
return i+1;
}
if (*buff==0x8) {
buff-=2;
i-=2;
}
if (*buff!='\n') buff++;
else i--;
}
*buff=0;
return i+1;
}/*SCI0へプロンプトを表示して,SCI0より整数値を入力*/
int getIntSCI0(char prompt[])
/*getting integer from serial port*/
/* format 123[ret] */
/* -123[ret] */
/* 0x1a[ret] */
/* -0x100[ret] */
{
int x=0,y,m=0,n=0,v=0,i=0;
char buff[16];
putStringSCI0(prompt);
getStringSCI0(buff,16);
y=buff[i];
while(y!=0){
if(y=='-') m=1;
if('a'<=y&&y<='z') y=y-'a'+'A';
if(y=='0') n=1;if(v==1){
if('0'<=y&&y<='9'){
y=y-'0';
}
else if('A'<=y&&y<='F'){
y=y-'A'+10;
}
x=16*x+y;
}if(n==1&&y=='X'){
v=1;
}
if(v==0&&'0'<=y&&y<='9'){
y=y-'0';
x=10*x+y;
}y=buff[++i];
}
if(m==1) x=-x;
return x;
}/* ------------------------------------------------- */
/* PUT BYTE TO SCI0 */
/* ------------------------------------------------- */
void putCharSCI0(char c)
{
unsigned char tmp;
if (c=='\n') putCharSCI0('\r');
do{
tmp = SCI0.SSR.BYTE;
} while((tmp & 0x80)==0);
SCI0.TDR = c;
SCI0.SSR.BIT.TDRE = 0;
return;
}void putStringSCI0(char *str)
{
while(*str){
putCharSCI0(*str);
str++;
}
}
/* Port3 -> LCD */
/* 7 6 5 4 3 2 1 0 */
/* ES RS DB7 DB6 DB5 DB4 *//* i/o */
#define init_LCD_Port() P3.DDR = 0x3f
#define LCD_Port P3.DR.BYTE
#define LCD_RegisterSelect P3.DR.BIT.B4
#define LCD_EnableSignal P3.DR.BIT.B5static void LCDmsecwait(unsigned int msec)
/*mesc間なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<4190;j++); /*4190は実測によって求めた値 25MHz駆動 */
}
}static void LCDmicrosecwait(unsigned int microsec)
{/*だいたいmicrosec間なにもしない時間稼ぎ関数*/
int i,j;
for (i=0;i<microsec;i++) {
for (j=0;j<4;j++);
}
}static void LCDputCommand(char command) /*command width must be 4bits*/
{
LCD_Port=(LCD_Port&0xf0)|command;
LCD_EnableSignal=1;
LCDmicrosecwait(5);
LCD_EnableSignal=0;
LCDmicrosecwait(40);
}static void LCDputCommand2(char command2)
{
LCDputCommand((command2>>4)&0xf);
LCDputCommand(command2&0xf);
}/************************************************************
LCDに向けた putchar()
************************************************************/
void LCDputchar(char data)
{
LCD_RegisterSelect=1;
LCDputCommand((data>>4)&0xf);
LCDputCommand(data&0xf);
LCD_RegisterSelect=0;
}/************************************************************
LCDに向けた puts()
************************************************************/
void LCDputs(char *str)
{
while(*str) LCDputchar(*str++);
}/************************************************************
LCDの初期化
************************************************************/
void initLCD( void )
{
init_LCD_Port(); /* output */
LCD_Port&=0xc0;
LCDmsecwait(30); /* wait 30ms */
LCDputCommand(0x3); /* function set */
LCDmicrosecwait(4100);
LCDputCommand(0x3); /* function set */
LCDmicrosecwait(100);
LCDputCommand(0x3); /* function set */
LCDmicrosecwait(100);
LCDputCommand(0x2); /* function set data width=4bit*/
LCDputCommand2(0x28); /* function set 4bit duty:1/16,size:5*7 */
LCDputCommand2(0x0c); /* display on,cursor off,blink off */
LCDputCommand2(0x06); /* address:auto increment,cursor shift:right */
LCDputCommand2(0x01); /* clear display */
LCDmicrosecwait(1640);
}/************************************************************
LCDに向けた gotoxy()
************************************************************/void LCDgotoxy(unsigned x,unsigned y)
{
unsigned char point;
point=0x80+x+0x40*y;
LCDputCommand2(point);
}/************************************************************
LCDに向けた clrscr() clear screen
************************************************************/
void LCDclrscr(void)
{
LCDputCommand2(0x01); /* clear display */
LCDmicrosecwait(1640);
}const char hexstring[]="0123456789abcdef0123456789ABCDEF";
#define MAXDIGIT 34
const int SCI0device=0;
const int SCI1device=1;
const int LCDdevice=2;void Device_printf(int device, char *format, va_list arg_ptr)
{
void (*device_putchar)(char ch);
void (*device_puts)(char *ptr);
char buf[MAXDIGIT];
unsigned char flag=0; /*%d:bit2 l:bit1 %:bit0 */
unsigned char digit=0; /* 桁数 */
unsigned char minus=0;
char fill=' ',format1=' ';
unsigned char radix=10; /*N進基数*/
char sign=' ';
char *ptr=buf; /*出力文字ポインタ*/
unsigned char cntr=0; /*出力文字数カウンタ*/
unsigned char shift=0; /*16進シフト 0 or 6*/
unsigned char i;
unsigned long int value=0;
if (device==SCI0device) {
device_putchar=putCharSCI0;
device_puts=putStringSCI0;
} else if (device==SCI1device) {
device_putchar=putCharSCI1;
device_puts=putStringSCI1;
} else { /*device==LCDdevice*/
device_putchar=LCDputchar;
device_puts=LCDputs;
}
/*va_start(arg_ptr,format);*/
while (*format) {
format1=*format;
if (flag==0) {
if (format1=='%') {
flag=1;
digit=0;
fill=' ';
minus=0;
radix=0;
ptr=&buf[MAXDIGIT-1];
*ptr--='\0';
cntr=0;
shift=0;
sign='+';
} else {
device_putchar(format1);
}
} else {
if (format1=='l') {
flag|=2;
} else if ('0'<=(format1)&&(format1)<='9') {
if (digit==0 && format1=='0') {
fill='0';
} else {
digit=digit*10+((format1)-'0');
if (MAXDIGIT-2<digit) digit=MAXDIGIT-2;
}
} else if (format1=='-') {
minus=1;
} else if (format1=='d') {
flag|=4;
radix=10;
} else if (format1=='u') {
radix=10;
} else if (format1=='x') {
radix=16;
} else if (format1=='X') {
radix=16;shift=16;
} else if (format1=='o') {
radix=8;
} else if (format1=='b') {
radix=2;
} else if (format1=='p') {
radix=16;shift=16;digit=8;fill='0';flag|=2;
} else if (format1=='c') {
device_putchar((unsigned char)(va_arg(arg_ptr,int)));
flag=0;
} else if (format1=='s') {
if (digit) {
cntr=0;ptr=va_arg(arg_ptr,char *);
while (ptr[cntr]) cntr++; /*cntrは文字数*/
if (!minus) for (i=cntr;i<digit;i++) device_putchar(' ');
device_puts(ptr);
if (minus) for (i=cntr;i<digit;i++) device_putchar(' ');
} else {
device_puts(va_arg(arg_ptr,char *));
}
flag=0;
} else {
device_putchar(format1);
flag=0;
}
if (radix) {
switch (flag&6) {
case 0: /* unsig int */
value=(unsigned int)va_arg(arg_ptr,int);
break;
case 2: /* unsig long int */
value=va_arg(arg_ptr,long int);
break;
case 4: /* sig int */
value=(long int)va_arg(arg_ptr,int);
if ((long int)value<0) {
value=-(long int)value;
sign='-';
}
break;
case 6: /* sig long int */
value=va_arg(arg_ptr,long int);
if ((long int)value<0) {
value=-(long int)value;
sign='-';
}
break;
default:
break;
}
while (value) {
*ptr--=hexstring[value%radix+shift];
cntr++;
value/=radix;
}
if (cntr==0) {
*ptr--='0';
cntr++;
}
if (fill==' ') {
if (sign=='-') {
*ptr--='-';
cntr++;
}
if (!minus) for (i=cntr;i<digit;i++) device_putchar(' ');
device_puts(++ptr);
if (minus) for (i=cntr;i<digit;i++) device_putchar(' ');
} else {
for (;cntr<digit-1;cntr++) *ptr--='0';
if (sign!='-'&&cntr<digit) *ptr--='0';
else if (sign=='-') *ptr--='-';
device_puts(++ptr);
}
flag=0;
}
}
format++;
}
/*va_end(arg_ptr);*/
}void SCI0_printf(char *format,...)
{
va_list arg;
va_start(arg, format);
Device_printf(SCI0device, format, arg);
va_end(arg);
}void SCI1_printf(char *format,...)
{
va_list arg;
va_start(arg, format);
Device_printf(SCI1device, format, arg);
va_end(arg);
}void LCD_printf(char *format,...)
{
va_list arg;
va_start(arg, format);
Device_printf(LCDdevice, format, arg);
va_end(arg);
}
/* ------------------------------------------------- */
/* LED INITIALIZATION */
/* ------------------------------------------------- */
/**********************************************************
LED 0:P5-0
LED 1:P5-1
LED 0:P5-2
LED 1:P5-3
**********************************************************/
void initLed()
{
P5DDR |= 0xf;
P5.DDR = P5DDR;
}/* ------------------------------------------------- */
/* LET LED ON */
/* ------------------------------------------------- */
/*numberは0または1*/
void turnOnLed(short int number)
{
static unsigned char mask[]={1,2,4,8};
P5.DR.BYTE |=mask[number];
}/* ------------------------------------------------- */
/* LET LED OFF */
/* ------------------------------------------------- */
/*numberは0または1*/
void turnOffLed(short int number)
{
static const unsigned char mask[]={0xfe,0xfd,0xfb,0xf7};
P5.DR.BYTE &=mask[number];
}/* ------------------------------------------------- */
/* PUSH SW INITIALIZATION */
/* ------------------------------------------------- */
/**********************************************************
押しボタンスイッチS0:P4-4
押しボタンスイッチS1:P4-5
押しボタンスイッチS2:P4-6
押しボタンスイッチS3:P4-7
**********************************************************/
void initPushSW(void)
{
P4DDR &= 0xf; /*P4-4,5,6,7は入力*/
P4.DDR=P4DDR;
P4.PCR.BYTE|=0xf0; /*P4-4,5,6,7はプルアップ */
}/* ------------------------------------------------- */
/* GET PUSH SW */
/* ------------------------------------------------- */
/*push swの状態をそのまま返す
short int getPushSW(void)
{
return (((unsigned char)(~P4.DR.BYTE))&0xf0);
}
*/
#define getPushSW() (((unsigned char)(~P4.DR.BYTE))&0xf0)short int checkPushSW(short int number)
/*push sw 0,1,2,3の状態を調べる number:0,1,2,or 3*/
/*押されていたら1、そうでなかったら0を返す*/
{
short int ret;
static const unsigned char mask[]={0x10,0x20,0x40,0x80};
if (P4.DR.BYTE&mask[number]) ret=0;
else ret=1;
return ret;
}/* ------------------------------------------------- */
/* PUSH 8 BIT SW INITIALIZATION */
/* ------------------------------------------------- */
void init8BitSW(void)
{
P2DDR &= 0x00;/*8bitSWのポートを入力に設定*/
P2.DDR = P2DDR;
P2.PCR.BYTE = 0xff;/*8bitSWのプルアップ設定*/
}/*8bitswの状態をそのまま返す
short int get8BitSW(void)
{
return (unsigned char)(~P2.DR.BYTE);
}
*/
#define get8BitSW() (unsigned char)(~P2.DR.BYTE)short int check8BitSW(short int number)
/*8bitsw 0,1,2,3,4,5,6,7の状態を調べる number:0,1,2,3,4,5,6,or 7*/
/*ONなら1、そうでなかったら0を返す*/
{
short int ret;
static const unsigned char mask[]={1,2,4,8,0x10,0x20,0x40,0x80};
if (P2.DR.BYTE&mask[number]) ret=0;
else ret=1;
return ret;
}/*タイマ割り込みは笠井君(1998),越智君(2001)による開発です*/
/* ------------------------------------------------- */
/* TIMER INITIALIZATION */
/* ------------------------------------------------- */
void initTimer1Int(unsigned short int period)
/*ITU1による割り込みタイマーの設定(越智君2001による)*/
/*割り込み間隔は引数peiodで単位はμsecである*/
/*値は20971以下でなければならない*/
/*20.971msecまで*/
{
unsigned int period25=(unsigned int)((25*(long int)period+4)>>3);
ITU1.TCR.BIT.CCLR=1; /*GRAのコンペアマッチでTCNTをクリア*/
ITU1.TCR.BIT.CKEG=0; /*立ち上がりエッジでカウント*/
ITU1.TCR.BIT.TPSC=3; /*内部クロックφ/8でカウント*/
ITU1.GRA=period25-1; /*割り込みの周期をperiod[μs]に指定*/
ITU1.TIER.BIT.IMIEA=1; /*TCNT=GRAとなったときの割り込み要求を許可*/
ITU1.TIER.BIT.OVIE=0; /*オーバー・アンダーフロー発生時の割り込みを禁止*/
ITU1.TIER.BIT.IMIEB=0; /*TCNT=GRBとなったときの割り込みを禁止*/
}void initTimer01Int(unsigned short int period)
/*ITU0とITU1による割り込みタイマーの設定(笠井君(1998)による)*/
/*割り込み間隔は引数peiodで単位はmsecである*/
/*値は65535以下でなければならない*/
/*65.535secまで*/
/*ただしポートPAの第3ビットが使用できなくなるので注意*/
{
ITU0.TCR.BYTE = 0xc3; /* タイマー CH0 GRBでクリア、内部1/8クロック */
ITU0.TIOR.BYTE = 0xb8; /* タイマー CH0 GRBのコンペアマッチでトグル出力 */
ITU0.GRB = 3125-1; /* タイマー CH0 ((25/8)MHz)/1000Hz=3125 */
ITU1.TCR.BYTE = 0xb7; /* タイマー CH1 GRAでクリア、TCLKDをカウントする */
ITU1.GRA = period-1; /* タイマー CH1 1000 = 1SEC */
ITU1.TIER.BYTE = 0xf9; /* タイマー CH1 GRAで割り込みする */
}/* ------------------------------------------------- */
/* TIMER START */
/* ------------------------------------------------- */
/*
Timer CH0 CH1 同時スタート
void startTimer01(void)
{
ITU.TSTR.BYTE |= 0x03;
}
Timer CH1 スタート
void startTimer1(void)
{
ITU.TSTR.BYTE |= 0x02;
}Timer CH0 CH1 同時ストップ
void stopTimer01(void)
{
ITU.TSTR.BYTE &= ~0x03;
}
Timer CH1 ストップ
void stopTimer1(void)
{
ITU.TSTR.BYTE &= ~0x02;
}
*/
#define startTimer01() (ITU.TSTR.BYTE |= 0x03) /* Timer CH0 CH1 同時スタート */
#define startTimer1() (ITU.TSTR.BYTE |= 0x02) /* Timer CH1 スタート */
#define stopTimer01() (ITU.TSTR.BYTE &= ~0x03) /* Timer CH0 CH1 同時ストップ */
#define stopTimer1() (ITU.TSTR.BYTE &= ~0x02) /* Timer CH1 ストップ */