ステップ9

電卓の製作

 ここまで学習してきたことを活かして電卓を作成してみよう。

電卓の回路図

■電卓の状態遷移図

 上図は状態遷移図といいます。
 S0は開始(結果表示)地点です。数値を打った場合、左値入力のS1に移行します。数値が繰り返し入力された場合は、S1をループし、1や10など数値を作り、演算子が入力された場合S2に移行します。もう一度数値が入力された場合は右値入力のS3に移行し、S1と同じく数値が入力されている間はS3をループします。=が入力された場合はS0に移行し、結果が表示されます。
 このような動きを書き示した図が状態遷移図となり、この動作をイメージしながらプログラムを書いていきます。

電卓のプログラミング

●main.c

#include <stdio.h>
#include "iodefine.h"
#include "intrinsics.h"
#include "sci.h"
#include "ctype.h"

#ifdef CPPAPP
//Initialize global constructors
extern "C" void __main()
{
  static int initialized;
  if (! initialized)
    {
      typedef void (*pfunc) ();
      extern pfunc __ctors[];
      extern pfunc __ctors_end[];
      pfunc *p;

      initialized = 1;
      for (p = __ctors_end; p > __ctors; )
	(*--p) ();

    }
}
#endif 

unsigned char	sw_prev[4];	// SWの直前のOn/Off状態
unsigned char	buff[32];	// 結果出力用バッファ
unsigned char	scan_cnt;	// 入力スキャン用カウンタ
unsigned char state;		// 状態変数
unsigned char	ope;		// 演算子
long	left; 	// 左値
long	right;	// 右値

static unsigned char keyTab[][4] = {
	{ 'C', '0', '=', '/' },
	{ '7', '8', '9', '*' },
	{ '4', '5', '6', '-' },
	{ '1', '2', '3', '+' }
};

void initIO(void){
	unsigned char i;

	P1.DDR = 0xff;
	P2.DDR = 0x00; // キー入力
	P3.DDR = 0xff;
	P4.DDR = 0xff;
	P5.DDR = 0xff;
	P6.DDR = 0xf0;
	P8.DDR = 0xff;
	P9.DDR = 0xff;
	PA.DDR = 0xff;
	PB.DDR = 0xff;

	P2.PCR.BYTE = 0xff; // 下位4ビット:プルアップ有効
	P1.DR.BYTE = 0xff;
	for(i = 0; i < 4; i++){
		P1.DR.BYTE = ~(1 << i);
		sw_prev[i] = 0x0f & P2.DR.BYTE;
		P1.DR.BYTE = 0xff;
	}
}

void all_clear(){
	state = 0;
	left = right = 0;
	ope = ' ';
}

void initApp(void){
	scan_cnt = 0;
	all_clear();
}

void initITU(void){
	set_imask_ccr(1);		// 割込み禁止
	ITU0.TCR.BIT.CCLR = 1;	// GRAコンペアマッチ-->TCNTクリア
	ITU0.TCR.BIT.CKEG = 0;	// 立ち上がりエッジでカウント
	ITU0.TCR.BIT.TPSC = 2;	// 分周比  0:φ 1:φ/2 2:φ/4 3:φ/8
	ITU0.TCNT = 0;
	ITU0.GRA = 62499;		// 1/100秒でコンペアマッチ
	ITU0.TSR.BIT.IMFA = 0;	// フラグクリア
	ITU0.TIER.BYTE = 1;		// コンペアマッチA-->割込み
	ITU.TSTR.BYTE = 1;		// ITU0 START
	set_imask_ccr(0);		// 割込み許可
}

int main(void)
{
	initIO();
	initApp();
	initITU();
	initSCI();

    while (1) {
    }
    return 0;
}

void printValue(){
	if (state == 0 || state == 1){
		sprintf(buff, "%ld\n", left);
		sci_puts(buff);
	} else if (state == 2){
		sprintf(buff, "%ld %c\n", left, ope);
		sci_puts(buff);
	} else if (state == 3){
		sprintf(buff, "%ld %c %ld\n", left, ope, right);
		sci_puts(buff);
	}
}

int isOperator(unsigned char c){
	return((c == '+')||(c == '-')||(c == '*')||(c =='/'));
}

//四則演算関数
void ope_execute(){ 
	if (ope == '+'){
		left += right;
	} else if (ope == '-'){
		left -= right;
	} else if (ope == '*'){
		left *= right;
	} else if (ope == '/'){
		if (right == 0){
			sci_puts("ERROR: divided by 0\n");
			all_clear();
		} else {
			left /= right;
		}
	}
}

// 左下が(0,0)
void keyTyped(int row, int col){
	unsigned char c;
	c = keyTab[row][col];

	if (c == 'C'){
		all_clear();
	} else	if (state == 0){
		if (isdigit(c)){
			left = c - '0';
			state = 1;
		} else if (c == '='){
			ope_execute();
		} else if (isOperator(c)){
			ope = c;
			state = 2;
		}
	} else if (state == 1){
		if (isdigit(c)){
			left = 10*left + c - '0';
		} else if (isOperator(c)){
			ope = c;
			state = 2;
		} else if (c == '='){
			ope = ' ';
			state = 0;
		}
	} else if (state == 2){
		if (isdigit(c)){
			state = 3;
			right = c - '0';
		} else if (c == '='){
			right = left;
			ope_execute();
			state = 0;
		} else if (isOperator(c)){
			ope = c;
		}
	} else if (state == 3){
		if (isdigit(c)){
			right = 10*right + c - '0';
		} else if (isOperator(c)){
			ope_execute();
			ope = c;
			state = 2;
		} else if (c == '='){
			ope_execute();
			state = 0;
		}
	}
	printValue();
}

int keyScan(void){
	unsigned char sw, sw_on;
	int i, j, cnt;

	cnt = 0;
	for(i = 0; i < 4; i++){
		P1.DR.BYTE = ~(1 << i);
		sw = 0x0f & P2.DR.BYTE;      // i行目をスキャン
		P1.DR.BYTE = 0xff;
		sw_on = (sw_prev[i] ^ sw) & ~sw; // 押されたスイッチを1
		for(j = 0; j < 4; j++){
			if (sw_on & (0x08 >> j)){
				// i行 j列のキーが Off→On
				cnt++;
				keyTyped(i, j);
			}
		}
		sw_prev[i] = sw; // i行目の状態を記憶しておく
	}
	return cnt;
}

/*
 * 1/100秒 = 10ms間隔でコールされる。
 */
void onTimer(void){
	scan_cnt++;
	if (scan_cnt >= 4){
		scan_cnt = 0;
		keyScan();
	}
}

●inthandler.c

#include "inthandler.h"
#include "iodefine.h"

void INT_IMIA0(void) {
	ITU0.TSR.BIT.IMFA = 0; // フラグクリア
	onTimer();
}

●sci.c

#include "iodefine.h"
#include "sci.h"


void sci_putc(unsigned char ch){
	while (SCI1.SSR.BIT.TDRE == 0)
		;
	SCI1.TDR = ch;
	SCI1.SSR.BIT.TDRE = 0;
}

void sci_puts(unsigned char *s){
	while (*s){
		if (*s == '\n')
			sci_putc('\r');
		sci_putc(*s++);
	}
}

unsigned char sci_read(void){
	unsigned char	c;
	if ( (SCI1.SSR.BYTE & 0x78) == 0 )
		return '\0';	// 入力なし

	if (SCI1.SSR.BIT.RDRF == 1){
		c = SCI1.RDR;
		SCI1.SSR.BIT.RDRF = 0;
		return c;
	}
	SCI1.SSR.BYTE &= 0x87;	// エラーフラグクリア
	return -1;
}

void initSCI(void){
	SCI1.SCR.BIT.TE = 0;	// 送信無効
	SCI1.SCR.BIT.RE = 0;	// 受信無効
	SCI1.SCR.BIT.CKE = 0;	// クロックソース:内部クロック
	SCI1.SMR.BYTE = 0;		// 調歩, キャラクタ8, パリティナシ, ストップ1, クロックφ
	SCI1.BRR = 80;			// 9600baud
	SCI1.SSR.BYTE &= 0x87;	// フラグクリア
	SCI1.SCR.BIT.TE = 1;	// 送信有効
	SCI1.SCR.BIT.RE = 1;	// 受信有効
}

●sci.h

#ifndef SCI_H_
#define SCI_H_

void initSCI(void);
void sci_putc(unsigned char ch);
void sci_puts(unsigned char *s);
unsigned char sci_read(void);

#endif /* SCI_H_ */

完成写真

このように電卓のように計算できたらOKです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です