マイクロコンピュータによるリアルタイム処理
科目名: メディアネットワーク実験IA(2007年)(電子工学実験I(2005年〜2006年))
対象: メディアネットワークコース3年目
実験室: 情報エレクトロニクス棟11階リサーチラウンジ
レポート提出締切: 次週の火曜日
レポート提出先: 情報エレクトロニクス棟6階6-08
連絡先: 青木 直史(Tel: 706-6532)(E-mail: aoki@nis-ei.eng.hokudai.ac.jp)
目的
マイクロコンピュータは,プログラム次第で様々な動作を実現できるICである.本実験は,PICマイコンによる応用回路の作成を通して,マイクロコンピュータに対する理解を深めることを目的としている.
1.はじめに
マイクロコンピュータは,プログラム次第で様々な動作を実現できるIC(Integrated Circuit)である.マイクロコンピュータのなかでも,CPU(Central Processing Unit),メモリ,インタフェース等の回路が1チップに集積されたものは,ワンチップマイコンと呼ばれる.
本実験では,Microchip Technology社のワンチップマイコンであるPIC(Peripheral Interface Controller)マイコンを用いて,いくつかの回路を作成する.
本実験で使用するPIC16F84Aのピン配置を図1に示す.
図1.PIC16F84Aのピン配置
PIC16F84AのRA0〜RA4,RB0〜RB7は外部回路との信号の入出力を行うインタフェース端子である.
OSC1およびOSC2はクロックを供給するための端子である.本実験では,これらの端子に20MHzのセラミック発振器を接続している.
VDDおよびVSSは電源端子であり,それぞれ,+5Vおよび0Vを供給する必要がある.
MCLRは,プログラムをリセットするときは0V,プログラムの実行中は+5Vを供給する必要がある.
図2.プログラム開発の手順
本実験では,C言語を使用してPICマイコンのプログラム開発を行う.図2に,プログラム開発の手順を示す.
本実験では,C言語コンパイラとして,CCS社のPCWHを使用し,PICマイコンの実行ファイルであるHEXファイルの生成を行っている.また,鰹H月電子通商のPIC Programmerを使用し,PICマイコンに対してHEXファイルの書き込みを行っている.
2.実験1:LEDの制御
図3の実験回路1をブレッドボード上に作成し,以下のプログラムを実行する.回路の動作を観察し,プログラムの内容について考察せよ.
図3.実験回路1
/* jikken01.c */ #include <16f84a.h> #fuses HS, NOWDT, PUT, NOPROTECT #use delay(CLOCK = 20000000) #use fast_io(a) void main(void) { set_tris_a(0x00); /* tris A: 0 0 0 0 0 0 0 0 */ while (TRUE) { output_a(0xFE); /* port A: 1 1 1 1 1 1 1 0 */ delay_ms(1000); /* delay time: 1000 ms */ output_a(0xFD); /* port A: 1 1 1 1 1 1 0 1 */ delay_ms(1000); /* delay time: 1000 ms */ } }
写真1に,ブレッドボード上に作成した実験回路1を示す.
写真1.実験回路1
このプログラムを実行すると,ポートAに接続した2個のLEDが1000ms間隔で交互に点滅を繰り返す.http://nis-ei.eng.hokudai.ac.jp/~aoki/laboratory03/jikken01.mpgに,回路の動作の様子を示す.
図4.ポートAの設定:(a) すべての端子を出力端子として設定する場合,(b) すべての端子を入力端子として設定する場合,(c) すべての端子を入力端子として設定する場合
main関数の冒頭にあるset_tris_a(0x00)は,ポートAのすべての端子を出力端子として設定するための命令である.図4に示すように,RA0からRA4までの端子を出力端子として設定するには,set_tris_a関数の引数を0x00にすればよい.
なお,ポートAには端子が5個しか用意されていないため,set_tris_a関数の引数については,上位3bitを0または1のどちらにしても問題はない.
例えば,RA0からRA4までの端子を入力端子として設定するには,set_tris_a関数の引数を0x1Fにしてもよいし,0xFFにしてもよい.
図5.ポートAの出力信号とLEDの状態:(a) 0xFEを出力した場合,(b) 0xFDを出力した場合
図5に,ポートAに対する出力信号とLEDの状態を示す.RA0またはRA1が0Vのときは+5Vの電源との間に電位差が生じるため,電源からこれらの端子に向かって電流が流れ,LEDが発光する.一方,これらの端子が+5Vのときは電源との間に電位差が生じないため電流は流れず,LEDは発光しない.
3.実験2:タクトスイッチによるLEDの制御
図6の実験回路2をブレッドボード上に作成し,以下のプログラムを実行する.回路の動作を観察し,プログラムの内容について考察せよ.
図6.実験回路2
/* jikken02.c */ #include <16f84a.h> #fuses HS, NOWDT, PUT, NOPROTECT #use delay(CLOCK = 20000000) #use fast_io(a) #use fast_io(b) void main(void) { set_tris_a(0x00); /* tris A: 0 0 0 0 0 0 0 0 */ set_tris_b(0xFF); /* tris B: 1 1 1 1 1 1 1 1 */ port_b_pullups(TRUE); while (TRUE) { if (input_b() == 0xFE) /* port B: 1 1 1 1 1 1 1 0 */ output_a(0xFE); /* port A: 1 1 1 1 1 1 1 0 */ else if (input_b() == 0xFD) /* port B: 1 1 1 1 1 1 0 1 */ output_a(0xFD); /* port A: 1 1 1 1 1 1 0 1 */ else output_a(0xFF); /* port A: 1 1 1 1 1 1 1 1 */ } }
写真2に,ブレッドボード上に作成した実験回路2を示す.
写真2.実験回路2
このプログラムは,2個のスイッチによって,ポートBに0または1の信号を入力し,それぞれのスイッチに対応するポートAのLEDを点灯させるものである.http://nis-ei.eng.hokudai.ac.jp/~aoki/laboratory03/jikken02.mpgに,回路の動作の様子を示す.
図7.スイッチの状態とポートBの入力信号:(a) 6番端子のスイッチON,(b) 7番端子のスイッチON
図7に,スイッチの状態とポートBに対する入力信号を示す.スイッチがONの場合は,RB0またはRB1は0Vになり,マイコンに0の信号が入力される.一方,スイッチがOFFの場合は,これらの端子は+5Vになり,マイコンに1の信号が入力される.
なお,port_b_pullups(TRUE)は,ポートBに内蔵されているプルアップ回路を有効にするための命令である.図8に示すように,プルアップ回路は,スイッチがOFFの場合,端子の電圧を+5Vに引き上げるためのしくみである.
プルアップ回路を有効にしないと,端子の電圧が不安定になり,スイッチがONの場合でなくても端子の電圧が0Vになってしまうことがある.そのため,スイッチの押下を正しく判定できなくなる.
もちろん,外部回路としてプルアップ回路を用意することも可能であるが,PICマイコンは,port_b_pullups関数によって,マイコンの内部にプルアップ回路を設定することができる.
図8.プルアップ回路:(a) スイッチがOFFの場合,(b) スイッチがONの場合
4.実験3:タクトスイッチによる音の制御
図9の実験回路3をブレッドボード上に作成し,以下のプログラムを実行する.回路の動作を観察し,プログラムの内容について考察せよ.
図9.実験回路3
/* jikken03.c */ #include <16f84a.h> #fuses HS, NOWDT, PUT, NOPROTECT #use delay(CLOCK = 20000000) #use fast_io(a) #use fast_io(b) void buzzer(int us) { output_a(0xFE); /* port A: 1 1 1 1 1 1 1 0 */ delay_us(us); /* delay */ output_a(0xFF); /* port A: 1 1 1 1 1 1 1 1 */ delay_us(us); /* delay */ } void main(void) { set_tris_a(0x00); /* tris A: 0 0 0 0 0 0 0 0 */ set_tris_b(0xFF); /* tris B: 1 1 1 1 1 1 1 1 */ port_b_pullups(TRUE); while (TRUE) { switch (input_b()) { case 0xFE : buzzer(255); break; /* c : 1 */ case 0xFD : buzzer(226); break; /* d : 8/9 */ case 0xFB : buzzer(201); break; /* e : 64/81 */ case 0xF7 : buzzer(191); break; /* f : 3/4 */ case 0xEF : buzzer(170); break; /* g : 2/3 */ case 0xDF : buzzer(151); break; /* a : 16/27 */ case 0xBF : buzzer(134); break; /* b : 128/243 */ case 0x7F : buzzer(127); break; /* c': 1/2 */ default : break; } } }
写真3に,ブレッドボード上に作成した実験回路3を示す.
写真3.実験回路3
このプログラムは,8個のスイッチによって,ポートBに0または1の信号を入力し,それぞれのスイッチに対応する音階の音をスピーカから出力させるものである.http://nis-ei.eng.hokudai.ac.jp/~aoki/laboratory03/jikken03.mpgに,回路の動作の様子を示す.
図10.ブザーに対する出力信号:(a) C音,(b) C'音
図10に示すように,0と1の信号を交互に出力すると,ブザーから音を鳴らすことができる.ここで,0と1の信号の変化が早いほど,波長が短くなり,高い音になる.なお,このプログラムにおける音階は,図11に示すピタゴラス音階の相対比率に基づいている.
図11.ピタゴラス音階における音階の相対比率
5.実験4:フルカラーLEDの制御
図12の実験回路4をブレッドボード上に作成し,以下のプログラムを実行する.なお,フルカラーLEDのピン配置を図13に示す.回路の動作を観察し,プログラムの内容について考察せよ.
図12.実験回路4
図13.フルカラーLEDのピン配置
/* jikken04.c */ #include <16f84a.h> #fuses HS, NOWDT, PUT, NOPROTECT #use delay(CLOCK = 20000000) #use fast_io(a) #use fast_io(b) #include "LCD_function.h" void main(void) { int color = 0; set_tris_a(0x00); /* tris A: 0 0 0 0 0 0 0 0 */ set_tris_b(0x00); /* tris B: 0 0 0 0 0 0 0 0 */ output_a(0xFF); LCD_init(); while (TRUE) { LCD_clear(); LCD_control(0x80); /* move to line 1 */ switch (color) { case 0 : output_a(0x07); printf(LCD_display, "0.off "); break; case 1 : output_a(0x06); printf(LCD_display, "1.blue "); break; case 2 : output_a(0x05); printf(LCD_display, "2.red "); break; case 3 : output_a(0x04); printf(LCD_display, "3.magenta"); break; case 4 : output_a(0x03); printf(LCD_display, "4.green "); break; case 5 : output_a(0x02); printf(LCD_display, "5.cyan "); break; case 6 : output_a(0x01); printf(LCD_display, "6.yellow "); break; case 7 : output_a(0x00); printf(LCD_display, "7.white "); break; default : break; } delay_ms(1000); color++; if (color > 7) { color = 0; } } }
写真4に,ブレッドボード上に作成した実験回路4を示す.
写真4.実験回路4
このプログラムを実行すると,ポートAに接続したフルカラーLEDの発光色が1000ms間隔で変化する.http://nis-ei.eng.hokudai.ac.jp/~aoki/laboratory03/jikken04.mpgに,回路の動作の様子を示す.
図14に示すように,フルカラーLEDは光の3原色(R,G,B)を混合させることによって,様々な色を作り出すことができる.
なお,このプログラムは,Sunlike Display社のSC1602Bという液晶ディスプレイを使用して,発光色の文字表示も行っている.
図14.フルカラーLEDにおける発光色
6.実験5:タクトスイッチによるフルカラーLEDの制御
図15の実験回路5をブレッドボード上に作成し,以下のプログラムを実行する.回路の動作を観察し,プログラムの内容について考察せよ.
図15.実験回路5
/* jikken05.c */ #include <16f84a.h> #fuses HS, NOWDT, PUT, NOPROTECT #use delay(CLOCK = 20000000) #use fast_io(a) #use fast_io(b) #include "LCD_function.h" int color = 0; int flag = 0; #INT_EXT void tact_switch(void) { disable_interrupts(INT_EXT); delay_ms(20); /* anti-chattering */ if (input(PIN_B0) == 0) /* anti-chattering */ { color++; if (color > 7) { color = 0; } } flag = 1; } void main(void) { set_tris_a(0x00); /* tris A: 0 0 0 0 0 0 0 0 */ set_tris_b(0x01); /* tris B: 0 0 0 0 0 0 0 1 */ port_b_pullups(TRUE); output_a(0xFF); LCD_init(); enable_interrupts(INT_EXT); enable_interrupts(GLOBAL); ext_int_edge(H_to_L); while (TRUE) { LCD_clear(); LCD_control(0x80); /* move to line 1 */ switch (color) { case 0 : output_a(0x07); printf(LCD_display, "0.off "); break; case 1 : output_a(0x06); printf(LCD_display, "1.blue "); break; case 2 : output_a(0x05); printf(LCD_display, "2.red "); break; case 3 : output_a(0x04); printf(LCD_display, "3.magenta"); break; case 4 : output_a(0x03); printf(LCD_display, "4.green "); break; case 5 : output_a(0x02); printf(LCD_display, "5.cyan "); break; case 6 : output_a(0x01); printf(LCD_display, "6.yellow "); break; case 7 : output_a(0x00); printf(LCD_display, "7.white "); break; default : break; } while (TRUE) { if (flag == 1) { flag = 0; enable_interrupts(INT_EXT); break; } } } }
写真5に,ブレッドボード上に作成した実験回路5を示す.
写真5.実験回路5
このプログラムは,PICマイコンの割り込み機能を利用し,フルカラーLEDの発光色を制御するものである.http://nis-ei.eng.hokudai.ac.jp/~aoki/laboratory03/jikken05.mpgに,回路の動作の様子を示す.
PIC16F84Aの6番端子は,通常の入出力端子RB0としてだけではなく,割り込み入力端子INTとして使用することができる.このプログラムでは,ext_int_edge(H_to_L)によって,割り込み入力端子に対する信号が1から0に変化したときに,割り込みが発生するように設定している.
なお,このプログラムは,スイッチを押したときに発生するチャタリングを考慮したものになっている.
チャタリングは,バネを使った機械式のスイッチに特有の現象である.こうしたスイッチでは,図16に示すように,スイッチを押してから数msの間は,バネの振動が原因となり,接触が不安定になる.その結果,スイッチが高速についたりきれたりするため,1回しかスイッチを押していないのにもかかわらず,何回もスイッチを押したことになってしまう現象が生じる.これがチャタリングである.
図16に示すように,このプログラムは,チャタリングが落ち着くのを待ってから,入力を再確認することにより,チャタリングによるスイッチの誤検出を回避している.
図16.チャタリングによるスイッチの誤検出の回避
7.実験6:リモコンによるフルカラーLEDの制御
図17の実験回路6をブレッドボード上に作成し,以下のプログラムを実行する.なお,赤外線リモコン受信モジュールのピン配置を図18に示す.回路の動作を観察し,プログラムの内容について考察せよ.
図17.実験回路6
図18.赤外線リモコン受信モジュールのピン配置
/* jikken06.c */ #include <16f84a.h> #fuses HS, NOWDT, PUT, NOPROTECT #use delay(CLOCK = 20000000) #use fast_io(a) #use fast_io(b) #include "LCD_function.h" int color = 0; int flag = 0; int custom_a = 0; int custom_b = 0; int data_a = 0; int data_b = 0; #INT_EXT void remote_controller(void) { int i; int b; disable_interrupts(INT_EXT); /* leader */ set_timer0(0); while (input(PIN_B0) == 0); if (get_timer0() < 156) /* < 175.78 (9.0ms * 20MHz/4 / 256) */ { flag = 1; return; } set_timer0(0); while (input(PIN_B0) == 1); if (get_timer0() < 68) /* < 87.89 (4.5ms * 20MHz/4 / 256) */ { flag = 1; return; } /* custom code */ custom_a = 0; for (i = 0; i < 8; i++) { set_timer0(0); while (input(PIN_B0) == 0); while (input(PIN_B0) == 1); if (get_timer0() < 33) /* 21.97 (1.125ms * 20MHz/4 / 256) */ b = 0; else /* 43.95 (2.250ms * 20MHz/4 / 256) */ b = 1; custom_a |= (b << i); } custom_b = 0; for (i = 0; i < 8; i++) { set_timer0(0); while (input(PIN_B0) == 0); while (input(PIN_B0) == 1); if (get_timer0() < 33) /* 21.97 (1.125ms * 20MHz/4 / 256) */ b = 0; else /* 43.95 (2.250ms * 20MHz/4 / 256) */ b = 1; custom_b |= (b << i); } /* data code */ data_a = 0; for (i = 0; i < 8; i++) { set_timer0(0); while (input(PIN_B0) == 0); while (input(PIN_B0) == 1); if (get_timer0() < 33) /* 21.97 (1.125ms * 20MHz/4 / 256) */ b = 0; else /* 43.95 (2.250ms * 20MHz/4 / 256) */ b = 1; data_a |= (b << i); } data_b = 0; for (i = 0; i < 8; i++) { set_timer0(0); while (input(PIN_B0) == 0); while (input(PIN_B0) == 1); if (get_timer0() < 33) /* 21.97 (1.125ms * 20MHz/4 / 256) */ b = 0; else /* 43.95 (2.250ms * 20MHz/4 / 256) */ b = 1; data_b |= (b << i); } if (data_a == ~data_b) /* data check */ { switch (data_a) { case 0x19 : color = 0; break; case 0x10 : color = 1; break; case 0x11 : color = 2; break; case 0x12 : color = 3; break; case 0x13 : color = 4; break; case 0x14 : color = 5; break; case 0x15 : color = 6; break; case 0x16 : color = 7; break; default : break; } } flag = 1; } void main(void) { set_tris_a(0x00); /* tris A: 0 0 0 0 0 0 0 0 */ set_tris_b(0x01); /* tris B: 0 0 0 0 0 0 0 1 */ port_b_pullups(TRUE); output_a(0xFF); LCD_init(); enable_interrupts(INT_EXT); enable_interrupts(GLOBAL); ext_int_edge(H_to_L); setup_counters(RTCC_INTERNAL, RTCC_DIV_256); while (TRUE) { LCD_clear(); LCD_control(0x80); /* move to line 1 */ switch (color) { case 0 : output_a(0x07); printf(LCD_display, "0.off "); break; case 1 : output_a(0x06); printf(LCD_display, "1.blue "); break; case 2 : output_a(0x05); printf(LCD_display, "2.red "); break; case 3 : output_a(0x04); printf(LCD_display, "3.magenta"); break; case 4 : output_a(0x03); printf(LCD_display, "4.green "); break; case 5 : output_a(0x02); printf(LCD_display, "5.cyan "); break; case 6 : output_a(0x01); printf(LCD_display, "6.yellow "); break; case 7 : output_a(0x00); printf(LCD_display, "7.white "); break; default : break; } while (TRUE) { if (flag == 1) { flag = 0; enable_interrupts(INT_EXT); break; } } } }
写真6に,ブレッドボード上に作成した実験回路6を示す.
写真6.実験回路6
実験5と同様,このプログラムは,PICマイコンの割り込み機能を利用し,フルカラーLEDの発光色を制御するものである.ただし,タクトスイッチのかわりに,リモコンを使って,LEDの発光色を変化させている.http://nis-ei.eng.hokudai.ac.jp/~aoki/laboratory03/jikken06.mpgに,回路の動作の様子を示す.
この実験は,NECフォーマットのリモコンを使用している.図19に示すように,NECフォーマットのリモコンは,リーダ,カスタムコード,データコードの順番に信号を送出するしくみになっている.
リモコンのボタンを押すと,それぞれのボタンに対応するデータコードが送出される.図20に,実験に使用したリモコンにおけるボタンとデータコードの対応を示す.
図19.NECフォーマット:(a) 信号全体,(b) 0の信号波形,(c) 1の信号波形
図20.audio-technicaテレビ専用マルチリモコンATV-551(NECフォーマット)のボタンとデータコードの対応
このプログラムは,タイマTMR0を使用して,時間の計測を行っている.
PICの1命令は1内部クロックで実行される.内部クロックの周期は,クロック周期の4倍である.したがって,内部クロックの周期は(1/20MHz)*4=0.2usとなる.TMR0は8bitのカウンタであるため,TMR0は,51.2us(0.2us * 256)までの時間を計測することができる.
さらに長い時間を計測するには,プリスケーラを設定し,内部クロックの周期を分周する必要がある.
例えば,プリスケーラを1/256に設定すると,内部クロックの周期は(1/20MHz)*4*256=51.2usとなる.この場合,TMR0は,13.1072ms(51.2us * 256)までの時間を計測することができる.
8.課題
1.各実験について,回路の動作とプログラムの内容について簡単に説明せよ.なお,http://nis-ei.eng.hokudai.ac.jp/~aoki/laboratory03.htmlに,各実験の簡単な説明を掲載しているので,疑問点があれば参考にせよ.
2.マイクロコンピュータに関して,自ら課題をひとつ設定し,調査せよ.分量はA4用紙2ページ以内に制限する.マイクロコンピュータに関連していればどのような課題でもよい.課題の例としては以下を挙げておく.
(a) マイクロコンピュータの歴史について
(b) マイクロコンピュータの応用について
3.実験の考察または感想を述べよ.
付録.液晶ディスプレイSC1602BS-Bのライブラリ
実験4〜6で使用する液晶ディスプレイSC1602Bのライブラリを以下に示す.
/* LCD_function.h */ void LCD_write(char data, char RS) { /* RB7, RB6, RB5, RB4, RB3, RB2, RB1, RB0 */ /* DB7, DB6, DB5, DB4, E, RS, x, x */ output_b(data & 0xF0); /* DB7, DB6, DB5, DB4 */ if (RS == 1) { output_high(PIN_B2); /* RS -> 1 */ } else { output_low(PIN_B2); /* RS -> 0 */ } delay_us(1); /* > 40 ns */ output_high(PIN_B3); /* E -> 1 */ delay_us(1); /* > 230 ns */ output_low(PIN_B3); /* E -> 0 */ } void LCD_display(char code) { LCD_write(code, 1); LCD_write(code << 4, 1); delay_us(50); /* > 40 us */ } void LCD_control(char code) { LCD_write(code, 0); LCD_write(code << 4, 0); delay_us(50); /* > 40 us */ } void LCD_clear(void) { LCD_write(0x01, 0); LCD_write(0x01 << 4, 0); delay_ms(2); /* > 1.64 ms */ } void LCD_init(void) { delay_ms(20); /* > 15 ms */ LCD_write(0x30, 0); /* 8 bit mode */ delay_ms(5); /* > 4.1 ms */ LCD_write(0x30, 0); /* 8 bit mode */ delay_ms(5); /* > 100 us */ LCD_write(0x30, 0); /* 8 bit mode */ delay_ms(5); /* > 4.1 ms */ LCD_write(0x20, 0); /* 4 bit mode */ delay_ms(5); /* > 40 us */ LCD_control(0x28); /* function set */ LCD_control(0x08); /* display off */ LCD_control(0x0C); /* display on */ LCD_control(0x06); /* entry mode set */ }
図21に示すように,SC1602Bに対してデータを転送するには,文字コードまたは制御コードのデータをDB4からDB7に対して出力するだけでなく,文字コードの場合は1,制御コードの場合は0をRSに対して出力する必要がある.
図21に示すように,DB4,DB5,DB6,DB7,RSに対してデータを出力した後,40ns以上の間隔をおいてから,Eに対して230ns以上の長さのパルスを出力すると,パルスが1から0に変化するタイミングでSC1602Bに対してデータを転送することができる.
図21.SC1602Bに対するデータの転送:(a) 文字コード,(b) 制御コード
図22に,SC1602Bの初期化の手順をまとめておく.
SC1602Bは,電源を投入した直後は8bitモードで動作するようになっているが,あらためて8bitモード設定の制御コードを3回転送し,確実に8bitモードにする必要がある.
続いて,4bitモードに変更した後,液晶ディスプレイの動作を設定するため,いくつかの制御コードを転送する必要がある.
図22.SC1602Bの初期化の手順
参考文献
青木 直史, ``ブレッドボードではじめるマイコンプログラミング,'' 技術評論社, 2010.
青木 直史, ``H8マイコンによるネットワーク・プログラミング - C言語ではじめる組み込みマイコン入門 - ,'' 技術評論社, 2008.
Last Modified: April 1 12:00 JST 2007 by Naofumi Aoki
E-mail: aoki@nis-ei.eng.hokudai.ac.jp