ステップ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です。