AKI-H8(H8/3069)の使い方
マザーボード上のSWとLEDによるプログラミング
Copyright(C) 8Oct2009
Copyright(C) 15Dec2001
coskx
0. はじめに
0.1 この文書の構成
この文書はH8/3069のCプログラムをコンパイルする方法,AKI-H8/3069にフラッシュメモリ書き込み(転送)する方法を修得した学習者が,AKI-H8/3069のマザーボードを用いてLEDをつけたりスイッチを取り込んだりするプログラムを学習するための文書です。
テンプレートフォルダなどのダウンロード
DownLoad |
0.2
AKI-H8(H8/3069)
AKI-H8/3069(図1)はルネサスの製品であるマイクロコンピュータH8製品群のH8/3069Fを用いて,秋月電子通商がCPUボード,マザーボードを製作販売している製品の商品名です。
マザーボード上には次の要素が搭載されています
(1)
4ビットディップスイッチ
(2) プッシュスイッチ
(3)
LED (発光ダイオード)
(4) LCD (液晶表示装置)
AKI-H8/3069でのプログラミングはパソコン上で次のように行ないます。
(1)
PC上でC言語またはアッセンブリ言語でプログラムを開発
(2)
PC上でコンパイル・リンクを行ない実行プログラムファイルを作ります
(3)
PC上でロードモジュールに変換します
(4)
RS232C通信でロードモジュールをH8マイコンに転送(フラッシュメモリに書き込む)します。
(5)
H8マイコン上でプログラムを実行します。
LEDは左から0,1 4bitDIPスイッチは左から0,1,2,3 プッシュスイッチは左から0,1,2,3,4 |
左上:Dsub9 シリアルコネクタ(SCI1) |
図0.1 AKI-H8/3069 AKI-マザーボード |
図0.2 AKI-H8/3069 LAN+CPU Card |
SCI1はCPUカード上にある。 DC5Vの供給口もCPUカード上にある LCD右側はDsub9シリアルコネクタ(SCI0) LEDは左から0,1 4bitDIPスイッチは左から0,1,2,3 プッシュスイッチは左から0,1,2,3,4 |
SCI1はCPUカード上にある。 DC5Vの供給口もCPUカード上にある LCD右側はDsub9シリアルコネクタ(SCI0) LEDは左から0,1 4bitDIPスイッチは左から0,1,2,3 プッシュスイッチは左から0,1,2,3,4 |
図0.3 AKI-H8/3069, TNCT試作マザーボード |
図0.4 AKI-H8/3069, TNCTマザーボード |
1. LEDの点滅のプログラム
図1.1のLEDが対象となるLEDです。
|
図1.1 2つのLED |
図1.2 CN2 (コネクタ2) |
1.1 LED点滅プログラム
マザーボード上の2つのLEDを点滅させるプログラムを作ります。
2つのLEDはH8/3069CPUのポート4の第6ビット,第7ビットの端子につけられています。
H8/3069CPUでポートというのは8ビットの出入り口のことです。各ビットは第0ビットから第7ビットのように呼びます。(上位が第7ビット,下位が第0ビット)
ポートがCPUのどのピンに接続されているかはCPU設計者によって決められ,CPUのマニュアルに書いてあります。
またCPUのピンがCPUカードのどのピンに接続されているかは,CPUカードの設計者によって決められ,CPUカードのマニュアルに書いてあります。
LED | ||
CPU | コネクタ | デバイス |
P4-bit6 | CN2-11 | LED1 |
P4-bit7 | CN2-12 | LED2 |
通常ポートは入出力兼用なので,この例のようにポート4の第6ビット,第7ビットの端子の先にLEDがついている場合は,CPUに対し,この2つのビットは出力に使用することを教えてあげなければなりません。(CPU起動後1回だけ設定しなければなりません。この作業はスタートアップルーチンで行われているので,プログラム内では行う必要がありません。)
参考 電流制限
CPUの出力端子はレベルHの時4.8V,レベルLの時0Vであり,LED単体の電圧降下が1.8V程度であるため,保護抵抗1.5kΩには3.0Vがかかっていることになります。この抵抗に流れる電流は3.0[V]/1500[Ω] = 0.0020[A] = 2.0[mA]となります。LEDは10mA駆動が標準ですが,2mA駆動でも光らないないわけではありません。一方CPUの端子出力電流は最大2.0mAとされている(H8/3069の仕様)ので,この例ではAKI-H8/3069はぎりぎりの仕様となっています。
1.2
LED点滅プログラムの考え方
(1)マイコン上での駆動の手順
(1) ポート4の上位2ビットに0または1を出力してLED点滅を行ないます。
0:OFF 1:ON
(3) 時間調整して1秒ごとに2つのLEDが交互に点滅するようにします。
(2)具体的なプログラミングの考え方
1.3 LED点滅プログラム以下を無限ループ
ポート4の第6ビットに1を出力
ポート4の第7ビットに0を出力
1秒間時間待ち
ポート4の第6ビットに0を出力
ポート4の第7ビットに1を出力
1秒間時間待ち
while(1)
{/*これは無限ループ*/
/*LED0をONにする*/
P4DR.BIT.B6=1;
/*LED1をOFFにする*/
P4DR.BIT.B7=0;
msecwait(1000);/*1000msecの間なにもしない*/
/*LED0をOFFにする*/
P4DR.BIT.B6=0;
/*LED1をONにする*/
P4DR.BIT.B7=1;
msecwait(1000);
}
勘どころ
ポート4のbit6とbit7にLEDをつけたのはマザーボードの設計者が決めたことなので,
回路がそのようにできている。
すでに取り付けられているLEDを別のポートを介して点滅させることは,ソフトウェア
の変更だけではできない。
別のポートを介してLEDを点滅させるには,そのポートがどの端子とつながっている
のか調べて,マザーボードの設計変更が必要になる。
1.4 実際のプログラム
led1st.c
/* msecwait関数で1秒ごとのLEDのON-OFFを行う */
#include <3069f.h>
void msecwait(int msec)
/*msec間なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<3352;j++); /*3352は実測によって求めた値 20MHz駆動*/
}
}
main()
{
while(1) {/*これは無限ループ*/
/*LED0をONにする P4のDRの第6ビットを1にする*/
/*DRとはDataRegister(データレジスタ)*/
P4DR.BIT.B6=1;
/*LED1をOFFにする P4のDRの第7ビットを0にする*/
P4DR.BIT.B7=0;
msecwait(1000);/*1000msecの間なにもしない*/
/*LED0をOFFにする P4のDRの第6ビットを0にする*/
P4DR.BIT.B6=0;
/*LED1をONにする P4のDRの第7ビットを1にする*/
P4DR.BIT.B7=1;
msecwait(1000);
}
}
実用プログラムでは,ポートの記述はできるだけ隠すようにしています。h8_3069.hはマザーボード上のハードウェア操作のプログラミングを関数呼び出しで行なえるようにしたものです。関数呼び出しを利用すると,どこのポートのどのビットをどうするということを考えずに済み,専用コマンドを使うような感覚でプログラミングでき,便利です。
もう1つ重要なことがあります。将来別のCPUを使うことになって,このプログラムを移植することになった場合を想定しよう。全体の動作を記述するプログラムと,CPUのハードウェアを操作するプログラムを分離することにより,移植性が高くなることが想像できるでしょう。
関数の定義はh8_3069.h中にあります。本Webページの末尾に「h8_3069.h」がありますが,その先頭部分には関数の説明があるので読んでください。
led2nd.c
#include <3069s.h>
#include "h8_3069.h"void msecwait(int msec)
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<3352;j++); /*3352は実測によって求めた値 20MHz駆動*/
}
}main()
{
while(1) {
turnOnLed(0); /*LED0のON*/
turnOffLed(1); /*LED1のOFF*/
msecwait(1000);
turnOffLed(0); /*LED0のOFF*/
turnOnLed(1); /*LED1のON*/
msecwait(1000);
}
}
練習問題 led2nd.cを元にして次のプログラムを作りなさい。 |
練習問題 次の作業を行ないなさい。
(mp1ex09.txt) |
LEDを高速にON-OFFを繰返し,ONになっている時間と周期との比(デューティ比)を変化させると,人間の目には点滅は見えず,デューティ比に応じて明るさが変化しているように見えます。
このような高速ON-OFFスイッチングで出力を制御する方法は「パルス幅変調(PWM)」「Pulse
Width
Modulation」と呼ばれます。
次のプログラムはLED0を点灯状態に保ち,LED1をPWM駆動します。3秒ごとに,デューティ比を90%,50%,10%と変化させています。
ledpwm.c
/**********************************************************
LEDのPWM(PulseWidthModulation)駆動
**********************************************************/
#include <3069f.h>
#include "h8_3069.h"void msecwait(int msec)
/*msec間なにもしない時間稼ぎ関数*/
{
int i,j;
for (i=0;i<msec;i++) {
for (j=0;j<3352;j++); /*3352は実測によって求めた値 20MHz駆動*/
}
}main()
{
int i;
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 <3069f.h>
#include "h8_3069.h"
main()
{
int p,i;
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) |
図2.1の4ビットスイッチの読み取りを行い,LEDを制御します。
4ビットスイッチのSW1がONならLED1をON。SW2がONならLED2をON,SW3がONならLED1・LED2ともにON,それ以外は2つのLEDはOFFにします。
4ビットスイッチはP5の第0ビットから第3ビットにつながっています。
図2.1 8ビットスイッチ |
参考
通常用いられるのは,図2.1の回路である。スイッチがOFFの時CPUの入力ピンには5Vが与えられ,スイッチがONの時CPUの入力ピンには0Vが与えられる。この用途で用いられている抵抗のことをプルアップ抵抗と呼んでいる。通常この抵抗値は10kΩから100kΩが用いられる。図2.2左はスイッチがOFFであるため,電流は流れず,抵抗で電圧降下が起こらないため,5Vが出力されているところを示している。図2.2右はスイッチがONであるため,電流が流れ,抵抗で電圧降下が起こり,0Vが出力されているところを示している。
H8CPUのポート2,4,5では,スイッチのON-OFF状態の取得等に都合の良い仕掛けがある。図2.4に示すように,図2.2のプルアップ抵抗をCPUユニットが内蔵しており,プルアップ抵抗を有効にするかどうかをソフトウェアで決めることができるようになっている。このプルアップ抵抗の有効無効を設定するのが,プルアップコントロールレジスタ(PCR)である。
AKI-H8のマザーボードでは,ディップスイッチ(4ビットスイッチ)がポート5の4つのビットに,プッシュスイッチ(4つ)がポート2の上位4ビットにつながっており,P2.PCRとP4.PCRの対応するビットに1を書き込むことで,プルアップ抵抗を有効にして使用することができる。そのため,マザーボード上のこれらのスイッチにはプルアップ抵抗がついていない。(P2.PCRとP4.PCRの対応するビットに1を書き込む作業はスタートアップルーチン内で行われている。)
図2.2 スイッチ状態のCPUへの入力 図2.3 プルアップ抵抗の役割 図2.4 H8のプルアップコントロール 外部からのポートのビット入力が5Vの時,CPU内部では1としてとらえ,0Vの時は0としてとらえます。
その結果,ハードウェアの構成に依存して,スイッチの状態ONを0,状態OFFを1として,レジスタに取り込まれることになります。
スイッチの状態 ポートのビット入力端子の電圧 レジスタに取り込まれるビット状態 ON
0V
0
OFF
5V
1
2.1 4ビットSWでLED駆動
4ビットスイッチのON-OFFの状態によってLEDのON-OFFを制御するプログラムを作成します。
LED駆動部のみh8_3069.hを用いてプログラムを作ります。
4ビットスイッチの各端子はH8内部でプルアップされる設定なので,スイッチがONになるとポート2の対応するビットは0になります。OFFになると1になります。
「4bitSWの1(ポート5の第0ビット)がONの時では」というのは「if (P5DR.BIT.B0==0)
{」のようになり,
「4bitSWの1(ポート5の第0ビット)がOFFの時では」というのは「if
(P5DR.BIT.B0==1)
{」のようになり,
通常の感覚と逆なので注意しなければなりません。
fourbitsw.c
/**********************************************************
マザーボードの4ビットスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"main()
{
while(1) {
if (P5DR.BIT.B0==0) { /*4bitSWの1がONの時*/
turnOnLed(0);
turnOnLed(1);
} else if (P5DR.BIT.B1==0) { /*4bitSWの2がONの時*/
turnOnLed(0);
turnOffLed(1);
} else if (P5DR.BIT.B2==0) { /*4bitSWの3がONの時*/
turnOffLed(0);
turnOnLed(1);
} else {
turnOffLed(0);
turnOffLed(1);
}
}
}
参考 P5DR.BIT.B0は「P5のDRをビットごと指定した時の第0ビット」と読めばよい。「P2DR.BIT.B0」に入る値は0または1です。
fourbitswFunc.c
/**********************************************************
マザーボードの4ビットスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"main()
{
while(1) {
if (check4BitSW(0)) { /*4bitSWの1がONの時*/
turnOnLed(0);
turnOnLed(1);
} else if (check4BitSW(1)) { /*4bitSWの2がONの時*/
turnOnLed(0);
turnOffLed(1);
} else if (check4BitSW(2)) { /*4bitSWの3がONの時*/
turnOffLed(0);
turnOnLed(1);
} else {
turnOffLed(0);
turnOffLed(1);
}
}
}
練習問題 fourbitswFunc.cを元にして次のプログラムを作りなさい。 |
練習問題
|
3. プッシュスイッチ
図3.1 プッシュスイッチ 図3.1のプッシュスイッチの読み取りを行い,LEDを制御します。
|
3.1 PushSWでLED駆動(「h8_3069.h」の利用)
pushsw.c
/**********************************************************
プッシュスイッチによってLEDのON-OFFを行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"main()
{
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」のプログラムでは,10msのタイマ割り込み初期設定が行なわれ,CPUの割り込み許可がなされ,タイマがスタートした後,プログラムの流れは
while(1);
となり,何もしない無限ループに突入します。しかし,10ms(0.01秒)ごとにタイマ割り込み関数「interrupt_cfunc()」が起動し,100回つきに1回のみLEDのON−OFFが継続して行なわれます。変数tickおよびcountはstatic修飾されているので,関数が呼び出されたときに,前回呼び出しの時の値が残っています。かつ0が代入されるのははじめの1回だけです。tick=1-tickの演算により,tickの値は0,1を繰り返します。
ロボットの制御には一定時間ごとに起動する定時間割り込み(タイマー割り込み)が良く用いられます。またPWMの生成にもこの定時間割り込みが用いられる場合があります。
関数void
msecwait(int msec)を用いた時間管理よりはるかに正確な時間管理ができます。(CPUクロックの精度依存)
4.1 割り込みでLED駆動(インターバルタイマによるタイマ割り込み)
プログラム中main()側で10msec(0.01秒)間隔でタイマ割り込みを設定します。
main()関数内でなにもしないループ動作をしている最中に,割り込み関数interrupt_cfunc()は0.01秒ごとに起動し,100回つきに1回のみLEDのON-OFFを行ないます。
このタイマ割り込みはITUのch1が使われています。
割り込みを実現するには次のことが必要になる。
(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/h8_3069Hardwaremanual.pdf
において,割り込みベクタテーブルについての記述は4.1.3に記述されている。
ここで用いているのは,ITUch1を使用したIMIA1という割り込み要因である。
(2)main関数内で記述
(3)main関数内で記述
E_INT(); /*CPU割り込み許可*/
この関数の本体はアセンブリ言語でないと書けないので,スタートアップルーチンのソースの後ろに
ついている。
(4)割り込み関数独特の宣言を行なう TimerIntFuncは割り込み関数名
#pragma interrupt (TimerIntFunc)
この宣言が行われた関数は,プログラム内から関数呼び出ししてはいけない。
int1st.c /**********************************************************
時間割り込みによってLEDのON-OFFを行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"
#define clearTimer1Flag() (ITU.TISRA.BIT.IMFA1=0)main()
{
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;
}
}ここではアセンブリ言語には踏み込まないが簡単に解説
(深入りはしない。後にアセンブリ言語を学んだ後に読み返せばよい)
#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_3069.h」にある関数initTimer1Int(),startTimer1()はITU1を使ったタイマ割り込みを可能にしている。
ハードウェアマニュアルのITUの基本動作(TCNT(カウンタ)がカウントアップ)を見なさい。
カウンタ(TCNT)がカウントアップしてあらかじめ設定した値(GRA)とコンペアマッチ(比較して一致)で割り込みが起こり,カウンタがクリアされ,この動作が継続される。カウンタは内部クロック(16MHz)の1/8の2MHz(周期は0.5μsec)で動作している。
このことにより,タイマ割り込みが行われている。
「1」で取り上げた関数msecwait()を使う方法と割り込みを使う方法とでは,時間精度が違う。高精度な時間管理には関数msecwait()を使うべきではない。タイマユニットが使えない場合に使用を検討するのがよい。
int2nd.c
/**********************************************************
プッシュスイッチと時間割り込みによってLEDのPWM制御を行う
**********************************************************/
#include <3069s.h>
#include "h8_3069.h"
#define clearTimer1Flag() (ITU.TISRA.BIT.IMFA1=0)volatile int led0,led1;
const int period=10; /*周期10msec*/
const int low=1;main()
{
led0=led1=0;
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 <3069s.h>
#include "h8_3069.h"void func1(void)
{
unsigned char sw,previous;
SCI1_printf("Printing 4-bitSW status...\n");
SCI1_printf("Change 4-bitSW and new status will appear.\n");
SCI1_printf("If any key on the keyboard, this test will quit.\n");
previous=sw=get4BitSW();
SCI1_printf("4-bitSW status= %2x[%08b]\n",sw,sw);
do {
sw=get4BitSW();
if (sw!=previous) {
SCI1_printf("4-bitSW 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の初期化*/
SCI1_printf("Hello. How are you?\n");
while (1) {
SCI1_printf("***************menu**********\n");
SCI1_printf("1: get 4-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;
}
}
}特別な関数の説明
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 get4BitSW()
4bitSWの状態をそのまま1バイトの値として読み込む関数。
ただし,値は反転しており,スイッチの状態がONのビットは1,OFFのビットは0で読み込む。
●画面への表示をそのままファイル化する方法(テキストキャプチャ)
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 <3069s.h>
#include "h8_3069.h"
#define clearTimer1Flag() (ITU.TISRA.BIT.IMFA1=0)volatile unsigned int counter;
main()
{
unsigned int counter1;
unsigned int t1,t2;
initSCI1(); /*SCI-ch1の初期化*/
initTimer1Int(20000); /*時間割り込み20000μsec=20msec ch1使用*/
E_INT(); /*CPU割り込み許可*/
startTimer1(); /*時間割り込みタイマスタートch1*/
counter=0;
SCI1_printf("\n\n\ntimer\n");
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 dutycount=0;
dutycount++;
if (dutycount==5) {
dutycount=0;
counter++;
}
clearTimer1Flag(); /*タイマステータスフラグのクリア 忘れないこと*/
}
練習問題 int2nd.c,sciout.c,timer.cを元にして次のプログラムを作りなさい。 (3)シリアル通信速度は"h8_3069.hの中の関数initSCI1で設定されている。 |
7.LCDへの表示プログラム
LCDに文字を表示してみよう。次の例は,プッシュスイッチの状態を16進数と2進数でLCDに表示します。
実際にはプッシュスイッチはポート4の上位4ビットに位置しているため,それを反映して,プッシュスイッチの1を押すと
16進数では「10」,2進数では「00010000」と表示される。
図7.1 LCDでプッシュスイッチの状態表示
この写真ではどこも押されていないので,すべて0表示
LCD.c |
/**********************************************************
|
LCDに関する関数 | |
initLCD() | LCDの初期化関数 |
LCDclrscr() |
LCDの画面初期化関数,全消去 |
LCDgotoxy(x,y): | LCDの表示ポインタ設定関数左上を(0,0)として,右側にx, 下側にy(0と1しかないが)の位置から次の表示を開始する。 |
LCD_printf() | LCDに対する関数printf(),ただし,"\n"や"\r"には対応していない。 |
8.独立した複数の作業の書き方
リアルタイムOSを使うと,もっと別な書き方になるが,ここではリアルタイムOSを使わない方法でプログラミングする
例題
プッシュスイッチ1(内部表現では0)を押しした瞬間から2秒間LED0が点灯し、消灯する。
プッシュスイッチ2(内部表現では1)を押しした瞬間から2秒間LED1が点灯し、消灯する。
ただし、LED0が点灯中にプッシュスイッチ1が押されたら、残り点灯時間には無関係に、その時点から新たに2秒間点灯するものとする。
同様に、LED1が点灯中にプッシュスイッチ2が押されたら、残り点灯時間には無関係に、その時点から新たに2秒間点灯するものとする。
また、LED0が点灯中にプッシュスイッチ2が押されたら、LED0の動作には影響を与えず(残り0.5秒の点灯時間があるなら1.5秒後まで点灯を続ける)、LED1は、その時点から1秒間点灯するものとする。
同様に、LED1が点灯中にプッシュスイッチ1が押されたら、LED1の動作には影響を与えず(残り0.5秒の点灯時間があるなら1.5秒後まで点灯を続ける)、LED2は、その時点から1秒間点灯するものとする。
このような2つの動作を独立動作と呼ぶ。
独立した2つの作業を行う
失敗作(プログラムはわかりやすいが失敗する)/**********************************************************
PushSW1でLED0をPushSW2でLED0を1秒間点灯させる
この2つの作業は独立に行われる
**********************************************************/
#include <3048f.h>
#include "h8_3048.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を1秒間点灯させる
この2つの作業は独立に行われる
**********************************************************/
#include <3069f.h>
#include "h8_3069.h"
#define clearTimer1Flag() (ITU.TISRA.BIT.IMFA1=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)ピン割り当て | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|