AKI-
H8(HitachiH8/3048F-ONE)の使い方
マザーボードSWとLEDによるプログラミング
Copyright(C) 31Jan2015
Copyright(C) 15Dec2001
coskx
1. はじめに
1.1 この文書の構成
この文書はH8/3048F-ONEのCプログラムをコンパイルする方法,AKI-H8
/3048foneにフラッシュメモリ書き込み(転送)する方法を修得した学習者が,AKI-H8/3048F-ONEのマザーボードを用いてLEDをつけたりスイッチを取り込んだりするプログラムを学習するための文書である。
TNCTマザーボード上のH8/3048FONE |
テンプレートフォルダなどのダウンロード
DownLoad |
1.2 AKI-H8(HitachiH8/3048F-ONE)
AKI-H8/3048F-ONE(図1)は日立製作所の製品であるマイクロコンピュータ
H8製品群のH8/3048foneを用いて,秋月電子通商がCPUボード,マザーボードを製作販売している製品の商品名である。
マザーボードはTNCT特製基板である。
マザーボード上には次の要素が搭載されている
(1) フラッシュメモリ(ROM)書き込み回路(CPUカードの裏側に隠れているので見えない)
(2) LCD
(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 TNCTマザーボード |
図2 AKI-H8 TNCTマザーボード下部拡大 |
2.PCとマイコンのシリアル通信とビット演算
2.1 マイコンの足し算プログラム
プログラム書き込み用通信回線(シリアル通信ケーブル)はそのままプログラム実行時にも使える。
マイコンにはPCのような大きなディスプレイがついていないため,表示出力は,通信を使ってPCにあるターミナルに対して行う。
シリアル通信ユニットを使うと,マイコンが文字列を送信すると,PC側のターミナル上に表示され,PCのキーボードから与えた
文字列がマイコンに送られる。ターミナルソフトとしてTeraTerm(テラターム)を使用する。
(テラタームでは,通信の設定は,Async, 8bit, NoParity, stop1
,38400baud, (Backspace:Ctrl+H, Space, Ctrl+H)で設定されている。)
テラタームを見ている限りは,PCのみで動作しているように感ずるが,実際の作業はマイコンが行っており,PCはデータの出入り口にすぎない。最初のプログラムは,2つの整数値を与えて,その和を計算させるものである。
手順で気を付けるのは,コンパイル+プログラム転送が終ってからテラタームを立ち上げ,マイコ
ンをRUNモードにして電源を入れることである。再び,プログラムを転送する際は,テラタームは通信しないようにしてからプログラムの転送を行
う。(テラタームを起動したままでは,新しいプログラムの書き込みができない)
どうして,排他的に作業をするかというと,通信線は1本しかなく,プログラム転送プログラムとテラタームが1本の通信線を奪い合うからである。
addition.c 加算プログラム
/**********************************************************
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
main()
{
int x,y,z;
initSCI1(); /*シリアル通信ユニット SCI-ch1の初期化(SCI1_printfを使うために必要)*/
SCI1_printf("Hello. How are you?\n"); /*printfと同様*/
SCI1_printf("Let's make an arithmetic addition\n");
while (1) {
x=SCI1_getInt("the first number = ");
y=SCI1_getInt("the second number = ");
z=x+y;
SCI1_printf("x=%d y=%d z=x+y=%d\n",x,y,z);
}
}実行結果(テラタームのターミナル画面) Hello. How are you?
Let's make an arithmetic addition
the first number = 12
the second number = 56
x=12 y=56 z=x+y=68
the first number = 100
the second number = -50
x=100 y=-50 z=x+y=50
the first number =SCI通信の時PC側の「Enterキー」入力では,テンキー部ではなく,文字キーの右側にある大きなenter-keyを使うこと。
テンキーのすべてが使えるわけではないことに注意。
マイコンプログラムの説明
・シリアル通信を行う際は,マイコンプログラムの先頭で,シリアル通信ユニットを初期化しな
ければならない。
・SCI1_printfはシリアル通信ユニットch1に対するprintfである。PCと通信ができるようになっていて,
PC上でターミナルソフトが動いていれば文字列がPCのターミナル上に表示される。
通常のCプログラムで使うprintfと異なる仕様が2つある。
(1)浮動小数点数の表示はできない(ソフトの軽量化のため)
(2)2進法表示ができる(書式で,%dではなく%bにすると2進法表示になる。)
・「while(1) {」というのは無限ループを表わす。
while文やif文でかっこの中の条件を書くところに,数値がある場合
0は偽,それ以外は真を表わす。よって,「while(2) {」でも「while(100) {」でも無限ルー
プになる。
・SCI1_getIntはScanfをint型専用にしたもので,シリアル通信から数値を表わす文字列を受け取り,
整数型変数に保存する。
その際,引数として与えた文字列をターミナルに送信する。
・詳細はh8_3048fone.hの最初の部分をエディタで開いて調べておくこと。
また,この中にSCI1_printfの関数本体も入っているので,興味があったら見ておくこと。
・#include+ファイル名というのは,#include文の位置に,ファイル名で指定されたファイルの
中身が書いてあるものとしてコンパイルせよという意味である。
2.2 ビット演算
マイコン特有のビット演算に慣れておこう。後にビット入出力ではよく使うことになる。
ビット演算は小坂の「Cプログラミング入門」の「B.演算子」の「B.2 C言語におけるビット演算子」に解説されている。
リンク
SCI_printfは%bによって2進法で表示するように拡張されているので助かる。
bitoperation.c bit演算プログラム
/**********************************************************
ビット演算
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"main()
{
short int x,y,z;
initSCI1(); /*シリアル通信ユニット SCI-ch1の初期化*/
SCI1_printf("Hello. How are you?\n"); /*printfと同様*/
SCI1_printf("Let's make an bit operation\n");
while (1) {
x=SCI1_getInt("the first number = ");
y=SCI1_getInt("the second number = ");
z=x&y;
SCI1_printf("x=%b y=%b z=x&y=%b\n",x,y,z);
z=x|y;
SCI1_printf("x=%b y=%b z=x|y=%b\n",x,y,z);
z=x^y;
SCI1_printf("x=%b y=%b z=x^y=%b\n",x,y,z);
z=x>>3;
SCI1_printf("x=%b z=x>>3=%b\n",x,z);
z=y<<3;
SCI1_printf("y=%b z=y<<3=%b\n",y,z);
}
}実行結果(テラタームのターミナル画面) Hello. How are you?
Let's make an bit operation
the first number = 12
the second number = 65
x=1100 y=1000001 z=x&y=0
x=1100 y=1000001 z=x|y=1001101
x=1100 y=1000001 z=x^y=1001101
x=1100 z=x>>3=1
y=1000001 z=y<<3=1000001000
the first number = 54
the second number = 29
x=110110 y=11101 z=x&y=10100
x=110110 y=11101 z=x|y=111111
x=110110 y=11101 z=x^y=101011
x=110110 z=x>>3=110
y=11101 z=y<<3=11101000
the first number =
補足説明 「x=SCI1_getInt("the first number = ")」について
この関数は "the first number = " をターミナル画面上に表示して,人間からの入力待ちになる。
人間がターミナル画面を通じてキーボードから整数値入力を行うと,関数から戻って,
int型変数xに入力された整数値を代入する。
次のプログラムは,与えられたshort int型の値を2進法表示した時に,有効な0の数を数える関数のテストである。
ただし,16ビット変数の上位に埋められている0は数えない。
countzeros.c bit演算プログラム例
/**********************************************************
short int 型の変数を2進法表現で見たとき,有効桁数内にある0
の個数を数える関数int countZeros(short int value)のテスト
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"/* short int 型の変数を2進法表現で見たとき,/
/ 有効桁数内にある0の個数を数える関数 */
int countZeros(short int value)
{
int count=0;
while (value!=0) {
count+=1-(value&1);
value=((unsigned short int)value)>>1;
/*unsignedにみなさないと,valueが負の数のとき,
最上位ビットが保たれてしまい,ループから抜け出
せなくなる*/
}
return count;
}main()
{
short int x,y;
initSCI1(); /*シリアル通信ユニット SCI-ch1の初期化*/
SCI1_printf("Hello. How are you?\n"); /*printfと同様*/
SCI1_printf("Let's count zeros\n");
while (1) {
x=SCI1_getInt("the number = ");
y=countZeros(x);
SCI1_printf("x=%b y=%d\n",x,y);
}
}実行結果(テラタームのターミナル画面) Hello. How are you?
Let's count zeros
the number = 100
x=1100100 y=4
the number = 1000
x=1111101000 y=4
the number = 10000
x=10011100010000 y=9
the number = -10000
x=1101100011110000 y=8
the number =
練習問題 次の作業を行ないなさい。 |
参考プログラム 正解とは限りません。自分で検証してください。
3. マイクロコンピュータのプログラムと,デジタルの0V,5Vの世界をつなぐ
マイクロコンピュータプログラムと入出力
PCでのCプログラムではメモリ上での操作が主であり,画面への表示やキーボードからの 入力においてはprintfやscanfなどの関数を呼び出して作業をしていた。原始的なマイクロコンピュータの実用プログラミングでは,関数を用いて IOポート入出力を簡単に記述できるような環境を用いる。しかし原始的なマイクロコンピュータの最初のプログラムでは,LEDを点滅させたり,ON- OFFスイッチの状態を取り込むのもすべて自分でbit操作プログラムを記述しなければならない。
マイクロコンピュータマザーボードのピン
図2に示すようにマイクロコンピュータ(CPU)はLSIと呼ばれるディジタル回路の部品の1つである,100本もの脚が付いており,それらの大部分はCPUカードからマザーボード上のピンに接続が伸びている。マザーボー ドとCPUカードをつないでいるのは,複数のコネクタであり,それぞれCN1,CN2,CN3のように名前が付いている。例えば図2ではCN3(コネクタ2)でピン番号が1から40まで付いている。 →参考1(こ のページ下部)
この表では,マザーボード上のCN3の31番ピンは,CPUの52番ピンに繋がっており, その名前はP5-0であることを示している。P5-0というのはポート5の第0ビットという意味である。CPU内部から見るとポート5というのは8ビット あり,P5-0,P5-1,P5-2,・・・,P5-7まである。
(P5はちょっと特別でCPUの外には4ビットしか見えていないのでP5-0~P5-3ま でしかこの表に出てこない)。またポートというのは出入り口のことである。
プログラムから見たポート操作
・ポートとは1バイトのメモリ
ポートはプログラム側から見ると,1つのアドレスをもつ8ビットのように扱える。H8CPUのハードウェアマ ニュアルによれば,P5は「0xfffca番地に置かれた1バイト」として扱える。これまでのCプログラムでは,あるアドレスに直接値を書き込んだり,あるアドレスのデータを読み取ったりすることはなかったが,C言語にはそのような記述ができる。
・あるアドレス(1バイト)への読み書き
例えば0xfffca番地に0x03を書き込むときには,「*(unsigned char *)0xfffca=0x03;」のように書く。これはポインタ表記を用いた記述である。キャストで変数の型を一時的に変更する方法を知っているだろう。例え ば,int型の変数xなどに対して(double)x,(double)100と記述すると,一時的にdouble型のxや100.0になる。だから, (unsigned char *)0xfffcaは,unsigned char を指すポインタとしての0xfffcaを意味し,*(unsigned char *)0xfffcaはそのポインタの指す値ということになるので,0xfffca番地に0x03を書き込むことになる。また例えば,0xfffca番地の値をunsigned char 型変数xxxに受け取るには,「xx=*(unsigned char *)0xfffca;」と書くことになる。・ポートへの読み書き
それでは,「*(unsigned char *)0xfffca=0x03;」が実行されるとどうなるかというと,0x03は2進法で表すと00000011であり第0ビット(一番右側のビット)は 1,第1ビットも1,第2ビットは0,第3ビットも0なので,それらはP5の各ビットに対応するCN3のピンに出力される。この時0 は0Vで,1は5Vで出力される。(P5-bit0:5V, P5-bit1:5V, P5-bit2:0V, P5-bit3:0V)プログラムの実行は一瞬で終わるが,これらの出力はそのまま出力され続け,別の値が出力されるまで変化しない。すなわちラッチ出力という ことになる。
また,「xx=*(unsigned char *)0xfffca;」が実行されると,どうなるかというと,P5の各ビットに対応するCN3のピンへの入力状態が変数xxに取得される。0Vが与えられ たピンは内部では0に,5Vが与えられたピンは内部では1に変換される。例えば,P5-0:5V, P5-1:5V, P5-2:0V, P5-3:0Vが与えられればxxの下位4ビットは0011となる。PC上のCプログラミングで,「printf("%02x\n", *(unsigned char *)12300);」のような記述は可能であり,コンパイルもできる。しかし,実行時にエラーを発生することがある。これはOSがメモリを管理しており, そのプログラムでアクセスできるメモリのアドレス範囲を制限しているからである。
ポートの初期化
・ポートは初期化設定しないと使えない
H8CPUでは各ポートを初期化することで入力にも出力にも使えるようになっている。
しかし,実際のプログラムでは,P5を入力に使ったり,出力に使ったりというようにはできない。
マザーボー ドを作った段階でP5を入力に使うか出力に使うか決まっている。例えばLEDを接続するように設計すれば出力として使うことになるし,スイッチを繋げば入力として使うことになる。
プログラム起動時に,P5をどちらで使うかをソフトウェア上で設定しなければならない。
その設定はハードウェアマニュアルによれば,アドレス0xfffc8の1バイトを使う。0xfffc8の各ビットごとに0,1を設定 するが,0を設定すると,対応する0xfffcaのビットは入力になり,1を設定すると,対応する0xfffcaのビットは出力とし て使得ることになる。例えば,0xfffc8に0x0a(二進表記で00001010)を書き込むと,0xfffcaの第1,第3 ビットが出力で,残りは入力として使うように設定したことになる。
(なぜ初期化しないと使えないような設計になっているんだろう。CPUは製造段階ではポートを入出力どちらかに決めておかない方が汎用性が高くなる。同じCPUでは多くのニーズに合わせることができるため,このような設計になっている)H8/3048foneハードウェアマニュアルの「9.6 ポート5,モード7」参照
http://tnct20.tokyo-ct.ac.jp/~kosaka/for_students/H8/j602093_h83048.pdf
図3のマザーボードではP5の第0ビットと第1ビット(下位2ビットとも言う)にLEDが接続されている。
そうすると次のようなプログラムで,LEDの点滅ができることになる。
primitiveLED.c
void waitmsec(int msec)
/*msec間なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<4190;j++); /*4190は実測によって求めた値 25MHz駆動*/
}
}main()
{
/* P5の下位2ビットを出力に設定 */
/* P5の入出力設定部の下位2ビットに1を与えるとこの設定になる*/
*(unsigned char *)0xfffc8 = 0x3; /*0x3 は 00000011(二進法表記)*/while(1) {/*これは無限ループ*/
/*P5の下位2ビットに01(二進法表記)を出力する*/
*(unsigned char *)0xfffca = 0x01; /*0x01 は 00000001(二進法表記)*/waitmsec(1000);/*1000msecの間なにもしない*/
/*P5の下位2ビットに10(二進法表記)を出力する*/
*(unsigned char *)0xfffca = 0x02; /*0x02 は 00000010(二進法表記)*/waitmsec(1000);
}
}
このプログラムは直接変更したいポートのアドレスを使って,値を書き込んで操作している。しかしポート操作に関しては。これから学ぶプロ
グラムでは便利な関数群を与えるため,このようなプログラムは書かず,ポートのアドレスのことは意識しないことが多い。しかしこの部分はポー
ト操作の本質であるため,課題の一部や試験では,ここで述べたことが必要になることがある。
ポート5を表わすのに,具体的な番地を記述すると,読みにくいしわかりにくいプログラムになってしまうため,記述方法に工夫をして,次の節で
とりあげるような記述をするが,コンパイラにより機械語に翻訳されるとほぼ同じになる。
練習問題 次の作業を行ないなさい。 |
参考プログラム 正解とは限りません。自分で検証してください。
4. LEDの点滅のプログラム
図3のLEDが対象となるLEDである。
(図1,2の写真では,LEDが4つついているが, 右側の2つが,P5-0,P5-1につながっている。 左側の2つはP5-2,P5-3につながっており, ここでは使わない。 左側の2つは実装されていなくても問題ない。) |
|
図3 2つのLED |
CN3-31,32を探そう |
4.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はわずかに仕様違反となっている。
4.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)具体的なプログラミングの考え方
4.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)
(4)この構造体の定義はファイル「3048fone.h」にあるが,このファイルはH8CPU用コンパイラフォルダ中にある。
PCのファイル検索で見つけることができる。
P5はDDRとDRの2つの8ビットのレジスタで構成されている
さらにDRはBYTEとBITの2つの方法で書き込みが可能である。参考図 レジスタP5の構造
構造体に関するさ らに詳細な説明
勘どころ
ポート5のbit0とbit1にLEDをつけたのはマザーボードの 設計者が決めたことなので,
回路がそのようにできている。
すでに取り付けられているLEDを別のポートを介して点滅させることは,ソフトウェア
の変更だけではできない。
別のポートを介してLEDを点滅させるには,そのポートがどの端子とつながっている
のか調べて,マザーボードの設計変更が必要になる。
ミニ知識
msec(ミリセカンド)はミリ秒のことである。ミリとは1/1000のことで,1mm(ミリメートル)は1/1000m(メートル),
1mg(ミリグラム)は1/1000g,1ml(ミリリットル)は1/1000l(リットル)のように使われている。
本来ミリセカンドはmsと表記すべきものであるが,わかりやすさのため,msecと表記している。
4.4 実際のプログラム
led1st.c
/* waitmsec関数で1秒ごとのLEDのON-OFFを行う */
#include <3048fone.h>
void waitmsec(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;
waitmsec(1000);/*1000msecの間なにもしない*/
/*LED0をOFFにする P5のDRの第0ビットを0にする*/
P5.DR.BIT.B0=0;
/*LED1をONにする P5のDRの第1ビットを1にする*/
P5.DR.BIT.B1=1;
waitmsec(1000);
}
}
実用プログラムでは,ポートの記述はできるだけ隠すようにしている。h8_3048fone.hはマザーボード上のハードウェア操作のプログラミングを関数呼び出しで行なえるようにしたものである。関数呼び
出しを利用すると,どこのポートのどのビットをどうするということを考えずに済み,専用コマンドを使うような感覚でプログラミングでき,
便利である。
もう1つ重要なことがある。将来別のCPUを使うことになって,このプログラムを移植することになった場合を想定しよう。全体の動作を記述するプロ
グラムと,CPUのハードウェアを操作するプログラムを分離することにより,移植性が高くなることが想像できる。
関数の定義はh8_3048fone.h中にある。本Webページの末尾に「h8_3048fone.h」があるが,その先頭部分には関数の説明がある。
led2nd.c
#include <3048fone.h>
#include "h8_3048fone.h"
void waitmsec(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*/
waitmsec(1000);
turnOffLed(0); /*LED0のOFF*/
turnOnLed(1); /*LED1のON*/
waitmsec(1000);
}
}
練習問題 led2nd.cを元にして次のプログラムを作りなさい。 (1)周期1秒,デューティ比50%で両方のLEDが同時に点滅するプログラム(mp3ex01.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 waitmsec(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*/
waitmsec(9);
turnOffLed(1); /*LED1のOFF*/
waitmsec(1);
}
for (i=0;i<300;i++) { /*ループ3秒間ループ デューティ比50%*/
turnOnLed(1); /*LED1のON*/
waitmsec(5);
turnOffLed(1); /*LED1のOFF*/
waitmsec(5);
}
for (i=0;i<300;i++) { /*ループ3秒間ループ デューティ比10%*/
turnOnLed(1); /*LED1のON*/
waitmsec(1);
turnOffLed(1); /*LED1のOFF*/
waitmsec(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*/
}
}
}
}
ここまでで,LEDを明るくつけたり暗くつけたりすることを学んだ。同じ原理で豆電球,電熱線,モータなどを制御することができる。
小坂のweb文書「h8CPU_Output.html」を参考にしなさい。
練習問題 次の作業を行ないなさい。
(mp3ex07.txt) |
図3の8ビットスイッチの読み取りを行い,LEDを制御する。
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
5.1 8ビットSWでLED駆動
8ビットスイッチのON-OFFの状態によってLEDの
ON-OFFを制御するプログラムを作成する。
LED駆動部のみh8_3048fone.hを用いてプログラムを作る。
8ビットスイッチの各端子はH8内部でプルアップされる設定なので,スイッチがONになるとポート2の対応するビットは0になる。
OFFになると1になる。
「8bitSWの0(ポート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の0がONの時*/
turnOnLed(0);
turnOnLed(1);
} else if (P2.DR.BIT.B1==0) { /*8bitSWの1がONの時*/
turnOnLed(0);
turnOffLed(1);
} else if (P2.DR.BIT.B2==0) { /*8bitSWの2が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となる。
参考 P2をアドレスでみると,P2.DDRは0xfffc1,P2.DRは
0xfffc3,P2.PCRは0xfffd8の1バイトということになる。 P2.DR.BIT.B2のチェック
よって,P2表記との対応を見ると次のようになる。
意味
P2表記
アドレス直接表記
P2.DRを入力設定
P2.DDR=0;
*(unsigned char *)0xfffc1=0;
P2.DRの全ビットプルアップ設定
P2.RCR=0xff;
*(unsigned char *)0xfffd8=0xff;
if (P2.DR.BIT.B2==0) {
if ((*(unsigned char *)0xfffc3 & 4)==0) {
5.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の0がONの時*/
turnOnLed(0);
turnOnLed(1);
} else if (check8BitSW(1)) { /*8bitSWの1がONの時*/
turnOnLed(0);
turnOffLed(1);
} else if (check8BitSW(2)) { /*8bitSWの2がONの時*/
turnOffLed(0);
turnOnLed(1);
} else {
turnOffLed(0);
turnOffLed(1);
}
}
}C言語では,if文のかっこの中に値があるときは,0なら偽,それ以外の値なら真と判断される。
check8BitSW()は対象のスイッチがONのとき1,OFFのとき0なので,その値で真偽判断すればよい。
当然 if(check8BitSW(0)==1)と if(check8BitSW(0)!=0),if(check8BitSW(0))は同じ働きとなる。
5.3 8ビットSWが入力されているポートの各ビットを見てみよう(「h8_3048fone.h」の利用)
直接P2の各ビットを見てみよう。またh8_3048fone.hに定義されているget8BitSW()では,ビッ
トパタンでスイッチの様子を
見ることができるが,ONのスイッチは1,OFFのスイッチは0になるように工夫されている。
print8bitswstatus.c
/**********************************************************
マザーボードの8ビットスイッチがつながっているP2の各ビットを
直接見てみよう。
また,関数get8BitSW()が得る値を見てみよう。
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"main()
{
unsigned char sw1,sw2;
initLed();
initSCI1();
init8BitSW();/*8bitSWの初期化*/
while(1) {
sw1=P2.DR.BYTE;
sw2=get8BitSW();
SCI1_printf("P2.DR.BYTE=%08b get8BitSW()=%08b\n",sw1,sw2);
}
}
練習問題 eightsw.cを元にして次のプログラム
を作りなさい。 |
練習問題
|
6. プッ シュスイッチ
図 4 プッシュスイッチ プッ シュスイッチはポート4の第4ビットから第7ビットにつながっている。 |
PushSWでLED駆動(「h8_3048fone.h」の利用)
図4の プッシュスイッチの読み取りを行い,LEDを制御する。
プッシュスイッチ0を押すとLED0が点灯し,プッシュスイッチ1を押すとLED1が点灯するプログラムとする。
pushsw.c
/**********************************************************
プッシュスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
main()
{
initLed();
initPushSW();/*PushSWの初期化*/
while(1) {
if (checkPushSW(0)==1) { /*PushSWの0がONの時*/
turnOnLed(0);
turnOffLed(1);
} else if (checkPushSW(1)==1) { /*PushSWの1がONの時*/
turnOffLed(0);
turnOnLed(1);
} else {
turnOffLed(0);
turnOffLed(1);
}
}
}
練習問題 pushsw.cを元にして次のプログラムを作りなさい。 (2)プッシュスイッチの0のみがONの時,0.5秒周期で2つのLED
を同時点滅, (3)プッシュスイッチ0を1回押すごとに,LEDの点灯,消灯の切り替えが起こり,
|
通常の関数は,関数がプログラム中の他の関数から呼び出されたときに作業を行なう。これに対して,割り込み関数はハードウェアの何らかの割り込み要因によって呼び出される関数である。
インターバルタイマ割り込み関数は,タイマ割り込み初期設定によって設定された時間間隔で起動する割り込み関数である。
「7.1」のプログラムでは,10000μs(μsは1秒の1/1000の1/1000)(10000μs=10ms=0.01s)のタイマ割り込み初期設定が行なわれ,CPUの割り込み許可がなされ,タイマがスタートした後,mainプログラムの流れは
while(1);
となり,何もしない無限ループに突入する。しかし,10ms(0.01秒)ごとにタイマ割り込み関数
「interrupt_cfunc()」が起動し,割り込み関数に記述されているように LEDのON-OFFが継続して行なわれる。
ロボットの制御には一定時間ごとに起動する定時間割り込み(タイマー割り込み)が良く用いられる。またPWMの生成にもこの定時間割り込み
が用いられる場合が
関数void msecwait(int msec)を用いた時間管理よりはるかに正確な時間管理ができる。(CPUクロックの精度依存)
7.1 割り込みでLED駆動(インターバルタイマによるタイマ割り込み)
タイマ割り込みを使って2秒周期デューティ比50%で2つのLEDを交互に点滅させるプログラムを作る。
プログラム中main()側で10000μsec(0.01秒)間隔でタイマ割り込みを設定する。
main()関数内でなにもしないループ動作をしている最中に,割り込み関数interrupt_cfunc()は0.01秒ごとに起動
し,
割り込み関数が起動して,100回に99回は何もせず,残りの1回はLEDのON-OFFを行なう。
このタイマ割り込みはITUのch1が使われている。
割り込みを実現するには次のことが必要になる。
(1)割り込みベクトルの設定(割り込みが発生した時に起動すべき関数の開始アドレスを所定の領域に書いておく)
(2)割り込み要因となる機能の初期化
(3)CPUの割り込み許可
(4)割り込み関数は割り込み関数として定義され,レジスタの退避復帰,割り込みリターンなどの特殊な作り方が必要
これらの必要事項は次のように記述される。
(1)アセンブラでしか書くことが出来ないのでインラインアセンブリで記述
(インラインアセンブリ : C言語プログラム中にアセンブリ言語を記述すること)
タイマ割り込みの関数のアドレスは0x70に書くことになっている。(割り込みベクタテーブル領域)
アセンブラから見るとCの関数「TimerIntFunc」の名前は「_TimerIntFunc」に見える。
関数の名前は関数の先頭アドレスを意味するので0x70の番地に関数の開始アドレスを書き込んだことになる。
#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"
main()
{
initLed();
initTimer1Int(10000); /*時間割り込み10000μsec=10msec ITUch1使用*/
E_INT(); /*CPU割り込み許可*/
startTimer1(); /*時間割り込みタイマスタート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;
static int count=0;
clearTimer1Flag(); /*タイマステータスフラグのクリア 忘れないこと*/
count++;
if (count==100) {
count=0;
if (tick==1) {
turnOnLed(0);
turnOffLed(1);
} else {
turnOffLed(0);
turnOnLed(1);
}
tick=1-tick;
}
}
TimerIntFunc()は0.01秒ごとに起動する関数である。
static int count はこの関数が起動する毎に1ずつ増え,100になったときに
countを0にするため, if (count==100)のif文の範囲は100回に1回のみ実行される。
すなわち if (count==100)のif文の範囲は1秒に1回しか実行されない。
このif文中に入るたびに,tickは0と1を繰り返している。(tick=1-tick)
そうすると,1秒に1回LEDの点灯・消灯を行うため,
LEDは2秒周期,デューティ比50%で点滅することになる。
ここではアセンブリ言語には踏み込まないが簡単に解説
(深入りはしない。後にアセンブリ言語を学んだ後に読み返せばよい)
#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 0 → LED 0 明るく点灯(10/10)
PushSW 1 → LED 0 暗く点灯(1/10)
PushSW 2 → LED 1 明るく点灯(10/10)
PushSW 3 → 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"
volatile int led0,led1;
const int period=10; /*周期10msec*/
const int low=1;
main()
{
initLed();
initPushSW();
led0=led1=0;
initTimer1Int(1000); /*タイマ割り込み1msec */
/*単位はμsec ITUch1のみ使用*/
E_INT(); /*CPU割り込み許可*/
startTimer1(); /*時間割り込みタイマスタート*/
while(1){
if (checkPushSW(0)==1) { /*PushSWの0がONの時*/
led0=period;
} else if (checkPushSW(1)==1) { /*PushSWの1がONの時*/
led0=low;
} else {
led0=0;
}
if (checkPushSW(2)==1) { /*PushSWの2がONの時*/
led1=period;
} else if (checkPushSW(3)==1) { /*PushSWの3が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を元にして次のプログラムを作りなさい。 |
参考プログラム 正解とは限りません。自分で検証してください。
8. パソコンとのシリアル通信
シリアル通信ユニットSCIを使って,マイコンの状態を表示するプログラムで,SCI関連の関数を使ってみよう。
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("8-bitSW status= %2x[%08b]\n",sw,sw);
previous=sw;
}
} while (SCI1_chkgetChar()<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 (SCI1_chkgetChar()<0);
}void func3(void)
{
short int x;
long int y;
x=SCI1_getInt("Key in a decimal number >>>");
SCI1_printf("The number you keyed in is %d %x\n",x,x);
x=SCI1_getInt("Key in a hexdecimal number (ex. 0x23ff) >>>");
SCI1_printf("The number you keyed in is %d %x\n",x,x);
y=SCI1_getInt4("Key in a decimal big number (ex. 12345678)>>>");
SCI1_printf("The number you keyed in is %ld %lx\n",y,y);
y=SCI1_getInt4("Key in a hexdecimal big number (ex. 0x23ff0000) >>>");
SCI1_printf("The number you keyed in is %ld %lx\n",y,y);
}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=SCI1_getChar(); /*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 SCI1_getChar()
SCI-ch1から1byte入力コードを得て関数の値として返す関数。通信エラーがあると-2が戻 る。
SIC-ch1入力バッファを検査し,データがあれば持ち帰るが,データがない場合はデータが来るまで待ち続け,
データが来たら,それを持ち帰ってくる
PCのキーボードが押され,SCI-ch1経由でデータが転送されてくる まで,永久に待つ関数である。
ANSIの関数getchar()と同じ動作をするshort int SCI1_chkgetChar()
SCI-ch1入力バッファを検査し,受信データがあれば1byte入力コードを得て関数の値として返す関数。
受信データガなければ-1が,通信エラーがあると-2が戻る。
SICバッファを検査し,データがあれば持ち帰るが,データがなくても -1をもってすぐに帰ってくる関数である。int get8BitSW()
8bitスイッチの状態をそのまま1バイトの値として読み込む関数。状態(00~ff)を関数の値として返す。
ただし,値は反転しており,スイッチの状態がONのビットは1,OFFのビットは0で読み込む。
int getPushSW()
プッシュスイッチの状態をそのまま1バイトの値として読み込む関数。4つのプッシュスイッチは
1バイト中で下位4ビットとして取り込まれ,状態(00~0f)を関数の値として返す。
ただし,値は反転しており,スイッチの状態がONのビットは1,OFFのビットは0で読み込む。
short int SCI1_getInt(char prompt[])
SCI-ch1からプロンプト付で,short intの値を受け取り,関数の値として返す。
(引数で与えた文字列を表示してから,short intの値を受け取る。)
正負の10進数または16進数を受け付ける。「0x」で始めまる文字列は16進数として受け取る。
long int SCI1_getInt4(char prompt[])
SCI-ch1からプロンプト付で,long intの値を受け取り,関数の値として返す。
(引数で与えた文字列を表示してから,long intの値を受け取る。)
正負の10進数または16進数を受け付ける。「0x」で始めまる文字列は16進数として受け取る。
●TeraTermの画面への表示をそのままファイル化する方法(テキストキャプチャ)
TeraTermのファイルメニューから「ログ」コマンドで,ファイル名を指定すると,その後では
TeraTermの画面への表示がそのままファイルに書きだされる。レポート作成に便利!
SCI通信の時,PC側の「Enterキー」入力では,テンキー部ではなく,文字キーの右側 にある大きなenter-keyを使うこと。
enter-keyについて
キーボードには2つのenter-keyがあるが,実は文字コードが異なる。
文字キーの右側にある大きなenter-key \r\n (0x0d,0x0a)
数字キーの右側にある小さなenter-key \r (0x0d)
である。
\r(0x0d) はコンソール上で文字ポインタを左端に戻すコード
\n(0x0a) はコンソール上で文字ポインタを次の行に進めるコード
練習課題
|
練習問題 次のプログラムを作りなさい。
(2)アドレス0x00000から0x0001FFまでのメモリを十六進ダンプするプログラムを作りなさい。
(3)指定したアドレスから
0x100byteをメモリダンプするプログラムを作りなさい。
|
「パソコンとのシリアル通信」,「タイマー割り込み」を用いた時計を作る。
timer.c
/**********************************************************
割り込みを用いた時計 起動時からの経過時間[秒]を
SCI1へ出力,WINDOWSのHyperTerminalなどで受信できる。
ただし,設定は
38400baud, Async, 8bit , NoParity, stop1
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"volatile unsigned int counter;/*0.1s単位でカウント*/
main()
{
unsigned int counter1;
unsigned int t1,t2;
initSCI1(); /*SCI-ch1の初期化*/
initTimer1Int(10000); /*ITUch1のインターバルタイマ初期化設定*/
/*単位μsなので,時間割り込み10msec*/
E_INT(); /*CPU割り込み許可*/
startTimer1(); /*ITUch1タイマスタート*/
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() /*インターバルタイマ割り込みルーチン*/
{
static int cnt=0;
if (++cnt==10) {
/*この割り込みルーチンは0.01秒ごとに起動するので /
/ 10回に1回の割合でcounter++をすれは,counterは /
/ 0.1秒にごとに1ずつ増えることにな る */
counter++;
cnt=0;
}
clearTimer1Flag(); /*ITUch1タイマフラグのクリア 忘れないこと*/
}
練習問題 int2nd.c,sciout.c,timer.cを元にして次
のプログラムを作りなさい。 (2)シリアル通信速度は"h8_3048fone.hの中の関数
initSCI1で設定されている。 |
10.独立した複数の作業の書き方
リアルタイムOSを使うと,もっと別な書き方になるが,ここではリアルタイムOSを使わない方法でプログラミングする
例題
プッシュスイッチ0を押しした瞬間から2秒間LED0が点灯し、消灯する。
プッシュスイッチ1を押しした瞬間から2秒間LED1が点灯し、消灯する。
ただし、LED0が点灯中にプッシュスイッチ0が押されたら、残り点灯時間には無関係に、その時点から新たに2秒間点灯するものとする。
同様に、LED1が点灯中にプッシュスイッチ1が押されたら、残り点灯時間には無関係に、その時点から新たに2秒間点灯するものとする。
また、LED0が点灯中にプッシュスイッチ1が押されたら、LED0の動作には影響を与えず、LED1は、その時点から2秒間点灯するも のとする。
同様に、LED1が点灯中にプッシュスイッチ0が押されたら、LED1の動作には影響を与えず、LED2は、その時点から2秒間点灯する ものとする。
このような2つの動作を独立動作と呼ぶ。
独立した2つの作業を行う
失敗作(プログラムはわかりやすいが失敗する)LED0が動作中に,LED1の動作をさ せようと思っても動作を開始しない。
LED0の動作が終了すれば,LED1の動作を開始できる。/**********************************************************
PushSW1でLED0をPushSW2でLED0を1秒間点灯させる
この2つの作業は独立に行われるはず??
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
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"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++;
}
練習問題(1) (mp9ex01.txt) プッシュスイッチ0を押した瞬間からLED0は1秒間点灯し、1秒間消灯
し、 ただし、LED0が定型動作中にプッシュスイッチ0が押されたら、残り定
型動作時間には無関係に、その時点か 練習問題(2) (mp9ex02.txt) 前課題の2つプッシュスイッチにより起動する独立動作に加え、プッシュスイッチ2によって起動するも
WindowsCプログラミングで,サンプルを作ると次のようになるので参考にするとよい。 #include <stdio.h>もしHello表示だけなら,次のようなプログラムになるであろう。 volatile int count=-1;/*カウンタ -1の時は休止中*/ |
参考プログラム 正解とは限りません。自分で検証してください。
11. LCDへの出力
h8_3048fone.hに定義されている関数を使って,LCDにpushSWの状態を表示する。
initLCD( ) LCDの初期化
LCD_gotoxy(y,x) 表示位置の設定 y:行,x:カラム
LCD_printf( ) LCD版printf
LCDout.c
/**********************************************************
LCDへ出力
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"main()
{
unsigned char sw,previous;
initPushSW();
initLCD(); /*LCDの初期化*/
LCD_gotoxy(0,0);
LCD_printf("pushSW status");
previous=sw=getPushSW();
LCD_gotoxy(0,1);
LCD_printf("%02x[%08b]",sw,sw);
while(1) {
sw=getPushSW();
if (sw!=previous) {
LCD_gotoxy(0,1);
LCD_printf("%02x[%08b]",sw,sw);
previous=sw;
}
}
}
練習問題 (mp9ex03.txt) 10の練習問題(2)(mp9ex02.txt)で提出した課題において,電光掲示板表示をLCDに表示するように変更しなさい。 |
参考1 H8ピン配置とポート割当
TNCT3048FONE・3052用 マザーボード回路図(pdf)
コネクタ1(CN1)ピン割り当て |
コネクタ2(CN2)ピン割り当て |
コネクタ3(CN3)ピン割り当て |
コネクタ4(CN4)ピン割り当て |
コネクタ5(CN5)ピン割り当て |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
使用していない。 |
|
|
使用していない。 |
|
ポート割当
参考2 h8_3048fone.h
h8_3048fone.h
/****************************************************************
h8_3048fone.h
Copyright (c) Kosaka Lab CS TNCTこのインクルードファイルは小坂研究室の代々の研究生が開発した
h8/3048用の有用な関数群を改良して小坂がまとめたものである。
06 Mar 2013 h8_3048fone.h 小坂 教材用にリファイン
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 SCI1_getChar()
SCI-ch1から1byte入力コード。エラーがあると-2が戻る。
short int SCI1_chkgetChar()
SCI-ch1を検査し,受信データがあれば1byte入力コード。なければ-1が,失敗すると -2が戻る。
int SCI1_getString(char *buff,int max)
SCI1から最大max-1文字の文字列を受け取る。(buffのサイズはmaxでよい)
short int SCI1_getInt(char prompt[])
SCI-ch1からプロンプト付で,short intの値を受け取る。
正負の10進数または16進数を受け付ける。16進数は0xで始まる
long int SCI1_getInt4(char prompt[])
SCI-ch1からプロンプト付で,long intの値を受け取る。
正負の10進数または16進数を受け付ける。16進数は0xで始まるvoid SCI1_putChar(char c)
SCI-ch1に1バイト出力する。
void SCI1_putString(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 SCI0_getChar()
SCI-ch0から1byte入力コード。エラーがあると-2が戻る。
short int SCI0_chkgetChar()
SCI-ch0を検査し,受信データがあれば1byte入力コード。なければ-1が,失敗すると -2が戻る。
int SCI0_getString(char *buff,int max)
SCI0から最大max-1文字の文字列を受け取る。(buffのサイズはmaxでよい)
short int SCI0_getInt(char prompt[])
SCI-ch0からプロンプト付で,short intの値を受け取る。
正負の10進数または16進数を受け付ける。16進数は0xで始まる
long int SCI0_getInt4(char prompt[])
SCI-ch0からプロンプト付で,long intの値を受け取る。
正負の10進数または16進数を受け付ける。16進数は0xで始まるvoid SCI0_putChar(char c)
SCI-ch0に1バイト出力する。
void SCI0_putString(char *str)
SCI-ch0に文字列を出力する。
void SCI0_printf(char *format,...)
関数printfのSCI0版
仕様はvoid SCI1_printf(char *format,...)参照【2】LCD関係
void LCD_putchar(char data)
LCDに向けた putchar()
void LCD_puts(char *str)
LCDに向けた puts()
void initLCD( void )
LCDの初期化
void LCD_gotoxy(unsigned x,unsigned y)
LCDに向けた gotoxy()
void LCD_clrscr(void)
LCDに向けた clrscr() clear screen
void LCD_printf(char *format,...)
関数printfのLCD版
仕様はvoid SCI1_printf(char *format,...)参照【3】文字列操作
void sprintf(char *buff,char *format,...)
仕様はvoid SCI1_printf(char *format,...)参照
buffのあふれは呼び出し側で起こらないようにしておく必要がある
long int atoi(char *buff)
文字列を整数型の値に変換する
正負の10進数または16進数の文字列を受け付ける。16進数は0xまたは-0xで始まる
例 "123"
"-123"
"0x1a"
"-0x100"【4】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)
押しボタンスイッチの初期化
unsigned char getPushSW(void)
押しボタンスイッチの取得 ビット反転し,ONは1,OFFは0で取得される。
押しボタンスイッチの状況は第0-第3ビットに現れる。
これはマクロ定義で実現されている
short int checkPushSW(short int number)
push sw 0,1,2,3の状態を調べる number:0,1,2,or 3
押されていたら1、そうでなかったら0を返すvoid init8BitSW(void)
8ビットスイッチの初期化
unsigned char get8BitSW(void)
8ビットスイッチの取得 ビット反転し,ONは1,OFFは0で取得される。
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を返す【5】インターバルタイマ割り込み
void initTimer1Int(unsigned short int period)
ITU1による割り込みタイマーの設定
割り込み間隔は引数peiodで単位はμsecである
値は20971以下でなければならない。20.971msecまで設定可能void startTimer1(void)
Timer CH1 スタート
これはマクロ定義で実現されている
void stopTimer1(void)
Timer CH1 ストップ
これはマクロ定義で実現されている
void clearTimer1Flag(void)
Timer CH1 割り込みフラグのクリア
これはマクロ定義で実現されている
****************************************************************/#include<stdarg.h>
extern void E_INT();
extern void D_INT();/*次の1行はMicrosoftHyperTerminal使用時に有効にする*/
/*有効な場合H8からシリアル送信時に\nを\r\nに自動変換する*/
#define HYPERTERMINAL/*システムクロック関係*/
#define SYS_CLOCK 25 /*MHz 16,20,25 程度を想定*/const int SCI0device=0;
const int SCI1device=1;
const int LCDdevice=2;
const int STRdevice= 3;/*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 = (unsigned char)((8138L*SYS_CLOCK+5000L)/10000L-1);
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 SCI1_getChar()
/* 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 SCI1_chkgetChar()
/* 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 SCI1_putString(char *str);
/*SCI1より文字列入力[return]が終端だが,'\n'は取得されない*/
/*^Hでバックスペイス*/
int SCI1_getString(char *buff,int max)
{
int i,ch;
for (i=0;i<max-1;i++) {
ch=SCI1_getChar(); /*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;
}/* ------------------------------------------------- */
/* PUT BYTE TO SCI1 */
/* ------------------------------------------------- */
void SCI1_putChar(char c)
{
unsigned char tmp;
#ifdef HYPERTERMINAL
if (c=='\n') SCI1_putChar('\r');
#endif
do{
tmp = SCI1.SSR.BYTE;
} while((tmp & 0x80)==0);
SCI1.TDR = c;
SCI1.SSR.BIT.TDRE = 0;
return;
}void SCI1_putString(char *str)
{
while(*str){
SCI1_putChar(*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 = (unsigned char)((8138L*SYS_CLOCK+5000L)/10000L-1);
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 SCI0_getChar()
/* 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 SCI0_chkgetChar()
/* 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 SCI0_putString(char *str);
/*SCI0より文字列入力[return]が終端だが,'\n'は取得されない*/
/*^Hでバックスペイス*/
int SCI0_getString(char *buff,int max)
{
int i,ch;
for (i=0;i<max-1;i++) {
ch=SCI0_getChar(); /*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;
}/*拡張atoi*/
/* 123,-123,0x1a,-0x1a の形の文字列をlong intに変換する*/
long int atoi(char *buff)
{
long int x=0;
int y,m=0,n=0,v=0,i=0;
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;
}#define SCI1_getInt(prompt) ((short int)SCI1_getInt4(prompt))
/*SCI1へプロンプトを表示して,SCI1より整数値を入力*/
long int SCI1_getInt4(char prompt[])
/*getting integer from serial port*/
/* format 123[ret] */
/* -123[ret] */
/* 0x1a[ret] */
/* -0x100[ret] */
{
char buff[16];
SCI1_putString(prompt);
SCI1_getString(buff,16);
return atoi(buff);
}#define SCI0_getInt(prompt) ((short int)SCI0_getInt4(prompt))
/*SCI0へプロンプトを表示して,SCI0より整数値を入力*/
long int SCI0_getInt4(char prompt[])
/*getting integer from serial port*/
/* format 123[ret] */
/* -123[ret] */
/* 0x1a[ret] */
/* -0x100[ret] */
{
char buff[16];
SCI0_putString(prompt);
SCI0_getString(buff,16);
return atoi(buff);
}
/* ------------------------------------------------- */
/* PUT BYTE TO SCI0 */
/* ------------------------------------------------- */
void SCI0_putChar(char c)
{
unsigned char tmp;
#ifdef HYPERTERMINAL
if (c=='\n') SCI0_putChar('\r');
#endif
do{
tmp = SCI0.SSR.BYTE;
} while((tmp & 0x80)==0);
SCI0.TDR = c;
SCI0.SSR.BIT.TDRE = 0;
return;
}void SCI0_putString(char *str)
{
while(*str){
SCI0_putChar(*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 LCD_waitmsec(unsigned int msec)
/*mesc間なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<168*SYS_CLOCK;j++);
}
}static void LCD_waitmicrosec(unsigned int microsec)
{/*だいたいmicrosec間なにもしない時間稼ぎ関数*/
int i,j;
for (i=0;i<microsec;i++) {
for (j=0;j<168*SYS_CLOCK/1000;j++);
}
}static void LCD_putCommand(char command) /*command width must be 4bits*/
{
LCD_Port=(LCD_Port&0xf0)|command;
LCD_EnableSignal=1;
LCD_waitmicrosec(5);
LCD_EnableSignal=0;
LCD_waitmicrosec(40);
}static void LCD_putCommand2(char command2)
{
LCD_putCommand((command2>>4)&0xf);
LCD_putCommand(command2&0xf);
}/************************************************************
LCDに向けた putchar()
************************************************************/
void LCD_putchar(char data)
{
LCD_RegisterSelect=1;
LCD_putCommand((data>>4)&0xf);
LCD_putCommand(data&0xf);
LCD_RegisterSelect=0;
}/************************************************************
LCDに向けた puts()
************************************************************/
void LCD_puts(char *str)
{
while(*str) LCD_putchar(*str++);
}/************************************************************
LCDの初期化
************************************************************/
void initLCD( void )
{
init_LCD_Port(); /* output */
LCD_Port&=0xc0;
LCD_waitmsec(30); /* wait 30ms */
LCD_putCommand(0x3); /* function set */
LCD_waitmicrosec(4100);
LCD_putCommand(0x3); /* function set */
LCD_waitmicrosec(100);
LCD_putCommand(0x3); /* function set */
LCD_waitmicrosec(100);
LCD_putCommand(0x2); /* function set data width=4bit*/
LCD_putCommand2(0x28); /* function set 4bit duty:1/16,size:5*7 */
LCD_putCommand2(0x0c); /* display on,cursor off,blink off */
LCD_putCommand2(0x06); /* address:auto increment,cursor shift:right */
LCD_putCommand2(0x01); /* clear display */
LCD_waitmicrosec(1640);
}/************************************************************
LCDに向けた gotoxy()
************************************************************/void LCD_gotoxy(unsigned x,unsigned y)
{
unsigned char point;
point=0x80+x+0x40*y;
LCD_putCommand2(point);
}/************************************************************
LCDに向けた clrscr() clear screen
************************************************************/
void LCD_clrscr(void)
{
LCD_putCommand2(0x01); /* clear display */
LCD_waitmicrosec(1640);
}static char *currentstrptr;
static void STR_putChar(char ch)
{
*currentstrptr++=ch;
*currentstrptr=0;
}static void STR_putString(char *ptr)
{
while(*ptr) STR_putChar(*ptr++);
}
const char hexstring[]="0123456789abcdef0123456789ABCDEF";
#define MAXDIGIT 34void 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=SCI0_putChar;
device_puts=SCI0_putString;
} else if (device==SCI1device) {
device_putchar=SCI1_putChar;
device_puts=SCI1_putString;
} else if (device==LCDdevice) {
device_putchar=LCD_putchar;
device_puts=LCD_puts;
} else { /*device==STRdevice*/
device_putchar=STR_putChar;
device_puts=STR_putString;
}
/*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);
}void sprintf(char *buff,char *format,...)
{
va_list arg;
va_start(arg, format);
currentstrptr=buff;
Device_printf(STRdevice, format, arg);
va_end(arg);
}/* ------------------------------------------------- */
/* LED INITIALIZATION */
/* ------------------------------------------------- */
/**********************************************************
LED 0:P5-0
LED 1:P5-1
LED 2:P5-2
LED 3:P5-3
下位4ビットを出力にする
**********************************************************/
void initLed()
{
P5.DDR = 0xf;
}/* ------------------------------------------------- */
/* LET LED ON */
/* ------------------------------------------------- */
/*numberは0または1*/
void turnOnLed(short int number)
{
static const 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
上位4ビットを入力にする.下位4ビットも一応入力にする
上位4ビットのプルアップコントロールをONにする
**********************************************************/
void initPushSW(void)
{
P4.DDR = 0;
P4.PCR.BYTE = 0xf0; /*P4-4,5,6,7はプルアップ */
}/* ------------------------------------------------- */
/* GET PUSH SW */
/* ------------------------------------------------- */
/*push swのポートを取得し,反転し,右4シフトして返す
unsigned char getPushSW(void)
{
return ((((unsigned char)(~P4.DR.BYTE))&0xf0)>>4);
}
*/
#define getPushSW() ((((unsigned char)(~P4.DR.BYTE))&0xf0)>>4)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 */
/* ------------------------------------------------- */
/**********************************************************
8ビットスイッチS0:P2-0
8ビットスイッチS1:P2-1
8ビットスイッチS2:P2-2
8ビットスイッチS3:P2-3
8ビットスイッチS4:P2-4
8ビットスイッチS5:P2-5
8ビットスイッチS6:P2-6
8ビットスイッチS7:P2-7
全8ビットを入力にする.
全8ビットのプルアップコントロールをONにする
**********************************************************/
void init8BitSW(void)
{
P2.DDR = 0;
P2.PCR.BYTE = 0xff;/*8bitSWのプルアップ設定*/
}/*8bitswのポートを取得し,反転して返す
unsigned char 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まで*/
/*割り込みベクトル H'000070 ;IMIA1*/
{
unsigned int periodGRA=(unsigned int)((SYS_CLOCK*(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=periodGRA-1; /*割り込みの周期をperiod[μs]に指定*/
ITU1.TIER.BIT.IMIEA=1; /*TCNT=GRAとなったときの割り込み要求を許可*/
ITU1.TIER.BIT.OVIE=0; /*オーバー・アンダーフロー発生時の割り込みを禁止*/
ITU1.TIER.BIT.IMIEB=0; /*TCNT=GRBとなったときの割り込みを禁止*/
}/* ------------------------------------------------- */
/* TIMER START */
/* ------------------------------------------------- */
/*
Timer CH1 スタート
void startTimer1(void)
{
ITU.TSTR.BYTE |= 0x02;
}Timer CH1 ストップ
void stopTimer1(void)
{
ITU.TSTR.BYTE &= ~0x02;
}Timer CH1 割り込みフラグのクリア
void clearTimer1Flag(void)
{
ITU1.TSR.BIT.IMFA=0;
}*/
#define startTimer1() (ITU.TSTR.BYTE |= 0x02) /* Timer CH1 スタート */
#define stopTimer1() (ITU.TSTR.BYTE &= ~0x02) /* Timer CH1 ストップ */
#define clearTimer1Flag() (ITU1.TSR.BIT.IMFA=0) /* Timer CH1 flagクリア */