連載
» 2008年12月17日 00時00分 公開

−ザ・組み込み−ソフトウェアのハードウェア化(5):「これって本来マイコンがするべき仕事なの?」に注目 (3/4)

[鳥海佳孝 設計アナリスト,@IT MONOist]

プログラムのオーバーヘッド部分はどこ?

 では、具体的に「オーバーヘッド部分はどこなのか」をさらに分析していきます。

 まず、C言語で“時間稼ぎ”をするために今回のプログラムで使用されているwait関数のような、いわゆる「forの空ループ」を使用している部分がそれ(つまり、オーバーヘッド)に当たります。

 実際にやっていることは単に時間を進めたいだけなのですが、前述したように一番の問題は「この処理を実行している最中は、割り込み以外のほかのことができない」ということです。また、その時間を稼ぐことに関しても、前ページのアセンブリの解析から、メモリアクセスが発生していますので、そこでも余計な時間が取られることになります。もし、この部分がなくなって、マイコン本来の仕事であるシーケンス制御に専念できれば、いままで追加することをあきらめていたプログラム(例えば、時計を実行しながらモーターを制御するなど)を付け加えて実行させても、十分な性能を実現できるはずです。

 以上のことから、マイコン上での“時間稼ぎ”をやめるために、マイコンで実行するプログラムの変更を行っていきます。

LCD時計プログラムの変更

 具体的にどのようにするかというと、

  1. LCDの制御は専用のハードウェアに任せ
  2. マイコンのプログラムは時計本来の仕事である、時を刻み、その時刻をしかるべきポートに出力するだけ

にします。後はその値を受け取ったハードウェアが、マイコンの動作とは無関係にLCDをコントロールして、受け取った値をLCDに表示するようにします(図1)。

LCDコントロールの部分のハード化 図1 LCDコントロールの部分のハード化

 1つのポートでこれを実現するためには、「時」「分」「秒」の変数を時系列にポートに出力する必要があります。また、「分」「秒」については60進、6ビットで表せるため、上位2ビットでどの値を出力しているのかを識別できるようにします(表1)(図2)。

ポート7、6ビット
01
10
11
表1 出力のためのビットアサイン

「時」「分」「秒」の表現について 図2 「時」「分」「秒」の表現について

 前述の変更を加えたプログラム「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.