TKM-Hに搭載しているドットマトリクスディスプレイ(以下DMD)を制御するとき,ダイナミック点灯を行っています.ダイナミック点灯とは,人間の目の残像現象を利用した表示方法です.今回使用するDMDには8×8=64個のLEDがあります.もし,通常の点灯方法ですと,64個の端子が制御するマイコンに必要となってしまいます.できれば端子の数を減らして制御したいときに用いられるのがダイナミック点灯です.

「ダイナミック」とは,日本語に直せば「動的」となります.その反対は「静的」です.時間に関係ないことを「静的」と呼び,時間により変化することを「動的」と呼びます.つまり,静的の場合にはLEDの点灯させたいときにマイコンから出力する信号を時間に関係なく変化させない方式です.動的の場合にはLEDの点灯させたいときにマイコンから出力する信号を時間の変化に応じて変化させる方式です.

下図はDMDの内部構造を表したものです.64個のLEDがマトリクス(行列)状に配置されています.上部および左側にある数字はピン番号であり,これらはマイコンで制御できるとします.さて,例えば13番ピンをLow,それ以外のCOL PIN(列ピン)である3,4,10,6,11,15,16をHigh(もしくはハイインピーダンス)にしたとします.この時,ROW PIN(行ピン)である9,14,8,12,1,7,2,5をHighにしたとするとどうなるでしょうか.この場合,13番ピンと接続されているLED1列がすべて点灯し,それ以外は消灯するはずです.今度は,3をLow,それ以外のCOL PINをHigh(もしくはハイインピーダンス)にしたとすると,今度は3番ピンと接続されているLED1列が点灯するはずです.このように,ある一定時間ごとにCOL PINを変化させることで,1列ずつ表示させることができます.この「一定時間」を10[ms]以下に抑えると,人間の目にはすべてのLEDが点灯しているように見えるのです.これが残像現象です.そして,この方式がダイナミック点灯なのです.

上記の例では全点灯をさせましたが,パターンを縦に切り分け,1列ごとに点灯させたいLEDをROW PINで制御すれば,表示させたいパターンを表示できるわけです.

DMD01

 

今回のDMDを制御するには,16個の端子があれば制御できることがお分かりいただけたと思います.しかし,PIC16F1936には他の電子デバイスを接続する関係で16本のも端子をDMDに割り当てることができませんでした.そこで,列ピンをシフトレジスタで制御することとしました.先ほど説明したように,列ピン8本のうち,Lowとなるのは1本でそれ以外はHighにすればよいです.そこで,シリアルインパラレルアウトのシフトレジスタを使いました.これにより,列ピン8本が2本(入力とクロック)で制御することができます.

下図にはマイコン,シフトレジスタ,DMDおよびトランジスタアレイの回路図を示します.シフトレジスタとDMDの間にトランジスタアレイがあるのは,DMDから流れてくる電流よりもシフトレジスタが流し込める電流が大きいためです.注意点としては,トランジスタアレイにより論理が反転することです.つまり,トランジスタアレイからHighを出力すると,トランジスタアレイはDMDから電気を引き込みその列のLEDは点灯し,トランジスタアレイからLowを出力すると,トランジスタアレイはDMDからの電気を引き込まず,LEDは消灯します.

DMD02

以上のことを踏まえて,プログラムを作成していきます.

    1. 各種初期化
      まずはプロジェクトを作成してください.例ではDotMatrixDisplayというプロジェクトを作成することとします.また,動作周波数は4[MHz]とします.プロジェクトの作成方法についてはこちらをご覧ください.
      新たに作成されたプロジェクトには次に示すC言語のソースファイルとヘッダファイルを追加してください.
      ・Main.c [メインのソースファイル]
      ・DotMatrixDisplay.c , DotMatrixDisplay.h [ドットマトリクスに関するソースおよびヘッダ]
      ・IntervalTimer.c , IntervalTimer.h [一定間隔割込みタイマのソースおよびヘッダ]
      ・Interrupt.c , Interrupt.h [割込み関数のソースおよびヘッダ]

      プロジェクトを作成し,上記ファイルを追加したのちの様子を下図に示します.
      DMD03


      Main.cには,コンフィギュレーションビット,動作周波数4[MHz]およびLCD制御モジュールの停止を行っておきます.Main.cを下のようにしてください.
      #include <xc.h>
      
      // CONFIG1
      #pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
      #pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
      #pragma config PWRTE = ON      // Power-up Timer Enable (PWRT disabled)
      #pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
      #pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
      #pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled) // !!!!!!!!PIC1716のときにはコメントアウト
      #pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
      #pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
      #pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
      #pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
      
      // CONFIG2
      #pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
      #pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable (All VCAP pin functionality is disabled) // !!!!!!!!PIC1716のときにはコメントアウト
      #pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
      #pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
      #pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
      #pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
      
      void main() {
          /* 内部オシレータの速度を4[MHz]とする */
          IRCF0 = 1;
          IRCF1 = 0;
          IRCF2 = 1;
          IRCF3 = 1;
      
          /* LCD制御モジュールを停止する */
          LCDEN = 0;
      }
      

       


    2. 行ピンの制御
      行ピンは,下図のようにマイコンと抵抗を介して接続されています.これらの端子を汎用ピンとした上で出力端子にする設定を行います.
       行番号 DMDの端子番号  マイコンの端子 
      0  9 RA6 
      14  RC0 
      RC1 
      3 12 RC2
      4 1 RC5
      5 7 RA7
      6 2 RB1
      7 5 RB2

      上記に示した端子の初期化をDotMatrixDisplay.cに「DotMatrixDisplay_initialize」関数をつくり,その中で行うものとします.この関数をDotMatrixDisplay.cに追加すると共に,DotMatrixDisplay.hに宣言し,他のソースから使えるようにします.まず,DotMatrixDisplay.cには,次のように関数を追加してください
      #include "DotMatrixDisplay.h"
      void DotMatrixDisplay_initialize(void) {
      }


      また,DotMatrixDisplay.hに次のプログラムを追記してください.

      #ifndef DOTMATRIXDISPLAY_H
      #define	DOTMATRIXDISPLAY_H
      
      extern void DotMatrixDisplay_initialize(void);
      
      #endif	/* DOTMATRIXDISPLAY_H */
      

       

      たとえば,RA6を出力ピンにするには,下のプログラムとなります.
      void DotMatrixDisplay_initialize(void) {
          /* ポートAビット6を出力端子とする */
          TRISA6 = 0;
      }

      上記と同様に,他のピンも出力端子にしましょう.

      次に,RB1とRB2についてはAD変換を行うピンとしても使用できてしまうので,これを防ぎ,デジタルピンとする必要があります.デジタルピンにするには次のようにします.
      /* ポートBビット1をデジタルピンとして使用する */
      ANSB1 = 0;
      

      上記と同様に,ポートBビット2についてもデジタルピンにしてください.

      以上をすべて行うと,DotMatrixDisplay_initialize関数は下のようになります.

      void DotMatrixDisplay_initialize(void) {
          /* ポートAビット6を出力端子とする */
          TRISA6 = 0;
          /* ポートCビット0を出力端子とする */
          TRISC0 = 0;
          /* ポートCビット1を出力端子とする */
          TRISC1 = 0;
          /* ポートCビット2を出力端子とする */
          TRISC2 = 0;
          /* ポートCビット5を出力端子とする */
          TRISC5 = 0;
          /* ポートAビット7を出力端子とする */
          TRISA7 = 0;
          /* ポートBビット1をデジタルピンとして使用する */
          ANSB1 = 0;
          /* ポートBビット1を出力端子とする */
          TRISB1 = 0;
          /* ポートBビット2をデジタルピンとして使用する */
          ANSB2 = 0;
          /* ポートBビット2を出力端子とする */
          TRISB2 = 0;
          
      }


      以上で行ピンの初期設定を終了しました.次に,行ピンを使って1列分の表示パターンを行う関数を作りましょう.ややこしい話ですが,もう一度下図をご覧ください.ROW PINとなっている9などのピンを「行ピン」と呼んでいますが,これらのピンの状態により,1列分の表示パターンが制御できることがお分かりいただけるでしょうか.このため,これから作成するのは「1列分の表示パターンを表示する関数」なのです.

      DMD01

      さて,DotMatrixDisplay.cに「_setColumnData」という名前の関数を作りましょう.この関数は,DotMatrixDisplay.c内でしか使わないものとしますので,staticにします.もちろん,_setColumnDataを呼び出す,DotMatrixDisplay.cの外部から呼び出すことが可能な関数を後で作成します.また,引数はuint8_t型のcolumn_dataとします.以上を踏まえると下のようになります.

      static void _setColumnData(uint8_t column_data){
      }

       column_dataの上位ビットが上側(つまり行ピン9),下位ビットが下側(つまり行ピン5)を表しています.それぞれのビットが1である場合には点灯させ,0である場合には消灯させるようにします.以上のようにするには,下のようなプログラムとなるでしょう.これをDotMatrixDisplay.cに追記してください

      static void _setColumnData(uint8_t column_data){
           /* ディスプレイの上方 */
           LATA6 = ((column_data&0x80) ? 1:0);
           LATC0 = ((column_data&0x40) ? 1:0);
           LATC1 = ((column_data&0x20) ? 1:0);
           LATC2 = ((column_data&0x10) ? 1:0);
           LATC5 = ((column_data&0x08) ? 1:0);
           LATA7 = ((column_data&0x04) ? 1:0);
           LATB1 = ((column_data&0x02) ? 1:0);
           LATB2 = ((column_data&0x01) ? 1:0);
           /* ディスプレイの下方 */
      }

       

    3. シフトレジスタの制御
      シフトレジスタを制御するのは,RA5とRA4です.したがってまずは,これらを出力端子にする必要があります.DotMatrixDisplay_initialize関数にそのための設定をしましょう.

      課題
      RA5とRA4を出力端子にするプログラムをDotMatrixDisplay_initialize関数に追加してください.

      次に,シリアルイン・パラレルアウトシフトレジスタの出力8個のうち,1個だけHigh,残り7個がLowとなるような関数を作成しましょう.関数名を「_controlShiftRegister」とし,static関数にしてください.引数はuint8_t型のcolumnとします.
      static void _controlShiftRegister(uint8_t column) {
      }

      columnはその名のとおり列番号を表しており,0から7までの値を取るものとします.0のとき,シリアルインを1とし,それ以外のときを0とすることで,出力からに1個だけHigh,その他7個はLowを出力することができます.また,シフトするタイミングは立ち上がりエッジとなっています.下図のように,シリアルインさせるデータはRA4,シフトするタイミングとなるクロックはRA5と接続されています.

      DMD04

      以上の前提を踏まえてプログラムを書くと下のようになります.この関数を呼び出すことで,表示する列を変えることができるのです.DotMatrixDisplay.cに下のプログラムを追記してください.
      static void _controlShiftRegister(uint8_t column) {
          /* 列番号が0の場合 */
          if (column == 0) {
              /* シフトレジスタに入れられる信号を1とする */
              LATA4 = 1;
          } else {
              /* シフトレジスタに入れられる信号を0とする */
              LATA4 = 0;
          }
      
          /* シフトさせる */
          LATA5 = 1;
          LATA5 = 0;
      }
      

       
      後ほど使うため,1列を消灯するための関数_clearColumnをDotMatrixDisplay.cに追加します.

      static void _clearColumn(void) {
          LATA6 = 0;
          LATC0 = 0;
          LATC1 = 0;
          LATC2 = 0;
          LATC5 = 0;
          LATA7 = 0;
          LATB1 = 0;
          LATB2 = 0;
      }
      

       

    4. パターンの定義
      8×8のドットマトリクスディスプレイにはさまざまなパターンを表示させることができます.ここでは,0から99までの数字,交差点の形状,いくつかの記号および矢印を表示できるようにするようにするための定数を定義します.下に表示できるパターンを示します.
      DMD05
      pdfはこちら

      上記パターンをC言語で定数を書くとすると,列ごとに1バイトの値とし,1パターンを8バイトで表すことにします.1列の表し方は,パターンの上側が上位ビット,パターンの下側が下位ビットとし,左側が最初,右側が最後とします.たとえば「10」を表示する場合には,下記のようになります.
      {0x41, 0xFF, 0x01, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 10


      この一連の配列をPattern型として次のように定義します.この定義をDotMatrixDisplay.hにコピーしてください.

      typedef struct{
          uint8_t _column[8];
      }Pattern;

       

      すべてのパターンを設定すると,下記のようになります.この_PATTERN定数をDotMatrixDisplay.cにコピーしてください.

      static const Pattern _PATTERN[]={
          {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 全消灯
          {0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF}, // ┓形
          {0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0}, // ┏形
          {0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFF}, // ┛形
          {0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}, // ┗形
          {0xFF, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, // ┣形
          {0xC0, 0xC0, 0xC0, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0}, // ┳形
          {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF}, // ┫形
          {0x03, 0x03, 0x03, 0xFF, 0xFF, 0x03, 0x03, 0x03}, // ┻形
          {0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18}, // ╋形
          {0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF}, // 四角形
          {0x18, 0x3C, 0x66, 0xC3, 0xC3, 0x66, 0x3C, 0x18}, // ひし形
          {0x42, 0xE7, 0x7E, 0x3C, 0x3C, 0x7E, 0xE7, 0x42}, // ×
          {0xE0, 0xFC, 0x1E, 0x07, 0x07, 0x1E, 0xFC, 0xE0}, // V字
          {0x01, 0x0F, 0x79, 0x81, 0x81, 0x79, 0x0F, 0x01}, // 上向き三角
          {0xFF, 0x42, 0x42, 0x42, 0x24, 0x24, 0x24, 0x18}, // 右向き三角
          {0x80, 0xF0, 0x8E, 0x81, 0x81, 0x8E, 0xF0, 0x80}, // 下向き三角
          {0x18, 0x24, 0x24, 0x24, 0x42, 0x42, 0x42, 0xFF}, // 左向き三角
          {0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00}, // ・
          {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // 全点灯
          {0x18, 0x30, 0x60, 0xFF, 0xFF, 0x60, 0x30, 0x18}, // 上向き矢印
          {0x02, 0x87, 0x8E, 0x9C, 0xB8, 0xF0, 0xE0, 0xFE}, // 右上向き矢印
          {0x18, 0x18, 0x18, 0x99, 0xDB, 0x7E, 0x3C, 0x18}, // 右向き矢印
          {0x40, 0xE1, 0x71, 0x39, 0x1D, 0x0F, 0x07, 0x7F}, // 右下向き矢印
          {0x18, 0x0C, 0x06, 0xFF, 0xFF, 0x06, 0x0C, 0x18}, // 下向き矢印
          {0x7F, 0x07, 0x0F, 0x1D, 0x39, 0x71, 0xE1, 0x40}, // 左下向き矢印
          {0x18, 0x3C, 0x7E, 0xDB, 0x99, 0x18, 0x18, 0x18}, // 左向き矢印
          {0xFE, 0xE0, 0xF0, 0xB8, 0x9C, 0x8E, 0x87, 0x02}, // 左上向き矢印
          {0x00, 0x7E, 0xE7, 0x81, 0x81, 0xE7, 0x7E, 0x00}, // 0
          {0x00, 0x21, 0x61, 0xFF, 0xFF, 0x01, 0x01, 0x00}, // 1
          {0x00, 0x61, 0xC3, 0x87, 0x8D, 0xD9, 0x71, 0x00}, // 2
          {0x00, 0x46, 0xD3, 0x91, 0x91, 0xDA, 0x6E, 0x00}, // 3
          {0x0C, 0x3C, 0x64, 0xC4, 0xFF, 0xFF, 0x04, 0x00}, // 4
          {0x00, 0xFA, 0xFB, 0x91, 0x91, 0x9B, 0x8E, 0x00}, // 5
          {0x00, 0x7E, 0xFF, 0x91, 0x91, 0xDB, 0x4E, 0x00}, // 6
          {0x00, 0xF0, 0xF0, 0x80, 0x80, 0xFF, 0xFF, 0x00}, // 7
          {0x00, 0x6E, 0xBB, 0x91, 0x91, 0xBB, 0x6E, 0x00}, // 8
          {0x00, 0x70, 0xD8, 0x88, 0xD8, 0xFF, 0x7F, 0x00}, // 9
          {0x41, 0xFF, 0x01, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 10
          {0x41, 0xFF, 0x01, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 11
          {0x41, 0xFF, 0x01, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 12
          {0x41, 0xFF, 0x01, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 13
          {0x41, 0xFF, 0x01, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 14
          {0x41, 0xFF, 0x01, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 15
          {0x41, 0xFF, 0x01, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 16
          {0x41, 0xFF, 0x01, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 17
          {0x41, 0xFF, 0x01, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 18
          {0x41, 0xFF, 0x01, 0x00, 0x62, 0x91, 0x7F, 0x00}, // 19
          {0x61, 0x83, 0x7D, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 20
          {0x61, 0x83, 0x7D, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 21
          {0x61, 0x83, 0x7D, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 22
          {0x61, 0x83, 0x7D, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 23
          {0x61, 0x83, 0x7D, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 24
          {0x61, 0x83, 0x7D, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 25
          {0x61, 0x83, 0x7D, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 26
          {0x61, 0x83, 0x7D, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 27
          {0x61, 0x83, 0x7D, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 28
          {0x61, 0x83, 0x7D, 0x00, 0x62, 0x91, 0x7F, 0x00}, // 29
          {0x52, 0x91, 0x6E, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 30
          {0x52, 0x91, 0x6E, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 31
          {0x52, 0x91, 0x6E, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 32
          {0x52, 0x91, 0x6E, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 33
          {0x52, 0x91, 0x6E, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 34
          {0x52, 0x91, 0x6E, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 35
          {0x52, 0x91, 0x6E, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 36
          {0x52, 0x91, 0x6E, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 37
          {0x52, 0x91, 0x6E, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 38
          {0x52, 0x91, 0x6E, 0x00, 0x62, 0x91, 0x7F, 0x00}, // 39
          {0x3C, 0x44, 0xFF, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 40
          {0x3C, 0x44, 0xFF, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 41
          {0x3C, 0x44, 0xFF, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 42
          {0x3C, 0x44, 0xFF, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 43
          {0x3C, 0x44, 0xFF, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 44
          {0x3C, 0x44, 0xFF, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 45
          {0x3C, 0x44, 0xFF, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 46
          {0x3C, 0x44, 0xFF, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 47
          {0x3C, 0x44, 0xFF, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 48
          {0x3C, 0x44, 0xFF, 0x00, 0x62, 0x91, 0x7F, 0x00}, // 49
          {0xF2, 0x91, 0x9E, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 50
          {0xF2, 0x91, 0x9E, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 51
          {0xF2, 0x91, 0x9E, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 52
          {0xF2, 0x91, 0x9E, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 53
          {0xF2, 0x91, 0x9E, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 54
          {0xF2, 0x91, 0x9E, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 55
          {0xF2, 0x91, 0x9E, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 56
          {0xF2, 0x91, 0x9E, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 57
          {0xF2, 0x91, 0x9E, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 58
          {0xF2, 0x91, 0x9E, 0x00, 0x62, 0x91, 0x7F, 0x00}, // 59
          {0x7E, 0x91, 0x4E, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 60
          {0x7E, 0x91, 0x4E, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 61
          {0x7E, 0x91, 0x4E, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 62
          {0x7E, 0x91, 0x4E, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 63
          {0x7E, 0x91, 0x4E, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 64
          {0x7E, 0x91, 0x4E, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 65
          {0x7E, 0x91, 0x4E, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 66
          {0x7E, 0x91, 0x4E, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 67
          {0x7E, 0x91, 0x4E, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 68
          {0x7E, 0x91, 0x4E, 0x00, 0x62, 0x91, 0x7F, 0x00}, // 69
          {0xE0, 0x87, 0xF8, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 70
          {0xE0, 0x87, 0xF8, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 71
          {0xE0, 0x87, 0xF8, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 72
          {0xE0, 0x87, 0xF8, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 73
          {0xE0, 0x87, 0xF8, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 74
          {0xE0, 0x87, 0xF8, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 75
          {0xE0, 0x87, 0xF8, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 76
          {0xE0, 0x87, 0xF8, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 77
          {0xE0, 0x87, 0xF8, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 78
          {0xE0, 0x87, 0xF8, 0x00, 0x62, 0x91, 0x7F, 0x00}, // 79
          {0x6E, 0x91, 0x6E, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 80
          {0x6E, 0x91, 0x6E, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 81
          {0x6E, 0x91, 0x6E, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 82
          {0x6E, 0x91, 0x6E, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 83
          {0x6E, 0x91, 0x6E, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 84
          {0x6E, 0x91, 0x6E, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 85
          {0x6E, 0x91, 0x6E, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 86
          {0x6E, 0x91, 0x6E, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 87
          {0x6E, 0x91, 0x6E, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 88
          {0x6E, 0x91, 0x6E, 0x00, 0x62, 0x91, 0x7F, 0x00}, // 89
          {0x62, 0x91, 0x7F, 0x00, 0x7E, 0xC3, 0x7E, 0x00}, // 90
          {0x62, 0x91, 0x7F, 0x00, 0x41, 0xFF, 0x01, 0x00}, // 91
          {0x62, 0x91, 0x7F, 0x00, 0x61, 0x83, 0x7D, 0x00}, // 92
          {0x62, 0x91, 0x7F, 0x00, 0x52, 0x91, 0x6E, 0x00}, // 93
          {0x62, 0x91, 0x7F, 0x00, 0x3C, 0x44, 0xFF, 0x00}, // 94
          {0x62, 0x91, 0x7F, 0x00, 0xF2, 0x91, 0x9E, 0x00}, // 95
          {0x62, 0x91, 0x7F, 0x00, 0x7E, 0x91, 0x4E, 0x00}, // 96
          {0x62, 0x91, 0x7F, 0x00, 0xE0, 0x87, 0xF8, 0x00}, // 97
          {0x62, 0x91, 0x7F, 0x00, 0x6E, 0x91, 0x6E, 0x00}, // 98
          {0x62, 0x91, 0x7F, 0x00, 0x62, 0x91, 0x7F, 0x00}  // 99
      };
      

       「10」を表示させるには,は上位4ビットが2,下位4ビットが6であるので,_PATTERNの添え字を0x26にすれば「10」パターンのデータの先頭アドレスにアクセスできます.


      さて,このままですと表示したいパターンのデータの先頭アドレスにアクセスするのに不便ですので,下のように添え字を定義しておきます.下記定義をDotMatrixDisplay.hへコピーしてください.

      typedef enum{
      	SPACE				=0x00, // 全消灯
      	UPPER_RIGHT_CORNER		=0x01, // ┓形 
      	UPPER_LEFT_CORNER		=0x02, // ┏形
      	LOWER_RIGHT_CORNER		=0x03, // ┛形
      	LOWER_LEFT_CORNER		=0x04, // ┗形
      	COUNTER_CLOCKWISE_TCROSS	=0x05, // ┣形
      	TCROSS				=0x06, // ┳形
      	CLOCKWISE_TCROSS		=0x07, // ┫形
      	INVERT_TCROSS			=0x08, // ┻形
      	CROSS				=0x09, // ╋形
      	RECTANGLE			=0x0A, // 四角形
      	ARGYLE				=0x0B, // ひし形
      	X_SHAPED			=0x0C, // ×
      	V_SHAPED			=0x0D, // V字
      	UPPER_TRIANGLE			=0x0E, // 上向き三角
      	RIGHT_TRIANGLE			=0x0F, // 右向き三角
      	LOWER_TRIANGLE			=0x10, // 下向き三角
      	LEFT_TRIANGLE			=0x11, // 左向き三角
      	DOT				=0x12, // ・
      	ALL_LIGHTING			=0x13, // 全点灯
      	UPPER_ARROW			=0x14, // 上向き矢印
      	UPPER_RIGHT_ARROW		=0x15, // 右上向き矢印
      	RIGHT_ARROW			=0x16, // 右向き矢印
      	LOWER_RIGHT_ARROW		=0x17, // 右下向き矢印
      	LOWER_ARROW			=0x18, // 下向き矢印
      	LOWER_LEFT_ARROW		=0x19, // 左下向き矢印
      	LEFT_ARROW			=0x1A, // 左向き矢印
      	UPPER_LEFT_ARROW		=0x1B, // 左上向き矢印
      } ShapeType;
      

      このようにすることで,DotMatrixDisplay.c以外のソースファイルからでも使用できるようになります.
      次に,数字パターンを表示するため,次の定数をDotMatrixDisplay.cに追記してください.

      #define _NUMBER_PATTERN_OFFSET	 (0x1C) // 数字を表すパターンのオフセット

      たとえば,10を表示するのであれば,10+NUMBER_PATTERN_OFFSETで0x26を得ることができます.


       

    5.  パターンを表示する関数

      以上でパターンを表示する準備ができました.数字以外のパターンを表示するDotMatrixDisplay_setPattern関数と,数字を表示するDotMatrixDisplay_setNumberPattern関数を作成しましょう.
      DotMatrixDisplay_setPattern関数では,引数としてPatternType型で表示したいパターンを設定します.この関数をDotMatrixDisplay.cに追記してください.

      void DotMatrixDisplay_setPattern(ShapeType type) {
      }


      どのファイルからもこの関数を呼び出せるようにするため,DotMatrixDisplay.hに宣言をしておいてください.

      extern void DotMatrixDisplay_setPattern(ShapeType type);

       

      type仮引数から,表示したいパターンのデータの先頭アドレスを得てdisplay_patternとします.それには次のようにします.下のプログラムをDotMatrixDisplay_setPattern関数に書いてください

      const Pattern *display_pattern = &_PATTERN[type];


      あとは,列ごとに表示していきます.下のように1列ごとに表示をしていきます.まず,シフトレジスタを制御し,1列のみ点灯するようにします.次に,1列分のデータをセットし表示します.この後,しばらく1列表示しておくことで明るく視認できるようになります.そのため,_nop関数を2個使用しています.この関数はPICのマシン語の命令を1回休むものですので,つまりウェイトとなります.最後に,次の列を表示することに備えるため,1列を消灯させます.以上の処理を繰り返すことで,パターンが表示されます.下のプログラムをDotMatrixDisplay_setPattern関数に書いてください

      uint8_t column;
      for(column=0; column<8; column++) {
          /* 列の表示/非表示を外部のシフトレジスタで行っているので */
          /* そのシフトレジスタを操作する */
          _controlShiftRegister(column);
          _setColumnData(display_pattern->_column[column]);
          /* 点灯している時間だけウェイトを入れる */
          /* つまり,このウェイトが長ければ明るくなる */
          _nop();
          _nop();
          /* 表示を消す */
          /* このようにしないと,前の列の残像が残ってしまう */
          _clearColumn();
      }
      

       
      次にDotMatrixDisplay_setNumberPattern関数を作成しましょう.この関数では,表示したい数字(value)を引数とします.DotMatrixDisplay.cに下記プログラムを追記してください.

      void DotMatrixDisplay_setNumberPattern(uint8_t value){
      }

       

      どこからでもこの関数を呼び出せるようにするため,DotMatrixDisplay.hに宣言をしておいてください.

      extern void DotMatrixDisplay_setNumberPattern(uint8_t value);

       
      DotMatrixDisplay_setNumberPattern関数で行うことは,ほとんどDotMatrixDisplay_setPattern関数と同じです.違うのはdisplay_pattern変数を得るとき,_PATTERN配列の添え字をvalue+NUMBER_PATTERN_OFFSETにするだけです.下のプログラムをDotMatrixDisplay_setNumberPattern関数に追記してください.

      const Pattern *display_pattern = &_PATTERN[value+_NUMBER_PATTERN_OFFSET];
      uint8_t column;
      for(column=0; column<8; column++) {
          /* 列の表示/非表示を外部のシフトレジスタで行っているので */
          /* そのシフトレジスタを操作する */
          _controlShiftRegister(column);
          _setColumnData(display_pattern->_column[column]);
          /* 点灯している時間だけウェイトを入れる */
          /* つまり,このウェイトが長ければ明るくなる */
          _nop();
          _nop();
          /* 表示を消す */
          /* このようにしないと,前の列の残像が残ってしまう */
          _clearColumn();
      }

       

      では,Main関数内からDotMatrixDisplay_setPattern関数を呼んでパターンを表示してみましょう.下記のようにDotmatrixDisplay.hをインクルードするとともに,main関数内からDotMatrixDisplay_setPattern関数を呼び出してください.正しく動けばドットマトリクスディスプレイに十字が表示されます.

      #include "DotMatrixDisplay.h"
      
      void main(){
      
          /* 十字 */
          ShapeType type = CROSS;
      
          /* ドットマトリクスディスプレイを初期化する */
          DotMatrixDisplay_initialize();
      
          while(1){
              /* パターンを表示する */
              DotMatrixDisplay_setPattern(type);
          }
      }
      

       

      課題
      同様に,Main関数内からDotMatrixDisplay_setNumberPattern関数を呼び出し,「30」を表示してください.