H8サンプルプログラム-その4
CPUボード:RY3048Fone を使って、
制御対象回路として「ものづくり kumamoto Ver.01」を制御するサンプルプログラムを作ります。
7セグメントLED(LED1、LED2)の点灯、スイッチ入力、アナログ入力(ここでは直流電圧)を制御します。小さなテーマの集まりですが、対象としているハードウェアの接続(ケーブル、回路内のすべて)を確認しながら進めてください。その一つひとつがプログラムの力になります。
■テーマ概要
・テーマは、次の通りです。
1)AD変換(10bit)で電圧変換し、その出力電圧値を7セグメントLED(2桁)に表示します。
2)AD変換(10bit)の結果をLED(8個)でレベル表示します。
3)(1)と2)を組み合わせ)AD変換の結果を7セグメント2桁表示とLED(8個)のレベル表示を行います。
■接続
制御対象回路 CPUボード:RY3048Fone
CN1 → J2
CN2 → J3
J1 → トグルスイッチ、プッシュスイッチ、透過センサー、直流可変電圧
■開発環境
ルネサスエレクトロニクス社 High-performance Embedded Workshop を使います。
最新版をルネサスエレクトロニクス社のホームページよりダウンロードしてセットアップしてください。
注:このプロジェクトでは、「H8S,H8/300 Standard Toolchain...」(「ビルド」メニューから起動)の設定を変更している。「標準ライブラリ」の「モード」をデフォルト設定の「ライブラリファイル指定なし」から、「ライブラリファイル作成(常に)」に設定した。そうしないと下記リンクエラーが発生する。
L2310 (E) Undefined external symbol "$sp_regsv$3" referenced in "C:\sakaki\・・\demo02.obj"
L2310 (E) Undefined external symbol "$spregld2$3" referenced in "C:\sakaki\・・\demo03.obj"
Optimizing Linkage Editor Abort
→この対応で一度、ライブラリファイルが作成されると、その後、以前の「ライブラリファイル指定なし」に戻してもリンクエラーは発生しなくなった。理由は、不明。しかし、今後の事が心配である為、「ライブラリファイル指定なし」には戻さない。
■準備-その1
ヘッダファイルを用意します。
ファイル名:ry3048fone.h
フォルダー:自分の開発環境に合わせる。
ヘッダファイルの内容は、次の通り。
---この次から---
void InitializeIO(void);
int GetSW1(void);
int GetSW2(void);
int GetSW3(void);
int ADConv(void);
void Wait1msec(void);
void WaitMsec(int msec);
#define COUNT_START 1
#define COUNT_STOP 0
#define LED_AL_OFF 0xff
#define LED_AL_ON 0x00
#define OE_ENABLE 0
#define OE_DISABLE 1
#define FET_ENABLE 1
#define FET_DISABLE 0
#define LED7SEG_ALL PA.DR.BYTE
#define OE_7SEG PB.DR.BIT.B7
#define OE_LED PB.DR.BIT.B6
#define A 0x01
#define B 0x02
#define C 0x04
#define D 0x08
#define E 0x10
#define F 0x20
#define G 0x40
#define SW1 P7.DR.BIT.B1
#define SW2 P7.DR.BIT.B3
#define SW3 P7.DR.BIT.B5
#define SW_ON 0
#define SW_OFF 1
#define True 1
#define False 0
const char LEDSegData[10] = {
~(A+B+C+D+E+F),
~(B+C),
~(A+B+G+E+D),
~(A+B+G+C+D),
~(F+G+B+C),
~(A+F+G+C+D),
~(A+F+G+C+D+E),
~(A+B+C),
~(A+B+C+D+E+F+G),
~(A+B+C+D+F+G) };
void InitializeIO(void)
{
/* 入出力端子の定義 */
PA.DR.BYTE = 0;
PA.DDR = 0xff;
PA.DR.BYTE = LED_AL_OFF;
PB.DR.BYTE = 0;
PB.DDR = 0xff;
/* ADコンバータの定義 */
AD.DRD = 0;
AD.CSR.BIT.ADF = 0;
AD.CSR.BIT.ADIE = 0;
AD.CSR.BIT.ADST = 0;
AD.CSR.BIT.SCAN = 0;
AD.CSR.BIT.CKS = 1;
AD.CSR.BIT.CH = 7;
/* タイマーの定義 */
ITU1.TCR.BYTE = 0x23;
ITU1.GRA = 0x0c00;
ITU.TSTR.BIT.STR1 = COUNT_START;
}
int ADConv(void)
{
int ADdata;
AD.CSR.BIT.ADST = 1;
while( !AD.CSR.BIT.ADF ) ;
ADdata = AD.DRD >> 6;
AD.CSR.BIT.ADF = 0;
return( ADdata );
}
void Wait1msec(void)
{
for(;;){
if( ITU1.TSR.BIT.IMFA != 0 ) break;
}
ITU1.TSR.BIT.IMFA = 0;
}
void WaitMsec(int msec)
{
int iCnt;
if( msec < 0 ) return;
for( iCnt = 0 ; iCnt < msec ; iCnt++ ) Wait1msec();
}
int GetSW1(void)
{
return( SW1 );
}
int GetSW2(void)
{
return( SW2 );
}
int GetSW3(void)
{
return( SW3 );
}
---この前まで---
■準備-その2
サンプル用のスタータプログラム(アセンブラ)を用意します。
ファイル名:demo04start.src
フォルダー:プロジェクト指定のフォルダー
---この次から---
.CPU 300HA:20
STACK_BASE: .EQU H'FFFF10 ; スタック基底アドレス(モード7)
.IMPORT _main ; 外部参照(main関数)
.SECTION V ; H'000000
.DATA.L RESET_START
.SECTION P ; H'000100
RESET_START:
MOV.L #STACK_BASE,ER7 ; スタックの設定
JSR @_main ; C言語のmain()関数へジャンプ
OWARI:
BRA OWARI
.END
---この前まで---
注:このファイル(拡張子が「src」)は、これまで作成してきた中に入っています。それをそのまま、ファイル名を変えて利用されても構いません。先に作成したファイル「demo01start.src」のファイル名称を「demo04start.src」変更して流用してもよいです。
■準備-その3
サンプル用のソースプログラムファイル(C言語)を用意します。
ファイル名:demo04.c
フォルダー:プロジェクト指定のフォルダー
---この次から---
#include "3048f.h"
#include "ry3048fone.h"
void main(void);
#define VREF10BIT 0.00488281
#define VREF8BIT 0.01953125
#define LEVEL10BIT 0.00097656
#define LEVEL8BIT 0.00390625
void main(void)
{
/* ローカル変数の定義 */
int Digit1st;
int Digit2nd;
int VoltOfAD;
int LevelDisplay;
int AdOutput;
/* 処理 */
InitializeIO();
for(;;){
/*ここにサンプルプログラムを書く*/
}
}
---この前まで---
それでは、次に「/*ここにサンプルプログラムを書く*/」のところに、実際にプログラムを書いてみましょう。
注:ここで使用されている記号文字定数の値について少し説明をします。詳しくは、事項「■サンプルプログラムの作成」を参照してください。
VREF10BIT=0.00488281→AD変換:電圧変換係数(10bit)=基準5(V)÷1024
VREF8BIT=0.01953125→AD変換:電圧変換係数(8bit)=基準5(V)÷256
LEVEL10BIT=0.00097656→AD変換:レベル変換係数(10bit)=1/1024
LEVEL8BIT=0.00390625→AD変換:レベル変換係数(8bit)=1/256
■サンプルプログラムの作成
1)AD変換(10bit)で電圧変換し、その出力電圧値を7セグメントLED(2桁)に表示します。
直流可変電圧を操作し、その出力電圧値を7セグメントLEDに表示する。
AD変換関数「ADConv()」は、RY3048Foneの端子J1-2ピン:信号P77をモニターしている。
入力されたアナログ信号は、基準電圧(H8のVcc(電源電圧))と比較され、内部の10bitのAD変換器を通して数値化(デジタル値化)される。
数値化された値は、10bit=0~1023の値をとる。つまり、基準電圧を1024のレベルに分割して表現してくるという事になる。
従って、得られたデジタル値と基準電圧で、現在の電圧が次の式で得られる。
電圧=電圧変換係数xAD変換値
電圧変換係数=基準電圧÷1024
これが基本式になる。
7セグメントLEDが2桁分なので、電圧表示として、1の位(左側:LED1)と10分の1の位(右側:LED2)の2桁とする。上記基本式の結果は、基準電圧が「5」なので最大値は、「5.0」となる。そこで上記基本式の値を10倍して、10分の1の位の二桁だけを整数値にすると、7セグメントLEDで扱える。
表示用電圧=電圧変換係数xAD変換値x10
2個の7セグメントLEDの表示で、ダイナミックスイッチングをしている為、その分の待ち時間があり、約2msec周期でのAD変換値の取得という事になる。
------
因みに、上記内容は、10bitでの変換結果を基にしている。
電圧変換係数を「基準電圧÷256」として、AD変換値を右シフトを2すると8bitでの変換として扱える。
VoltOfAD = (int)( VREF8BIT * (ADConv() >> 2) * 10 );
(今回の両方で実験を行った所、結果は、いずれも同じ値の表示となった)
VoltOfAD = (int)( VREF10BIT * ADConv() * 10 );
Digit2nd = VoltOfAD / 10;
Digit1st = VoltOfAD - ( Digit2nd * 10 );
OE_7SEG = OE_ENABLE;
OE_LED = OE_DISABLE;
LED7SEG_ALL = LEDSegData[Digit2nd];
LED7SEG_1 = FET_ENABLE;
LED7SEG_2 = FET_DISABLE;
Wait1msec();
LED7SEG_ALL = LEDSegData[Digit1st];
LED7SEG_1 = FET_DISABLE;
LED7SEG_2 = FET_ENABLE;
Wait1msec();
2)AD変換(10bit)の結果をLED(8個)でレベル表示します。
直流可変電圧を操作し、その出力電圧値を7セグメントLEDに表示する。
AD変換関数「ADConv()」は、RY3048Foneの端子J1-2ピン:信号P77をモニターしている。
入力されたアナログ信号は、基準電圧(H8のVcc(電源電圧))と比較され、内部の10bitのAD変換器を通して数値化(デジタル値化)される。
数値化された値は、10bit=0~1023の値をとる。つまり、基準電圧を1024のレベルに分割して表現してくるという事になる。
従って、得られたデジタル値を8個のレベルに分けるとよい。と基準電圧で、現在の電圧が次の式で得られる。
レベル=レベル変換係数xAD変換値x8+0.5
レベル変換係数=1/1024
これが基本式になる。最後に0.5を加算しているのは、10分の1の位での四捨五入の為。
LEDが8個あるのでので、レベル表示として、下位(左側:LED10)と上位(右側:LED3)とする。
電圧が0だと消灯、低いとLED10から順に点灯し、最大LED3迄点灯する。
前項(1)に合わせて待ち時間を作り、約2msec周期でのAD変換値の取得とする。
------
因みに、上記内容は、10bitでの変換結果を基にしている。
レベル変換係数を「1/256」として、AD変換値を右シフトを2すると8bitでの変換として扱える。
VoltOfAD = (int)((LEVEL10BIT * (ADConv() >> 2) * 8) + 0.5);
(今回の両方で実験を行った所、結果は、いずれも同じレベル表示となった)
VoltOfAD = (int)((LEVEL10BIT * ADConv() * 8) + 0.5);
OE_7SEG = OE_DISABLE;
OE_LED = OE_ENABLE;
LED7SEG_1 = FET_DISABLE;
LED7SEG_2 = FET_DISABLE;
LevelDisplay = 0;
for(;;){
if( VoltOfAD == 0 ) break;
LevelDisplay = LevelDisplay + 0x80;
if( VoltOfAD == 1 ) break;
LevelDisplay = LevelDisplay + 0x40;
if( VoltOfAD == 2 ) break;
LevelDisplay = LevelDisplay + 0x20;
if( VoltOfAD == 3 ) break;
LevelDisplay = LevelDisplay + 0x10;
if( VoltOfAD == 4 ) break;
LevelDisplay = LevelDisplay + 0x08;
if( VoltOfAD == 5 ) break;
LevelDisplay = LevelDisplay + 0x04;
if( VoltOfAD == 6 ) break;
LevelDisplay = LevelDisplay + 0x02;
if( VoltOfAD == 7 ) break;
LevelDisplay = LevelDisplay + 0x01;
break;
}
LED7SEG_ALL = ~LevelDisplay;
Wait1msec();
Wait1msec();
3)(1)と2)を組み合わせ)AD変換の結果を7セグメント2桁表示とLED(8個)のレベル表示を行います。
2個の7セグメントLEDの表示、LEDの表示と、ダイナミックスイッチングをしている為、その分の待ち時間があり、約3msec周期でのAD変換値の取得という事になる。
AdOutput = ADConv();
VoltOfAD = (int)( VREF10BIT * AdOutput * 10 );
Digit2nd = VoltOfAD / 10;
Digit1st = VoltOfAD - ( Digit2nd * 10 );
OE_7SEG = OE_ENABLE;
OE_LED = OE_DISABLE;
LED7SEG_ALL = LEDSegData[Digit2nd];
LED7SEG_1 = FET_ENABLE;
LED7SEG_2 = FET_DISABLE;
Wait1msec();
LED7SEG_ALL = LEDSegData[Digit1st];
LED7SEG_1 = FET_DISABLE;
LED7SEG_2 = FET_ENABLE;
Wait1msec();
VoltOfAD = (int)((LEVEL10BIT * AdOutput * 8) + 0.5);
OE_7SEG = OE_DISABLE;
OE_LED = OE_ENABLE;
LED7SEG_1 = FET_DISABLE;
LED7SEG_2 = FET_DISABLE;
LevelDisplay = 0;
for(;;){
if( VoltOfAD == 0 ) break;
LevelDisplay = LevelDisplay + 0x80;
if( VoltOfAD == 1 ) break;
LevelDisplay = LevelDisplay + 0x40;
if( VoltOfAD == 2 ) break;
LevelDisplay = LevelDisplay + 0x20;
if( VoltOfAD == 3 ) break;
LevelDisplay = LevelDisplay + 0x10;
if( VoltOfAD == 4 ) break;
LevelDisplay = LevelDisplay + 0x08;
if( VoltOfAD == 5 ) break;
LevelDisplay = LevelDisplay + 0x04;
if( VoltOfAD == 6 ) break;
LevelDisplay = LevelDisplay + 0x02;
if( VoltOfAD == 7 ) break;
LevelDisplay = LevelDisplay + 0x01;
break;
}
LED7SEG_ALL = ~LevelDisplay;
Wait1msec();