OAKS16プログラミングテキスト

OAKS16プログラミングテキスト
OAKS16 プログラミングテキスト
目次
はじめに ......................................................................... 4
1.C言語の基礎知識 ................................................................ 6
1.1.
C言語とは............................................................... 6
1.2.
組み込み用のC言語とは................................................... 7
1.3.データ型 ................................................................... 8
1.3.1.NC30 で扱う定数 ......................................................... 8
1.3.2.変数 .................................................................. 10
1.4.演算子 .................................................................... 11
1.5.制御文 .................................................................... 12
1.6.関数 ...................................................................... 16
1.7 配列 ....................................................................... 19
1.7.1. 1 次元配列 ............................................................ 19
1.7.2. 2 次元配列 ............................................................ 20
1.8.構造体 .................................................................... 22
1.8.1.ビットフィールドの宣言................................................. 24
1.9.共用体 .................................................................... 25
2.OAKS16 でC言語を使って開発する前に ............................................. 27
2.1.スタートアッププログラム................................................... 30
2.1.1.NC30 のサンプルスタートアッププログラムの構成 .......................... 30
2.1.2.スタートアッププログラムの作成......................................... 31
2.2.プリプロセッサの処理....................................................... 42
2.2.1. sfr設定の為のヘッダファイル............................................. 43
3.プログラミング ................................................................ 49
3.1.基本のプログラム........................................................... 49
3.1.1.論理演算 .............................................................. 52
3.1.2.データの反転........................................................... 53
3.1.3.データのマスク処理..................................................... 56
3.2.スイッチ回路の考え方....................................................... 59
3.2.1.チャタリングとは....................................................... 63
3.2.2.S/Wでのチャタリング除去................................................ 64
3.3.割り込みプログラム......................................................... 66
3.3.1.M16Cの割り込み......................................................... 67
3.3.2.割り込みを使用するために必要な処理..................................... 68
3.3.1.INT0 割り込み端子からの割り込み ........................................ 70
2/113
OAKS16 プログラミングテキスト
3.3.2.タイマを使った割り込み................................................. 76
3.4.LCDモジュールの制御........................................................ 84
3.4.1.LCDモジュールの構成.................................................... 84
3.1.2.OAKS16LCDBOARDの配線................................................... 85
3.1.3.LCDモジュールの初期設定................................................ 86
3.1.4.LCDモジュール制御の為の関数............................................ 88
3.1.5.サンプルプログラム 1 ................................................... 95
3.1.6.サンプルプログラム 2 .................................................. 104
3/113
OAKS16 プログラミングテキスト
はじめに
このテキストは、OAKS16 基礎テキストの続編として作成されています。OAKS16 基礎テキスト
終了後にお使いください。
このテキストは、前半は、OAKS16 のプログラムを C 言語で開発するための基礎知識、後半は
OAKS16 のプログラミングについての説明をしています。C 言語の文法等はある程度説明してい
きますが、さらに詳しい文法を知りたい方は、市販の C 言語の入門書をご覧下さい。
また、テキスト中の例題は全て、TM で動作できるようにフォルダで添付してあります。
「OAKS16
で TM をお使いになる方のために」を参考にして、動作を確認してください。
LCDボードの準備
本テキストではプログラムのターゲットとして OAKS16LCD ボードを使用します。OAKS16LCD ボ
ードをご購入いただくか、添付の資料を参考に同等の回路を作成して下さい。
①ボードの準備
基礎テキストで使用していた EXBOARD から CPU ボードを取り外し、LCD ボード上に差し替えて
ください。
(必ず、電源回路をはずした状態で行なってください。)その後、電源、パソコンと
再び接続してください。
②LCD ボード部品配置図
電源コネクタ
Dsubコネクタ
SW11
(電源SW)
LCD
M306230FCAFP
(CPUボード)
SW11
LED8
LED1
SW10
SW8
SW1
図 0.1
4/113
OAKS16 プログラミングテキスト
③LCD ボードインタフェース仕様書
LED1 から LED8
点灯
消灯
LED1(P00)
L
H
LED2(P01)
L
H
LED3(P02)
L
H
LED4(P03)
L
H
LED5(P04)
L
H
LED6(P05)
L
H
LED7(P06)
L
H
LED8(P07)
L
H
ON
OFF
SW1(P30)
L
H
SW2(P31)
L
H
SW3(P32)
L
H
SW4(P33)
L
H
SW5(P34)
L
H
SW6(P35)
L
H
SW7(P36)
L
H
SW8(P37)
L
H
ON
OFF
SW9 (P82)
L
H
SW10(P83)
L
H
SW1 から SW8
SW9,SW10
LCD モジュール
-
-
DB7(P43)
RS(P46)
RS=0(コマンド)、RS=1(データ)
DB6(P42)
R/W(P45)
R=1,W=0
DB5(P41)
E(P44)
E=1 でイネーブル
DB4(P40)
5/113
データバス
OAKS16 プログラミングテキスト
1.C言語の基礎知識
1.1. C言語とは
C 言語はミニコンピュータ用の OS である UNIX システムの開発用言語としてベル研究所の D.M.
リッチーによって作られました。そのため C 言語は主に UNIX ユーザに多く使われてきました。
その後、パソコン上の OS(windows など)上で動かす為のコンパイラが多く開発され、プログ
ラミングの中心言語になってきました。一方、組み込み用のシステムでは、かなり長い間、ア
センブリ言語が使われてきました。その理由は、アセンブリ言語が、機械語に一番近い言語で、
プログラムサイズを小さくすることが出来たからです。組み込みシステムでは、メモリの容量
が限られる為、出来るだけ小さいサイズのプログラムを作ることが求められていました。
しかし、近年組み込みシステムに使われる CPU も高性能になり、それに伴いアセンブリ言語
でプログラムを記述することが難しくなってきました。その代わりに、メモリの作成技術も進
歩し、容量の多いメモリが使えるようになりました。さらに、組み込み用の CPU のコンパイラ
も開発・改良され、比較的効率の良いプログラムを作成できるようになりました。
このような過程から、現在では、多くの組み込み用プログラムが C 言語で書かれています。
最近、書店のコンピュータ書籍のコーナーにはたくさんの C 言語の関連書が並んでいます。
これらの本の大部分は、C 言語の文法を習得するためのものです。しかし、これらの本の例題
はほとんどがパソコン上で動かす為のプログラムです。もちろん OAKS16 で開発するための C
言語の文法はこれらの本から学べるのです。しかし、汎用機(パソコン、ミニコンなど)で動
かすプログラムと OAKS16 を動かすプログラムではいくつかの違いがあります。
本テキストでは、この違いを認識しながら、プログラミングの基礎技術を習得していただきま
す。
ANSI について
C言語が普及されたのを受け、ANSI(American National Standards Institute:アメリカ国内標
準規格協会)という委員会が発足しました。ここで決められた規格が、ANSI 規格と呼ばれるもの
です。ANSI 規格はコンパイラ作成のための規格ですが、コンパイラの仕様は、プログラムを記述
する上で必要な知識です。現在、多くのコンパイラが ANSI 準拠を謳っていますが、+αの機能を
持ったコンパイラも多く存在します。今回使用する NC30 も、ANSI 準拠ですが、ANSI に規定され
ていない便利な機能も多く持っています。このテキストでは、全ての機能については説明できま
せんので、C 言語についてある程度理解できてきたら、NC30 のマニュアルにも目を通してくださ
い。
6/113
OAKS16 プログラミングテキスト
1.2. 組み込み用のC言語とは
皆さんは、今までに C 言語の入門書をご覧になったことがおありでしょうか?まだの方は書
店へ行ってほんの少しページをめくってください。ほとんどの本が CRT に“welcome C”もし
くは似たような言葉を表示させるところから始まります。これは、C 言語を学習する為の環境
として CRT、キーボードを標準の入出力として準備してあることが前提となっているからです。
ANSI の企画書にもこれらの入出力装置を使うための関数が標準ライブラリ関数として仕様が
規定されています。
しかし、皆さんにこれからプログラムを作っていただく OAKS16LCD ボードには、CRT もキー
ボードもありません。代わりになりそうなのは、LCD とスイッチそれに LED だけです。
はじめは、LCD を CRT の代わりとしてテキストを書いてみようと考えました。しかし、すぐ
に挫折することになりました。私たちが、LCD ボードにデータを表示させるのは、C 言語のテ
キストの著者が言うように一文の記述
printf(“welcome C”);ではだめなのです。なぜならば、
CRTやキーボードのような規格が決まっているものは、コンパイラのサポート内ですが、
OAKS16 の LCD ボードのような特殊な規格のものは、サポートされていないからです。そのため
に、OAKS16LCD ボードを使うためには、自分で制御用の関数を作らなければならないのです。
そのためには C 言語の文法プログラミングの定石(?)そしてハードウエアの仕様を全て把握
していかなければなりません。組み込み用の C 言語プログラマが大変なのは、複数の分野の知
識を必要とするところなのです。
汎用マシンで動作させる為のC言語プログラム
① OS から起動し、終了後は OS に戻る。
② 標準関数を有効利用できる。
③ メモリや入出力機器(デバイス)のアドレスを意識しないでプログラミングする。
1.2.2.組み込み用C言語プログラム
① リセット解除後、プログラムを起動する→C 言語では記述できないので、アセンブリ言語
でリセット解除後に実行されるプログラム(スタートアッププログラムと言う。)を記述し、
そこから呼びだす形をとる。終了は基本的にないので、無限ループの形で記述する。
② システムによって、周辺機器が異なるので、標準関数は出来るだけ使わず、自作する。
③ ROM、RAM の違いを認識し、正しいメモリ領域に、プログラム、データ領域を配置する。
これらの事柄に注意しながら、このあと出来るだけ多くの例題を使用して、OAKS16 のためのプ
ログラミングを説明して行きます。
7/113
OAKS16 プログラミングテキスト
1.3.データ型
コンピュータを制御する場合、数値や文字などのデータを多く扱います。これらのデータは、
CPU 内のレジスタやメモリに格納され、演算されたり、加工されたりします。C 言語で扱うこ
との出来るデータ(定数)は、
「整数」
「実数」
「1 文字」
「文字列」の 4 種類です。それ以外に、
プログラム実行中に値が変わるデータとして変数が存在します。変数は、メモリにデータを入
れるための領域確保を行ないますので、容量をコンパイラに認識させる為に型宣言を行ないま
す。ANSI 規格ではデータ型をターゲット CPU の BIT に依存させていますので、データ型の名前
と表す数値が統一されていません。今回は、NC30 のデータ型を使って説明していきます。
(このあと、M16C 以外の CPU を使って開発することがあった場合は、そのコンパイラの仕様を
確認してください。)
1.3.1.NC30 で扱う定数
整数定数
整数定数は 10 進数、16 進数、8 進数の 3 種類の方法で記述できます。(2 進数記述は出来ませ
ん)データ表記は大文字、小文字を区別しません。
表 1.1
種類
表記方法
例
10 進数
通常の数学表記(何もつけない)
127、+127、-56
16 進数
数値の前に「0x(ゼロ・エックス)または「0X」を付ける
0x3b、0X3B
8 進数
数値の前に「0」を付ける
07、041
実数定数(浮動小数点定)
符号付きの実数を 10 進数で表したものを「浮動少数点定数」と言います。表記方法としては
通常の小数点表記と、「e」または「E」を用いた指数表記が記述できます。
① 通常の小数点表記
② 「e」または「E」を用いた指数表記
例:175.5、-0.007
例:1.755e2、-7.0E-3
1 文字定数
1 文字定数はシングルクォーテーション(‘)で囲みます。英数字のほかに、制御コードも 1
文字定数として扱うことが出来ます。
8/113
OAKS16 プログラミングテキスト
文字列定数
英数字や制御コードの並びをダブルクォーテーション(“)で囲むと文字列定数として扱うこ
とが出来ます。文字列定数ではデータの最後にヌルコード‘¥0’が自動的に付けられ、文字
列の終わりであることを表します。
メモリ
メモリ
アスキーコード
整数
1
0x01
‘1’
整数定数
1文字定数
メモリ
{‘a’,‘b’}
1文字定数
の集まり
0x31
‘a’
‘b’
メモリ
2バイトの
データ領域
を使用
“ab”
文字列定数
‘a’
‘b’
‘¥0’
?
ヌルコード
図 1.1
9/113
3バイトの
データ領域
を使用
OAKS16 プログラミングテキスト
1.3.2.変数
ここでは NC30 で扱える変数のデータ型と宣言方法について説明します。
NC30 の基本データ型
表 1.2
データ型
整
数
実
数
ビット長
表現できる数値の範囲
(unsigned) char
8 ビット
0~255
signed char
8 ビット
-128~127
unigned short (int)
16 ビット
0~65535
(signed) short( int)
16 ビット
-32768~32767
unsigned int
16 ビット
0~65535
(signed) int
16 ビット
-32768~32767
unsigned long (int)
32 ビット
0~65535
(signed) long (int)
32 ビット
-32768~32767
Float
32 ビット
有効桁数
9桁
Double
64 ビット
有効桁数
17 桁
long double
64 ビット
有効桁数 17 桁
char は character のことを意味し、通常は文字データを格納するために使われます。
int は integer(整数)の意味です。
変数の宣言
変数の宣言は、「データ型
変数名」という書式で行ないます。
例:変数cを char 型として宣言する。(8 ビットのデータ領域をaと言う名前で確保する)
charc;
「データ型
変数名
=
初期値;」と記述すると、宣言と同時にその変数に対して初期値を
設定する事ができます。
例:char 型の変数cに初期値として‘A’を設定する。
char c = ‘A’;
また、複数の変数名をカンマ(‘,’)で区切って列記すると、同じデータ型の変数を同時に宣
言できます。
10/113
OAKS16 プログラミングテキスト
1.4.演算子
NC30 で使用できる演算子を以下の表に示します。
詳しい内容は、プログラミングの中で説明していきます。
表 1.3
単項算術演算子
+ +
- -
-
二項算術演算子
+
*
/
シフト演算子
<<
ビット演算子
&
|
^
関数演算子
>
<
>=
論理演算子
&&
代入演算子
=
-
%
>>
||
&=
+=
|=
条件演算子
?:
sizeof 演算子
sizeof()
キャスト演算子
(型)
アドレス演算子
&
ポインタ演算子
*
コンマ演算子
,
~
<=
==
!=
!
-=
*=
^=
11/113
/=
%=
<<=
>>=
OAKS16 プログラミングテキスト
1.5.制御文
C 言語にはプログラムの流れを変えるための命令(制御命令)が存在します。これらを使う
ことにより、構造化された理解しやすいプログラムを記述することが出来ます。
構造化プログラミング
プログラムの流れをわかりやすくする為に、プログラムの流れを限定した手法だけを使って
行なうというプログラミング手法が構造化プログラミングです。この手法は、
「順次処理」
「分
岐処理」「繰り返し処理」の 3 つを使用して行ないます。
処理A
上から下へ、トップダウン
順次処理
で実行する
処理B
偽
条件P
分岐命令
条件Pの真偽によって
真
処理Aと処理Bに
処理A
処理B
振り分ける
偽
条件P
真
繰り返し処理
条件Pが成立している間、
処理Aを繰り返す
処理A
図 1.2
12/113
OAKS16 プログラミングテキスト
これらの制御文を実現する為の制御命令について説明します。
if 文
「もし~なら」という制御です。以下に代表的な記述と記述例を示します。
「if文」の構造
書き方
式
if(式) 文;
else 文2;
if(式){
文1;
}
文2;
・
・
・
偽(=0)
if(a=0) a=a+1;
真
文
式
if(式) 文1;
記述例
フローチャート
偽(=0)
else a=0;
真
文1
式
if(a!=0) a=a+1;
文2
偽(=0)
if(a!=0){
a=a+1;
真
文1
b=0;
}
文2
・
・
・
図 1.3
13/113
OAKS16 プログラミングテキスト
wlile 文
ある条件の間処理を繰り返します。
「while文」の構造
書き方
while(式) 文;
記述例
フローチャート
偽(=0)
式
while(a!=0) a=a+1;
真
文
while(式){
文1;
}
文2;
・
・
・
while(a!=0){
偽(=0)
式
a=a+1;
真
文1
b=0;
}
文2
・
・
・
図 1.4
14/113
OAKS16 プログラミングテキスト
For 文
ある条件の間、処理を繰り返します。
「for文」の構造
書き方
for(式1,式2,式3)文;
フローチャート
式1
式2
記述例
for(a=0,a<3,a=a+1) b=b+1;
偽(=0)
真
文
式3
for(式1,式2,式3){
文1;
}
文2;
・
・
・
for(a=0,a<3,a=a+1){
式1
式2
b=b+1;
c=c-1;
偽(=0)
}
真
文1
文1
・
・
・
式3
図 1.5
15/113
OAKS16 プログラミングテキスト
1.6.関数
C 言語のプログラムは基本的に関数の集まりです。C 言語を理解する為には関数を知らなけ
ればなりません。
C 言語の関数の親分は main 関数です。全ての C 言語プログラムは main 関数から始まり、その
中にいくつかの関数が書かれます。
関数
関数
main(){
int i,j;
func1();
・
・
・
func1(){
・
func11();
・
・
}
関数
関数
func2();
}
・
・
・
func11(){
・
・
・
}
func2(){
・
func21();
・
・
}
図 1.6
16/113
func21(){
・
・
・
}
OAKS16 プログラミングテキスト
関数を作成するためには「関数の型宣言(プロトタイプ宣言)」、「関数の定義」、「関数の呼
び出し」の 3 つの手続きが必要です。
関数の型宣言(プロトタイプ宣言)
C 言語で関数を使用する前には、必ず関数の型宣言(プロトタイプ宣言)を行なわなければ
なりません。関数の型とは、関数の引数と戻り値のデータ型です。
戻り値のデータ型
関数名(引数のデータ型のならび)
;
戻り値や引数がないときは、空(から)を意味する“void”という型を記述します。
関数の定義
関数本体では、引数を受け取る為の「仮引数」のデータ型と名称を定義します。また、戻り
値は、「return 文」を使って返します。
戻り値のデータ型
関数名(仮引数 1 のデータ型 仮引数 1,・・・)
{
制御文
return
戻り値;
}
関数の呼び出し
関数を呼び出すとき、その関数に対する引数を記述します。また呼び出した関数からの戻り
値は代入演算子を用いて受け取ります。(代入演算子:1.3.4 演算子参照)
関数名(引数 1,・・・);+
戻り値があるとき
変数=関数名(引数 1,・・・);
17/113
OAKS16 プログラミングテキスト
関数の記述例
引数なし
戻り値なし
メイン関数
char型
main
int型
int型
int型
戻り値なし
関数1
func1
関数2
func2
関数1
int func1(int x)
{
int z
.
.
.
return z;
メモリ
メイン関数
z
void main()
c
{
int a=40,b=29
int ans;
char c=0xFF;
ans=func1(a);
func2(b,c);
}
ans
1Dh
関数2
b
void func2(int y,char m)
00h
}
{
28h
.
.
.
a
00h
sp
}
図 1.7
*関数間でのデータの引渡しはスタックを使用して行なわれます。
18/113
OAKS16 プログラミングテキスト
1.7 配列
一つの変数名で複数のデータを格納することを配列といいます。この変数名を”配列名”と呼
びます。この配列名の下なる各々のデータを“要素”と呼び、それを特定するのに 0 から始ま
る数を使います.個別の要素は、配列名[要素番号]で表します。
配列には 1 次元配列、2 次元配列などがあります。
1.7.1. 1 次元配列
1次元配列は、配列の基本となるものです。
配列の宣言
データ型 配列名[要素数];
例1)char型の配列table1を宣言する。table1は要素数が5である。
table1という名前でchar型(8ビット)の
データ領域5つ分が確保される。
<メモリ>
char table1[5];
table1
← 要素番号0
← 要素番号1
← 要素番号2
← 要素番号3
← 要素番号4
図 1.8
配列を宣言して初期化する
データ型 配列名[要素数]= {要素 0,要素 1,要素 2,・・・};
例1)char型の配列table1を宣言する。
table1は要素数が5で、初期値として1,2,3,4,5を代入する。
char table1[5] = {1,2,3,4,5};
<メモリ>
table1
1
← 要素番号0
2
← 要素番号1
3
← 要素番号2
4
← 要素番号3
5
← 要素番号4
19/113
図 1.9
OAKS16 プログラミングテキスト
1.7.2. 2 次元配列
配列の要素は変数や定数だけでなく配列も要素として扱うことができます。2次元配列は1
次元配列を要素にもった配列です。
<メモリ>
table
1次元配列
1次元配列
1次元配列が配列になっている。
1次元配列
これを2次元配列という。
1次元配列
1次元配列
図 1.10
2次元配列は、行と列で表現されます。
配列の宣言
データ型
配列名〔行数〕〔列数〕
;
例1)char型の2次元配列table2を宣言する。
char table2[5][3];
0
1
列
2
3
4
0
(0,0 )
( 0,1)
(0,2)
( 0,3)
( 0,4 )
行 1
(1,0 )
( 1,1)
(1,2)
( 1,3)
( 1,4 )
2
(2,0 )
( 2,1)
(2,2)
( 2,3)
( 2,4 )
<メモリ>
table2[0]
table2[1]
table2[2]
(0,0)
(0,1)
(0,2)
(0,3)
(0,4)
(1,0)
(1,1)
(1,2)
(1,3)
(1,4)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
20/113
図 1.11
OAKS16 プログラミングテキスト
配列を宣言して初期化する
データ型 配列名[行数][列数]= {要素(0,0),要素(0,1),要素(0,2),・・・};
例1)char型の2次元配列table2を宣言する。
char table2[5][3] = {
'a','b','c','d','e',
1,2,3,4,5,
"oaks",
}
<メモリ>
table2[0]
table2[1]
table2[2]
61
62
63
64
65
01
02
03
05
05
6F
61
6b
73
00
'a'
'b'
'c'
'd'
'e'
1
2
3
4
5
'o'
'a'
'k'
's'
'\0'
文字はアスキーコードで
メモリに格納される
10進数は16進数(2進数)
で格納される
文字列は最後にヌルコードを加えて
アスキーコードで格納される
図 1.12
文字列の配列は、LCD 表示プログラムなどで使用することがあります。
21/113
OAKS16 プログラミングテキスト
1.8.構造体
構造体は C 言語のデータ型を自由に組み合わせて一つのまとまったかたまりとして管理する
ものです。この後説明する M16C の SFR を定義するヘッダファイル記述に使われているので、
ここで説明しておきます。
構造体の宣言には、型枠(テンプレート)を宣言する機能と、型枠で示されたサイズのメモ
リ領域を実際に確保する機能があります。この二つの機能を一緒に宣言することもできます。
型枠の宣言
struct
構造体タグ
{構造体リスト};
型を指定して記憶領域を確保する
struct
構造体タグ
構造体変数リスト;
型枠と記憶領域を同時に宣言する
struct
構造体タグ{構造体リスト}構造体変数リスト;
構造体タグは新しく定義された構造体型の名前(型枠名)です。構造体変数リストは単純変数
や配列、ポインタをコンマ(,)で区切って並べます。
プログラム中での参照の仕方
“.”で構造体変数名とメンバ名をつなぎます。
22/113
OAKS16 プログラミングテキスト
<構造体タグ宣言>
struct 構造体タグ {構造体リスト}
8bit
例)
struct rei{
char c;
int i;
long l;
};
c
i
l
rei
このような型枠が作られます。
(まだメモリにはとられません。)
<構造体の型を指定して記憶領域を確保する>
struct 構造体タグ 構造体変数リスト
例)
data
struct rei data,table[2]
実際にメモリ上に、
構造体枠が取られます。
table[1]
table[2]
<構造体型枠と記憶領域を同時に宣言する>
struct 構造体タグ {構造体リスト}構造体変数リスト;
例)
struct rei{
char c;
int i;
long l;
}data,table[2];
data
table[1]
table[2]
<構造体メンバを参照する。>
構造体変数名.メンバ名
例)
data.char = 3;
table[2].long = 0xffffffff;
図 1.13
23/113
実際にメモリ上に、
構造体枠が取られます。
OAKS16 プログラミングテキスト
1.8.1.ビットフィールドの宣言
C 言語では、構造体のメンバにビット単位で区別するビットフィールドが定義できます。
例として、全体が 8 ビットで上位から 1 ビット、1 ビット、6 ビットと割り当ててみます。
<ビットフィールドの宣言>
struct bit {
a b
unsigned a:1;
c
unsigned b:1;
unsigned c:6;
};
図 1.14
これによって、C 言語では存在しないビット単位の変数を扱うことが出来るようになります。
(これは、後述の SFR の定義で使われます。)
24/113
OAKS16 プログラミングテキスト
1.9.共用体
共用体とは、同一メモリ領域を別の複数の変数名を重ねて割り付けて操作できるようにする
ものです。書き方や参照の仕方は構造体とほぼ同じですが、struct の代わりに union を使いま
す。
共用体の宣言には、型枠(テンプレート)を宣言する機能と、型枠で示されたサイズのメモ
リ領域を実際に確保する機能があります。この二つの機能を一緒に宣言することもできます。
型枠の宣言
union 共用体タグ {共用体リスト};
型を指定して記憶領域を確保する
union 共用体タグ 共用体変数リスト;
型枠と記憶領域を同時に宣言する
union 共用体タグ{共用体リスト}共用体変数リスト;
共用体タグは新しく定義された共用体型の名前(型枠名)です。共用体変数リストは単純変
数や配列、ポインタをコンマ(,)で区切って並べます。
プログラム中での参照の仕方
“.”で共用体変数名とメンバ名をつなぎます。
25/113
OAKS16 プログラミングテキスト
<共用体タグ宣言>
union 共用体タグ {共用体リスト}
例)
union rei{
char c;
int i;
long l;
};
8bit
c
i
l
rei
このような型枠が作られます。
(まだメモリにはとられません。)
<共用体の型を指定して記憶領域を確保する>
union 共用体タグ 共用体変数リスト
例)
data
union rei data,table[2]
実際にメモリ上に、
共用体枠が取られます。
table[1]
table[2]
<共用体型枠と記憶領域を同時に宣言する>
union 共用体タグ {共用体リスト}共用体変数リスト;
例)
union rei{
char c;
int i;
long l;
}data,table[2];
data
table[1]
table[2]
<共用体メンバを参照する。>
共用体変数名.メンバ名
例)
data.char = 3;
table[2].long = 0xffffffff;
図 1.15
26/113
実際にメモリ上に、
共用体枠が取られます。
OAKS16 プログラミングテキスト
2.OAKS16 でC言語を使って開発する前に
「OAKS16 基礎テキスト」でも説明したように、組み込み用のプログラム開発に置いては、全て
の記述を C 言語だけで行なうことは出来ません。パソコン上で動かすソフトが OS(Windows な
ど)から呼び出されるように、電源投入後 C 言語 CPU が動き出したときから、C 言語の main
関数を呼び出すまでの作業をアセンブリ言語のプログラムで記述する必要があります。このプ
ログラムを OAKS16 では「スタートアッププログラム」と呼んでいます。
スタートアッププログラムの内容
① スタックポインタの設定
② マイコンの初期設定
③ メモリ領域の初期化
④ 割り込みテーブルレジスタ“INTB”の設定
⑤ main 関数の呼び出し
⑥ 割り込みベクタテーブルの設定
①、③について:
メモリマップを確認します
OAKS16 のメモリマップでは、デバッ
KD30使用中(デバッグ時)
ROM化終了時(完成品)
00000h
SFR
SFR
ユーザRAM
ユーザRAM
00400h
グ時とフラッシュ ROM に書き込んだ
ときのメモリマップが異なります。
しかし、デバッグできない領域に開
024BFh
024C0h
発することは無理があるので、デバ
02BFFh
モニタRAM
ッグ用のメモリマップに合わせて開
発を行ないます。
(モニタ領域に使わ
れていた部分は使わないことにしま
す。)
E0000h
ユーザROM
ROM:400h から 24BFh
RAM:E0000h から FBDFFh
②について:
FBDFFh
FBE00h
(これらは、モニタプログラムの設
定にあわせています。
使用禁止領域
FC000h
モニタROM
CPU モード:ワンチップモード
クロック:分周なし
ユーザROM
FFFDCh
ベクタテーブル
図 2.1
27/113
ベクタテーブル
OAKS16 プログラミングテキスト
異なる設定をしたい場合は、OAKS16 マニュアルとデータブックを読んで、ご自身の責任で行な
ってください。)
③について:
メモリの初期化は、この後説明していく C 言語のコンパイラのための設定です。C 言語のプロ
グラムでは、データを型宣言という形で、どの領域を使うかを決めていきます。そのため、ス
タートアッププログラムでは、コンパイラの要求する名前でメモリ領域をシステムに合わせた
容量で確保していきます。
NC30 のセクション
OAKS16 では NC30 というコンパイラを使用します。ここであえてこの件を確認するのは、コン
パイラによってはメモリ領域の取り方、呼び方が異なる場合もあるからです。
NC30 のデータ領域についてここで説明しておきます。
<NC30が管理するセクション>
int i = 1;
char c = '0';
int j,k;
初期値を持つ
静的変数
data section
初期値を持たない
静的変数
bss section
RAM
stack section
const char cc = 'a';
void main(void)
{
intl,m;
i=i+k;
自動変数
(コンパイラは
生成しない)
プログラム
program section
文字列・定数
rom section
}
初期値
図 2.2
28/113
data_l section
ROM
OAKS16 プログラミングテキスト
C 言語のデータ型については後で説明しますので、ここではデータ型の宣言によって、決めら
れたメモリ領域に配置されると言うことだけ意識して置いてください。
セクションの属性:NC30 が生成するセクションは、初期値の有無、配置される療育、データサ
イズなどの属性によってさらに細かく分類されます。
表 2.1
属性
内容
対象セクションベース名
I
データの初期値を保持するセクション
data
N/F/S
N-near 属性(絶対番地 0~0FFFF の 64K バイトの領域)
data、bss、rom
F-far 属性(0~FFFFF 番地の 1M バイト全メモリ領)
S-SBDATA 属性
(SB 相対アドレッシングを使用できる領域)
E/O
E-データサイズが偶数
data、bss、rom
O-データサイズが奇数
ここでの“near”や“far”はこのあとのスタートアッププログラムの説明で出ますので、な
にを意味するかを知っておいてください。
29/113
OAKS16 プログラミングテキスト
2.1.スタートアッププログラム
「OAKS16 基礎テキスト」の例題では、スタートアッププログラムは、最低限の設定だけをす
るものを紹介しました。しかし、三菱では、それぞれの CPU に対する標準のスタートアッププ
ログラムを提供しています。TM を使ってプログラムを開発する場合でも、プロジェクト作成時
に、標準のスタートアッププログラムを選択できるようになっています。
そこで、ここでは、M16C の標準スタートアッププログラムの内容説明と、OAKS16 に対応す
るように変更する方法説明をしていきます。
2.1.1.NC30 のサンプルスタートアッププログラムの構成
NC30 のサンプルスタートアッププログラムは、
“ncrt0.a30”と“sect30.inc”の 2 つのファ
イルで構成されています。
ncrt0.a30
heap領域のサイズの設定
sect30.inc
スタック領域のサイズの設定
各セクションの配置の設定
割り込みベクタテーブルの
先頭アドレスの設定
セクション開始アドレスの設定
可変ベクタテーブルの設定
.include sect30.inc
固定ベクタテーブルの設定
SB領域の設定
変数領域の初期化用
マクロ定義
プログラム部
プロセッサモードの設定
スタックポインタの初期化
FB,SBレジスタ初期化
INTBレジスタ初期化
データのnear領域の初期化
データのfar領域の初期化
heap領域の初期化
標準入出力関数ライブラリ
の初期化
main関数の呼び出し
図 2.3
30/113
OAKS16 プログラミングテキスト
2.1.2.スタートアッププログラムの作成
サンプルスタートアッププログラムは、作成するプログラムに合わせて変更する必要があり
ます。
サンプルスタートアッププログラムの変更点
ncrt.a30(スタートアッププログラム本体)
①
スタック(SP、ISP)セクションのサイズ設定
②
可変割り込みベクタの先頭アドレス設定
③
プロセッサモードなどの各種レジスタの設定
sect30.inc(セクション定義ファイル)
①
各セクションの配置と先頭アドレスの設定
②
可変/固定割り込みベクタの設定
③
スペシャルページベクタの設定
では、これからサンプルスタートアッププログラムを変更していきます。
はじめに、sect30.inc の説明、続いて ncrt.a30 の説明を行ないます。
31/113
OAKS16 プログラミングテキスト
sect30.inc(セクション定義ファイル)
;*******************************************************************************
;
;
C Compiler for M16C/60
;
Copyright 1995-1998 MITSUBISHI ELECTRIC CORPORATION
;
AND MITSUBISHI ELECTRIC SEMICONDUCTOR SYSTEMS CORPORATION
;
All Rights Reserved.
;
;
Written by T.Aoyama
;
;
sect30.inc
: section definition
;
This program is applicable when using the basic I/O library
;
;
$Id: sect30.inc,v 1.9 2000/06/20 09:07:11 simomura Exp $
;
;******************************************************************************
;--------------------------------------------------------------;
;
Arrangement of section
;
;--------------------------------------------------------------; Near RAM data area
OAKS16 ではそのままでOK
;--------------------------------------------------------------; SBDATA area
.section data_SE,DATA
Near RAM 領域の先頭アドレス設定
.org 400H
data_SE_top:
.section
bss_SE_top:
bss_SE,DATA,ALIGN
.section
data_SO_top:
data_SO,DATA
.section
bss_SO_top:
bss_SO,DATA
各データ領域の先頭にラベルを
; near RAM area
.section data_NE,DATA,ALIGN
data_NE_top:
.section
bss_NE_top:
bss_NE,DATA,ALIGN
.section
data_NO_top:
data_NO,DATA
設定する
E属性セクションはアライ
メント指定し、偶数番地か
ら配置する
スタック領域にユーザスタックと
.section bss_NO,DATA
bss_NO_top:
割り込みスタックの領域を確保す
;--------------------------------------------------------------; Stack area
る。
;--------------------------------------------------------------.section stack,DATA
“STACKSIZE”と“ISTACKSIZE”は ncrt0.a30
.blkb STACKSIZE
stack_top:
の先頭で、“equ”命令で設定する
.blkb ISTACKSIZE
istack_top:
(今回は、300h)
Heap セクションはメモリ管理関数を使うときに使用
;--------------------------------------------------------------;
heap section
する。このテキストでは使わないのでコメントアウ
;--------------------------------------------------------------;
.section heap,DATA
トする。
(使用方法は、NC30 のマニュアルを参照のこ
;heap_top:
32/113
と)
;
.blkb HEAPSIZE
注)コメントアウト:
行頭に“;”を記述することによって
コメント文とし、あとで修正が可能なよう
OAKS16 プログラミングテキスト
にしておく
;--------------------------------------------------------------OAKS16 ではこの領域に ROM を配置していない
; Near ROM data area
;-----------------------------------------------------のでコメントアウトする
---------;
.section rom_NE,ROMDATA,ALIGN
;rom_NE_top:
;
Near 領域(0ffffh 番地まで)に ROM
;
.section rom_NO,ROMDATA
;rom_NO_top:
が存在する場合のアドレス設定
;
;--------------------------------------------------------------; Far RAM data area
;--------------------------------------------------------------Near 領域以外にに RAM が存在する場合
;
.section data_FE,DATA
;
.org
10000H
のアドレス設定
;data_FE_top:
;
;
.section bss_FE,DATA,ALIGN
;bss_FE_top:
OAKS16 ではこの領域に RAM を配置していない
;
;
.section data_FO,DATA
のでコメントアウトする
;data_FO_top:
;
;
.section bss_FO,DATA
;bss_FO_top:
;
ROM 領域のアドレス設定
;
プログラムエリアは別に取るので、この場合は
;--------------------------------------------------------------; Far ROM data area
固定データの設定に使う領域を意味する
;--------------------------------------------------------------.section rom_FE,ROMDATA
.org
0F0000H
rom_FE_top:
システムに応じて変える。今回はこのままで OK
.section
rom_FO_top:
rom_FO,ROMDATA
;--------------------------------------------------------------初期値を持つデータのセクション設定
; Initial data of 'data' section
;--------------------------------------------------------------例)SEI:
.section data_SEI,ROMDATA
SB データ・データサイズが偶数・データを保
data_SEI_top:
.section
data_SOI_top:
data_SOI,ROMDATA
.section
data_NEI_top:
data_NEI,ROMDATA
.section
data_NOI_top:
data_NOI,ROMDATA
持するセクション
属性
I
N/F/S
内容
データの初期値を保持するセクション
N-near属性(絶対番地0~0FFFFの64Kバイトの領域)
F-far属性(0~FFFFF番地の1Mバイト全メモリ領域)
;
.section data_FEI,ROMDATA
;data_FEI_top:
;
;
.section data_FOI,ROMDATA
;data_FOI_top:
;
S-SBDATA属性
(SB相対アドレッシングを使用できる領域)
E/O
E-データサイズが偶数
O-データサイズが奇数
<セクションの属性>
33/113
OAKS16 プログラミングテキスト
コ ン パ イ ル オ プ シ ョ ン
;--------------------------------------------------------------; Switch Table Section
“-fswitch_other_section(-fSOS)”を指
;--------------------------------------------------------------;
.section
switch_table,ROMDATA
定しない場合はコメントアウトする
;switch_table_top:
;
このテキストでは使用しないので
;
;--------------------------------------------------------------コメントアウト
; code area
;--------------------------------------------------------------.section
program
.section interrupt
;.org ;must be set internal ROM area
.section program_S
Main プログラム配置セクション
スタートアッププログラム配置セクション
;--------------------------------------------------------------可変ベクタアドレスの先頭アドレス
; variable vector section
;--------------------------------------------------------------.section vector
; variable vector table
VECTOR_ADR は ncrt0.a30 の先頭で設定
.org VECTOR_ADR
;
;.if M62TYPE==1
;
.lword
dummy_int
; BRK (vector 0)
ベクターテーブル
;
.org (VECTOR_ADR+16)
;
.lword
dummy_int
; int3(for user)(vector 4)
が CPU のタイプで選
;
.lword
dummy_int
; timerB5(for user)(vector 5)
択できるようにな
;
.lword
dummy_int
; timerB4(for user)(vector 6)
;
.lword
dummy_int
; timerB3(for user)(vector 7)
っているが、OAKS16
;
.lword
dummy_int
; si/o4 /int5(for user)(vector 8)
;
.lword
dummy_int
; si/o3 /int4(for user)(vector 9)
では CPU が決まって
;
.lword
dummy_int
; Bus collision detection(for user)(v10)
;
.lword
dummy_int
; DMA0(for user)(vector 11)
いるので必要ない
;
.lword
dummy_int
; DMA1(for user)(vector 12)
ところはコメント
;
.lword
dummy_int
; Key input interrupt(for user)(vect 14)
;
.lword
dummy_int
; A-D(for user)(vector 14)
アウトする(削除し
;
.lword
dummy_int
; uart2 transmit(for user)(vector 15)
;
.lword
dummy_int
; uart2 receive(for user)(vector 16)
ても可)
;
.lword
dummy_int
; uart0 transmit(for user)(vector 17)
;
.lword
dummy_int
; uart0 receive(for user)(vector 18)
;
.lword
dummy_int
; uart1 transmit(for user)(vector 19)
;
.lword
dummy_int
; uart1 receive(for user)(vector 20)
;
.lword
dummy_int
; timer A0(for user)(vector 21)
;
.lword
dummy_int
; timer A1(for user)(vector 22)
;
.lword
dummy_int
; timer A2(for user)(vector 23)
;
.lword
dummy_int
; timer A3(for user)(vector 24)
;
.lword
dummy_int
; timer A4(for user)(vector 25)
;
.lword
dummy_int
; timer B0(for user)(vector 26)
;
.lword
dummy_int
; timer B1(for user)(vector 27)
;
.lword
dummy_int
; timer B2(for user)(vector 28)
;
.lword
dummy_int
; int0 (for user)(vector 29)
;
.lword
dummy_int
; int1 (for user)(vector 30)
;
.lword
dummy_int
; int2 (for user)(vector 31)
34/113
OAKS16 プログラミングテキスト
コメントアウト
;.else
.lword
dummy_int
.org (VECTOR_ADR +44)
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.org (VECTOR_ADR +68)
.lword
dummy_int
.lword
dummy_int
.lword
0fcb6bh
.lword
0fcb6bh
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
;.endif
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
; vector 0 (BRK)
;
;
;
;
DMA0 (for user)
DMA1 2 (for user)
input key (for user)
AD Convert (for user)
; uart0 trance (for user)
; uart0 receive (for user)
; uart1 trance (for user)
; uart1 receive (for user)
; TIMER A0 (for user)
; TIMER A1 (for user)
; TIMER A2 (for user)
; TIMER A3 (for user)
; TIMER A4 (for user) (vector 25)
; TIMER B0 (for user) (vector 26)
; TIMER B1 (for user) (vector 27)
; TIMER B2 (for user) (vector 28)
; INT0 (for user) (vector 29)
; INT1 (for user) (vector 30)
; INT2 (for user) (vector 31)
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
vector
vector
vector
vector
vector
vector
vector
vector
vector
vector
vector
vector
vector
vector
vector
vector
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
(for
(for
(for
(for
(for
(for
(for
(for
(for
(for
(for
(for
(for
(for
(for
(for
user
user
user
user
user
user
user
user
user
user
user
user
user
user
user
user
or
or
or
or
or
or
or
or
or
or
or
or
or
or
or
or
OAKS16 ではモニタが UART1
を使用しているので、必ず
このアドレスを設定する。
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
MR30)
注)可変割り込みのベクタ設定方法は、割り込みプログラムの項目で説明します。
35/113
OAKS16 プログラミングテキスト
;===============================================================
; fixed vector section
;--------------------------------------------------------------.section fvector
; fixed vector table
;===============================================================
; special page defination
スペシャルページ関数呼び出しのベクタ
;--------------------------------------------------------------;
macro is defined in ncrt0.a30
アドレス設定
;
Format: SPECIAL number
;
飛び先番地の設定は ncrt0.a30 内で定義
;--------------------------------------------------------------;
SPECIAL 255
される
;
SPECIAL 254
;
SPECIAL 253
;
:
;
:
;
(omitted)
このテキストでは使用しないのでコメントアウト
;
:
;
:
;
SPECIAL 24
;
SPECIAL 23
;
SPECIAL 22
;
SPECIAL 21
;
SPECIAL 20
;
SPECIAL 19
;
SPECIAL 18
;
;===============================================================
; fixed vector section
------------------;--------------------------------------------固定割り込みベクタの先頭アドレス設定
.org 0fffdch
UDI:
.lword
dummy_int
OVER_FLOW:
.lword
dummy_int
BRKI:
固定割り込みベクタの設定
.lword
dummy_int
ADDRESS_MATCH:
「OAKS16 基礎テキスト」の例題では別
.lword
dummy_int
SINGLE_STEP:
ファイルに用意しましたが、三菱の標準
.lword
dummy_int
スタートアッププログラムにはこのよ
WDT:
.lword
dummy_int
うに記述されています
DBC:
.lword
dummy_int
NMI:
.lword
dummy_int
RESET:
.lword
start
リセットベクタ
;
;*******************************************************************************
;
;
C Compiler for M16C/60
;
Copyright 1995-1998 MITSUBISHI ELECTRIC CORPORATION
;
AND MITSUBISHI ELECTRIC SEMICONDUCTOR SYSTEMS CORPORATION
;
All Rights Reserved.
;
36/113
OAKS16 プログラミングテキスト
ncrt.a30(スタートアッププログラム本体)
;*************************************************************************** ;
;
C COMPILER for M16C/60
;
Copyright 1995-1998 MITSUBISHI ELECTRIC CORPORATION
;
AND MITSUBISHI ELECTRIC SEMICONDUCTOR SYSTEMS CORPORATION
;
All Rights Reserved.
;
;
ncrt0.a30 : NC30 startup program
;
;
This program is applicable when using the basic I/O library
バンク切り替えの為の設定:このテキスト
;
;
$Id: ncrt0.a30,v 1.13 2000/06/22 13:17:04 simomura Exp $
では使用しないのでコメントアウト
;
;***************************************************************************
;
.glb __BankSelect
;__BankSelect .equ 0BH
;--------------------------------------------------------------------; HEEP SIZE definition
Heap 領域のサイズ設定
;--------------------------------------------------------------------;HEAPSIZE .equ 300h
このテキストでは使用し
ないのでコメントアウト
ユーザスタック領域のサイズを設定する
;--------------------------------------------------------------------; STACK SIZE definition
;--------------------------------------------------------------------STACKSIZE .equ 300h
スタックサイズの算出方法は後で説明するが、このテキ
;--------------------------------------------------------------------ストでは領域が十分あるので
; INTERRUPT STACK SIZE definition
;--------------------------------------------------------------------ISTACKSIZE .equ 300h
300h のままで OK
割り込みスタック領域のサイズを設定する
;--------------------------------------------------------------------; INTERRUPT VECTOR ADDRESS definition
;--------------------------------------------------------------------可変割り込みベクタテーブルの先頭アドレスを
VECTOR_ADR
.equ 0fa000h
設定
;--------------------------------------------------------------; special page definition
;--------------------------------------------------------------;
macro define for special page
OAKS16 ではユーザが使用できるのは fbdffh まで
;
;Format:
なので、余裕をとってこのアドレスを設定する。
;
SPECIAL
number
;
スペシャルページマクロの定義
SPECIAL
.macro
NUM
.org 0FFFFEH-(NUM*2)
.glb __SPECIAL_@NUM
.word __SPECIAL_@NUM & 0FFFFH
.endm
37/113
OAKS16 プログラミングテキスト
;--------------------------------------------------------------------; Section allocation
;--------------------------------------------------------------------.list OFF
.include sect30.inc
.list ON
スタティックベースレジスタ(SB)の値を
;--------------------------------------------------------------------; SBDATA area definition
コンパイラに対して定義
;--------------------------------------------------------------------.glb __SB__
__SB__
.equ data_SE_top
;====================================================================
; Initialize Macro declaration
;--------------------------------------------------------------------RAM 領域初期化用マクロ定義
N_BZERO
.macro
TOP_ ,SECT_
mov.b #00H, R0L
Near 領域の RAM クリアの為の
mov.w #(TOP_ & 0FFFFH), A1
mov.w #sizeof SECT_ , R3
マクロ
sstr.b
.endm
N_BCOPY .macro FROM_,TO_,SECT_
mov.w #(FROM_ & 0FFFFH),A0
mov.b #(FROM_ >>16),R1H
mov.w #TO_ ,A1
mov.w #sizeof SECT_ , R3
smovf.b
.endm
Near 領域の初期値コピー(ROM から RAM へ)
の為のマクロ
BZERO .macro
TOP_,SECT_
push.w
#sizeof SECT_ >> 16
push.w
#sizeof SECT_ & 0ffffh
pusha TOP_ >>16
pusha TOP_ & 0ffffh
.stk 8
.glb _bzero
.call _bzero,G
jsr.a _bzero
.endm
Far 領域の RAM クリアの為のマクロ
BCOPY .macro
FROM_ ,TO_ ,SECT_
push.w
#sizeof SECT_ >> 16
push.w
#sizeof SECT_ & 0ffffh
pusha TO_ >>16
pusha TO_ & 0ffffh
pusha FROM_ >>16
pusha FROM_ & 0ffffh
.stk 12
.glb _bcopy
.call
_bcopy,G
jsr.a _bcopy
.endm
Far 領域の初期値コピーの為のマクロ
38/113
OAKS16 プログラミングテキスト
;====================================================================
; Interrupt section start
;--------------------------------------------------------------------.insf start,S,0
.glb start
リセット解除後はここからスタート
.section interrupt
start:
;--------------------------------------------------------------------; after reset,this program will start
;--------------------------------------------------------------------- 割り込みスタックポインタ設定
ldc
#istack_top,
isp
mov.b #02h,0ah
;set istack pointer
プロセッサモードレジスタ(04h)を書き換えるために、プロテ
クトレジスタ(0ah)のビット 1(プロセッサモードレジスタへ
;
bset 1,0ah
の書き込み許可ビットをセット(書き込み許可)
mov.b #00h,04h
;set processer mode
シングルチップモードに設定:
;
bclr 1,0ah
詳細はユーザーズマニュアルp1-23 参照
mov.b #00h,0ah
プロテクトレジスタ(0ah)のビット 1 をクリア(書
込み禁止)
フラグレジスタ設定:通常のスタックは USP
ldc
#0080h,
flg
ldc
#stack_top,
sp
;set stack pointer
ldc
#data_SE_top,
sb
;set sb register
(ユーザスタックポインタを使用)
ユーザスタックポインタの設定
スタティックベースレジスタ設定
ldintb
#VECTOR_ADR
割り込みテーブルレジスタ設定:このアドレスに加算する形で割り
込み処理プログラムの先頭アドレスを決定するので、割り込みを使
う場合には必ず設定する
39/113
OAKS16 プログラミングテキスト
;====================================================================
Near 領域の初期設定
; NEAR area initialize.
;-------------------------------------------------------------------; bss zero clear
;-------------------------------------------------------------------Near 領域の bss セクション(0ffffh 番地までで初
N_BZERO
bss_SE_top,bss_SE
期値を持たない静的変数領域)の初期化(0 クリア)
N_BZERO
bss_SO_top,bss_SO
N_BZERO
bss_NE_top,bss_NE
N_BZERO
bss_NO_top,bss_NO
;---------------------------------------------------------------------Near 領域の data セクション(初期値を
; initialize data section
;--------------------------------------------------------------------持つ静的変数領域)の初期化(ROM 領域
N_BCOPY data_SEI_top,data_SE_top,data_SE
から初期値データをコピーする)
N_BCOPY
data_SOI_top,data_SO_top,data_SO
N_BCOPY
data_NEI_top,data_NE_top,data_NE
N_BCOPY
data_NOI_top,data_NO_top,data_NO
;====================================================================
; FAR area initialize.
;--------------------------------------------------------------------; bss zero clear
;--------------------------------------------------------------------;
BZERO bss_FE_top,bss_FE
;
BZERO bss_FO_top,bss_FO
;
;--------------------------------------------------------------------; Copy edata_E(O) section from edata_EI(OI) section
;--------------------------------------------------------------------;
BCOPY data_FEI_top,data_FE_top,data_FE
;
BCOPY data_FOI_top,data_FO_top,data_FO
;
;
ldc #stack_top,sp
;
.stk -40
40/113
Far 領域の RAM の初期化
(OAKS16 ではこの領域に RAM を配
置していないので使用しない→コ
メントアウトする)
OAKS16 プログラミングテキスト
;====================================================================
; heap area initialize
Heap 領域の初期化:このテキストで
;--------------------------------------------------------------------;
.glb __mbase
はメモリ管理関数を使用しないので
;
.glb __mnext
コメントアウト
;
.glb __msize
;
mov.w #(heap_top&0FFFFH), __mbase
;
mov.w #(heap_top>>16), __mbase+2
;
mov.w #(heap_top&0FFFFH), __mnext
;
mov.w #(heap_top>>16), __mnext+2
;
mov.w #(HEAPSIZE&0FFFFH), __msize
;
mov.w #(HEAPSIZE>>16), __msize+2
標準入出力関数の初期化:このテキス
;
トでは、標準入出力関数を使用しない
;====================================================================
; Initialize standard I/O
;----------------------------------------------------------------のでコメントアウト(組み込み用のプ
----;
.glb _init
ログラムでは出来るだけ使わないこ
;
.call _init,G
;
jsr.a _init
とをお勧めします。)
;
;====================================================================
; Call main() function
;--------------------------------------------------------------------ldc #0h,fb
; for debuger
Main 関数の呼び出し
.glb _main
jsr.a _main
Exit 関数部:組み込みマイコンのプログラムで
;====================================================================
; exit() function
は基本的には存在しません。ですが、リアルタ
;--------------------------------------------------------------------;
.glb _exit
イム OS などの使用によって必要な場合も考え
;
.glb $exit
られます。(このテキストでは使用しません)
;_exit:
; End program
;$exit:
;
jmp _exit
;
.einsf
;====================================================================
; dummy interrupt function
ダミー割り込み処理関数部:想定して
;--------------------------------------------------------------------dummy_int:
いない割り込みが発生してしまった
reit
.end
場合何も処理を行なわずに本のプロ
;***************************************************************************
;
グラムに復帰させる
;
C COMPILER for M16C/60
;
Copyright 1995-1998 MITSUBISHI ELECTRIC CORPORATION
;
AND MITSUBISHI ELECTRIC SEMICONDUCTOR SYSTEMS CORPORATION
以上が OAKS16 で使用するスタートアッププログラムの作り方です。このスタートアッププロ
グラムをこれからあとの例題のスタートアッププログラムとして使用していきます。
変更したスタートアッププログラムはもとのプログラムの名前と同じですので、混同しないよ
うに注意して使用してください。
41/113
OAKS16 プログラミングテキスト
2.2.プリプロセッサの処理
C コンパイラには、プリプロセッサというコンパイルの前処理を行なうソフトが標準でつい
ています。ここでは、マクロ展開や条件付コンパイル、及び指定されたファイルの包含などが
行なわれます。プアリプロセッサへの指示(プリプロセスコマンド)は#で始まります。
OAKS16 で使用している NC30 のプリプロセッサ ccp30 には#include(ファイルの包含)や
#define(マクロ定義)のほか、#PRAGMA ではじまる NC30 特有の拡張機能が存在します。
(#
PRAGMA ではじまる記述は、コンパイルの対象となる CPU に依存する記述を表します。)
<NC30がコンパイルする手順>
C言語ソースファイル
プリプロセッサ
cpp30
プリプロセス結果
C言語ソースファイル
コンパイラ
ccom30
アセンブリ言語
ソースファイル
図 2.4
42/113
OAKS16 プログラミングテキスト
2.2.1. sfr設定の為のヘッダファイル
三菱のマイコンには SFR(スペシャルファンクションレジスタ)という領域が存在します。
ここは、マイコンの動作モードや周辺 I/O の設定を行なうレジスタで、1 つのアドレスに 8 ビ
ットのレジスタを持っています。この SFR に値を設定したり、SFR から値を読み出したりする
場合、プログラム中に対象となるアドレスを記述すればよいのですが、それでは、後でリスト
をみたときに、何をしているのか解りにくいのです。ところで、SFR のそれぞれのレジスタに
はシンボルやビットシンボルが決められています。プログラムのリスト中にこのシンボルで記
述出来れば、プログラムがずっとわかりやすくなります。
OAKS16 では、M30620FCAFP の SFR のシンボル定義をまとめた oaks_sfr.h を用意しました。
このファイルでは、プリプロセスコマンドで、SFR のマクロ定義やシンボル、ビットの定義を
行なっています。
このファイルを MTOOL のフォルダ内(パスを通したフォルダ)内に入れて下さい。C 言語のソ
ースファイルの先頭で
#inclide <oaks_sfr.h> (#include はファイルを包含する為のプリプロセスコマンドです)
と記述すれば、シンボルやビットシンボルを使用して記述できます。
ここで、oak_sfr.h の説明をします。ファイルの内容が多いので、部分的に取り上げて説明し
ますので、ファイル内容全てを確認する為には付属のファイルをご覧下さい。
①#pragma ADDRESS による変数の絶対アドレスの指定
/****************************************************************************
*
ファイル名 :oaks_sfr.h
*
*
*
*
内容
: definition of M16C/62A's SFR
*
*
*
*
作成
: oaks16kit support
*
*
*
*
Version
: 1.00 ( 2002- 3-8 ) Initial
*
*****************************************************************************/
/********************************************************
*
declare SFR addresses
*
********************************************************/
#pragma ADDRESS
pm0_addr
0004H
/* Processor mode register 0 */
#pragma ADDRESS
pm1_addr
0005H
/* Processor mode register 1 */
#pragma ADDRESS
・
・
・
・
#pragma ADDRESS
cm0_addr
0006H
/* System clock control register 0 */
pur2_addr
03feH
/* Pull-up control register 2 */
#pragma ADDRESS
pcr_addr
03ffH
/* Port control register */
テキスト内省略
43/113
SFR のシンボルの
アドレスを定義し
ます。
OAKS16 プログラミングテキスト
②8 ビット単位で使用する(ビットごとのアクセスができない)SFR の定義
/********************************************************
*
declare SFR char
*
********************************************************/
unsigned char da0_addr;
/* D-A register 0 */
#define
da0
da0_addr という 8 ビットの変数を用意する
da0_addr
unsigned char da1_addr;
#define
da1
da1_addr
/* D-A register 1 */
da0 という記述は、da0_addr のことで
あるというマクロ宣言
/*-------------------------------------------------------Up/down flag ; Use "MOV" instruction when writing to this register.
--------------------------------------------------------*/
unsigned char udf_addr;
/* UP/down flag */
①で#pragma ADDRESS da0_addr
03d8h
と宣言しているので、この記述以降の da0 という記述は、
“03d8h 番地の 8 ビットのデータ領域”
を表すことになります。
da1,udf についても同様です。
③16 ビット単位で使用する SFR の定義
/********************************************************
*
declare SFR short
*
********************************************************/
/*-------------------------------------------------------Timer registers : Read and write data in 16-bit units.
--------------------------------------------------------*/
unsigned short
ta11_addr;
/* Timer A1-1 register */
#define
ta11
ta11_addr
tb2_addr;
tb2_addr
数を用意する
ta11 という記述は ta11_addr の
unsigned short
ta21_addr;
/* Timer A2-1 register */
#define
ta21
ta21_addr
・
・
テキスト内省略
・
unsigned short
#define
tb2
ta11_addr という 16 ビットの変
ことであるというマクロ宣言
/* Timer B2 register */
①で#pragma ADDRESS ta11_addr 0342h
と宣言しているので、この記述以降の ta11 という記述は、
“0342h 番地の 16 ビットのデータ領
域”を表すことになります。
44/113
OAKS16 プログラミングテキスト
④構造体と共用体を使ったビット宣言
ここでは、C 言語の記述“構造体”と“共用体”を使用して SFR のビット宣言をします。
SFR 領域のデータの中にはは、ビット一つ一つが意味を持つものがあります。これらをプログ
ラム中で扱うにはビットシンボルで記述したほうがわかりやすくなります。NC30 ではビット記
述でのコンパイルを可能にしていますので、ビットの定義をして使用します。
・8 ビット構成の SFR のビット定義
/********************************************************
*
declare SFR bit
*
********************************************************/
struct bit_def {
char
b0:1;
bit_def という名前の構造体ビットフィールドの宣言。b0 か
char
b1:1;
らb7 までの名前で 1 ビットずつの領域を確保する構造体枠
char
b2:1;
char
b3:1;
を宣言している。
char
b4:1;
char
b5:1;
char
b6:1;
char
b7:1;
byte_def という名 前の共用体 の宣言。メ ンバーとして
};
bit_def 型の bit という名の構造体と char 型の byte という
union byte_def{
struct bit_def bit;
char
byte;
};
名の変数を宣言している。
共用体の領域をメモリ上に確保する。(pm0_addr
/*-----------------------------------------------------Processor mode register 0
------------------------------------------------------*/
union byte_def pm0_addr;
は 0004h 番地)
Byte データとビットデ
#define
pm0
#define
#define
#define
#define
#define
#define
#define
#define
pm00
pm01
pm02
pm03
pm04
pm05
pm06
pm07
ータのマクロ定義
pm0_addr.byte
pm0_addr.bit.b0
pm0_addr.bit.b1
pm0_addr.bit.b2
pm0_addr.bit.b3
pm0_addr.bit.b4
pm0_addr.bit.b5
pm0_addr.bit.b6
pm0_addr.bit.b7
/*
/*
/*
/*
/*
/*
/*
/*
(詳細は次ページ参照)
Processor mode bit */
Processor mode bit */
R/W mode select bit */
Software reset bit */
Multiplexed bus space select bit */
Multiplexed bus space select bit */
Port P40 to P43 function select bit */
BCLK output disable bit */
(省略)
/*-----------------------------------------------------dm1sl
------------------------------------------------------*/
#define
dm1sl
dm1sl_addr.byte
#define
#define
#define
#define
#define
dsel0_dm1sl
dsel1_dm1sl
dsel2_dm1sl
dsel3_dm1sl
dms_dm1sl
dm1sl_addr.bit.dsel0
dm1sl_addr.bit.dsel1
dm1sl_addr.bit.dsel2
dm1sl_addr.bit.dsel3
dm1sl_addr.bit.dms
/*
/*
/*
/*
/*
45/113
DMA
DMA
DMA
DMA
DMA
request
request
request
request
request
cause
cause
cause
cause
cause
select bit */
select bit */
select bit */
select bit */
expansion bit */
OAKS16 プログラミングテキスト
ここで、もう少し詳しく順を追って、構造体、共用体宣言による SFR 定義の内容を説明しま
す。
①
struct bit_def{
char
char
char
char
char
char
char
char
};
②
b7
b0:1;
b1:1;
b2:1;
b3:1;
b4:1;
b5:1;
b6:1;
b7:1;
b6
b5
b4
b3
b2
b1
b0
構造体のビットフィールド宣言で
1ビットずつ8個の領域を取る型枠を宣言する。
union byte_def{
struct bit_def bit;
char
byte;
};
b7
b6
b5
b4
b3
b2
b1
b0
byte
共用体宣言で構造体宣言したbit_defと
char型のデータbyteを同じ領域に割り付ける型枠を宣言する。
③
union byte_def pm0_addr;
pm0_addrという名前でbyte_def型の共有体をメモリ上に確保する。
pm0_addrは、このファイル(oaks_sfr.h)の最初で
アドレス定義をしてあるので、0004H番地に領域が取られる。
pm0_addr
(0004h)
b7
b6
b5
b4
b3
b2
b1
b0
byte
④
#define
pm0
#define
pm00
pm0_addr.byte
pm0_addr.bit.b0
←この宣言でpm0_addrのデータを8ビットまとめて扱う場合は、
“pm0”と記述すればよいことになる。
←この宣言でpm0_addrの最下位のビットデータを扱う場合は、
“pm00”と記述すればよいことになる。
図 2.5
46/113
OAKS16 プログラミングテキスト
・共用体による SFR の定義
SFR の中には、16 ビット構成のもの、20 ビット構成のもの、2byte 構成のもの、3byte 構成
のものなども存在します。それらの SFR も、シンボルやビットシンボルで扱えるように定義し
てあります。考えかたは、今まで説明してきたことと同じですので、確認して使用してくださ
い。
/********************************************************
*
declare SFR union
*
********************************************************/
union{
struct{
char
b0:1;
char
b1:1;
char
b2:1;
char
b3:1;
char
b4:1;
char
b5:1;
char
b6:1;
char
b7:1;
char
b8:1;
char
b9:1;
char
b10:1;
char
b11:1;
char
b12:1;
char
b13:1;
char
b14:1;
char
b15:1;
char
b16:1;
char
b17:1;
char
b18:1;
char
b19:1;
}bit;
struct{
char
low;
/* low 8 bit */
char
mid;
/* mid 8 bit */
char
high;
/* high 8 bit */
char
nc;
/* non use */
}byte;
unsigned long
dword;
}rmad0_addr,rmad1_addr,sar0_addr,sar1_addr,dar0_addr,dar1_addr;
#define
rmad0
rmad0_addr.dword
/* Address match interrupt register 0 32 bit */
#define
rmad0l
rmad0_addr.byte.low
/* Address match interrupt register 0 low 8 bit */
#define
rmad0m
rmad0_addr.byte.mid
/* Address match interrupt register 0 mid 8 bit */
#define
rmad0h
rmad0_addr.byte.high
/* Address match interrupt register 0 high 8 bit */
47/113
OAKS16 プログラミングテキスト
以上のような形で、SFR の宣言ができます。C 言語のリストの中に
#include
<oaks_sfr.h>
と記述して、使用してください。
この記述は、NC30 のパスの通った INC フォルダに oaks_sfr.h ファイルがあることが前提で
す。C 言語ソースファイルと同じフォルダにヘッダファイルを置くこともできますが、その場
合は C 言語ファイル中の記述を、
#include
“oaks_sfr.h”
としてください。
(NC30 マニュアル内、#include 記述の項参照のこと)
48/113
OAKS16 プログラミングテキスト
3.プログラミング
ここから、実際に LCD ボードを使用して、プログラムを作っていきます。プログラムには、
定石と言うような、定番となった考え方がいくつかあります。それらの方法をいくつか紹介し
ていきます。
まずここで、LCD ボードを使用したプログラムを 1 つ作ってみましょう。C 言語の文法をお
さらいするものとして、このプログラムを完成させ、そのプログラムを用途に応じて修正して
いくことによって、プログラム技法を紹介していくことにします。
3.1.基本のプログラム
仕様
LCD ボードの sw1 から sw8 を led1 から led8 に対応させ、スイッチが ON のとき LED を点灯させ、
OFF のとき LED を消灯させる。(例題 1)
仕様:SWがONのときLED点灯
LED8
LED1
SW8
SW1
on
on
off
off
図 3.1
考え方
スタートアッププログラムは2章で作成したものをそのまま使います。
C言語ソースプログラム内では oaks_sfr.h をインクルードして記述します。
49/113
OAKS16 プログラミングテキスト
フローチャート
仕様:SWがONのとき(データ0)LED点灯(データ0)
SWが押されると
ポートの状態が‘H(0)’になる
main
SW8
SW1
on
ポート
off
I/O初期設定
SWのデータ読み
込み
LED1
LED1
ポート
‘L(0)’を出力すると
LED点灯
LEDにデータを
出力
図 3.2
この仕様の場合、スイッチから入力したデータをそのまま LED に出力すれば OK です。
ですが、この後の仕様変更を考慮して、スイッチから入力したデータを一度メモリに入力して、
それから LED に出力します。
書き方 1:(メモリを使用しない)
p7=p3;
書き方 2:(メモリを使用する)
unsigned char data; /* データ領域を 8 ビットで取りたいので char 型を使用する */
data=p3;
p7=data;
50/113
OAKS16 プログラミングテキスト
リスト
/****************************************************/
/*
プロジェクト名: rei1
*/
/*
ファイル名:
rei1.c
*/
/*
内容:
SW の ON した LED 点灯
*/
/*
日付:
2002.3.8
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/****************************************************/
#include
<oaks_sfr.h>
/* プロトタイプ宣言 */
void _main(void);
/* メインプログラム*/
/* マクロ定義 */
#define PORTIN 0x00 /* ポート方向レジスタを入力に設定する為のデータ */
#define
PORTOUT
0xff /* ポート方向レジスタを出力に設定する為のデータ */
#define
LEDOFF
0xff /* LED1 から LED8 までを消灯するデータ */
void main( void ){
unsigned char data; /* 変数宣言(スイッチの接続されているポートのデータを入れる) */
p7=LEDOFF;
/* ポート 7 に消灯データを設定 */
pd7=PORTOUT;
/* ポート 7 を出力に設定 */
pd3=PORTIN;
/* ポート 3 を入力に設定 */
for(;;){
data = p3; /* ポート 3(sw1 から sw8)のデータ読み込み */
p7=data; /* ポート 7(LED1 から LED8)にデータ出力 */
}
}
51/113
OAKS16 プログラミングテキスト
3.1.1.論理演算
先ほどの基本のプログラムでは、入力したデータをそのまま出力していました。ここでは、論
理演算を使用したデータの加工方法を説明します。論理演算とはデジタル回路等で 1(真と偽)
の二つの値だけを扱う演算です。この演算はマイコンの制御でよく使われるものです。
用語の意味
真:値がONになっている状態。1 も同等の意味。
偽:値が OFF になっている状態。0 も同等の意味。
真理値表:入出力の全てのパターンを表す表。
MIL 記号:論理回路上での論理演算を記号で表したもの。
AND(論理積)
<真理値表>
A B C
A
B
C
<MIL記号>
OR(論理和)
0
0
1
1
0
1
0
1
C
<MIL記号>
入力A,Bのどちも1のときだ
け出力Cは1
<真理値表>
A B
A
B
0
0
0
1
0
0
1
1
0
1
0
1
C
0
1
1
1
入力A,Bのどちらか一方で
も1ならば出力Cは1
ExOR(排他的論理和)
<真理値表>
A B C
A
B
C
<MIL記号>
0
0
1
1
0
1
0
1
0
1
1
0
入力A,Bが違う値ならば出
力Cは1
図 3.3
これらの論理演算はデータを加工する為に良く使われます。たとえば、データのあるビット
を残して他のビットをすべて 0 にしたいとき(他のビットを「マスクする」といいます。)は、
論理積を使います。0 と AND をとったビットはすべて 0 になり、1 と AND をとったビットはそ
のままの値が残ります。
また、特定のビット以外のビットを 1 にしたいときは、論理和を使います。1 と OR をとった
52/113
OAKS16 プログラミングテキスト
ビットはすべて 1 となり、0 と OR をとったビットだけがそのまま残ります。
また、ビットを反転したいときには 1 との排他的論理和をとります。これにより、0 は 1 に、1
は 0 になります。
3.1.2.データの反転
今度はデータのビットを反転させるプログラムを考えてみます。
仕様
SW が OFF のとき LED を点灯、ON のとき LED を消灯させて見ます。
(例題 1_a)
仕様:SWがOFFのときLED点灯
LED8
LED1
SW8
SW1
on
on
off
off
図 3.4
考え方
先ほどの排他的論理和を使用し、データを加工します。
SW8
SW1
on
off
<swデータ>
0
0
0
0
0
0
1
1
<EX_OR>
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
0
<LEDデータ>
LED1
LED1
図 3.5
53/113
“1”と排他的論理和をとることで、
ビット反転する。
OAKS16 プログラミングテキスト
フローチャート
仕様:SWがOFFのとき(データ1)LED点灯(データ0)
SWが押されると
ポートの状態が‘H(0)’になる
main
SW8
SW1
on
ポート
off
I/O初期設定
SWのデータ読み
込み
<swデータ>
0
0
0
0
0
0
1
1
<EX_OR>
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
0
<LEDデータ>
データ処理
LEDにデータを
出力
LED1
LED1
ポート
‘L(0)’を出力すると
LED点灯
図 3.6
54/113
OAKS16 プログラミングテキスト
リスト
/****************************************************/
/*
プロジェクト名: rei1_a
*/
/*
ファイル名:
rei1_a.c
*/
/*
内容:
sw の OFF した LED 点灯
*/
/*
日付:
2002.3.8
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/****************************************************/
#include
<oaks_sfr.h>
/* プロトタイプ宣言 */
void _main(void);
/* メインプログラム*/
/* マクロ定義 */
#define PORTIN 0x00 /* ポート方向レジスタを入力に設定する為のデータ */
#define
PORTOUT
0xff /* ポート方向レジスタを出力に設定する為のデータ */
#define
LEDOFF
0xff /* LED1 から LED8 までを消灯するデータ */
void main( void ){
unsigned char data;
/* 変数宣言(スイッチの接続されているポートのデータを入れる) */
p7=LEDOFF;
/* ポート 7 に消灯データを設定 */
pd7=PORTOUT;
/* ポート 7 を出力に設定 */
pd3=PORTIN;
/* ポート 3 を入力に設定 */
一度取り込んだデータを FF
と排他的論理和(EXOR)をと
for(;;){
data = p3; /* ポート 3(sw1 から sw8)のデータ読み込み */
data ^=0xff;
/* data と 0xff の排他的論理和をとる */
p7=data; /* ポート 7(LED1~LED8)にデータ出力 */
}
}
55/113
る
OAKS16 プログラミングテキスト
3.1.3.データのマスク処理
今度はデータのマスク処理について考えてみます。
仕様
SW1 がONのとき LED 全てを点灯、OFFのとき LED 全てを消灯させて見ます。(例題 1_b)
仕様:SW1がONのときLEDをすべて点灯
SW1がOFFのときLEDをすべて消灯
on
LED8
LED1
SW8
SW2 SW1
off
on
off
ここだけに着目する
SW1がON
SW2からSW8まではLEDの表示に影響を与えない
on
LED8
LED1
SW8
SW2 SW1
off
on
off
ここだけに着目する
SW1がOFF
SW2からSW8まではLEDの表示に影響を与えない
図 3.7
考え方
p3 のデータを読み込むと sw1 が ON の場合は 256×1/2 パターンあります。そのパターン全てを
分岐条件にするのは大変なので、通常は判断に使用するビット以外は 0 にしてしまう(「マス
クする」といいます)という方法をとります。これには先ほどの論理積を使用しますと 0 と AND
をとったビットはすべて 0 になり、1 と AND をとったビットはそのままのこります。
56/113
OAKS16 プログラミングテキスト
フローチャート
main
I/O初期設定
SWのデータ
読み込み
sw1=on
no
yes
LED点灯
LED消灯
図 3.8
57/113
OAKS16 プログラミングテキスト
リスト
/****************************************************/
/*
プロジェクト名: rei1_b
*/
/*
ファイル名:
rei1_b.c
*/
/*
内容:
SW1 が ON なら LED すべて点灯
*/
/*
日付:
2002.3.8
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/****************************************************/
#include
<oaks_sfr.h>
/* プロトタイプ宣言 */
void _main(void);
/* メインプログラム*/
/* マクロ定義 */
#define PORTIN 0x00
#define
PORTOUT
#define
LEDOFF
#define
LEDON 0x00
/* ポート方向レジスタを入力に設定する為のデータ */
0xff /* ポート方向レジスタを出力に設定する為のデータ */
0xff /* LED1 から LED8 までを消灯するデータ */
/* LED1 から LED8 までを点灯するデータ */
void main( void ){
unsigned char data; /* 変数宣言(スイッチの接続されているポートのデータを入れる)
p7=LEDOFF;
/* ポート 7 に消灯データを設定 */
pd7=PORTOUT;
/* ポート 7 を出力に設定 */
pd3=PORTIN;
/* ポート 3 を入力に設定 */
Sw のデータを 00000001b と AND をとり、
最下位ビット以外は強制的に“0”にす
る
for(;;){
data = p3; /* ポート 3(sw)のデータ読み込み */
data &= 0x01;/* sw1 以外のビットをマスク */
if(data == 0){
p7 = LEDON;/* SW1 が ON なら LED 点灯 */
}
else{
p7 = LEDOFF;/* sw1 が off なら LED 消灯 */
}
}
58/113
*/
OAKS16 プログラミングテキスト
3.2.スイッチ回路の考え方
今度は、スイッチを 1 回 ON するたびに LED をシフトすることを考えてみます。
仕様
最初に LED1 だけを点灯させておき、sw9 が一回押されたら LED2 だけが点灯する、もう一度 sw9
が押されたら LED3 だけが点灯するというように led の点灯位置を 1 つ左に移動させる。
(例題
2)
仕様:SW9が押されるたびにLED点灯移動
SW9
LED1
LED8
初期状態
SW9を1回押す
SW9
LED1
LED8
SW9を1回押す
SW9
LED1
LED8
SW9を押すたびぬ点灯位置が左へシフトする
SW9
LED1
LED8
一番左端まで行ったら、
右端へ戻る
SW9を1回押す
SW9
LED1
LED8
図 3.9
59/113
OAKS16 プログラミングテキスト
考え方
sw9 は port8 の bit2 に接続されています。この端子は割り込み端子(int0)としても使用でき
ますがここではただのポートとして使用します。
Sw9 はプッシュスイッチですので、
「一回押されたら」という判断はポートの状態をただ読むだ
けでは判断できません。
「スイッチが OFF から ON になった」という変化を読み取る必要があり
ます。
「ON」になったと判断したら、一度「OFF」の状態が存在してから再び「ON」にならない
と次のカウントをしてはいけません。このようなプログラムを作成するときには、判断の為の
データ(フラグと呼びます)を使用します。これは M16C の内部のフラグではなく、メモリに
変数として確保します。今回の場合スイッチ処理終了フラグを用意し、sw9 が ON の場合の処理
が終わったら、フラグをセットします。このフラグがセットされている間は、同じ処理は行わ
ず、sw9 が OFF になったときに処理終了フラグをクリアします。次に SW9 が ON されれば最初の
一回はスイッチ処理終了フラグがクリアなので処理を行い、またフラグをセットしておきます。
このようにプログラムを作成することで、「プッシュスイッチを押すたびに」というプログラ
ムを作成することが出来ます。
port
5V
0V
←ON→
←OFF→
←ON→
★「1回押された」という判断は、
OFFの状態からONに変わった時を判断する。
5V
0V
②
←ON→
①
スイッチ処理終了フラグ
0
1
③
1
←OFF→
←ON→
0
①スイッチがOFFならフラグを0にする。
②スイッチがONでフラグが0ならスイッチを押された場合の処理を行い
フラグを1(終了を意味する)に変える。
③スイッチが押されていても、フラグが1(終了)であったら
スイッチがが押されたときの処理は終わっているので何もしない。
図 3.10
60/113
OAKS16 プログラミングテキスト
フローチャート
main
I/O初期設定
SWのデータ
読み込み
sw9=on
no
yes
yes
処理済み?
no
処理済みフラグ
クリア
LEDシフト
表示データ
最終?
no
yes
表示データ初期化
処理済フラグ
セット
図 3.11
61/113
OAKS16 プログラミングテキスト
リスト
/*******************************************************************/
/*
プロジェクト名: rei2
*/
/*
ファイル名:
rei2.c
*/
/*
内容:
SW1 を ON するたびに LED の点灯位置をシフト
*/
/*
日付:
2002.3.8
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/********************************************************************/
#include
<oaks_sfr.h>
/* プロトタイプ宣言 */
void _main(void);
/* メインプログラム*/
/* マクロ定義 */
#define PORTIN 0x00 /* ポート方向レジスタを入力に設定する為のデータ */
#define
PORTOUT
0xff /* ポート方向レジスタを出力に設定する為のデータ */
void main( void ){
unsigned char leddata;
/* LED に出力するデータのための変数 */
unsigned char swdata;
/* SW の状態を表すデータのための変数 */
unsigned char swflg; /* スイッチ処理終了フラグ */
leddata=0x01;
swflg=0;
/* LED データの初期値 */
/* スイッチ処理終了フラグの初期値 */
p7=leddata^0xff;
pd7=PORTOUT;
pd8=PORTIN;
/* LED にデータ出力 */
/* ポート 7 を出力に設定 */
/* ポート 8 を入力に設定 */
for(;;){
swdata = p8 & 0x04;
/* ポート 8 のデータを読み、sw9 の状態だけをデータとして取り出す */
if(swdata == 0){
/* スイッチが ON なら */
if(swflg==0){
/* スイッチ処理終了フラグがクリアなら */
leddata<<=1;
/* LED データを左へ 1 シフト */
if(leddata==0)
leddata=0x01;
p7 = leddata^0xffff; /* LED データ出力 */
swflg = 1;
/* スイッチ処理終了フラグをセットする */
}
}
else{
swflg=0;
/* スイッチ処理終了フラグをクリアする */
}
}
}
62/113
OAKS16 プログラミングテキスト
3.2.1.チャタリングとは
例題 3 で思うような動作にならなかった方はいらっしゃいませんか?一度しか押していない
のにとなりの LED ではなく、2,3 個となりの LED が点灯したりしていませんか?これはチャタ
リングと呼ばれる現象です。スイッチは押された瞬間に接点がバウンド(付いたり離れたりす
る)します。そのため、短い時間で処理を行なうマイコンでは、一度しか押されていないのに
何度も押されたように判断してしまうのです。
port
5V
0V
←ON→
←OFF→
←ON→
チャタリング
(数~数十ms続く)
解決策: 一度スイッチのデータを読み込んだら、
次にスイッチのデータを読み込むまでに時間をおく。
OFF
ON
ON
時間をあける
時間をあける
OFF
OFF
時間をあける
ON
時間をあける
ON
時間をあける
ON
時間をあける
このようにプログラムを組めば、チャタリングの起こっている間に
何度もスイッチの状態をよんでしまうことがなくなる。
図 3.12
63/113
OAKS16 プログラミングテキスト
3.2.2.S/Wでのチャタリング除去
それでは、具体的にチャタリングを避けるためのプログラムを考えてみましょう。
方法はいくつかありますが、ここでは、プログラムの中に、「ソフトウエイト」を入れる方法
を実行してみます。この、「ソフトウエイト」とは、入出力に何も影響を与えずに、必要な時
間だけを経過させるというものです。以下にソフトウエイトのプログラム例を示します。
サイクル数
Loop:
MOV.W
#FFFFH,A0
NO
P
NO
P
NO
P
DEC.W
JNZ
A0レジスタに
FFFFをセット
3
1
A0レジスタ
FF FF
時間を増やす為に
NOP命令(何もしない)
を実行
1
1
1
A0レジスタの内容を-1する
2/2
NO
0になった
か?
YES
OAKS16のクロック = 16MHz
CPUのクロックを分周無しにした場合
命令1サイクルの実行時間は
1/(16×10)6 =0.0625×10-6
=62.5×10-6
=62.5(ns)
上記のプログラムの実行時間は
3+(1+1+1+1+4)×ffff-2=524281(サイクル)
524285×62.5ns=32767562.5ns
=約32ms
図 3.13
このプログラムはアセンブリ言語記述で書いています。C 言語ではどのようなアセンブリ言
語に翻訳されるかわからないので、プログラムの実行時間を気にする処理では、アセンブリ言
語記述を使用したほうが処理時間は、比較的明確になります。この場合、C 言語のリスト中に
記述するので
#pragma
ASM~#pragmaENDASM というアセンブリ言語記述のための書き方を使用します。以
下に、このソフトウエイトを使用したリストを示します。
64/113
OAKS16 プログラミングテキスト
リスト
/********************************************************************/
/*
プロジェクト名: rei2_a
*/
/*
ファイル名:r ei2_a.c
*/
/*
内容:
SW1 を ON するたびに LED の点灯位置をシフト
*/
/*
チャタリング除去(ソフトウエイト)
*/
/*
日付:
2002.3.8
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/*********************************************************************/
#include <oaks_sfr.h>
/* プロトタイプ宣言 */
void _main(void);
/* メインプログラム*/
void wait(void);
/* ソフトウエイトプログラム */
/* マクロ定義 */
#define PORTIN 0x00 /* ポート方向レジスタを入力に設定する為のデータ */
#define
PORTOUT
0xff /* ポート方向レジスタを出力に設定する為のデータ */
void main( void ){
unsigned char leddata;
/* LED に出力するデータのための変数 */
unsigned char swdata;
/* SW の状態を表すデータのための変数 */
unsigned char swflg; /* スイッチ処理終了フラグ */
leddata=0x01;/* LED データの初期値 */
swflg=0;/* スイッチ処理終了フラグの初期値 */
p7=leddata^0xff;
pd7=PORTOUT;
pd8=PORTIN;
/* LED にデータ出力 */
/* ポート 7 を出力に設定 */
/* ポート 8 を入力に設定 */
for(;;){
swdata = p8 & 0x04;
/* ポート 8 のデータを読み、sw9 の状態だけをデータとして取り出す */
wait();
/* チャタリング除去のための WAIT */
if(swdata == 0){
/* スイッチが ON なら */
if(swflg==0){
/* スイッチ処理終了フラグがクリアなら */
leddata<<=1;
/* LED データを左へ 1 シフト */
if(leddata==0) /* 左端までのシフトが終了したら */
leddata=0x01;/* LED データ初期値を入れる */
p7 = leddata^0xffff;/* LED データ出力 */
swflg = 1;
/* スイッチ処理終了フラグをセットする */
}
}
else{
swflg=0; /* スイッチ処理終了フラグをクリアする */
}
}
}
void wait(void){
/* 約 32ms のウエイト */
#pragma ASM
/*アセンブラ表記*/
MOV.W
#0FFFFH,A0
/*カウンタ初期値セット*/
LOOP:
NOP
NOP
NOP
DEC.W A0
JNZ LOOP
/*ループ終了?*/
#pragma ENDASM
/*アセンブラ表記終了*/
}
65/113
OAKS16 プログラミングテキスト
3.3.割り込みプログラム
割り込みとは、元々CPU が実行していた処理に別の処理を割り込ませる機能です。割り込み
が発生するとそのとき実行されていた処理は中断され、割り込み処理が実行されます。そして、
割り込み処理が終了した後で、元の処理の続きが実行されます。M16C では外部端子や内部タイ
マなどの回路から割り込み信号を入力することによって割り込みを発生させる方法と、プログ
ラム内で命令語を実行することによって割り込みを発生させる方法があります。割り込みのプ
ログラムを実行する為には割り込みプログラムの記述、CPU の設定など必要な処理があります。
メインプログラム
割り込みプログラム
割り込み要求
・割り込み要求が発生すると、今までの処理を止めて、割り込みプログラムを実行する。
・割り込み処理が終了すると、中断していた処理を続きから実行する。
このために、スタックの設定や、レジスタの退避など
割り込み特有の設定、処理等が必要になる。
図 3.14
66/113
OAKS16 プログラミングテキスト
3.3.1.M16Cの割り込み
M16C には以下に示すような割り込みがあります。このテキストでは、ハードウエア周辺 I/O
の中の INT1 による外部端子からの割込み、タイマ 0 を使用したタイマ割り込みの例を説明し
ていきます。
ノンマスカブル割り込み
未定義命令(UND命令)
オーバーフロー(INT0)命令
ソフトウエア
BRK命令
INT命令
リセット
割り込み
NMI
特殊
DBC
監視タイマ
シングルステップ
ハードウエア
アドレス一致
周辺I/O(注1)
マスカブル割り込み
注1.周辺I/O割り込みは、内蔵される周辺機能による割り込みです。
マスカブル割り込み
割り込み許可フラグ(Iフラグ)による割り込みの許可(禁止)や
割り込み優先レベルによる割り込み優先順位の変更が可能
ノンマスカブル割り込み
割り込み許可フラグ(Iフラグ)による割り込みの許可(禁止)や
割り込み優先レベルによる割り込み優先順位の変更が不可能
図 3.15
67/113
OAKS16 プログラミングテキスト
3.3.2.割り込みを使用するために必要な処理
プログラムで割り込みを使用するためには、次の処理が必要です。今回は、C 言語でプログ
ラム記述をしているので、割り込み処理も C 言語で記述します。
① 割り込み関数の記述
② 割り込みの許可
③ 割り込みベクタの設定
割り込み関数
割り込みで実行させたい処理を記述する関数です。
割り込み関数は#pragma INTERRUPT 関数名 で宣言します。
割り込み関数は、元の処理を中断して実行されるので、割り込み関数の中で書き換えられたく
ないレジスタを割り込み前後に退避・復帰する必要があります。(C 言語で記述していると、レ
ジスタを使用していないような気になりますが、C 言語ソースはアセンブリ言語に落とされま
すのでそこでレジスタは使用されています。)また、割り込み関数が終了したあと、元の関数
へ戻る処理も必要です。#pragma INTERRUPT を使用することで、これらの処理を自動的に行な
います。
割り込み許可
割り込み許可は次の 2 条件で許可され、両方の条件が揃ったときのみ、割り込みが受け付け
られます。リセット後はどちらも禁止の状態になっています。
・I フラグ(割り込み禁止フラグ)による許可
全てのマスカブル割り込みが許可されます。
・割り込み要因ごとの割り込み優先レベルの設定による許可
該当する要因の割り込みのみを許可します。
割り込み優先レベルは1~7のとき割り込み許可です。1~7では、数値が大きいほ
ど優先レベルが高くなります。したがって、重要な割り込みが優先して実行されるよ
うに、割り込みに優先順位を設定します。
割り込みベクタの設定
割り込みベクタは、割り込み関数の先頭番地を格納しておくところです。割り込み関数は、他
の関数と違い、別の関数から呼ばれて実行されるのではありません。割り込み要求が受け付け
られたときに割り込みベクタに格納された番地からプログラムを実行するという動作によっ
て割り込み関数が実行されます。割り込みベクタの設定は、2.1.2.スタートアッププログラム
の作成を参照して下さい。
68/113
OAKS16 プログラミングテキスト
割り込みの処理順序
割り込みは次のような手順で実行されます。
① 割り込み要因からの割り込み要求
② 割り込み優先レベル判定
③ 割り込み処理
a)割り込みシーケンス(割り込み要求の受付から割り込み関数の先頭番地に到るまで
の処理で、マイコンのハードウエアが自動的に行ないます。)
b)レジスタを退避します。
c)割り込み関数の実行
d)レジスタ復帰、元の処理への復帰
NC30 では#pragma INTERRUPT を使用すると、コンパイラが自動的に必要な命令語(レジスタの
退避、復帰、リターン)を付加してくれます。そのため b)、d)の処理は自動的に行なわれ、割
り込み関数内に記述する必要はありません。
割り込みシーケンス
① 00000h 番地を読むことで、CPU は割り込み情報(割り込み番号、割り込み要求レベル)を
獲得する。その後、該当する割り込みの要求ビットが“0”になる。
(このため、プログラム中で 00000h 番地を読み出さないで下さい。割り込み要求がキャン
セルされる場合があります。)
② 割り込みシーケンス直前のフラグレジスタ(FLG)の内容を CPU 内部の一時レジスタ(注 1)
に退避する。
③ 割り込み許可フラグ(I フラグ)、デバッグフラグ(D フラグ)、及びスタックポインタ指定
フラグ)を“0”にする。
(ただし U フラグは、ソフトウエア割り込み番号 32~63 の INT 命
令を実行した場合は変化しない)
(スタックポインタ指定フラグ“0”は、ISP(割り込みスタックポインタ)の使用を指定
します。ISP は、リセット時に、00000h になっていますので、スタートアッププログラ
ム内で、必ず、スタック領域の最終番地を指定してください。)
④ CPU 内部の一時レジスタの内容をスタック領域に退避する。
⑤ プログラムカウンタの内容をスタック領域に退避する。
⑥ プロセッサ割り込み優先レベル(IPL)に、受け付けた割り込みの割り込み優先レベルを設定
する。
割り込みシーケンス終了後は、割り込みルーチンの先頭番地から命令を実行する。
69/113
OAKS16 プログラミングテキスト
3.3.1.INT0 割り込み端子からの割り込み
OAKS16LEDboard には、INT0 と INT1 割り込み端子にプッシュスイッチを接続してあります。残
念ながら、このスイッチは H/W でチャタリングの除去をしていませんので、割り込みプログラ
ムを書くときにもチャタリングを考えなければなりません。そのため、ここでは、1 回だけ割
り込み処理を行なうプログラムをつかって、割り込みプログラムの記述方法を説明します。
INT0 を制御するレジスタ
<INT0の割り込みを制御するレジスタ>
割り込み制御レジスタ
(005Dh番地)
リセット後:XX00X000b
b7
b0
X X 0
割り込み優先レベル選択ビット
b2 b1 b0
0 0 0 :レベル0(割り込み禁止)
0 0 1 :レベル1
0 1 0 :レベル2
0 1 1 :レベル3
1 0 0 :レベル4
1 0 1 :レベル5
1 1 0 :レベル6
1 1 1 :レベル7
割り込み要求ビット
0:割り込み要求なし
1:割り込み要求あり
極性切替ビット
0:立ち下がりエッジを選択
1:立ち上がりエッジを選択
必ず“0”を選択
読み出し時:不定
書き込み時:“0”にする
割り込み要因レジスタ
(035Fh番地)
リセット後:00000000b
b7
b0
IFSR7 IFSR6 IFSR5 IFSR4 IFSR3 IFSR2 IFSR1 IFSR0
片エッジを選択した場合のみ立ち上が
り、立ち下がりの切り替え可能。
両エッジの場合は“0”に固定する
INT0割り込み極性切替ビット
0:片エッジ
1:両エッジ
INT1割り込み極性切替ビット
0:片エッジ
1:両エッジ
INT2割り込み極性切替ビット
0:片エッジ
1:両エッジ
INT3割り込み極性切替ビット
0:片エッジ
1:両エッジ
INT4割り込み極性切替ビット
0:片エッジ
1:両エッジ
INT5割り込み極性切替ビット
0:片エッジ
1:両エッジ
割り込み要因切替ビット
0:SIO3
1:INT4
割り込み要因切替ビット
0:SIO4
1:INT5
図 3.16
70/113
OAKS16 プログラミングテキスト
仕様
プログラムスタート時 LED1~LED8 まで消灯。INT0 のスイッチが押されたら LED1~LED8 まで
点灯。(例題 3)
仕様:INT0(SW9)が押されたらLED点灯(割込みを使用)
INT0(SW9)
LED1
LED8
初期状態
INT0(SW9)を1回押す
INT0(SW9)
LED1
LED8
図 3.17
考え方
main プログラム:LED を消灯させたら、割り込み待ちの無限ループを組む。
割り込みプログラム:LED を点灯させ割り込みから復帰させる。
71/113
OAKS16 プログラミングテキスト
フローチャート
メインプログラム
割り込みプログラム
ポート7(LED)
初期設定(消灯)
LEDに点灯データを
出力
割り込み要因選択
レジスタの設定
割り込み許可
図 3.18
ファイルの分割
スタートアッププログラム:o_srt0.a30+o_sec30.inc
メインプログラムファイル:rei3.c+OAKS16.h
ファイルの変更
① o_sect30.inc の割り込みベクタのアドレスを設定する。
② main プログラムに割り込みプログラムを記述する。
72/113
OAKS16 プログラミングテキスト
リスト
o_ncr0.a30(OAKS16 用のスタートアッププログラム:本テキスト 2.2.1.参照)
;*************************************************************************** ;
;
C COMPILER for M16C/60
;
Copyright 1995-1998 MITSUBISHI ELECTRIC CORPORATION
;
AND MITSUBISHI ELECTRIC SEMICONDUCTOR SYSTEMS CORPORATION
;
All Rights Reserved.
;
;
ncrt0.a30 : NC30 startup program
;
;
This program is applicable when using the basic I/O library
;
;
$Id: ncrt0.a30,v 1.13 2000/06/22 13:17:04 simomura Exp $
;
;***************************************************************************
;
.glb __BankSelect
;__BankSelect .equ 0BH
;--------------------------------------------------------------------; HEEP SIZE definition
;--------------------------------------------------------------------;HEAPSIZE .equ 300h
割り込み用のスタックサイズを
;--------------------------------------------------------------------; STACK SIZE definition
定義する
;--------------------------------------------------------------------STACKSIZE .equ 300h
;--------------------------------------------------------------------可変ベクタの先頭アドレスを定義する。ユーザ
; INTERRUPT STACK SIZE definition
;--------------------------------------------------------------------が使用できる ROM 領域で、ユーザプログラムに
ISTACKSIZE .equ 300h
支障のない領域にする。また、できるだけ偶数
;--------------------------------------------------------------------; INTERRUPT VECTOR ADDRESS definition
番地を設定する。
;--------------------------------------------------------------------VECTOR_ADR
.equ 0fa000h
(省略)
ISP(割り込みスタックポイン
start:
タ)に値をセットする。
;--------------------------------------------------------------------; after reset,this program will start
;--------------------------------------------------------------------ldc #istack_top,
isp ;set istack pointer
mov.b #02h,0ah
;プロセッサモードレジスタ書き込みイネーブル
ここでは USP を指定しているが、
;
bset 1,0ah
mov.b #00h,04h
;シングルチップモード
割り込み処理のときだけ自動的
mov.b #00h,05h
;非拡張、ノーウエイト
に ISP が使われる。ここで、ISP
;
bclr 1,0ah
mov.b #00h,0ah
;プロセッサモードディゼーブル
を設定して USP を使わないとい
;
mov.b #01h,0ah
;クロックコントロールレジスタ書き込みイネーブル うことも可能。
mov.b #08h,06h
;発振
mov.b #20h,07h
;分周なし
mov.b #00,0ah
;クロックコントロールレジスタ書き込みディゼーブル
;
;
ldc #0080h,
flg
;ユーザスタックを指定
ldc #stack_top,
sp
;set stack pointer
ldc #data_SE_top, sb
;set sb register
ldintb
#VECTOR_ADR
可変ベクタアドレスの設定
73/113
OAKS16 プログラミングテキスト
o_sect30.a30(OAKS16 用セクション定義ファイル:本テキスト 2.2.1.参照)
;*******************************************************************************
;
;
C Compiler for M16C/60
;
Copyright 1995-1998 MITSUBISHI ELECTRIC CORPORATION
;
AND MITSUBISHI ELECTRIC SEMICONDUCTOR SYSTEMS CORPORATION
;
All Rights Reserved.
;
;
Written by T.Aoyama
;
;
sect30.inc
: section definition
;
This program is applicable when using the basic I/O library
;
;
$Id: sect30.inc,v 1.9 2000/06/20 09:07:11 simomura Exp $
;
********************************************************************************
(省略)
;--------------------------------------------------------------; Stack area
割り込みスタックを確保し、その採取番地に
;--------------------------------------------------------------.section stack,DATA
“istak_top”のラベルをつける
.blkb STACKSIZE
stack_top:
.blkb ISTACKSIZE
istack_top:
(省略)
;--------------------------------------------------------------; variable vector section
;--------------------------------------------------------------.section vector
; variable vector table
.org VECTOR_ADR
;
.lword
dummy_int
; vector 0 (BRK)
.org (VECTOR_ADR +44)
KD30 で使用しています。必
.lword
dummy_int
; DMA0 (for user)
.lword
dummy_int
; DMA1 2 (for user)
ず、このように記述してく
.lword
dummy_int
; input key (for user)
ださい!
.lword
dummy_int
; AD Convert (for user)
.org (VECTOR_ADR +68)
.lword
dummy_int
; uart0 trance (for user)
.lword
dummy_int
; uart0 receive (for user)
.lword
0fcb6bh
; uart1 trance (for user)
.lword
0fcb6bh
; uart1 receive (for user)
.lword
dummy_int
; TIMER A0 (for user)
.lword
dummy_int
; TIMER A1 (for user)
.lword
dummy_int
; TIMER A2 (for user)
.lword
dummy_int
; TIMER A3 (for user)
.lword
dummy_int
; TIMER A4 (for user) (vector 25)
.lword
dummy_int
; TIMER B0 (for user) (vector 26)
.lword
dummy_int
; TIMER B1 (for user) (vector 27)
.lword
dummy_int
; TIMER B2 (for user) (vector 28)
.glb _int0
.lword
_int0
; INT0 (for user) (vector 29)
.lword
dummy_int
; INT1 (for user) (vector 30)
INT0 のプログラムの先頭番地を記述する。
.lword
dummy_int
; INT2 (for user) (vector 31)
プログラムは、このファイルとは別ファイルに
なるので、“.glb”で宣言する。
74/113
OAKS16 プログラミングテキスト
rei3.c(メインプログラム+割り込みプログラム)
/****************************************************/
/*
プロジェクト名: rei3
*/
/*
ファイル名:
rei3.c
*/
/*
内容:
int0 割り込みチェック
*/
/*
日付:
2001.10.5
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/****************************************************/
/* インクルードファイル */
#include <oaks_sfr.h>
/* OAKS16 用定義ファイル */
/* プロトタイプ宣言 */
void main(void);
void int0(void);
#pragma
INTERRUPT int0
INT0 の割り込み関数の定義:
#pragma でレジスタの退避、復帰、割り込みから
のリターンをコンパイラが自動的にコード生成
/* マクロ定義 */
#define PORTIN 0x00
#define
PORTOUT
0xff
する(記述しないで済む)
/*---------------------------------------------------------------------------------------------*/
割り込み制御レジスタの設定。割り込
/* 関数名 :main()
*/
/* 機能
:LED の初期設定をし、消灯させたまま割り込み待ち み要因レジスタは、リセット時に片エ
*/
/*----------------------------------------------------------------------------------------------*/
ッジ選択になっているので、変更しな
void main( void ){
い場合は、設定しなくてもよい。
p7=0xff;
pd7=PORTOUT;
/* LED に消灯データ出力 */
/* ポート 7 を出力に設定 */
int0ic=0x07;
/* int0 の割り込み要因設定(立下りエッジ、優先レベル 7 )*/
_asm("\tFSET I");
/* 割り込み許可 */
for(;;){
}
/* 何も処理を行なわず、割り込み待ち */
割り込み許可:この命令は、C 言語で
は記述できないので、インラインアセ
}
ンブラ記述をする。
/*--------------------------------------------------------------------------------------------------*/
/* 関数名 :int0()
*/
/* 機能
:int0 割り込み関数
*/
/*
:割り込みが発生したら LED に点灯データを出力する。
*/
/*--------------------------------------------------------------------------------------------------*/
割り込みプログラム
void int0(void){
/* 割り込みプログラム */
p7=0x00;
/* LED に点灯データを出力 */
}
75/113
OAKS16 プログラミングテキスト
3.3.2.タイマを使った割り込み
マイコン制御で時間を計測する為にはタイマを使用します。タイマの基本機能は基準になる
クロックやパルスをカウントすることです。OAKS16 に搭載されている M30620FCAFP は、16 ビ
ットタイマを 11 本内蔵しています。11 本のタイマは、持っている機能によってタイマ A(5
本)とタイマ B(6 本)の 2 種類に分類できます。タイマの詳しい機能はマニュアルを参照し
て下さい。これらのタイマは様々な機能を持っていますがここでは、タイマ A0 を基本機能で
あるタイマモードで使用して、一定時間ごとの割り込みをかける方法を説明します。
タイマ A0 の構成
データバス上位
データバス下位
下位8ビット
上位8ビット
リロードレジスタ(16)
カウントソー
ス
カウンタ(16)
割り込み要求発生
カウント
開始フラグ
カウンタ:
リロードレジスタ:
カウントソース:
カウントソースをカウントし、
タイマがオーバーフローすると割り込みが発生する。
タイマモードで周期的に動作させる場合の初期値を入れるレジスタ。
タイマがオーバーフローするたびに、ここからタイマにリロードされる。
f1:メインクロックを1分周した動作クロック
f8:メインクロックを8分周した動作クロック
f32:メインクロックを32分周した動作クロック
fc32:サブクロックを32分周した動作クロック
図 3.19
76/113
この中から
選択する
OAKS16 プログラミングテキスト
タイマ A0 関連レジスタ
<タイマA0関連レジスタ>
タイマA0モードレジスタ TA0MR(0396h番地) リセット時:0000000b
b7 b6 b5 b4 b3 b2 b1 b0
動作モード選択ビット
00:タイマモード
01:イベントカウントモード
10:ワンショットパルスモード
11:パルス幅変調(PWM)モード
動作モードによって機能が異なる
カウントソース選択ビット
(動作モードによって機能が異なる)
タイマA0レジスタ TA0(0386h0387h番地) リセット時:不定
b15
b0
カウンタの値をセットする
カウント開始フラグ
b7 b6 b5 b4
TABRS(0380h番地) リセット時:0000000b
b3 b2 b1 b0
タイマA0カウント開始フラグ
タイマA1カウント開始フラグ
タイマA2カウント開始フラグ
タイマA3カウント開始フラグ
タイマA4カウント開始フラグ
タイマB0カウント開始フラグ
タイマB1カウント開始フラグ
タイマB2カウント開始フラグ
図 3.20
77/113
OAKS16 プログラミングテキスト
M16C のタイマは、様々な機能を持っているため、関連レジスタの設定が難しくなっています。
そのため、ユーザーズマニュアル内では、使用する条件ごとに設定の説明がなされています。
本テキストでも、タイマ割り込みを使用するために必要な設定に限定して説明します。その他
の使用方法については、ユーザーズマニュアルを参照して下さい。
設定内容
・カウントソース f32(メインクロックを 32 分周したクロック)
↑一秒の計測をしたいのでカウントソースの周期の最大のもの
・パルス出力なし
・ゲート機能なし
<タイマA0を使用するための設定>
タイマA0モードレジスタ TA0MR(0396h番地) リセット時:0000000b
b7 b6 b5 b4 b3 b2 b1 b0
0
0
0
0
0
0
動作モード選択ビット
00:タイマモード
パルス出力機能選択ビット
0:パルス出力なし
ゲート機能選択ビット
b4 b3
0 0
0 1
ゲート機能なし
(TA0IN端子は通常のポート端子となる)
タイマモードでは“0”を設定する
カウントソースの周期
カウントソース選択ビット
f(XIN):16MHzの場合
b7 b6
62.5ns
0 0 f1
500ns
0 1 f8
1 0 f32
2μs
OAKS16ではサブクロックを
1 1 fC32
接続していないので選択できない
タイマA0レジスタ TA0(0386h0387h番地) リセット時:不定
b15
b0
カウンタの値をセットする
0000hからFFFFhを設定可能
カウント開始フラグ
b7 b6 b5 b4
TABRS(0380h番地) リセット時:0000000b
b3 b2 b1 b0
タイマA0カウント開始フラグ
0:カウント停止
1:カウント開始
タイマA1~B2カウント開始フラグ
図 3.21
78/113
OAKS16 プログラミングテキスト
仕様
LED1~8 を 1 秒ごとに 2 進数でカウントアップさせる。(例題 4)
仕様:LEDを1秒ごとに2進数でカウントアップ
LED8
LED1
初期状態
1秒経過
LED8
LED1
1秒経過
LED8
LED1
1秒経過
LED8
LED1
1秒ごとにカウントアップ
LED8
LED1
“11111111b”まで表示したら“00000000b”にもどる。
図 3.22
考え方
タイマ A0 を使用して、カウントソースを作る。
1秒計時
タイマ A0 のカウントソースを f32 とすると 1 カウントは 1/(16MHz/32)=2μS
計時するには 1/2μs=500000 であるが 16 ビットカウンタなので 500000 回は設定できない。
79/113
OAKS16 プログラミングテキスト
1 秒=100ms×10 回とすると 100ms=2μS×50000 回
100ms の割り込みをかけて、これを 10 回カウントすればよいことになる。
フローチャート
メインプログラム
タイマ割り込みプログラム
1秒用カウンタ初期値設定
割り込み認識フラグを
セットする
LEDで0~FFまでカウントす
るためのカウンタを初期化
タイマA0の設定
カウント開始
割り込み許可
TA0割り込みあり?
割り込みフラグ=1?
NO
TA0フラグクリア
1秒用カウンタ-1
1秒たった?
NO
LEDカウンタ+1
LEDにデータを出力する
(LEDは0で点灯するので、
カウントデータは、
ビット反転する)
1秒用カウンタ初期値設定
図 3.23
80/113
OAKS16 プログラミングテキスト
ファイルの分割
スタートアッププログラム:o_srt0.a30+o_sec30.inc
メインプログラムファイル:rei3.c+OAKS16.h
ファイルの変更
③ o_sect30.inc の割り込みベクタのアドレスを設定する。
④ main プログラムに割り込みプログラムを記述する。
リスト
スタートアッププログラム(o_ncr0.a30)の変更は INT0 割り込みのときと同じなので省略し
ます。
o_sect30.a30(OAKS16 用セクション定義ファイル)の変更も、基本的には INT0 のときと同じで
すが、INT0 は使用しないので,]ベクタの内容をダミーに戻し、TA0 のベクタに、TA0 割り込み
プログラムの先頭アドレスをセットします。
;--------------------------------------------------------------; variable vector section
;--------------------------------------------------------------.section vector
; variable vector table
.org VECTOR_ADR
;
.lword
dummy_int
; vector 0 (BRK)
.org (VECTOR_ADR +44)
.lword
dummy_int
; DMA0 (for user)
KD30 が使用するので必ずこのよ
.lword
dummy_int
; DMA1 2 (for user)
.lword
dummy_int
; input key (for user)
うに記述する。
.lword
dummy_int
; AD Convert (for user)
.org (VECTOR_ADR +68)
.lword
dummy_int
; uart0 trance (for user)
.lword
dummy_int
; uart0 receive (for user)
.lword
0fcb6bh
; uart1 trance (for user)
.lword
0fcb6bh
; uart1 receive (for user)
.glb _ta0int
.lword
_ta0int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
.lword
dummy_int
lword
dummy int
;
;
;
;
;
;
;
;
;
;
; TIMER A0 (for user)
TIMER A1 (for user)
TIMER A2 (for user)
TIMER A3 (for user)
TIMER A4 (for user) (vector
TIMER B0 (for user) (vector
TIMER B1 (for user) (vector
TIMER B2 (for user) (vector
INT0 (for user) (vector 29)
INT1 (for user) (vector 30)
INT2 (for user) (vector 31)
81/113
タイマ A0 割り込みプログラムの
先頭番地設定
25)
26)
27)
28)
OAKS16 プログラミングテキスト
リスト(1/2)
/****************************************************/
/*
プロジェクト名: rei4
*/
/*
ファイル名:
rei4.c
*/
/*
内容:
LED のカウントアップ
*/
/*
日付:
2001.10.5
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/****************************************************/
/* インクルードファイル */
#include <oaks_sfr.h>
/* OAKS16 用定義ファイル */
/* プロトタイプ宣言 */
void main(void);
void ta0int();
#pragma
INTERRUPT ta0int
/*
マクロ定義*/
#define
PORTOUT
#define
#define
/* メイン関数
/* 割込み関数
0xff
CNT_TA0
CNT1S_TA0 10
*/
*/
/* 出力ポート設定データ */
50000-1
/* タイマ A0 カウンタ値
/* タイマ A0 1 秒カウンタ値 */
*/
/* 変数の宣言 */
static
char ta0flag;
/* ta0 割り込みフラグ */
static
char cnt1s;
/* 1 秒用カウンタ */
static
char ledcnt;
/* led 表示用カウンタ */
/*----------------------------------------------------------------------------------------------------------*/
/* 関数名 :main()
*/
/* 機能
:1秒ごとに LED1 から LED8 までを使って二進数カウントアップ */
/*
:1秒計時(タイマ A0)
*/
/*
:
タイマ A0 のカウントソースを f32 とする
*/
/*
:
1 カウントは 1/(16MHz/32)=2μS
*/
/*
:
計時するには 1/2μs=500000 であるが
*/
/*
:
16 ビットカウンタなので 500000 回は設定できない
*/
/*
:
1 秒=100ms*10 回とすると
*/
/*
:
100ms=2μS*50000 回
*/
/*-----------------------------------------------------------------------------------------------------------*/
void main(void)
{
cnt1s=CNT1S_TA0;
ledcnt=0x00;
p7=ledcnt^0xff;
pd7=PORTOUT;
udf =
ta0mr
ta0 =
ta0ic
tabsr
/* LED データ表示準備
/* ポート 7 出力設定
0x00;
= 0x80;
CNT_TA0;
= 0x06;
= 0x01;
_asm( "\tFSET
/* 1 秒用カウンタ初期化(10)
/* led カウンタ初期化 */
/*
/*
/*
/*
I");
/* ダウンカウント設定
タイマモード クロック:1/32
タイマ値の初期化
割り込みレベルの設定
カウント開始
/* 割り込み許可
82/113
*/
*/
*/
*/
*/
*/
*/
*/
*/
OAKS16 プログラミングテキスト
リスト(2/2)
while(1){
if(ta0flag==1){
ta0flag=0;
cnt1s--;
if(cnt1s==0){
ledcnt++;
p7=ledcnt^0xff;
cnt1s = CNT1S_TA0;
}
}
}
/* ta0flag をクリア */
/* 1 秒用カウンタ-1 */
/* 1 秒用カウンタが 0 なら(もし 1 秒たったら) */
/* LED カウンタ+1
*/
/* 1 秒用カウンタ初期化(10)
}
/*---------------------------------------------------------------------*/
/* 関数名 :ta0int()
*/
/* 機能
:ta0 割り込み関数
*/
/*
:割り込みのたびに ta0flag をセットする。*/
/*----------------------------------------------------------------------*/
void ta0int()
{
ta0flag=1;
}
/* ta0flag をセット */
83/113
*/
OAKS16 プログラミングテキスト
3.4.LCDモジュールの制御
LCD モジュールとは、LCD 表示器に LCD コントローラが予め組み込まれた表示用の部品です。
OAKS16 の LCD ボードで使用している SC1602BS は広く一般に使用されておりますが、あまり詳
しい資料がありません。そこで、本テキストでは、LCD モジュールの使い方を含めて、プログ
ラムの記述を説明していきます。
3.4.1.LCDモジュールの構成
以下に LCD モジュール(SC1602BS)の構成を示します。
表示面
データ入力
D0
D1
D2
D3
D4
D5
D6
D7
制御信号入力
RS
E
R/W
LCD明るさ調整用
アナログ電圧入力
VLC
L
C
D
コ
ン
ト
ロ
|
ラ
制御信号
メモリ部
データ
RS:レジスタセレクト
1→データ入力
0→制御入力
E:イネーブル
1→イネーブル
R/W:リード/ライト
1→データ読み出し
0→データ書き込み
図 3.24
LCD モジュールに表示データを書き込むと、LCD モジュール内部で文字表示位置に対応する
メモリ(DDRAM:表示データ RAM)に書き込まれます。それが書き改められない限りは、内部の
LCD コントローラの制御により自動的に表示され続けます。
84/113
OAKS16 プログラミングテキスト
3.1.2.OAKS16LCDBOARDの配線
LCD モジュール(SC1602BS)は、データ線を 8 本持っていますが、4 本だけ接続して使用す
ることができます。マイクロコンピュータで制御する場合、制御に使用するポートはできるだ
け少ない方が好まれるので、4 本で制御される場合が多いようです。OAKS16 でも、4 本で制御
するように配線してありますので、まず、配線を確認してください。
M16C
LCDモジュール(SC1602SB)
P47
P46
P45
P44
(ノンコネクト)
Vcc
GND
VO
RS
R/W
E
DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
P43
P42
P41
P40
図 3.25
85/113
可変抵抗
30KΩ
OAKS16 プログラミングテキスト
3.1.3.LCDモジュールの初期設定
SC1602SB は、電源 ON のあと、一連の初期設定を行ないます。これは、この部品を使用する
際の決まりのようなものですので、無条件で実行してください。
① リセットのためのファンクションを機械的に 8 ビット転送で 3 回行ないます。
電源オン
15ms以上のウエイト
ファンクションセット
同じファンクションセットの
データを3回転送します。
RS
4.1ms以上のウエイト
0
R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0
0 0 1 1 0 0 0 0
ファンクションセット
100μs以上のウエイト
ファンクションセット
ファンクションセット
のコード
M16Cからは、出力しないが、
GNDに接続してあるので、
LCDモジュールは“0”の
データを受け取る。
インタフェース長
0:4ビット
1:8ビット
図 3.26
この段階で、実は LCD モジュールは 8 ビットでデータを受信します。OAKS16 の LCDBOARD で
は、LCD モジュールの下位 4 ビットは GND に接続してありますので、下位 4 ビットには“0”の
データが転送されます。ここで、3 回ファンクションデータを送ることにより、確実に LCD モ
ジュールが 8 ビットでデータを受け取る状態にします。(この LCD モジュールは 4 ビットでデ
ータを送る場合、時分割で 2 回に分けてデータを送受信しますので、4 ビット指定か 8 ビット
指定かで動作が大きく異なります。
そのため、まず標準である 8 ビットの転送を確実にします。
この後、4 ビットでデータを送るための設定、表示の初期化などの、本当の意味の初期設定
が続きます。
86/113
OAKS16 プログラミングテキスト
初期設定続き
R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
RS
0
0
0 0 1 0 0 0 0 0
ファンクションセット
インタフェース長を
4ビットにする
ファンクションセット
のコード
表示オフ
このデータはLCDボード側は、
8ビットモードで受け取る。
(下位8ビットは“0000b”を受
け取る。)
インタフェース長
0:4ビット
1:8ビット
ファンクションセット
インタフェース長を
4ビットにする
表示を2桁、
5×7ドットに設定する
M16Cからは、出力しないが、
GNDに接続してあるので、
LCDモジュールは“0”の
データを受け取る。
R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
RS
0
表示クリア
0
0 0 1 0 1 0 * *
ファンクションセット
のコード
エントリーモード
セット
デューティ
0:1桁
1:2桁
フォント
0:5×7ドット
1:5×10ドット
インタフェース長
0:4ビット
1:8ビット
RS
0
R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0
0 0 0 0 1 0 0 0
表示ON・OFF
のコード
RS
0
RS
0
全表示の
ON・OFF
0:表示OFF
1:表示ON
B(ブリンク)
0:なし
カーソルの
1:あり
ON・OFF
0:なし
1:あり
R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0
0 0 0 0 0 0 0 1
表示クリア
のコード
R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0
0 0 0 0 0 1 0 0
エントリモード
のコード
カーソルの進む方向
0:ディクリメント
1:インクリメント
図 3.27
以上が、初期設定になります。
87/113
表示のシフト
0:なし
1:あり
このデータから、
LCDボードは4ビットモード
で受け取れる。
データを、上位4ビット、下位4
ビットの順に送ると、
LCD内部では8ビットデータとし
処理される。
OAKS16 プログラミングテキスト
3.1.4.LCDモジュール制御の為の関数
ここで、LCD モジュールの制御の為の関数を作りましょう。
① ウエイト関数
今回の LCD ボードの制御には、ウエイト時間が 3 種類要求されています。
15ms、4.1ms、0.1ms のそれぞれのウエイト関数を作ります。
ここでは、チャタリング除去で使用したソフトウエイトを利用します。
サイクル数
MOV.W #FFFFH,A0
Loop: NOP
1
NOP
1
1
NOP
DEC.W
JNZ
A0レジスタに
FFFFをセット
3
時間を増やす為に
NOP命令(何もしない)
を実行
1
A0レジスタの内容を-1する
2/2
NO
0になっ
たか?
YES
OAKS16のクロック = 16MHz
CPUのクロックを分周無しにした場合
命令1サイクルの実行時間は
上記のプログラムの実行時間は
524285×62.5ns=32767812.5ns
=約32ms
1/16×10 =0.0625×10
=62.5×10
=62.5(ns)
カウンタの値を変える事で、ウエイト時間を変えることができます。
①15msのウエイト
15ms/62.5ns=240000サイクル
3+(1+1+1+1+4)×(カウンタ値)-2=240000(サイクル)
カウンタ値=30000(7530h)
①4.1msのウエイト
4.1ms/62.5ns=65600サイクル
3+(1+1+1+1+4)×(カウンタ値)-2=65600(サイクル)
カウンタ値=8199(2007h)
①0.1msのウエイト
0.1ms/62.5ns=1600サイクル
3+(1+1+1+1+4)×(カウンタ値)-2=1600(サイクル)
カウンタ値=200(C8h)
*カウンタ値は、割り切れない場合は近似値になっています。
図 3.28
88/113
A0レジスタ
FF FF
OAKS16 プログラミングテキスト
② 8 ビットコマンド出力関数
初期設定の最初は、4 ビットでコマンドデータを送れません。そのため、8 ビットでデータ
を送る関数を作成します。
LCDモジュールはリセット後動作が不安定なので、
8ビットデータ転送でデータを3回転送します。
コマンド出力
M16C
P47
P46
P45
P44
P43
P42
P41
P40
LCDモジュール
(SC1602SB)
ポート4初期設定
・データ00h
RW=0、RS=0,E=0
・出力ポート
(NC)
RS
R/W
E
DB7
DB6
DB5
DB4
DB3
DB2
DB1
DB0
データ
データ出力
RS(L)
R/W(L)
E
E(H)
(H)
(L)
220nsウエイト
220ns以上
E(L)
/*----------------------------------------------------------*/
/* 関数名
:lcdwriteinit()
*/
/* 機能
:LCD初期設定コマンドセット
*/
/*----------------------------------------------------------*/
void lcdwriteinit( unsigned char command )
{
p4 = 0x00;
pd4 = PORTOUT;
/*P4初期値セット RW-0(W指定) RS-0(コマンド指定)E-0 */
/*P4出力に設定 */
command &= 0x0f; /*P4コントロールデータセット(引数から)RW-0(W指定) RS-0(コマンド指定)E-0 */
p4 = command;
/*P4コントロールデータ出力*/
LCDE = HIGH;
/*E-1*/
#pragma ASM
/*アセンブラ表記*/
NOP
/* 時間調整の為のNOP */
NOP
NOP
NOP
#pragma ENDASM
/*アセンブラ表記終了*/
LCDE = LOW;
/*E-0*/
}
図 3.29
89/113
OAKS16 プログラミングテキスト
③ 4 ビットモードでコマンドを送る関数
コマンドデータは、8 ビットで意味をなしますが、4 ビット転送では上位 4 ビット、かい4
ビットの 2 回に分けて転送する必要があります。そのための関数です。
4ビットデータを2回転送します。
ポート4へ8ビットでセットする場合、
上位4ビットは制御信号なので注意が必要です。
コマンド出力
ポート4初期設定
・データ00h
RW=0、RS=0,E=0
・出力ポート
上位データ出力
LCDモジュール
(SC1602SB)
M16C
P47
P46
P45
P44
P43
P42
P41
P40
E(H)
(NC)
RS
R/W
E
DB7
DB6
DB5
DB4
DB3
DB2
DB1
DB0
データ
220nsウエイト
RS(L)
E(L)
R/W(L)
E
(H)
下位データ出力
(L)
220ns以上
220ns以上
E(H)
220nsウエイト
E(L)
/*----------------------------------------------------------*/
/* 関数名
:lcdwrite1command()
*/
/* 機能
:LCDコマンド出力
*/
/*----------------------------------------------------------*/
void lcdwrite1command( unsigned char command )
{
unsigned char outcommand;
p4 = 0x00;
pd4 = PORTOUT;
/*P4初期値セット RW-0(W指定) RS-0(コマンド指定)E-0 */
/*P4出力に設定 */
outcommand = command>>4;
/*上位4ビットを下位4ビットへ*/
outcommand &= 0x0f;
/*マスク処理*/
p4 = outcommand;
/*コマンドデータ上位4ビットを出力 RW-0(W指定) RS-0(コマンド指定)E-0 */
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
outcommand = command&0x0f;
p4 = outcommand;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
/*E-1*/
/*アセンブラ表記*/
/*時間調整の為のNOP*/
/*アセンブラ表記終了*/
/*E-0*/
/*コマンドデータの下位4ビット抽出(上位4ビットマスク)*/
/*コマンドデータ下位4ビットを出力*/
/*E-1*/
/*アセンブラ表記*/
/*時間調整の為のNOP*/
/*アセンブラ表記終了*/
/*E-0*/
}
図 3.30
90/113
OAKS16 プログラミングテキスト
④4 ビットでデータを送る関数
コマンドとデータでは転送するときに RS の値を変えます。そのため、コマンド転送とは別
に関数を作ります。
コマンド出力
4ビットデータを2回転送します。
ポート4へ8ビットでセットする場合、
上位4ビットは制御信号なので注意が必要です。
ポート4初期設定
・データ00h
RW=0、RS=0,E=0
・出力ポート
上位データ出力
RS(H)
LCDモジュール
(SC1602SB)
M16C
P47
P46
P45
P44
P43
P42
P41
P40
E(H)
(NC)
RS
R/W
E
DB7
DB6
DB5
DB4
DB3
DB2
DB1
DB0
データ
RS
220nsウエイト
(H)
E(L)
(L)
R/W (L)
E
(H)
(L)
220ns以上
220ns以上
下位データ出力
RS=0
RS(H)
RS=1
E(H)
220nsウエイト
E(L)
/*----------------------------------------------------------*/
/* 関数名
:lcdwrite1data()
*/
/* 機能
:LCDデータ出力
*/
/*----------------------------------------------------------*/
void
{
lcdwrite1data( unsigned char data )
unsigned char lcddata;
p4 = 0x00;
pd4 = PORTOUT;
lcddata = data>>4;
lcddata &= 0x0f;
p4 = lcddata;
LCDRS = LCD_DATA;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
lcddata =data & 0x0f;
p4 = lcddata;
LCDRS = LCD_DATA;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
}
図 3.31
/*P4初期値セット RW-0(W指定) RS-0(コマンド指定)E-0 */
/*P4出力に設定 */
/*LCDデータ上位4ビットを下位4ビットへ*/
/*マスク処理*/
/*LCDデータ上位4ビットを出力*/
/*RS-1(データ指定)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為のNOP */
/*アセンブラ表記終了*/
/*E-0*/
/*LCDデータの下位4ビット抽出(上位4ビットマスク)*/
/*LCDデータ下位4ビットを出力*/
/*RS-1(データ指定)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為のNOP */
/*アセンブラ表記終了*/
/*E-0*/
91/113
OAKS16 プログラミングテキスト
⑤ BUSY チェック関数
LCD モジュールは前の書き込みが終了しないと次のデータを送れません。書き込み中は、LCD
モジュールの BUSY フラグが“1”になっていますので、
“0”になるのを待って次の書き込みを
行なう必要があります。このため、BUSY フラグを読み出す関数を作ります。
ビジーチェック関数内では、コマンドの呼び出しでビジーフラグを判断し、戻り値として、ビ
ジーの有無(BUSY(1)・NOBUSY(0))を返します。
ビジーチェック関数の呼び出し側
ビジーチェック関数
関数呼び出し
While(BYSY==lcdbusycheck())
;
メモリ(スタック)
unsigned char lcdbusycheck(void)
{
・
・
・
・
・
ビジーチェック関数を呼び出す。
戻り値が“BUSY”である間、
繰り返し呼び出し続ける。
return(b_data)
}
関数からの復帰
図 3.32
92/113
OAKS16 プログラミングテキスト
ビジーチェック
4ビットデータを2回読み込みます。
ビジー状態のときはビット7のデータが“1”です。
ポート4初期設定
・データ00h
LCDモジュール
(SC1602SB)
M16C
P47
P46
P45
P44
P43
P42
P41
P40
・上位4ビット出力
・下位4ビット入力
(NC)
RS
R/W
E
DB7
DB6
DB5
DB4
DB3
DB2
DB1
DB0
RW(H)
RS
(H)
E(H)
(L)
220nsウエイト
(H)
R/W
E
(L)
上位データ読み込み
(H)
E(L)
(L)
上位
データ
読み込み
下位
読み込み
/*----------------------------------------------------------*/
/* 関数名
:lcdbusycheck()
*/
/* 機能
:LCDビジーチェック
*/
/*----------------------------------------------------------*/
unsigned char lcdbusycheck( void )
{
unsigned char command_high,command_low,b_data;
上位データ処理
E(H)
220nsウエイト
下位データ読み込み
E(L)
ビジーフラグ処理
p4 = 0x00;
pd4 = PORTOUTIN;
/*P4初期値セット RW-0(W指定) RS-0(コマンド指定)E-0 */
/*ポート方向レジスタを上位4ビットを出力下位4ビットを入力に設定*/
LCDRW = HIGH;
/*RW-1(R指定)*/
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
command_high = p4;
LCDE = LOW;
command_high <<=4;
command_high &= 0xf0;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
command_low = p4;
LCDE = LOW;
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為のNOP */
/*アセンブラ表記終了*/
/*コマンド上位4ビット読み込み*/
/*E-0*/
/*下位4ビットを上位へ*/
/*上位4ビット抽出(下位4ビットマスク処理)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為のNOP */
/*アセンブラ表記終了*/
/*コマンド下位4ビット読み込み*/
/*E-0*/
command_low &= 0x0f;
/*下位4ビット抽出(上位4ビットマスク処理)*/
b_data = command_high|command_low;
/* 2つの4ビットコマンドデータを8ビットにまとめる*/
b_data &= 0x80;
/*ビット7のマスク処理*/
if(b_data==0)
b_data = NOBUSY; /*ビット7が0ならLCD書き込みokを返す*/
else
b_data = BUSY; /*ビット7が1ならLCD書き込みbuzyを返す*/
return(b_data);
}
図 3.33
93/113
RW=0、RS=0,E=0
OAKS16 プログラミングテキスト
⑥LCD モジュール初期設定の関数
LCD モジュールを初期設定する関数を作ります。LCD モジュールで決められた仕様通りに設
定するだけなので、ここではリスト上で説明します。
/*----------------------------------------------------------*/
/* 関数名 :lcd_init()
*/
/* 機能
:LCD 初期設定
*/
/*----------------------------------------------------------*/
void lcd_init(void)
{
wait3();
lcdwriteinit( 0x03 );
wait2();
lcdwriteinit( 0x03 );
wait1();
lcdwriteinit( 0x03 );
wait1();
lcdwriteinit( 0x02 );
wait1();
lcdwrite1command(0x28);
wait1();
ウエイトを入れて、同じコードを三
回送る。
(8 ビット転送のモードをき
/*15ms ウエイト*/
/*LCD ファンクションセット*/
/*4.1ms ウエイト*/
/*LCD ファンクションセット*/
/*0.1ms ウエイト*/
/*LCD ファンクションセット*/
ちんと動作させる。
4 ビット転送モードに変える。
/*LCD データを 4 ビット長に設定*/
/*4bit、2 行文、5×7 ドットに設定*/
/*
ここまでで 4 ビット×2 回のデータ転送ができるように設定されたので
この後はビジーチェックをしてからデータ転送を行なう。 */
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x08);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x01);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x06);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x0c);
/*表示オフ*/
/*表示クリア*/
/*エントリーモード、インクリメント*/
/*LCD ビジーチェック*/
/*表示オン、カーソルオフ*/
}
94/113
最初のコマンド転送では、上位 4
ビットしか読み取られていないの
で、2 度目のコマンド転送(4 ビッ
ト×2)で、はじめて全てのコマン
ドが LCD モジュールに受け取って
もらえる。
OAKS16 プログラミングテキスト
3.1.5.サンプルプログラム 1
では、実際に LCD を使用したプログラムを作成してみます。
仕様
LCD に“welcome to oaks16”と表示する。
仕様:LCDに“Welcome to OAKS16 LCDボード”と表示する。
W
O
e
l
c
o
A K
S
1 6
m
e
L
C
D
t
o
ホ
゙
ー
ト
図 3.34
考え方
LCDに表示示する為の文字列を配列を使って ROM 上に確保する。
LCD モジュールの初期化を行なった後、LCD 表示用配列から LCD モジュールの DDRAM にデータ
を出力する。
95/113
OAKS16 プログラミングテキスト
フローチャート
メインプログラム
ポート初期設定
A
LCD初期設定
yes
yes
LCDビジー?
LCDビジー?
LCD2行目
アドレスセット
LCD1行目
アドレスセット
yes
yes
LCDビジー?
LCDビジー?
LCD2行目
データ出力
LCD1行目
データ出力
no
no
16文字終了?
16文字終了?
yes
yes
図 3.35
A
ファイルの分割
スタートアッププログラム:o_srt0.a30+o_sec30.inc
メインプログラムファイル:rei5.c+OAKS16.h
ファイルの変更
スタートアッププログラムの変更無し
96/113
OAKS16 プログラミングテキスト
リスト
リスト(1/7)
/****************************************************/
/*
プロジェクト名: rei5
*/
/*
ファイル名:
rei5.c
*/
/*
内容:
LCD 表示
*/
/*
日付:
2002.3.30
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/****************************************************/
/* インクルードファイル */
#include <oaks_sfr.h>
/* OAKS16 用定義ファイル */
/* プロトタイプ宣言 */
void main(void);
/* メイン関数 */
void lcd_init(void);
/* LCD 初期設定 */
void lcdwriteinit( unsigned char);
/* LCD 初期設定コマンド出力 */
void lcdwrite1command( unsigned char);
/* LCD1 コマンド出力 */
void lcdwrite1data( unsigned char ); /* LCD1 データ出力 */
void wait1( void );
/* 0.1ms ウエイト */
void wait2( void );
/* 4.1ms ウエイト */
void wait3( void );
/* 15ms ウエイト */
unsigned char lcdbusycheck( void );
/* LCD ビジーチェック */
/*
マクロ定義*/
#define PORTIN 0x00 /*ポート方向レジスタを入力に設定する為のデータ*/
#define
PORTOUT
0xff /*ポート方向レジスタを出力に設定する為のデータ*/
#define
PORTOUTIN 0xf0/*ポート方向レジスタを上位 4 ビットを出力*/
/*下位 4 ビットを入力に設定する為のデータ*/
#define
LCDRS p4_6
/* RS 端子 */
#define
LCDRW p4_5
/* RW 端子 */
#define
LCDE p4_4
/* E 端子 */
#define
HIGH 1
/* 端子出力“H” */
#define
LOW
0
/* 端子出力“L” */
#define
LCD_COMMAND
0
/* RS-command 指定 */
#define
LCD_DATA
1
/* RS-data 指定 */
#define
BUSY 1
/* LCD 書き込み buzy */
#define
NOBUSY
0
/* LCD 書き込み OK */
/* 変数の宣言 */
const static char pat[2][16]={{' ',' ','W','e','l','c','o','m','e',' ',' ','t','o',' ',' ',' '},
{' ','O','A','K','S','1','6',' ','L','C','D','ホ','"','-','ト','"'}};
/*LCD 表示用データ */
97/113
OAKS16 プログラミングテキスト
リスト(2/7)
/*-----------------------------------------------------------*/
/* 関数名 :main()
*/
/* 機能
:LCD に'Welcom to OAKS16 LCD ボード'を表示する
*/
/*-----------------------------------------------------------*/
void main(void)
{
int i;
/*** 初期設定 ***/
p4 = 0x00;
pd4 = PORTOUT;
lcd_init();
/*** LCD 表示
/*ループカウンタ*/
/*ポート 4(LCD)初期値設定*/
/*ポート 4 方向を出力に設定*/
/*LCD 初期設定*/
***/
while(BUSY == lcdbusycheck())
;
lcdwrite1command ( 0x80 );
/*LCD ビジーチェック*/
/*1 行目先頭アドレスセット*/
for(i=0;i<16;i++)
{
while(BUSY == lcdbusycheck())/*LCD ビジーチェック*/
;
lcdwrite1data(pat[0][i]); /*LCD データ 1 行目出力*/
}
while(BUSY == lcdbusycheck())
;
lcdwrite1command ( 0xc0 );
/*LCD ビジーチェック*/
/*2 行目先頭アドレスセット*/
for(i=0;i<16;i++)
{
while(BUSY == lcdbusycheck())/*LCD ビジーチェック*/
;
lcdwrite1data(pat[1][i]); /*LCD データ 2 行目出力*/
}
while(1);
/*無限ループ*/
}
98/113
OAKS16 プログラミングテキスト
リスト(3/7)
/*--------------------------------------------*/
/* 関数名 :lcd_init()
*/
/* 機能
:LCD 初期設定
*/
/*--------------------------------------------*/
void lcd_init(void)
{
wait3();
lcdwriteinit( 0x03 );
wait2();
lcdwriteinit( 0x03 );
wait1();
lcdwriteinit( 0x03 );
wait1();
lcdwriteinit( 0x02 );
wait1();
lcdwrite1command(0x28);
wait1();
/*15ms ウエイト*/
/*LCD ファンクションセット*/
/*4.1ms ウエイト*/
/*LCD ファンクションセット*/
/*0.1ms ウエイト*/
/*LCD ファンクションセット*/
/*LCD データを 4 ビット長に設定*/
/*4bit、2 行文、5×7 ドットに設定*/
/*
ここまでで 4 ビット×2 回のデータ転送ができるように設定されたので
この後はビジーチェックをしてからデータ転送を行なう。 */
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x08);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x01);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x06);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x0c);
/*LCD ビジーチェック*/
/*表示オフ*/
/*表示クリア*/
/*エントリーモード、インクリメント*/
/*表示オン、カーソルオフ*/
}
99/113
OAKS16 プログラミングテキスト
リスト(4/7)
/*---------------------------------------*/
/* 関数名 :lcdwriteinit()
*/
/* 機能
:LCD 初期設定コマンドセット
*/
/*----------------------------------------*/
void lcdwriteinit( unsigned char command )
{
p4 = 0x00;
pd4 = PORTOUT;
/*P4 初期値セット RW-0(W 指定) RS-0(コマンド指定)E-0 */
/*P4 出力に設定 */
command &= 0x0f;
/*P4 コントロールデータセット(引数から)RW-0(W 指定) RS-0(コマンド指定)E
-0 */
p4 = command;
/*P4 コントロールデータ出力*/
LCDE = HIGH;
/*E-1*/
#pragma ASM
/*アセンブラ表記*/
NOP
/* 時間調整の為の NOP */
NOP
NOP
NOP
#pragma ENDASM
/*アセンブラ表記終了*/
LCDE = LOW;
/*E-0*/
}
/*---------------------------------------*/
/* 関数名 :lcdwrite1command()
*/
/* 機能
:LCD コマンド出力
*/
/*---------------------------------------*/
void lcdwrite1command( unsigned char command )
{
unsigned char outcommand;
p4 = 0x00;
pd4 = PORTOUT;
/*P4 初期値セット RW-0(W 指定) RS-0(コマンド指定)E-0 */
/*P4 出力に設定 */
outcommand = command>>4;/*上位 4 ビットを下位 4 ビットへ*/
outcommand &= 0x0f;
/*マスク処理*/
p4 = outcommand;
/*コマンドデータ上位 4 ビットを出力 RW-0(W 指定) RS-0(コマンド指定)E-0 */
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
/*E-1*/
/*アセンブラ表記*/
/*時間調整の為の NOP*/
/*アセンブラ表記終了*/
/*E-0*/
outcommand = command&0x0f; /*コマンドデータの下位 4 ビット抽出(上位 4 ビットマスク)*/
p4 = outcommand;
/*コマンドデータ下位 4 ビットを出力*/
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
/*E-1*/
/*アセンブラ表記*/
/*時間調整の為の NOP*/
/*アセンブラ表記終了*/
/*E-0*/
100/113
OAKS16 プログラミングテキスト
リスト(5/7)
/*--------------------------------------------*/
/* 関数名 :lcdwrite1data()
*/
/* 機能
:LCD データ出力
*/
/*--------------------------------------------*/
void lcdwrite1data( unsigned char data )
{
unsigned char lcddata;
p4 = 0x00;
/*P4 初期値セット RW-0(W 指定) RS-0(コマンド指定)E-0 */
pd4 = PORTOUT;
/*P4 出力に設定 */
lcddata = data>>4;
lcddata &= 0x0f;
p4 = lcddata;
LCDRS = LCD_DATA;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
lcddata =data & 0x0f;
p4 = lcddata;
LCDRS = LCD_DATA;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
}
/*LCD データ上位 4 ビットを下位 4 ビットへ*/
/*マスク処理*/
/*LCD データ上位 4 ビットを出力*/
/*RS-1(データ指定)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為の NOP */
/*アセンブラ表記終了*/
/*E-0*/
/*LCD データの下位 4 ビット抽出(上位 4 ビットマスク)*/
/*LCD データ下位 4 ビットを出力*/
/*RS-1(データ指定)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為の NOP */
/*アセンブラ表記終了*/
/*E-0*/
/*----------------------------------------------------------*/
/* 関数名 :wait1()
*/
/* 機能
:0.1ms ウエイト
*/
/*-----------------------------------------------------------*/
void wait1(void){
/* 約 0.1ms のウエイト */
#pragma ASM
/*アセンブラ表記*/
MOV.W
#0C8H,A0
/*カウンタ初期値セット*/
LOOP1:
NOP
NOP
NOP
DEC.W A0
JNZ LOOP1
/*ループ終了?*/
#pragma ENDASM
/*アセンブラ表記終了*/
}
101/113
OAKS16 プログラミングテキスト
リスト(6/7)
/*----------------------------------------------------------*/
/* 関数名 :wait2()
*/
/* 機能
:4.1ms ウエイト
*/
/*---------------------------------------*/
void wait2(void){
/* 約 4.1ms のウエイト */
#pragma ASM
/*アセンブラ表記*/
MOV.W
#2007H,A0
/*カウンタ初期値セット*/
LOOP2:
NOP
NOP
NOP
DEC.W A0
JNZ LOOP2
/*ループ終了?*/
#pragma ENDASM
/*アセンブラ表記終了*/
}
/*---------------------------------------*/
/* 関数名 :wait3()
*/
/* 機能
:15ms ウエイト
*/
/*---------------------------------------*/
void wait3(void){
/* 約 15ms のウエイト */
#pragma ASM
/*アセンブラ表記*/
MOV.W
#7530H,A0
/*カウンタ初期値セット*/
LOOP3:
NOP
NOP
NOP
DEC.W A0
JNZ LOOP3
/*ループ終了?*/
#pragma ENDASM
/*アセンブラ表記終了*/
}
102/113
OAKS16 プログラミングテキスト
リスト(7/7)
/*---------------------------------------*/
/* 関数名 :lcdbusycheck()
*/
/* 機能
:LCD ビジーチェック
*/
/*---------------------------------------*/
unsigned char lcdbusycheck( void )
{
unsigned char command_high,command_low,b_data;
p4 = 0x00;
pd4 = PORTOUTIN;
/*P4 初期値セット RW-0(W 指定) RS-0(コマンド指定)E-0 */
/*ポート方向レジスタを上位 4 ビットを出力下位 4 ビットを入力に設定*/
LCDRW = HIGH;
/*RW-1(R 指定)*/
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
command_high = p4;
LCDE = LOW;
command_high <<=4;
command_high &= 0xf0;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
command_low = p4;
LCDE = LOW;
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為の NOP */
/*アセンブラ表記終了*/
/*コマンド上位 4 ビット読み込み*/
/*E-0*/
/*下位 4 ビットを上位へ*/
/*上位 4 ビット抽出(下位 4 ビットマスク処理)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為の NOP */
/*アセンブラ表記終了*/
/*コマンド下位 4 ビット読み込み*/
/*E-0*/
command_low &= 0x0f; /*下位 4 ビット抽出(上位 4 ビットマスク処理)*/
b_data = command_high|command_low; /* 2 つの 4 ビットコマンドデータを 8 ビットにまとめる*/
b_data &= 0x80;
/*ビット 7 のマスク処理*/
if(b_data==0)
b_data = NOBUSY;
/*ビット 7 が 0 なら LCD 書き込み ok を返す*/
else
b_data = BUSY;
/*ビット 7 が 1 なら LCD 書き込み buzy を返す*/
return(b_data);
}
103/113
OAKS16 プログラミングテキスト
3.1.6.サンプルプログラム 2
仕様
LCD に“switchdata=**h”と表示する。**には SW1 から SW8 までで表される 16 進数を表
示する。データはスイッチが ON で“1”、OFF で“0”とする。
仕様:LCDにSW1~SW8までで表される16進データを表示する。
LCD
s w
i t
c
h d
a t a =
F C h
SWの状態を変えると
LCDの表示が変わる
SW8
SW1
on
on
off
off
図 3.36
考え方
LCD に表示する為の文字列を配列を使って ROM 上に確保する。
数値データをアスキーコードに変換する為のテーブルを用意する。
104/113
OAKS16 プログラミングテキスト
フローチャート
メインプログラム
ポート初期設定
LCD初期設定
LCD初期表示
SW1からsw8読み込み
スイッチデータを上位
4ビット、下位4ビット
に分割
スイッチ上位4ビット
表示アドレス設定
スイッチ上位4ビット
データ出力
スイッチ下位4ビット
表示アドレス設定
スイッチ下位4ビット
データ出力
図 3.37
105/113
OAKS16 プログラミングテキスト
リスト
リスト(1/7)
/****************************************************/
/*
プロジェクト名: rei6
*/
/*
ファイル名:
rei6.c
*/
/*
内容:
swdata を LCD に表示する
*/
/*
日付:
2002.3.30
*/
/*
コンパイラ:
NC30WA (Ver.4.00)
*/
/*
note:
OAKS16 対応
*/
/*
作成者:
OAKS16KIT support
*/
/****************************************************/
/* インクルードファイル */
#include <oaks_sfr.h>
/* OAKS16 用定義ファイル */
/* プロトタイプ宣言 */
void main(void);
void lcd_init(void);
void lcdwriteinit( unsigned char);
void lcdwrite1command( unsigned char);
void lcdwrite1data( unsigned char );
unsigned char lcdbusycheck( void );
void wait1( void );
void wait2( void );
void wait3( void );
/*メイン関数*/
/*LCD 初期設定*/
/*LCD 初期設定コマンド出力*/
/*LCD1 コマンド出力*/
/*LCD1 データ出力*/
/*LCD ビジーチェック*/
/*0.1ms ウエイト*/
/*4.1ms ウエイト*/
/*15ms ウエイト*/
/*
マクロ定義*/
#define PORTIN 0x00 /*ポート方向レジスタを入力に設定する為のデータ*/
#define
PORTOUT
0xff /*ポート方向レジスタを出力に設定する為のデータ*/
#define
PORTOUTIN 0xf0/*ポート方向レジスタを上位 4 ビットを出力*/
/*下位 4 ビットを入力に設定する為のデータ*/
#define
#define
#define
#define
#define
#define
#define
#define
#define
LCDRS p4_6
LCDRW p4_5
LCDE p4_4
HIGH 1
LOW
0
LCD_COMMAND
LCD_DATA
BUSY 1
NOBUSY
0
/*RS 端子*/
/*RW 端子*/
/*E 端子*/
0
1
/*RS-command 指定*/
/*RS-data 指定*/
/*LCD 書き込み buzy*/
/*LCD 書き込み OK*/
/* 変数の宣言 */
const static
const static
char pat[17]={"switchdata= h "};
char patdata[17]={"0123456789abcdef"};
106/113
/*LCD 表示用データ*/
/*パターン表示用データ*/
OAKS16 プログラミングテキスト
リスト(2/7)
/*----------------------------------------------------------------------------------------*/
/* 関数名 :main()
*/
/* 機能
:LCD に'Welcom to OAKS16 LCD ボード'を表示する
*/
/*-----------------------------------------------------------*/
void main(void)
{
unsigned char sw,sw_low,sw_high;
int i;
/*** 初期設定
/*変数宣言(スイッチの接続されているポートのデータを入れる)*/
/*ループカウンタ*/
***/
pd3=PORTIN;
/*ポート 3 を入力に設定*/
p4 = 0x00;
pd4 = PORTOUT;
lcd_init();
/*** LCD 表示
***/
while(BUSY == lcdbusycheck())
;
lcdwrite1command ( 0xc0 );
/*ポート 4(LCD)初期値設定*/
/*ポート 4 方向を出力に設定*/
/*LCD 初期設定*/
/*LCD ビジーチェック*/
/*2 行目先頭アドレスセット*/
for(i=0;i<16;i++)
{
while(BUSY == lcdbusycheck())/*LCD ビジーチェック*/
;
/*OAKS16 LCD ボードを表示*/
lcdwrite1data(pat[i]);
/*LCD データ出力*/
}
for(;;)
{
sw=p3;
sw=sw^0xff;
sw_low=sw&0x0f;
sw_high=(sw&0xf0);
sw_high>>=4;
while(BUSY == lcdbusycheck())
;
lcdwrite1command ( 0xcb );
/*LCD ビジーチェック*/
/*スイッチデータ上位表示アドレスセット*/
while(BUSY == lcdbusycheck()) /*LCD ビジーチェック*/
;
lcdwrite1data(patdata[sw_high]);/*スイッチ上位 4 ビット表示データ出力*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command ( 0xcc );
/*LCD ビジーチェック*/
/*スイッチデータ下位表示アドレスセット*/
while(BUSY == lcdbusycheck()) /*LCD ビジーチェック*/
;
lcdwrite1data(patdata[sw_low]); /*スイッチ下位 4 ビット表示データ出力*/
}
}
107/113
OAKS16 プログラミングテキスト
リスト(3/7)
/*----------------------------------------------------------*/
/* 関数名 :lcd_init()
*/
/* 機能
:LCD 初期設定
*/
/*---------------------------------------*/
void lcd_init(void)
{
wait3();
lcdwriteinit( 0x03 );
wait2();
lcdwriteinit( 0x03 );
wait1();
lcdwriteinit( 0x03 );
wait1();
lcdwriteinit( 0x02 );
wait1();
lcdwrite1command(0x28);
wait1();
/*15ms ウエイト*/
/*LCD ファンクションセット*/
/*4.1ms ウエイト*/
/*LCD ファンクションセット*/
/*0.1ms ウエイト*/
/*LCD ファンクションセット*/
/*LCD データを 4 ビット長に設定*/
/*4bit、2 行文、5×7 ドットに設定*/
/*
ここまでで 4 ビット×2 回のデータ転送ができるように設定されたので
この後はビジーチェックをしてからデータ転送を行なう。 */
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x08);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x01);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x06);
/*LCD ビジーチェック*/
while(BUSY == lcdbusycheck())
;
lcdwrite1command(0x0c);
/*LCD ビジーチェック*/
/*表示オフ*/
/*表示クリア*/
/*エントリーモード、インクリメント*/
/*表示オン、カーソルオフ*/
}
108/113
OAKS16 プログラミングテキスト
リスト(4/7)
/*----------------------------------------------------------*/
/* 関数名 :lcdwriteinit()
*/
/* 機能
:LCD 初期設定コマンドセット
*/
/*----------------------------------------------------------*/
void lcdwriteinit( unsigned char command )
{
p4 = 0x00;
pd4 = PORTOUT;
/*P4 初期値セット RW-0(W 指定) RS-0(コマンド指定)E-0 */
/*P4 出力に設定 */
command &= 0x0f;
/*P4 コントロールデータセット(引数から)RW-0(W 指定) RS-0(コマンド指定)E-0 */
p4 = command;
/*P4 コントロールデータ出力*/
LCDE = HIGH;
/*E-1*/
#pragma ASM
/*アセンブラ表記*/
NOP
/* 時間調整の為の NOP */
NOP
NOP
NOP
#pragma ENDASM
/*アセンブラ表記終了*/
LCDE = LOW;
/*E-0*/
}
/*------------------------------------------------------------*/
/* 関数名 :lcdwrite1command()
*/
/* 機能
:LCD コマンド出力
*/
/*------------------------------------------------------------*/
void lcdwrite1command( unsigned char command )
{
unsigned char outcommand;
p4 = 0x00;
pd4 = PORTOUT;
/*P4 初期値セット RW-0(W 指定) RS-0(コマンド指定)E-0 */
/*P4 出力に設定 */
outcommand = command>>4;/*上位 4 ビットを下位 4 ビットへ*/
outcommand &= 0x0f;
/*マスク処理*/
p4 = outcommand;
/*コマンドデータ上位 4 ビットを出力
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
RW-0(W 指定) RS-0(コマンド指定)E-0 */
/*E-1*/
/*アセンブラ表記*/
/*時間調整の為の NOP*/
/*アセンブラ表記終了*/
/*E-0*/
outcommand = command&0x0f; /*コマンドデータの下位 4 ビット抽出(上位 4 ビットマスク)*/
p4 = outcommand;
/*コマンドデータ下位 4 ビットを出力*/
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
/*E-1*/
/*アセンブラ表記*/
/*時間調整の為の NOP*/
/*アセンブラ表記終了*/
/*E-0*/
}
109/113
OAKS16 プログラミングテキスト
リスト(5/7)
/*--------------------------------------------*/
/* 関数名 :lcdwrite1data()
*/
/* 機能
:LCD データ出力
*/
/*--------------------------------------------*/
void lcdwrite1data( unsigned char data )
{
unsigned char lcddata;
p4 = 0x00;
/*P4 初期値セット RW-0(W 指定) RS-0(コマンド指定)E-0 */
pd4 = PORTOUT;
/*P4 出力に設定 */
lcddata = data>>4;
lcddata &= 0x0f;
p4 = lcddata;
LCDRS = LCD_DATA;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
lcddata =data & 0x0f;
p4 = lcddata;
LCDRS = LCD_DATA;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
LCDE = LOW;
}
/*LCD データ上位 4 ビットを下位 4 ビットへ*/
/*マスク処理*/
/*LCD データ上位 4 ビットを出力*/
/*RS-1(データ指定)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為の NOP */
/*アセンブラ表記終了*/
/*E-0*/
/*LCD データの下位 4 ビット抽出(上位 4 ビットマスク)*/
/*LCD データ下位 4 ビットを出力*/
/*RS-1(データ指定)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為の NOP */
/*アセンブラ表記終了*/
/*E-0*/
/*--------------------------------------------*/
/* 関数名 :wait1()
*/
/* 機能
:0.1ms ウエイト
*/
/*--------------------------------------------*/
void wait1(void){
/* 約 0.1ms のウエイト */
#pragma ASM
/*アセンブラ表記*/
MOV.W
#0C8H,A0
/*カウンタ初期値セット*/
LOOP1:
NOP
NOP
NOP
DEC.W A0
JNZ LOOP1
/*ループ終了?*/
#pragma ENDASM
/*アセンブラ表記終了*/
}
110/113
OAKS16 プログラミングテキスト
リスト(6/7)
/*--------------------------------------*/
/* 関数名 :wait2()
*/
/* 機能
:4.1ms ウエイト
*/
/*---------------------------------------*/
void wait2(void){
/* 約 4.1ms のウエイト */
#pragma ASM
/*アセンブラ表記*/
MOV.W
#2007H,A0
/*カウンタ初期値セット*/
LOOP2:
NOP
NOP
NOP
DEC.W A0
JNZ LOOP2
/*ループ終了?*/
#pragma ENDASM
/*アセンブラ表記終了*/
}
/*---------------------------------------*/
/* 関数名 :wait3()
*/
/* 機能
:15ms ウエイト
*/
/*---------------------------------------*/
void wait3(void){
/* 約 15ms のウエイト */
#pragma ASM
/*アセンブラ表記*/
MOV.W
#7530H,A0
/*カウンタ初期値セット*/
LOOP3:
NOP
NOP
NOP
DEC.W A0
JNZ LOOP3
/*ループ終了?*/
#pragma ENDASM
/*アセンブラ表記終了*/
}
111/113
OAKS16 プログラミングテキスト
リスト(7/7)
/*---------------------------------------*/
/* 関数名 :lcdbusycheck()
*/
/* 機能
:LCD ビジーチェック
*/
/*---------------------------------------*/
unsigned char lcdbusycheck( void )
{
unsigned char command_high,command_low,b_data;
p4 = 0x00;
pd4 = PORTOUTIN;
/*P4 初期値セット RW-0(W 指定) RS-0(コマンド指定)E-0 */
/*ポート方向レジスタを上位 4 ビットを出力下位 4 ビットを入力に設定*/
LCDRW = HIGH;
/*RW-1(R 指定)*/
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
command_high = p4;
LCDE = LOW;
command_high <<=4;
command_high &= 0xf0;
LCDE = HIGH;
#pragma ASM
NOP
NOP
NOP
NOP
#pragma ENDASM
command_low = p4;
LCDE = LOW;
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為の NOP */
/*アセンブラ表記終了*/
/*コマンド上位 4 ビット読み込み*/
/*E-0*/
/*下位 4 ビットを上位へ*/
/*上位 4 ビット抽出(下位 4 ビットマスク処理)*/
/*E-1*/
/*アセンブラ表記*/
/* 時間調整の為の NOP */
/*アセンブラ表記終了*/
/*コマンド下位 4 ビット読み込み*/
/*E-0*/
command_low &= 0x0f; /*下位 4 ビット抽出(上位 4 ビットマスク処理)*/
b_data = command_high|command_low; /* 2 つの 4 ビットコマンドデータを 8 ビットにまとめる*/
b_data &= 0x80;
/*ビット 7 のマスク処理*/
if(b_data==0)
b_data = NOBUSY;
/*ビット 7 が 0 なら LCD 書き込み ok を返す*/
else
b_data = BUSY;
/*ビット 7 が 1 なら LCD 書き込み buzy を返す*/
return(b_data);
}
112/113
OAKS16 プログラミングテキスト
OAKS16 キット
OAKS16 プログラミングテキスト(Ver.1.01)
資料番号:OAKS16 プログラミングテキスト(Ver.1.01)
発 行 所:オークス電子株式会社
〒101-0025 東京都千代田区佐久間町 3 丁目21番地(第一千代田ビル3F)
TEL:03-3863-1121
ホームページ
FAX:03-3863-1130
http://www.oaks-ele.com
禁無断転載
本書の一部または全部を、当社に断りなく、いかなる形でも転載または複製すること
を堅くお断りします。
113/113