Arduino用ヘッダシールドの製作(4)

このページをスマホなどでご覧になる場合は、画面を横長にする方が読みやすくなります。
目次へ  前のページへ (1) (2) (3) (4) 次のページへ
2015年07月16日 公開。

5-4.ロジックアナライザを使ったLCDの制御信号の観察

それでは、いよいよロジックアナライザを使い、LCDの制御信号を観察します。なお、この記事の趣旨は、今回使ったLAP-C(16064)というロジックアナライザの使い方の説明ではないので、ロジックアナライザの操作法については、あまり詳細に説明しません。

5-4-1.初期化信号を観察する

LCDの動作テストのスケッチでは、まず最初にLCD(AQM1248A)に初期化信号を送ります。そこで、まず、その初期化信号をロジックアナライザで観察してみます。

下の図は、/CS信号(Arduinoの10番ピン)が初めてHからLに変化する部分でトリガをかけた時の、LCD制御信号の波形です。

図6、最初の/CSの立ち下がりでトリガをかけた場合のLCD制御信号
図6、最初の/CSの立ち下がりでトリガをかけた場合のLCD制御信号

ここで、各信号について、改めて説明しておきます。

RS信号(Arduinoの9番ピン)は、/CS、MOSI、SCKの3つの信号で構成される、SPIバスで送信される信号が、コマンドなのか、データ(ディスプレイデータRAMに書き込むビットマップデータ)なのかを区別するための信号線です。RSがHなら、SPIバスからはデータが、RSがLなら、SPIバスからはコマンドが送られています。上の波形の場合、RSはずっとLなので、SPIバスからはコマンドが送信されています。

/CS信号(Arduinoの10番ピン)は、SPIインターフェースにおいて、チップセレクトを表わす信号で、SPIバスを通じて情報をやり取りするには、マスタ側(SPIバスの制御権を握っている側、この場合はArduino側)が通信の続く間、Lに保つ必要があります。

MOSI信号(LCDピッチ変換キットにはSDIと表記、Arduinoの11番ピン)は、マスター側からスレーブ側(SPIバスの制御権を持っていない側、この場合はLCD側)へ送られるシリアル信号です。この後説明するSCK信号にタイミングを合わせて信号を送ります。

SCK信号(LCDピッチ変換キットにはSCLKと表記、Arduinoの13番ピン)は、マスター側からスレーブ側へ送られるクロック信号です。SPIバスでやりとりされる信号は、みなこのSCLKに同期して送信されます。

本来なら、SPIインターフェースには、MISO信号という、スレーブ側からマスター側へ情報を送る信号線があるのですが、AQM1248AというグラフィックLCDモジュールは(というより、SPI接続のグラフィックLCDモジュールはたいてい)、LCD側からマスター側への信号線(MISO信号)を持っていません。LCDを制御するマイコンの側からすれば、何の返答もLCD側から返ってこないので、LCDがちゃんと待機状態になっているのかが分かりませんし、それどころか、SPIバスの先にLCDがつながっているのかどうかすら分かりません。また、LCDに内蔵されているディスプレイデータRAM(VRAM)の内容をマイコンが読み取る手段がありませんから、表示されている内容を知りたければ、マイコンは、自身のRAM上に、LCDに表示させた内容のコピーをおいておく必要があります。

広告

5-4-2.SPI信号について

先ほどの波形を読み解く前に、簡単にSPIバスを使った情報の送信方法について説明します。とはいっても、SPIバス一般の話を書く余裕がありませんので、今回のLCD動作テストに関係の深いところだけを説明します。SPIバス一般の話については、用語集のSPIの項目をご覧ください。

SPIバスでやり取りされるデータの形式(フォーマット)には、いくつかバリエーションがあるのですが、ここでは、AQM1248Aの制御に使う、ビット長8、MSBファースト、モード3という形式の送信方法に絞って説明します。

図7に、マスターがスレーブに、5AH(01011010B)というデータをSPIバスで送る場合の波形を示します。なお、RSはSPIインターフェースに含まれない信号線なので、図7には書いていません。

図7、SPIバスの波形
図7、SPIバスの波形

この図に示す様に、SPIバスでは、/CSがLの間に8ビットの情報を送ってしまいます。(/CSがHならば、データを送っても、スレーブ側で無視します)データは、MOSI信号において、SCK信号と同期し、MSB(最上位ビット)側からLSB(最下位ビット)側に向けて8ビット送信されます。スレーブ側では、SCKの立ち上がりでMOSIを取り込みます。

5-4-3.初期化信号を解読する

先ほどのロジックアナライザの画面では、トリガがかかって8μs位で情報を送っていました事が分かりましたが、この部分を拡大して、一体どういう情報を送っているのかを、解読してみましょう。

情報を送信している部分を、ロジアナで拡大して観察すると、次の様な波形になります。

図8、最初の送信部の拡大波形
図8、最初の送信部の拡大波形

MOSI信号には、SCKのクロックに同期して、順に10101110の2進数が送られています。つまり、2進数で10101110B、16進数表記だと、AEHを送信している事になります。

ここで、前のページに載せたスケッチのInitializeLcdという関数を再掲します。この関数は、SPIインターフェースの初期化が終わった直後に呼ばれる関数です。

void InitializeLcd()
{
  LcdCommand(0xae);
  LcdCommand(0xa0);
  LcdCommand(0xc8);
  LcdCommand(0xa3);

  LcdCommand(0x2c);
  delay(2);
  LcdCommand(0x2e);
  delay(2);
  LcdCommand(0x2f);

  LcdCommand(0x23);
  LcdCommand(0x81);
  LcdCommand(0x1c);

  LcdCommand(0xa4);
  LcdCommand(0x40);
  LcdCommand(0xa6);
  LcdCommand(0xaf);
} // InitializeLCD

LcdCommand関数が多く使われていますが、この関数は、RS信号をLにした状態で、引数の8ビットの数をSPIバスに送信する関数です。

最初に呼ばれるLcdCommand関数には、引数として0xaeが渡されていますので、その部分をロジックアナライザで観察したのが、先ほどの拡大波形という訳です。

なお、今回紹介しているLAP-C(16064)というロジックアナライザには、プロトコルアナライザの機能があります。プロトコルアナライザは、SPI、I2C、UART、USB、パラレルバスなどの、各種インターフェース・バスを流れる情報を解析する装置の事です。

それでは、プロトコルアナライザ機能を用いて、SPI バス上の信号を解析してみましょう。/CSとMOSIとSCKの3つがSPIバスの信号線ですから、それを一つのバスとして登録します。

図9、バス登録後のロジックアナライザの画面
図9、バス登録後のロジックアナライザの画面

上の様に、/CS、MOSI、SCKの信号を、ひとつのバスにまとめ、そのバスにSPIという名前を付けました。バスを登録すると、デフォルトではパラレルバスと解釈されます。SPIの行には、0X4や0X6などの16進数が見えますが、これはバスをパラレルバスだと仮定して、バス上の情報を解読した結果です。

実際にはパラレルバスではなく、SPIバスなので、その事を登録します。Bus Propertyの設定で、ZEROPLUS LA SPI MODULEを選択して、登録したバスをSPIバスとして、信号を解析させます。

図10、SPIバスとして信号を解析させる
図10、SPIバスとして信号を解析させる

また、SPIバスの詳細な設定をするために、Parameters Config...のボタンをクリックして、次の画面の様に各種設定を行います。

図11、SPIバスの各種設定
図11、SPIバスの各種設定

バスのモードは、モード3の場合、CPHA=1,CPOL=1と指定します。詳しくは、「SPI」の解説というページに、SPIバスのモードの話が載っていますので、そちらを参照してください。

以上の様にSPIバスの設定をすると、SPIバス上のデータは、ロジックアナライザ(内蔵のプロトコルアナライザ)が自動的に解析するようになります。

図12、SPIバスの信号の自動解析
図12、SPIバスの信号の自動解析

また、パケット表示のモードを選択すると、SPIバスで送信されたデータを、表の形式で見ることもできるようになります。

図13、パケット表示モード
図13、パケット表示モード

この表示から、最初のデータはAEHで、トリガ後0nsから送信されており、2番目のデータはA0Hで、トリガ後24.31μsから送信されており…といった事が読み取れます。

AEH、A0H、C8H、A3H、2CHというデータの並びは、先ほど掲載したInitializeLcd関数の冒頭の5つのLcdCommand関数の引数に対応しています。分かりやすいように、該当部分だけ再掲します。

  LcdCommand(0xae);
  LcdCommand(0xa0);
  LcdCommand(0xc8);
  LcdCommand(0xa3);

  LcdCommand(0x2c);

ところで、SPIバスの波形を見ていると、/CS信号が立ち下がってからデータを送信し始めるまでと、データを送信し終わってから/CS信号が立ち上がるまでに、かなり時間がかかっていることに気がつきます。

図14、データ送信前後の長い待ち時間
図14、データ送信前後の長い待ち時間

この様に、データ送信前後に長い待ち時間が発生するのは、/CS信号やRS信号の制御にdigitalWrite関数を使っているのが原因です。digitalWrite関数は、Arduinoのピン番号からATmega328Pのポートへの変換や、ピンがPWM出力に割り当てられていないか監視しているなど、オーバーヘッドが多く、呼び出しに時間がかかる関数です。digitalWrite関数を使う代わりに、ATmega328Pのポートを直接操作する様にすれば、動作がかなり高速化します。ここら辺の事情は、なんでも独り言というブログのArduinoの高速化という記事や、gajeというブログのArduino core の実装メモという記事に説明があるので、参考にしてください。

前のページで紹介したスケッチでは、LcdCommand関数内で、digitalWrite関数を3回呼び出していました。

void LcdCommand(uint8_t cmd)
{
  SPI.beginTransaction(SPISettings(RATE,MSBFIRST,SPI_MODE3));
  digitalWrite(CS_PIN,LOW);
  digitalWrite(DI_PIN,LOW);
  SPI.transfer(cmd);
  digitalWrite(CS_PIN,HIGH);
  SPI.endTransaction();
} // LcdCommand

Arduino Uno/Duemilanoveの9番ピン(D9ピン)がATmega328PのPB1(ポートBの第1ビット)に割り当てられており、Arduino Uno/Duemilanoveの10番ピン(D10ピン)がATmega328PのPB2(ポートBの第2ビット)に割り当てられている事を考慮し、dititalWrite関数を使わないようにLcdCommand関数を書き換えると、次の様になります。

void LcdCommand(uint8_t cmd)
{
  SPI.beginTransaction(SPISettings(RATE,MSBFIRST,SPI_MODE3));
  //digitalWrite(CS_PIN,LOW);
  __asm__ volatile("cbi %0,2\n\t" :: "I"(_SFR_IO_ADDR(PORTB))); // clear PB2

  //digitalWrite(DI_PIN,LOW);
  __asm__ volatile("cbi %0,1\n\t" :: "I"(_SFR_IO_ADDR(PORTB))); // clear PB1

  SPI.transfer(cmd);

  //digitalWrite(CS_PIN,HIGH);
  __asm__ volatile("sbi %0,2\n\t" :: "I"(_SFR_IO_ADDR(PORTB))); // set PB2

  SPI.endTransaction();
} // LcdCommand

digitalWrite関数は、ひとつのCBI命令またはSBI命令に置き換えられています。CBI命令とSBI命令は、指定されたポートの指定されたビットを、それぞれ0および1にする機械語命令です。インラインアセンブラの書き方が少しややこしいですが、AVR Libc Home PageというサイトのInline Assembler Cookbookというページ(英文)が参考になります。

dititalWrite関数を使わないようにLcdCommand関数を書き換えると、次に示す様にデータの送信が高速化します。

図15、CBI命令/SBI命令によるデータ送信高速化の様子
図15、CBI命令/SBI命令によるデータ送信高速化の様子

5-5.ロジックアナライザを用いた信号観察のまとめ

以上の説明で、ロジックアナライザを使うと、バス上の信号の解析が、非常に分かりやすく行える事が理解できたと思います。スケッチの改善により信号の送信が高速化する様子なども、手に取るように分かります。

ただし、ロジックアナライザに、観察したい信号を引き出すのが少々面倒です。今回はSPIバスの観察をしたので、4本の信号線で済みましたが、パラレルバスを観察する場合などは、10本以上の信号線を引き出す必要が生じます。この様な時に、ヘッダシールドを作っておき、そこから信号を引き出すと、デバッグの時間を節約する事ができます。

目次へ  前のページへ (1) (2) (3) (4) 次のページへ

このページで使われている用語の解説

関連ページ

Arduino 電子工作
このサイトの記事が本になりました。
書名:Arduino 電子工作
ISBN:978-4-7775-1941-5
工学社の書籍の内容の紹介ページ
本のカバーの写真か書名をクリックすると、Amazonの書籍購入ページに移動します。