マイクロコンピュータによるリアルタイム処理
科目名: メディアネットワーク実験IA(2008年〜2013年)
対象: メディアネットワークコース3年目
実験室: 情報エレクトロニクス棟5階会議室
レポート提出締切: 次週の火曜日
レポート提出先: 情報エレクトロニクス棟6階6-08
連絡先: 青木 直史(Tel: 706-6532)(E-mail: aoki@ime.ist.hokudai.ac.jp)
目的
マイクロコンピュータは,プログラム次第で様々な動作を実現できるICである.本実験は,PICマイコンによる応用回路の作成を通して,マイクロコンピュータに対する理解を深めることを目的としている.
1.はじめに
マイクロコンピュータは,プログラム次第で様々な動作を実現できるIC(Integrated Circuit)である.マイクロコンピュータのなかでも,CPU(Central Processing Unit),ROM(Read Only Memory),RAM(Random Access Memory),I/O(Input/Output),タイマ等の基本機能がひとつのチップに集積されたものはシングルチップマイコンと呼ばれる.
本実験では,Microchip Technology社のシングルチップマイコンであるPICマイコンを用いて,いくつかの回路を作成する.
図1は,本実験で使用するPIC16F84Aの端子の配置である.
図1.PIC16F84Aの端子
PIC16F84AのRA0〜RA4,RB0〜RB7は外部回路との信号の入出力を行うI/O端子である.
OSC1およびOSC2はクロックを供給するための端子である.本実験では,これらの端子に20MHzのセラミック発振器を接続している.
VDDおよびVSSは電源端子であり,それぞれ,+5Vおよび0Vを供給する必要がある.
MCLRは,プログラムをリセットするときは0V,プログラムの実行中は+5Vを供給する必要がある.
図2.プログラミングの手順
本実験では,C言語を使用してPICマイコンのプログラム開発を行う.図2に,プログラミングの手順を示す.
本実験では,C言語コンパイラとして,mikroElektronika社のmikroCを使用し,PICマイコンの実行ファイルであるHEXファイルの生成を行っている.また,鰹H月電子通商のPICプログラマを使用し,PICマイコンに対してHEXファイルの書き込みを行っている.
2.実験1:LEDの制御
図3の実験回路1をブレッドボード上に作成し,以下のプログラムを実行する.回路の動作を観察し,プログラムの内容について考察せよ.
図3.実験回路1
/* jikken01.c */ void main(void) { TRISA = 0x00; /* 出力端子: RA0〜RA4 */ PORTA = 0x03; /* ポートA: 0 0 0 0 0 0 1 1 */ TRISB = 0x00; /* 出力端子: RB0〜RB7 */ PORTB = 0x00; /* ポートB: 0 0 0 0 0 0 0 0 */ while (1) /* 無限ループ */ { PORTA = 0x02; /* ポートA: 0 0 0 0 0 0 1 0 */ Delay_ms(1000); /* 1000msの時間待ち */ PORTA = 0x01; /* ポートA: 0 0 0 0 0 0 0 1 */ Delay_ms(1000); /* 1000msの時間待ち */ } }
このプログラムを実行すると,ポートAに接続した2個のLEDが1000ms間隔で交互に点滅を繰り返す.
図4.PORTAに0x02を書き込んだときのPORTAの状態
図4に示すように,PORTAに0x02を書き込むと,RA0は0V,RA1は5Vとなり,RA0に接続したLEDだけが発光することになる.
図5.PORTAに0x01を書き込んだときのPORTAの状態
図5に示すように,PORTAに0x01を書き込むと,RA0は5V,RA1は0Vとなり,RA1に接続したLEDだけが発光することになる.
3.実験2:タクトスイッチによるLEDの制御
図6の実験回路2をブレッドボード上に作成し,以下のプログラムを実行する.回路の動作を観察し,プログラムの内容について考察せよ.
図6.実験回路2
/* jikken02.c */ void main(void) { TRISA = 0x00; /* 出力端子: RA0〜RA4 */ PORTA = 0x03; /* ポートA: 0 0 0 0 0 0 1 1 */ TRISB = 0xFF; /* 入力端子: RB0〜RB7 */ OPTION_REG &= 0x7F; /* プルアップ有効 */ while (1) /* 無限ループ */ { if (PORTB == 0xFE) /* ポートB: 1 1 1 1 1 1 1 0 */ { PORTA = 0x02; /* ポートA: 0 0 0 0 0 0 1 0 */ } else if (PORTB == 0xFD) /* ポートB: 1 1 1 1 1 1 0 1 */ { PORTA = 0x01; /* ポートA: 0 0 0 0 0 0 0 1 */ } else { PORTA = 0x03; /* ポートA: 0 0 0 0 0 0 1 1 */ } } }
jikken02.cは,それぞれのスイッチに対応したLEDを発光させるプログラムになっている.
図7.6番端子のスイッチだけをオンにしたときのPORTBの状態
図7に示すように,6番端子のスイッチだけをオンにするとRB0だけが0Vとなり,PORTBは0xFEとなる.
図8.7番端子のスイッチだけをオンにしたときのPORTBの状態
図8に示すように,7番端子のスイッチだけをオンにするとRB1だけが0Vとなり,PORTBは0xFDとなる.
4.実験3:タクトスイッチによる音の制御
図9の実験回路3をブレッドボード上に作成し,以下のプログラムを実行する.回路の動作を観察し,プログラムの内容について考察せよ.
図9.実験回路3
/* jikken03.c */ void buzzer(unsigned short int us) { unsigned short int i; PORTA = 0x00; /* ポートA: 0 0 0 0 0 0 0 0 */ for (i = 0; i < us; i++) { Delay_us(1); /* 1usの時間待ち */ } PORTA = 0x01; /* ポートA: 0 0 0 0 0 0 0 1 */ for (i = 0; i < us; i++) { Delay_us(1); /* 1usの時間待ち */ } } void main(void) { TRISA = 0x00; /* 出力端子: RA0〜RA4 */ PORTA = 0x01; /* ポートA: 0 0 0 0 0 0 0 1 */ TRISB = 0xFF; /* 入力端子: RB0〜RB7 */ OPTION_REG &= 0x7F; /* プルアップ有効 */ while (1) /* 無限ループ */ { switch (PORTB) { case 0xFE : buzzer(239); break; /* C7 */ case 0xFD : buzzer(213); break; /* D7 */ case 0xFB : buzzer(190); break; /* E7 */ case 0xF7 : buzzer(179); break; /* F7 */ case 0xEF : buzzer(159); break; /* G7 */ case 0xDF : buzzer(142); break; /* A7 */ case 0xBF : buzzer(127); break; /* B7 */ case 0x7F : buzzer(119); break; /* C8 */ default : break; } } }
jikken03.cは,8個のスイッチを鍵盤として,それぞれのスイッチに割り当てられた高さの音を鳴らすプログラムになっている.
図10.矩形波:(a) C7音,(b) C8音
このプログラムは矩形波を使って音を鳴らしている.図10に示すように,矩形波は0と1が交互に繰り返す波形となっており,基本周期が長いと低い音,基本周期が短いと高い音になる.
図11.12平均律音階:(a) 基本周波数,(b) 基本周期
8個のスイッチには,12平均律音階のC7〜C8音が割り当てられている.図11に,C7〜C8音の基本周波数と基本周期を示す.
5.実験4:フルカラーLEDの制御
図12の実験回路4をブレッドボード上に作成し,以下のプログラムを実行する.なお,図13は,本実験で使用するフルカラーLEDの端子の配置である.回路の動作を観察し,プログラムの内容について考察せよ.
図12.実験回路4
図13.フルカラーLEDの端子
/* jikken04.c */ void main(void) { unsigned short int color; TRISA = 0x00; /* 出力端子: RA0〜RA4 */ PORTA = 0xFF; /* ポートA: 1 1 1 1 1 1 1 1 */ TRISB = 0x00; /* 出力端子: RB0〜RB7 */ PORTB = 0xFF; /* ポートB: 1 1 1 1 1 1 1 1 */ color = 1; while (1) /* 無限ループ */ { PORTA = color; Delay_ms(1000); /* 1000msの時間待ち */ color++; if (color == 8) { color = 0; } } }
このプログラムを実行すると,ポートAに接続したフルカラーLEDの発光色が1000ms間隔で変化する.
図14.フルカラーLEDの発光色
図14に示すように,R(レッド),G(グリーン),B(ブルー)という光の三原色を混ぜ合わせると,さまざまな中間色を発光させることができる.
6.実験5:タクトスイッチによるフルカラーLEDの制御
図15の実験回路5をブレッドボード上に作成し,以下のプログラムを実行する.回路の動作を観察し,プログラムの内容について考察せよ.
図15.実験回路5
/* jikken05.c */ unsigned short int color; void interrupt(void) { INTCON &= 0x7F; /* 割り込み禁止 */ INTCON &= 0xEF; /* INT割り込み禁止 */ INTCON &= 0xFD; /* INT割り込みフラグのクリア */ Delay_ms(20); /* 20msの時間待ち */ if ((PORTB & 0x01) == 0) { color++; if (color == 8) { color = 0; } PORTA = color; /* LEDの発光を切り替える */ } INTCON |= 0x10; /* INT割り込み許可 */ INTCON |= 0x80; /* 割り込み許可 */ } void main(void) { TRISA = 0x00; /* 出力端子: RA0〜RA4 */ PORTA = 0x00; /* ポートA: 0 0 0 0 0 0 0 0 */ TRISB = 0xFF; /* 入力端子: RB0〜RB7 */ OPTION_REG &= 0x7F; /* プルアップ有効 */ OPTION_REG &= 0xBF; /* 5V->0VでINT割り込みをかける */ color = 1; PORTA = color; INTCON |= 0x10; /* INT割り込み許可 */ INTCON |= 0x80; /* 割り込み許可 */ while (1) /* 無限ループ */ { /* INT割り込み */ } }
jikken05.cは,PICマイコンの割り込み機能を利用し,スイッチが押されるたびにをフルカラーLEDの発光色を変化させるものになっている.
PIC16F84Aの6番端子は,通常のデータ入出力端子RB0としてだけではなく,割り込み入力端子INTとして利用することもできる.このプログラムではOPTION_REGレジスタを操作することで,割り込み入力端子に対する信号が1から0に変化したときに,割り込みが発生するように設定している.
なお,このプログラムは,スイッチを押したときに発生するチャタリングを考慮したものになっている.
バネを使った機械式のスイッチは,図16(a)に示すように,スイッチを押してからしばらくの間は,バネの振動によってオンとオフが不安定になる.このとき,図16(b)に示すように,チャタリングの持続時間よりも短い時間間隔でスイッチをチェックするとスイッチの誤検出を起こしてしまう.
こうしたスイッチの誤検出を回避するには,図16(c)に示すように,チャタリングの持続時間よりも長い時間間隔でスイッチをチェックすることがひとつの解決策となる.
図16.スイッチのチェック:(a) チャタリング,(b) 短い時間間隔でスイッチのチェックを行う場合,(c) 長い時間間隔でスイッチのチェックを行う場合
7.実験6:リモコンによるフルカラーLEDの制御
図17の実験回路6をブレッドボード上に作成し,以下のプログラムを実行する.なお,図18は,本実験で使用する赤外線リモコン受信モジュールの端子の配置である.回路の動作を観察し,プログラムの内容について考察せよ.
図17.実験回路6
図18.赤外線リモコン受信モジュールのピン配置
/* jikken06.c */ unsigned short int color; void interrupt(void) { unsigned short int i; unsigned short int custom_code_a, custom_code_b; unsigned short int data_code_a, data_code_b; INTCON &= 0x7F; /* 割り込み禁止 */ INTCON &= 0xEF; /* INT割り込み禁止 */ INTCON &= 0xFD; /* INT割り込みフラグのクリア */ /* リーダー */ TMR0 = 0; while ((PORTB & 0x01) == 0) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } if (TMR0 < 156 || TMR0 > 196) /* 9.0ms * 20MHz/4 / 256 = 176 */ { INTCON |= 0x10; /* INT割り込み許可 */ INTCON |= 0x80; /* 割り込み許可 */ return; } TMR0 = 0; while ((PORTB & 0x01) == 1) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } if (TMR0 < 68 || TMR0 > 108) /* 4.5ms * 20MHz/4 / 256 = 88 */ { INTCON |= 0x10; /* INT割り込み許可 */ INTCON |= 0x80; /* 割り込み許可 */ return; } /* カスタムコード(下位8bit) */ custom_code_a = 0x00; for (i = 0; i < 8; i++) { TMR0 = 0; while ((PORTB & 0x01) == 0) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } while ((PORTB & 0x01) == 1) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } if (TMR0 < 33) /* 1.125ms * 20MHz/4 / 256 = 22 */ { custom_code_a &= ~(0x01 << i); } else /* 2.250ms * 20MHz/4 / 256 = 44 */ { custom_code_a |= (0x01 << i); } } /* カスタムコード(上位8bit) */ custom_code_b = 0x00; for (i = 0; i < 8; i++) { TMR0 = 0; while ((PORTB & 0x01) == 0) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } while ((PORTB & 0x01) == 1) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } if (TMR0 < 33) /* 1.125ms * 20MHz/4 / 256 = 22 */ { custom_code_b &= ~(0x01 << i); } else /* 2.250ms * 20MHz/4 / 256 = 44 */ { custom_code_b |= (0x01 << i); } } /* データコード(a) */ data_code_a = 0x00; for (i = 0; i < 8; i++) { TMR0 = 0; while ((PORTB & 0x01) == 0) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } while ((PORTB & 0x01) == 1) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } if (TMR0 < 33) /* 1.125ms * 20MHz/4 / 256 = 22 */ { data_code_a &= ~(0x01 << i); } else /* 2.250ms * 20MHz/4 / 256 = 44 */ { data_code_a |= (0x01 << i); } } /* データコード(b) */ data_code_b = 0x00; for (i = 0; i < 8; i++) { TMR0 = 0; while ((PORTB & 0x01) == 0) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } while ((PORTB & 0x01) == 1) { if (TMR0 == 255) /* タイムアウトのチェック */ { break; } } if (TMR0 < 33) /* 1.125ms * 20MHz/4 / 256 = 22 */ { data_code_b &= ~(0x01 << i); } else /* 2.250ms * 20MHz/4 / 256 = 44 */ { data_code_b |= (0x01 << i); } } if (data_code_a == ~data_code_b) /* 通信エラーのチェック */ { if (data_code_a == 0x00) /* チャンネル+のボタンのデータ */ { color++; if (color == 0x08) { color = 0x00; } PORTA = color; /* LEDの発光を切り替える */ } } INTCON |= 0x10; /* INT割り込み許可 */ INTCON |= 0x80; /* 割り込み許可 */ } void main(void) { TRISA = 0x00; /* 出力端子: RA0〜RA4 */ PORTA = 0x00; /* ポートA: 0 0 0 0 0 0 0 0 */ TRISB = 0xFF; /* 入力端子: RB0〜RB7 */ OPTION_REG &= 0x7F; /* プルアップ有効 */ OPTION_REG &= 0xBF; /* 5V->0VでINT割り込みをかける */ OPTION_REG &= 0xDF; /* クロックでタイマを動作させる */ OPTION_REG &= 0xF0; /* プリスケーラ有効 */ OPTION_REG |= 0x07; /* プリスケーラ1/256 */ color = 1; PORTA = color; INTCON |= 0x10; /* INT割り込み許可 */ INTCON |= 0x80; /* 割り込み許可 */ while (1) /* 無限ループ */ { /* INT割り込み */ } }
実験5と同様,このプログラムは,PICマイコンの割り込み機能を利用し,フルカラーLEDの発光色を制御するものである.ただし,スイッチのかわりに,リモコンを使って,LEDの発光色を変化させている.
この実験は,NECフォーマットのリモコンを使用している.図19に示すように,NECフォーマットのリモコンは,リーダー,カスタムコード,データコードの順番に信号を送出するしくみになっている.
リーダーは,リモコンの信号を検出するための手がかりとなる識別信号である.
カスタムコードは,メーカーの番号などを表すデータである.
リモコンのボタンを押すと,それぞれのボタンに対応するデータコードが送出される.図20に,本実験で使用するリモコンのボタンとデータコードを示す.
なお,カスタムコードとデータコードは,エラーチェックのため,それぞれ2回ずつ送出される.ただし,2番目のデータは,1番目のデータをビット反転したものになっている.
図19.NECフォーマットの信号
図20.NECフォーマットのデータコード
このプログラムは,PIC16F84Aのタイマを利用して,時間の計測を行っている.
PIC16F84Aのタイマは,クロックの1/4倍の分解能で時間を計測できるようになっている.たとえば,20MHzのクロックを利用する場合,タイマの分解能は(1/20MHz)*4=0.2usとなる.
タイマによって計測された時間はTMR0レジスタに書き込まれる.TMR0レジスタは8bitのカウンタとなっており,51.2us(0.2us * 256)までの時間を計測できるようになっている.
さらに長い時間を計測するには,OPTION_REGレジスタを操作することでプリスケーラを設定し,タイマの分解能を小さくする必要がある.
たとえば,プリスケーラを1/256に設定すると,タイマの分解能は(1/20MHz)*4*256=51.2usとなる.この場合,TMR0レジスタは13.1072ms(51.2us * 256)までの時間を計測できるようになる.
8.レポートについて
下記3点についてレポートを書きなさい.
1.マイコンを使ってLEDを発光させるしくみについて説明しなさい.(5点)
2.スイッチを使ってマイコンにデータを入力するしくみについて説明しなさい.(5点)
3.もし,あなたがマイコンを使って何かを作ることになったらどうするか,実現に向けたアイデアを述べなさい.具体的な考察があるものを高く評価します.優秀な作品は表彰します.(10点)
参考文献
青木 直史, ``ブレッドボードではじめるマイコンプログラミング,'' 技術評論社, 2010.
青木 直史, ``H8マイコンによるネットワーク・プログラミング - C言語ではじめる組み込みマイコン入門 - ,'' 技術評論社, 2008.
Last Modified: May 2 12:00 JST 2014 by Naofumi Aoki
E-mail: aoki@ime.ist.hokudai.ac.jp