PICでCの練習 2007.06.06(水)〜 LessonC_25。2則演算電卓です。
著作者名: 中野 良知 作成開始: 2007.06.05(火) : 2007.06.06(水) 実行結果を追加 : 2007.06.08(金) switch文と関数の戻り値の項目を追加 : 実行結果にユニバーサル基板画像を追加。
目次 1. 目的 2. ゼネラルフローチャート 3. プログラムソースコード 4. キーワード 4.1. キーマトリックス 4.1.1. 回路 4.1.2. タイミングチャート 4.1.3. キーコード 4.2. switch case文 4.3. 関数の戻り値 5. 実行結果 6. 実験回路 1. 目的 +・−の2則演算電卓プログラムを制作します。 キー入力は2×7のマトリックス方式です。 4桁の7セグメントLEDをダイナミック駆動します。 完成予想図 ┏━━━━━━━━━━━━━┓ ┃ ┌┴┴┴┐ ┃ ┃ │16F84A│ ┃ ┃ └┬┬┬┘ ┃ ┃┏━━┳━━┳━━┳━━┓┃ ┃┃ 8 ┃ 8 ┃ 8 ┃ 8 ┃┃ ┃┗━━┻━━┻━━┻━━┛┃ ┃ ┏━┓┏━┓┏━┓┏━┓ ┃ ┃ ┃7┃┃8┃┃9┃┃C┃ ┃ ┃ ┗━┛┗━┛┗━┛┗━┛ ┃ ┃ ┏━┓┏━┓┏━┓┏━┓ ┃ ┃ ┃4┃┃5┃┃6┃┃ー┃ ┃ ┃ ┗━┛┗━┛┗━┛┗━┛ ┃ ┃ ┏━┓┏━┓┏━┓┏━┓ ┃ ┃ ┃1┃┃2┃┃3┃┃+┃ ┃ ┃ ┗━┛┗━┛┗━┛┗━┛ ┃ ┃ ┏━┓ ┏━┓ ┃ ┃ ┃0┃ ┃=┃ ┃ ┃ ┗━┛ ┗━┛ ┃ ┗━━━━━━━━━━━━━┛ 2. ゼネラルフローチャート (main) ↓ プルアップ許可 ↓ TMR0割り込み許可 ↓ ポートの初期化 ↓ ├←───────────────┐ ↓ no │ <10msec経過?>→──────────┤ ↓yes │ キーコード取得 │ ↓ no │ <=?> ───────────┐ │ ↓yes − │ │ <演算子?>─────┐ │ │ ↓+ │ │ │ 答=被数+加数 答=被数−減数 │ │ ↓ │ │ │ ├────────┘ │ │ 表示No=答 │ │ ↓ │ │ ├─────────────┘ │ ↓ │ <C?> ──────┐ │ ↓yes │ │ 一項目の入力モード │ │ ↓ │ │ 被数項=加減数項=0 │ │ ↓ │ │ ├────────┘ │ ↓ no │ <+orー?>─────┐ │ ↓yes │ │ 演算子保存 │ │ ↓ │ │ 二項目の入力モード │ │ ↓ │ │ ├────────┘ │ ↓ no │ <0から9?>────┐ │ ↓yes │ │ 一又は二項目に入力 │ │ ↓ │ │ 表示No=入力項 │ │ ↓ │ │ ├────────┘ │ ↓ │ 表示Noの2進数を10進数に │ 変換して表示バッファーへ │ ↓ │ └────────────────┘ (TMR0割り込み) ↓ TMR0=1msec ↓ 割り込みフラグリセット ↓ 全桁消灯 ↓ キーマトリックス0列入力(0〜6) ↓ キーマトリックス1列入力(7〜9,=,+.-,C) ↓ 走査順の桁データを セグメントデータに変換 ↓ セグメントデータ出力 ↓ 走査順の桁を点灯 ↓ 走査順を更新 ↓ 1msecカウンタインクリメント ↓ (終了) 3. プログラムソースコード //***************************************************************************** // タイトル : LesssonC_25 // 作者 : ioio // 目的 : 2則演算電卓を作成します。 // マイコン : PIC16F84A // クロック : 20MHz // Config : PROTECT=OFF, WDTE=OFF, XTAL // Compiler : PICC Lite(V9.6)。 HI-TEC software製。 // Compile : >picl -16f84a lesson.c // ASM List : >picl -16f84a --ASMLIST lesson.c //***************************************************************************** // 変更履歴 // 2007.06.03(日) 作成開始 // 2007.06.05(火) キー走査にNOP()追加で入力を安定化。 // 桁溢れや負数時の"----"表示を追加。 //***************************************************************************** //----------------------------------------------------------------------------- // ヘッダーファイル #include "pic.h" // デバイス関連の定義を展開 //----------------------------------------------------------------------------- // コンフィグレーション定義 __CONFIG(0x3FFA); // PROTECT=OFF, WDTE=OFF, HS_OSILLATOR //----------------------------------------------------------------------------- // 関数定義 unsigned char get_key(void); //----------------------------------------------------------------------------- // 定数の定義 // セグメント変換(アノードコモン用) const unsigned char seg_data[11] = { 0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x58, 0x00, 0x18, 0x3F}; // 表示桁走査ビット(アノードコモン用) const unsigned char scan[4] = {1,2,4,8}; #define SET_TMR0 256-156 // TMR0設定=156*32*200ns=998.4us #define EQU 10 // キー'='のコード #define PLUS 11 // キー'+'のコード #define MINUS 12 // キー'-'のコード #define CLR 13 // キー'CLR'のコード #define KYOFF 255 // キー オフ #define OVER 10000 // 入力や計算のオーバーorアンダーフロー #define BAR 10 // 表示"-"コード //----------------------------------------------------------------------------- // グローバル変数定義 // 表示用 unsigned char digit_no; // 表示走査桁指定 unsigned char dsp_buf[4]={0,0,0,0}; // 表示バッファー int t_cnt; // TMR0割込み回数 // キー入力用 unsigned char key_new[2]; // キーマトリクス最新入力 unsigned char key_old[2]; // キーマトリクス前回入力 unsigned char key_fix[2]; // キーマトリクス入力確定 unsigned char key_step; // キー入力ステップ //----------------------------------------------------------------------------- // ビットフィールド構造体定義 //----------------------------------------------------------------------------- // ポートマップ // NAME PIN NO I/O USE // -----+--------+------+----------------------------------------- // RA0 17 O 1st Digit。d0 // RA1 18 O 2nd Digit。d1 // RA2 1 O 3rd Digit。d2 // RA3 2 O 4th Digit。d3 // RA4 3 O キーマトリックス列0走査出力 // RB0 6 O a segment。キーマトリックス入力 // RB1 7 O b segment。キーマトリックス入力 // RB2 8 O c segment。キーマトリックス入力 // RB3 9 O d segment。キーマトリックス入力 // RB4 10 O e segment。キーマトリックス入力 // RB5 11 O f segment。キーマトリックス入力 // RB6 12 O g segment。キーマトリックス入力 // RB7 13 O キーマトリックス列1走査出力 //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void main(void) { // 2則演算用 unsigned int item1; // 式の一項目 unsigned int item2; // 式の二項目 unsigned char opr; // 式の演算子 long ans; // 計算の答 unsigned int dsp_no; // 表示用の数値 unsigned int temp1; // 10進数変換に使用 unsigned int temp2; // 10進数変換に使用 int i; // 10進数変換に使用 int n; // 10進数変換に使用 unsigned char key; // キーコード INTCON = 0b10100000; // 割り込み制御初期化設定 OPTION = 0b01000100; // プリスケール=1/32 TMR0 = SET_TMR0; // タイマー0設定 TRISA = 0x10; // PortA b4=In、b3,b2,b1,b0=Out TRISB = 0x80; // PortB b7=In、b6,b5,b4,b3,b2,b1,b0=Out PORTB = 0x00; // ポートB初期化。 PORTA = 0x00; // ポートA初期化。 digit_no = 0; // 走査桁NoをLSDに初期化 t_cnt = 0; // TMR0割込み回数を初期化 key_step = 0; // キー入力の順番 while(1){ // キーマトリックスの入力処理 if(t_cnt == 10){ // 10msec毎にキー押し判定 t_cnt = 0; // TMR0割込み回数初期化 key = get_key(); //if(key != 0xFF){ // dsp_no = key; //} if(key == EQU && (key_step == 1 || key_step == 2)){ // '='キーがオンで演算子の入力済みなら if(opr == PLUS){ ans = item1 + item2; if(ans >= OVER){ ans = OVER; } } if(opr == MINUS){ ans = item1 - item2; if(item1 < item2){ ans = OVER; } } item1 = (unsigned int)ans; dsp_no = item1; key_step = 2; }else if(key == PLUS || key == MINUS){ // 演算子キー(+ or −)がオンなら key_step = 1; opr = key; dsp_no = 0; // 表示をクリア }else if(key == CLR){ // 'CLR'キーオンなら item1 = 0; // 式の一項目をクリア item2 = 0; // 式の二項目をクリア opr = 0; // 演算子クリア key_step = 0; // キーの入力順をクリア dsp_no = 0; // 答をクリア }else if(key != 0xFF){ // 数値キーオンなら if(key_step == 2){ // =キー押しがあれば"CLR"と同じ key_step = 0; item1 = 0; item2 = 0; opr = 0; } switch(key_step){ case 0: // 一項目(被数)入力 item1 *= 10; item1 += key; if((item1 > 9999) || (item1 < dsp_no)){ item1 = OVER; } dsp_no = item1; break; case 1: // 二項目(加減数)入力 item2 *= 10; item2 += key; if((item2 > 9999) || (item2 < dsp_no)){ item2 = OVER; } dsp_no = item2; break; default: break; } } } // dsp_noを4桁の10進数に変換 if(dsp_no == OVER){ for(i = 0; i < 4; i++){ dsp_buf[i] = BAR; } }else{ temp1 = dsp_no; n = 1000; for(i = 3; i >= 0; i--){ temp2 = temp1 / n; // 千、百、十、一の個数を計算 dsp_buf[i] = (unsigned char)temp2; // 表示バッファーに代入 temp1 = temp1 % n; // 余りを求める n /= 10; } } } } //----------------------------------------------------------------------------- // 機能 : キーオンのチェックとキーコードの取得 // 引数 : なし // 戻値 : キーコード // : 0-9,=(128),+(129),-(130),CLR(131),キーオフ(255) // 作成 : 2007.06.04(月) //----------------------------------------------------------------------------- unsigned char get_key(void) { unsigned char i; // キーマトリックス配列走査カウンタ unsigned char n; // オンビット検索ループカウンタ unsigned char bit_scan; // オンビット走査 unsigned char rt = 255; // 戻り値。初期値=キーオフ // キーマトリックス配列をスキャンしてキーオン情報を取得 for(i = 0; i < 2; i++){ if(key_new[i] ^ key_old[i]){ // 入力に変化あり key_old[i] = key_new[i]; // 新しい入力データを保存 }else{ // 前回入力と一致(チャタリングなし) if((key_new[i] ^ key_fix[i]) && key_new[i] != 0x7F){ // 入力が変化していて、オフでなければキーオンのビット検索 bit_scan = 1; for(n = 0; n < 7; n++){ if(!(key_new[i] & bit_scan)){ // ビット=0なら break; // 検索終了 } bit_scan <<= 1; } if(n < 7 && rt == 0xFF){ //rt = key_code[i][n]; // キーコード取得 rt = n + i * 7; // キーコード取得 } } key_fix[i] = key_new[i]; // 確定した入力を保存 } } return rt; } //----------------------------------------------------------------------------- // 機能 : TMR0割り込み関数 // 引数 : なし // 戻値 : なし // 作成 : 2007.06.04(月) //----------------------------------------------------------------------------- static void interrupt isr(void) { TMR0 = SET_TMR0; // タイマー0設定 T0IF = 0; // タイマー0オーバーフローフラグリセット // 表示のブランク期間中にキーマトリックスの走査 // キーマトリックスの列0を走査 PORTB = 0xFF; // PortB b7=0 b6,b5,b4,b3,b2,b1,b0=Pullup TRISB = 0x7F; // PortB b7=Out b6,b5,b4,b3,b2,b1,b0=In TRISA = 0x00; // PortA b4,b3,b2,b1,b0=Out PORTA = 0x00; // 表示走査オフ。キー0列走査(RA4) NOP(); key_new[0] = PORTB & 0x7F; // キー0列取得 RA4 = 1; // キー0列走査オフ(Open Drain) // キーマトリックスの列1を走査 RB7 = 0; // キー1列走査オン NOP(); key_new[1] = PORTB & 0x7F; // キー1列取得 RB7 = 1; // キー1列走査オフ // セグメントデータ出力 TRISA = 0x10; // PortA b4=In b3,b2,b1,b0=Out TRISB = 0x80; // PortB b7=In、b6,b5,b4,b3,b2,b1,b0=Out PORTB = seg_data[dsp_buf[digit_no]]; PORTA = scan[digit_no]; // 表示走査ビット出力 if(++digit_no == 4){ // 一巡したら digit_no = 0; // 表示桁を1桁目に設定 } t_cnt++; // TMR0割込み回数インクリメント } //***************************************************************************** //***************************************************************************** 4. キーワード 4.1. キーマトリックス PORTAのb4とPORTBのb7の2ビットでキーマトリックスの列を走査します。 キー押しの状態は、PORTBのb0からb6の7ビットで入力します。 列0走査時のキー入力はkey_new[0]に、列1走査時のキー入力はkey_new[1]に格納しま す。 4.1.1. 回路 RB7 列1走査 ────────────┐ │ RA4 列0走査 ────┐ 0 │ 7 │ ─┴─ │ ─┴─ ●─○ ○─┐ ●─○ ○─┐ RB0 キー入力0 ───│─────●─│─────●─ │ 1 │ 8 │ ─┴─ │ ─┴─ ●─○ ○─┐ ●─○ ○─┐ RB1 キー入力1 ───│─────●─│─────●─ │ 2 │ 9 │ ─┴─ │ ─┴─ ●─○ ○─┐ ●─○ ○─┐ RB2 キー入力2 ───│─────●─│─────●─ │ 3 │ = │ ─┴─ │ ─┴─ ●─○ ○─┐ ●─○ ○─┐ RB3 キー入力3 ───│─────●─│─────●─ │ 4 │ + │ ─┴─ │ ─┴─ ●─○ ○─┐ ●─○ ○─┐ RB4 キー入力4 ───│─────●─│─────●─ │ 5 │ ー │ ─┴─ │ ─┴─ ●─○ ○─┐ ●─○ ○─┐ RB5 キー入力5 ───│─────●─│─────●─ │ 6 │ C │ ─┴─ │ ─┴─ ●─○ ○─┐ ●─○ ○─┐ RB6 キー入力6 ───│─────●─│─────●─ │ │ 4.1.2. タイミングチャート ダイナミック駆動で表示の桁を切り換える時の表示ブランク期間中に、キーマトリッ クスの走査を行います。 LEDを表示中のキーマトリックスの列走査ポートは、入力状態にして表示に影響しな いようにします。 表示ブランク期間に LED表示 キーマトリックス走査 LED表示 ────→┼←────────→┼←───── 1 1 HiZ(Input)┌─┐ ┌─────┐HiZ(Input) ─────┘ │ 0 │ └────── A4 列0走査 └──┘ 1 HiZ(Input)┌─────┐ ┌─┐HiZ(Input) ─────┘ │ 0 │ └────── B7 列1走査 └──┘ ├──┤├──┤ 列0入力 列1入力 4.1.3. キーコード チャタリングを除去したキー入力データはkey_fix[0]とkey_fix[1]に入ります。 "2"のキーが押された場合のデータはkey_fix[0]に入ります。 b7 b6 b5 b4 b3 b2 b1 b0 ┌─┬─┬─┬─┬─┬─┬─┬─┐ key_fix[0] │0 │1 │1 │1 │1 │0 │1 │1 │ └─┴─┴─┴─┴─┴─┴─┴─┘ PB7は列1の走査用に使用する為、b7をマスクします。 b0からb6まで順にビットを検査して、0になっているビット番号を求めます。 列0に接続されたキーのコードは、0になっているビット番号になります。 "9"のキーが押された場合のデータはkey_fix[1]に入ります。 b7 b6 b5 b4 b3 b2 b1 b0 ┌─┬─┬─┬─┬─┬─┬─┬─┐ key_fix[1] │0 │1 │1 │1 │1 │0 │1 │1 │ └─┴─┴─┴─┴─┴─┴─┴─┘ b0からb6まで順にビットを検査して、0になっているビット番号を求めます。 列1に接続されたキーのコードは、0になっているビット番号に7を加えた値になりま す。 4.2. switch case文 switch(key_step){ case 0: 処理1; break; case 1: 処理2; break; default: break; } key_stepが0の場合はcase 0の処理1を実行します。breakで終了します。 key_stepが1の場合はcase 1の処理2を実行します。breakで終了します。 key_stepが0でも1でもなければdefault以後を実行し、breakで終了します。 if(key_step == 0){ 処理1; }else if(key_step == 1){ 処理2; }else{ } と同様の働きをします。 この場合、最後のelse{}は不要です。 4.3. 関数の戻り値 unsigned char get_key(void) { unsigned char rt; 処理; return rt; } unsigned char型の戻り値があります。 return rt; で戻り値rt(キーコード)を設定します。 5. 実行結果 上:列0走査信号。オープンドレインを10Kでプルアップしているため、1の出力で徐々 に電圧が上昇します。 下:列1走査信号 表示の上を回避して空中配線。 表示部とマイコン部を追加して完成。 6. 実験回路 PIC16F84A ┌────┐14 │ VDD├──────●──────●────●───← +5V │ │ │ │0.1u │ │ │ R10K ┴ /50V │┴│100u/16V │ │4 │ 0.1u/50V ┬ └┬┘ │ -MCLR├──────●─┤├───●────●──── 0V │ │ │ │ │16 ┌ ─ ─ ─ ┐ │ │ OSC1├───●─┤├──┐ │ │ │ │┴ ││ │ │ │ □ 20MHz │ │ │ │15 │┬ ││ │ │ OSC2├───●─┤├──● │ │ │ └ ─ ─ ─│┘ │ │ │5 ┌───────┘ │ │ Vss├─●───────────┘ │ │13 │ RB7├────────┐ │ │3 │ │ RA4├─────┐ │ │ │ ┏──0──7─ RB0 │ │ ┠──1──8─ RB1 │ │ ┠──2──9─ RB2 │ │ ┠──3──=─ RB3 │ │ ┠──4──+─ RB4 │ │ ┠──5──−─ RB5 │ │ ┠──6──C─ RB6 │ │ ┃ キーマトリックスの回路を参照 │ │17 ┃ │ RA0├───────────────────────┐ │ │18 ┃ │ │ RA1├──────────────────┐ │ │ │1 ┃ │ │ │ RA2├─────────────┐ │ │ │ │2 ┃ │ │ │ │ RA3├────────┐ │ │ │ │ │ ┃ │d3 │d2 │d1 │d0 │ │ ┃ ┌┴─┐ ┌┴─┐ ┌┴─┐ ┌┴─┐ │ │ ┃ │ 8 │ │ 8 │ │ 8 │ │ 8 │ │ │ ┃ └┰┰┘ └┰┰┘ └┰┰┘ └┰┰┘ │ │6 ┃ a ┃┃a〜g ┃┃a〜g ┃┃a〜g ┃┃a〜g │ RB0├──╂─R820━━╋┻━━━┻┻━━━┻┻━━━┻┛ │ │7 ┃ b ┃ │ RB1├──╂─R820━━┫ │ │8 ┃ c ┃  │ RB2├──╂─R820━━┫ │ │9 ┃ d ┃ │ RB3├──╂─R820━━┫ │ │10 ┃ e ┃ │ RB4├──╂─R820━━┫ │ │11 ┃ f ┃ │ RB5├──╂─R820━━┫ │ │12 ┃ g ┃ │ RB6├──┸─R820━━┛ │ │ └────┘ 電源パスコン0.1u/50VのセラミックコンデンサーはVDDとVssの近くに接続します。 発振子のGNDはVssの直ぐ近くに接続します。
LessonCのTopへ
サイトのTopへ
法律条項 この資料により生じたいかなる障害や損害に対し、著者は全てを免責されるものとします。 この資料は、著作権法の下で保護され、入手先、著者、日付、法律条項を含んだ場合にのみ複製が可能です。
inserted by FC2 system