「これって本来マイコンがするべき仕事なの?」に注目:−ザ・組み込み−ソフトウェアのハードウェア化(5)(3/4 ページ)
アセンブリレベルの解析でプログラムのオーバーヘッド部分が見えてくる!! ソフトウェアのハードウェア化の考え方とは?
プログラムのオーバーヘッド部分はどこ?
では、具体的に「オーバーヘッド部分はどこなのか」をさらに分析していきます。
まず、C言語で“時間稼ぎ”をするために今回のプログラムで使用されているwait関数のような、いわゆる「forの空ループ」を使用している部分がそれ(つまり、オーバーヘッド)に当たります。
実際にやっていることは単に時間を進めたいだけなのですが、前述したように一番の問題は「この処理を実行している最中は、割り込み以外のほかのことができない」ということです。また、その時間を稼ぐことに関しても、前ページのアセンブリの解析から、メモリアクセスが発生していますので、そこでも余計な時間が取られることになります。もし、この部分がなくなって、マイコン本来の仕事であるシーケンス制御に専念できれば、いままで追加することをあきらめていたプログラム(例えば、時計を実行しながらモーターを制御するなど)を付け加えて実行させても、十分な性能を実現できるはずです。
以上のことから、マイコン上での“時間稼ぎ”をやめるために、マイコンで実行するプログラムの変更を行っていきます。
LCD時計プログラムの変更
具体的にどのようにするかというと、
- LCDの制御は専用のハードウェアに任せ
- マイコンのプログラムは時計本来の仕事である、時を刻み、その時刻をしかるべきポートに出力するだけ
にします。後はその値を受け取ったハードウェアが、マイコンの動作とは無関係にLCDをコントロールして、受け取った値をLCDに表示するようにします(図1)。
1つのポートでこれを実現するためには、「時」「分」「秒」の変数を時系列にポートに出力する必要があります。また、「分」「秒」については60進、6ビットで表せるため、上位2ビットでどの値を出力しているのかを識別できるようにします(表1)(図2)。
ポート7、6ビット | |
---|---|
時 | 01 |
分 | 10 |
秒 | 11 |
表1 出力のためのビットアサイン |
前述の変更を加えたプログラム「lcd_clock_new.c」をリスト2に示します。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #include "h8_sample.h" 5 //#include "h8_lcd.h" 6 7 //static unsigned char buffer[16] = ""; 8 static int counter = 0; 9 10 static unsigned char hour = 0; 11 static unsigned char minute = 0; 12 static unsigned char sec = 0; 13 14 // 15 // 8ビットタイマー初期化(100ms) 16 // 17 void Init_Timer8(void) 18 { 19 T8TCR0.BIT.CKS = 0; // クロック一時停止 20 T8TCR0.BIT.CMIEB = 0; // CMFBによる割り込み禁止 21 T8TCR0.BIT.CMIEA = 0; // CMFAによる割り込み禁止 22 T8TCR0.BIT.OVIE = 0; // OVFによる割り込み禁止 23 T8TCR0.BIT.CCLR = 1; // コンペアマッチAでクリア 24 25 T8TCR1.BIT.CKS = 4; // 8TCNT0コンペアマッチAでカウント 26 T8TCR1.BIT.CMIEB = 0; // CMFBによる割り込み禁止 27 T8TCR1.BIT.CMIEA = 1; // CMFAによる割り込み許可 28 T8TCR1.BIT.OVIE = 0; // OVFによる割り込み禁止 29 T8TCR1.BIT.CCLR = 1; // コンペアマッチAでクリア 30 31 T8TCSR0.BYTE = 0; // クリア 32 T8TCSR1.BYTE = 0; // クリア 33 34 T8TCNT = 0; // カウンタクリア 35 T8TCORA0 = 249; // 20,000,000 / 64 / 250 = 1250 36 T8TCORA1 = 124; // 1250 / 125 = 10Hz (100ms) 37 38 T8TCR0.BIT.CKS = 2; // 内部クロック20MHz φ/64 39 } 40 41 42 // 43 // 8ビットタイマーチャンネル0 44 // コンペアマッチA1/B1割り込み 45 // 46 #pragma interrupt 47 void int_cmiab1(void) 48 { 49 T8TCSR1.BIT.CMFA = 0; // コンペアマッチフラグAクリア 50 counter++; 51 if( counter > 9 ) { 52 counter = 0; 53 if( ++sec > 59 ) { 54 sec = 0; 55 if( ++minute > 59 ) { 56 minute = 0; 57 if( ++hour > 23 ) { 58 hour = 0; 59 } 60 } 61 } 62 } 63 } 64 65 66 int main() 67 { 68 ABWCR.BYTE = 0xFF; // バス8ビットモードに設定 69 wait20us(); 70 71 P4DDR.BYTE = 0xFF; // PORT4 = OUTPUT 72 wait20us(); 73 74 P5DDR.BYTE = 0xF0; // PORT5下位4ビット入力 75 wait20us(); 76 P5PCR.BYTE = 0xFF; // プルアップ 77 78 // LCD_Init(); 79 80 hour = 0; 81 minute = 0; 82 sec = 0; 83 counter = 0; 84 Init_Timer8(); 85 86 _sti(); // 割り込み許可 87 88 while(1) { 89 90 int i; 91 for( i = 0 ; i < 5 ; i++ ) { 92 // sprintf(buffer, "%02d:%02d:%02d" ,hour, minute, sec); 93 // LCD_Locate(0,0); 94 // LCD_Putstr(buffer); 95 wait(100); 96 } 97 98 P4DR.BYTE = 0x40 + hour ; 99 P4DR.BYTE = 0x80 + minute ; 100 P4DR.BYTE = 0xC0 + sec ; 101 102 if( ! P5DR.BIT.B0 ) { 103 if( ++hour > 23 ) hour = 0; 104 } 105 if( ! P5DR.BIT.B1 ) { 106 if( ++minute > 59 ) minute = 0; 107 } 108 if( ! P5DR.BIT.B2 ) sec = 0; 109 } 110 return 0; 111 } 112
リスト2 lcd_clock_new.c(→ダウンロードはこちら) |
いかがでしょうか。ことごとくLCDをコントロールするのに必要な関数がコメントアウトされていますね。また、LCDなどにつながっていたポート4の上位2ビットは、「時」「分」「秒」の値を区別するための、“2ビットの識別コマンド”として出力しています(リスト2の98〜100行目)。
それでは、修正したプログラム「lcd_clock_new.c」をコンパイルしてください(ファイル名「lcd_clock_new.c」としていますので、「Makefile」のファイル名に相当する部分を書き直します)。
# make
生成された「*.mot」ファイルを比べてみると、以下のようにファイルサイズが大きく異なっていることが確認できます。
# ls -l *.mot -rwxr-xr-x 1 root root 122046 12月 2日 13:29 lcd_clock.mot* -rwxr-xr-x 1 root root 2418 12月 2日 13:21 lcd_clock_new.mot* ↑ ココ
ではさらに、逆アセンブルしてみます。
# h8300-linux-elf-objdump -D lcd_clock_new.elf > lcd_clock_new.objdump
この逆アセンブルした結果を比較してみても、以下のようにファイルサイズが大きく異なっていることが確認できます。
# ls -l *.objdump -rw-r--r-- 1 root root 5265219 12月 2日 13:33 lcd_clock.objdump -rw-r--r-- 1 root root 17562 12月 2日 13:22 lcd_clock_new.objdump ↑ ココ
Copyright © ITmedia, Inc. All Rights Reserved.