マイコン工作実験日記

Microcontroller を用いての工作、実験記録

針の進み方

2013-10-29 13:40:36 | LCD
今回は時計ウィジェットについて。FT800では、CMD_CLOCKを使うことで簡単にアナログ時計を描画することができます。使い方は、
Ft_Gpu_CoCmd_Clock(phost, x, y, r, option, hour, min, sec, msec);

という感じ。中心位置と半径で時計の位置と大きさを指定して、時刻データを引数として渡すだけでアナログ時計のできあがりです。もちろん、FT800は描画するだけですので、針を動かすためにはホスト側から時刻データを更新して、繰り返して呼び出す必要があります。



アナログ時計を模していますので、秒針の進み方もアナログ的。ちゃんと、途中の位置も表示可能。msを0として1秒毎に値を更新してやれば、秒針はチクタクと1秒毎に進んでいきますが、50ms毎に更新してやれば秒針も連続的にスゥーっと動いているかのように見えます。

このように簡単に時計が秒ができますが、例によって形状を変化させることはできません。描いてくれるのは丸形の時計だけであり、文字盤に数字もふってくれません。必要であれば、自分で装飾を追加する必要有り。その代わり、文字盤無しで針だけとか、秒針しか描かないといったことをoptionで指定可能です。したがって、4角を最初に別途描画しておき、その上に針だけを描画すれば、角の四角い時計も作れるということにあります。

足が無いと手も出せない

2013-10-25 12:40:10 | Weblog
8ピンCortex-M0+マイコンのLPC810. 実際に使ってみると思っていた以上にデバック作業がシンドイです。何しろ全てのピンを使ってしまっているので、SWDを使ってのデバックはもちろんUARTへの出力もできません。LCDをつないだままだとフラッシュ書き込みもうまくできないので、書き込みの度にLCDをつなげたりはずしたりと、面倒くさったいったらありゃしません。

しかしどこまでコードが走っているのかもわからない全くの盲目状態では、どこを修正すれば良いのかもわかりません。そこで電源状態表示のために用意したLEDをEveのPDN端子に移動。PDN端子は通常状態ではHレベルなのでLEDは点灯します。これをLレベルに落とすコードを適宜挟むことで、どこまで動作したかを確認しながら、コードを追加するという地道な作業の繰り返し。いやはや、ピン数が少ないとデバックも大変ですね。いったん、LPC812あたりを使ってコード開発を進めて、動作確認できたコードをLPC810に持って来た方が遥かに作業効率が良かっただろうと思われます。まぁ、そんな効率なんか気にしないで苦しみ続けるのが、少ピンマイコンで遊ぶ醍醐味のひとつかもしれません。

さて、そんな苦労の積み重ねを経て、どうにかEveに画面表示できるようになりました。




いったん表示できてしまえば、あとは画面にデバックメッセージを出すことで動作の様子を把握することができるので、圧倒的に作業が楽になります。画面左側の数字はそんなデバック情報となっています。画面の色使いとか醜いですが、そこはこれからなんとかしていくことにします。時計ウィジェット等については、今後の記事で説明していくことにします。


8ピンマイコンでつなぐ

2013-10-21 21:48:55 | Weblog
ちかごろ巷で流行りのLPC810を入手したので、わたしも遅まきながら手を出してみることにしました。

LPC8xxにはLPC-Mini Kitとか、LPC800-MAXボードとかあって、みなさんの記事をみるとNXPのプロモーションで入手している方も多いのでしょうか?今回、入手したのは8ピンのLPC810なので、LPC800 Mini-Kitを真似て秋月A基板に組んでみました。




上側の10ピンヘッダは、ConnectEVEをつなくためのものです。スピーカは、LPC810ではなく、ConnectEVEにつながります。スピーカを除いた8本の線は、(電源を含めて)全てLPC810につながります。つまり、LPC810のピンを全部使ってつなぐことになります。

フラッシュへの書き込みにはFlash Magicを使うつもりだったのですが、MacOSバージョンではLPC800がサポートされていないじゃありませんか。ショック。lpc21ispを見てみたら、ちゃんとLPC810にも対応しているので、こちらの最新版を入手して動作確認。環境は整ったので、ボチボチと作業を始めることにします。

EVE姐さんを回転してみる

2013-10-18 23:12:47 | LCD
前回はJPEG画像を展開、表示してみました。今回は、その画像を縮小表示。そしてさらに回転してみました。




CMD_TRANSLATEを使って縮小した後に、さらにCMD_TRANSLATEによる移動とCMD_ROTATEによる回転を施しています。実際の処理の流れは次のようになっています。



  • まずCMD_SCALEで縦横の大きさを1/3に縮小。表示画素数も1/3になるので、BITMAP_SIZEの値も1/3に変更しておきます。
  • 次にCMD_ROTATEを使って30度回転してみます。ビットマップの左上を中心として回転がおこなわれるので、図の上段中央のように表示されます。BITMAP_SIZEによってクリッピングされていることがわかります。
  • 次にCMD_TRANSLATEを使ってX方向にビットマップ幅の1/2, Y方向にビットマップ高の1/2だけ平行移動して表示すると、下段左側のようにクリッピングにより画像の左上部分だけが表示されます。
  • 続いて、30度回転してから平行移動して元の位置に戻すと、下段右側の表示となります。
  • まだクリップングが働いて画像に見えない部分があるので、BITMAP_SIZEをひとまわり大きくしてやって、平行移動することで回転した画像がちゃんと見えるようになりました。(上段右側)


EVE姐さんを表示してみる

2013-10-13 09:25:31 | LCD
ノロノロとではありますが、FT800の実験を続けています。きょうのお題はJPEGファイルの表示です。

FT800には、JPEGの展開機能が用意されています。CMD_IMAGELOADコマンドに続いてJPEGファイルのデータを送ってやると、これを指定したFT800のGraphics RAM(RAM_G)メモリ領域のアドレス移行に展開してくれます。展開した結果は、RGB565のビットマップやモノクロのビットマップ形式として変換することができます。




試しに、FTDIのサイトから拾ったEVE姐さんの画像を表示してみました。元の画像データは496×351ドットだったので、この縦横比を保って384×272ドットの大きさに変換したJPEGファイルを用意しました。このファイルをボード上のSPIフラッシュ上に書き込んでおき、これをFatFsで読み込んで表示しています。実際のコードはこんな感じです。概要を示すために、細かなエラーチェックは省略しています。
FATFS fatfs;
FIL f_info;

void do_Image(Ft_Gpu_Hal_Context_t *phost)
{
  FRESULT res;
  int nb, eof;

  res = f_mount(0, &fatfs);
  res = f_open(&f_info, "/PReve1.jpg", FA_READ);
 
  /* Load JPEG image into RAM_G space */
  Ft_Gpu_Hal_WrCmd32(phost, CMD_LOADIMAGE);
  Ft_Gpu_Hal_WrCmd32(phost, RAM_G);     /* Destination address of jpg decode */
  Ft_Gpu_Hal_WrCmd32(phost, 0); /* output format */

  eof = 0;
  while (!eof) {
    res = f_read(&f_info, (void*)image_buffer, sizeof(image_buffer), &nb);
    if (res != FR_OK || nb ≤ 0)
      eof = 1;
    else
      Ft_Gpu_Hal_WrCmdBuf(phost, image_buffer, nb);
  }
  f_close(&f_info);

  Ft_Gpu_CoCmd_Dlstart(phost);
  Ft_App_WrCoCmd_Buffer(phost, CLEAR_COLOR_RGB(0xc0,0xc0,0xc0)); /*  background */
  Ft_App_WrCoCmd_Buffer(phost, CLEAR(1,1,1));
  Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(255,255,255));
  Ft_App_WrCoCmd_Buffer(phost, BITMAP_SOURCE(RAM_G));
  /* Source bitmap size is 384x272 */
  Ft_App_WrCoCmd_Buffer(phost, BITMAP_LAYOUT(RGB565,384*2,272));
  Ft_App_WrCoCmd_Buffer(phost, BITMAP_SIZE(BILINEAR,BORDER,BORDER,384,272));
  Ft_App_WrCoCmd_Buffer(phost, BEGIN(BITMAPS));
  Ft_App_WrCoCmd_Buffer(phost, VERTEX2II(0,0,0,0));
  Ft_App_WrCoCmd_Buffer(phost, END());
  Ft_App_WrCoCmd_Buffer(phost, DISPLAY());
  Ft_Gpu_CoCmd_Swap(phost);
  /* Download the commands into fifo */
  Ft_App_Flush_Co_Buffer(phost);
}

RGB565に展開されたビットマップ画像は、BEGIN(BITMAPS)/ENDで囲まれたVERTEX2IIで指定される座標に表示されます。表示するには、ビットマップ画像の大きさやレイアウト情報が必要となるので、それをBITMAP_LAYOUTとBITMAP_SIZEで与えています。BITMAP_SIZEでは縦横384×272ドットの画像であることを示しています。この画像はRGB565形式であるため1画素あたり2バイトが必要です。そのため、メモリ上でのレイアウトとしてはX方向には384×2バイトの領域が必要となります。BITMAP_LAYOUTではこの情報を渡しているわけです。この例では画像のサイズは既知であるので、定数として埋め込んでいますが、実際にはJPEG画像のヘッダー部分をデコードしてサイズを求めた方が一般性のあるコードとなります。

RAM_Gの領域サイズは256kBとなっているので、展開結果のビットマップサイズが、これを超える画像は扱うことができません。RGB565形式では一画素あたり2バイトを必要としますので、480×272ドットの画像がギリギリで表示できることになります。そのため、デジカメで撮影したデータを表示するフォトフレームのようなアプリを直接実現することはできません。今時のデジカメは、最小撮影サイズでも640×480ドットはありますので、あらかじめ縮小しておくことが必要となってしまいます。このような制限があるものの、画像データを圧縮して持てるので、フラッシュ容量の大きいマイコンを使用すれば、かなりの枚数の画像を保持することも可能となります。

クリック音を付ける

2013-10-06 17:43:56 | LCD
先週の記事ではFT800のTAG機能を使ってキーパッド入力を拾ってみました。これに、もうひとつ手を加えてキーが押される度に効果音としてクリック音を生成してみます。

FT800にはLCD表示機能、タッチパネル制御機能に加えて、サウンド機能が用意されています。あらかじめ用意された効果音のテーブルを持っているので、レジスタを叩くだけで効果音の生成がおこなえます。手順は次のとおり。
  1. まずREG_GPIOレジスタの1ビット目を操作して、外付けのサウンド増幅用アンプをイネーブル。
  2. REG_VOL_SOUNDレジスタに音量を設定
  3. REG_SOUNDレジスタに効果音の種類、音程を設定
  4. REG_PLAYに1を書いて鳴動開始。
  5. REG_SOUNDに0を書いて、REG_PLAYに1を書くことで鳴動停止。

効果音には、ビープ音やDTMF音のように鳴動を開始すると鳴り続けるものと、クリック音のように一定の長さで再生を終えるものとがあります。鳴り続ける効果音に対しては、上記のように明示的に停止する操作が必要となります。また、ビープ音やピアノやオルガンのような楽器音にでは音程を指定することができます。クリック音は長さも音程も変えられないので、一度REG_SOUNDを設定しておけば、あとはキーが押される度にREG_PLAYに1を書くだけで音が出せることになります。

アプリの始まりと終わりではREG_GPIOのビット1を操作することでアンプのイネーブル/ディスエーブルをおこないます。音出しが必要ない期間はディスエーブルしておくことで、省電力化が計れます。データシートでは、GPIOのビット1をアンプをシャットダウンする目的で使用することを推奨すると明記されており、ConnectEVEもこれに従っています。しかし、データシートではその信号を正論理とするか負論理とするかまでを規定しているわけではありません。このようなゆるい規定も影響してか、ConnectEVEでは正論理でシャットダウンするアンプが使用されている一方で、FTDI純正の開発ボードであるVM800Cでは負論理でシャットダウンするアンプが使用されています。



スピーカとしては秋月で売られている表面実装用スピーカを使ってみました。音量が大きすぎると音が割れてしまうのでREG_VOL_SOUNDレジスタの設定値は0x90で使用中。

キーはTAGで拾う -- 続編

2013-10-03 21:05:37 | LCD
前回の記事でとりあげた、TAGを使ったキー入力の主要コードを掲載しておくことにします。

#define DIGIT_LENGTH    15
char digits[DIGIT_LENGTH+1];

int ft800_getKey(Ft_Gpu_Hal_Context_t *phost;)
{
  int tag;

  ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR,
        &ev_ft800, INT_TAG, CTL_TIMEOUT_NONE, 0);
  tag = Ft_Gpu_Hal_Rd8(phost, REG_TOUCH_TAG);
  return tag;
}

void do_Button(Ft_Gpu_Hal_Context_t *phost)
{
  int option, inspos, ckey, keyval;
 
  Ft_Gpu_CoCmd_FgColor(phost, 0xe0e000); /* Button color is yellow */
  SAMAPP_CoPro_Widget_Calibrate();

  digits[0] = 0;
  ckey = 0;
  inspos = 0;
  do {
    Ft_Gpu_CoCmd_Dlstart(phost);
    Ft_App_WrCoCmd_Buffer(phost,CLEAR_COLOR_RGB(0,0,0)); /* Black background */
    Ft_App_WrCoCmd_Buffer(phost,CLEAR(1,1,1));

    Ft_Gpu_CoCmd_FgColor(phost, 0x003880); /* Button color is light blue */
    Ft_App_WrCoCmd_Buffer(phost,COLOR_RGB(255,255,255)); /* Label color is white */

    /* Draw Dial Keypad */
    Ft_Gpu_CoCmd_Keys(phost, 10,  80, 300, 35, 28, ckey, "123");
    Ft_Gpu_CoCmd_Keys(phost, 10, 120, 300, 35, 28, ckey, "456");
    Ft_Gpu_CoCmd_Keys(phost, 10, 160, 300, 35, 28, ckey, "789");
    Ft_Gpu_CoCmd_Keys(phost, 10, 200, 300, 35, 28, ckey, "#0*");
    /* Draw delete key */
    Ft_Gpu_CoCmd_FgColor(phost, 0xc01000); /* Button color is red */
    Ft_App_WrCoCmd_Buffer(phost,TAG('D'));
    option = (ckey == 'D') ? OPT_FLAT : 0;
    Ft_Gpu_CoCmd_Button(phost, 320,  80, 100, 50, 29, option, "Delete");
    /* Draw call key */
    Ft_App_WrCoCmd_Buffer(phost,COLOR_RGB(192,0,0)); /* Label color is red */
    Ft_Gpu_CoCmd_FgColor(phost, 0x00c000);
    Ft_Gpu_CoCmd_GradColor(phost,0xc0c0c0);
    Ft_App_WrCoCmd_Buffer(phost,TAG('C'));
    option = (ckey == 'C') ? OPT_FLAT : 0;
    Ft_Gpu_CoCmd_Button(phost, 320, 185, 100, 50, 29, option, "Call");

    digits[inspos] = '_';
    digits[inspos+1] = 0;
    Ft_App_WrCoCmd_Buffer(phost,COLOR_RGB(255,255,255)); /* Text color is white */
    Ft_Gpu_CoCmd_Text(phost, 10, 20, 30, 0, digits);

    Ft_App_WrCoCmd_Buffer(phost,DISPLAY());
    Ft_Gpu_CoCmd_Swap(phost);

    /* Download the commands into fifo */
    Ft_App_Flush_Co_Buffer(phost);
    Ft_Gpu_Hal_WaitCmdfifo_empty(phost);

    keyval = ft800_getKey(phost);
    if (keyval == 0) {      /* Key has released */
      if ((ckey >= '0' && ckey <= '9') || (ckey == '*') || (ckey == '#')) {
        if (inspos < DIGIT_LENGTH) { 
          digits[inspos++] = ckey;
        }
      } else if (ckey == 'D') {
        if (inspos > 0) {
          digits[inspos] = 0';
          inspos--;
        }
      }
    }
    ckey = keyval;
  } while (ckey != 'C');
  /* Call key has pressed. */
}

キーの状態変化はft800_getKey()で拾います。この関数では、割り込み処理ハンドラで生成されるイベントを待って、REG_TOUCH_REGを読み取るだけです。これだけで、キーがタッチされた時には、そのキーに対応するTAGの値を、タッチが終わった時には値としてゼロが返ってきます。

呼び出し側ではタッチが終わったこと時に対応するキーの値を表示しています。キーの状態変化があるたびに全てのキーを描画しなおしていますが、描画速度にはまったく問題ありません。FT800の描画エンジンは、ディスプレイリスト(DL)と呼ばれる一連の描画コマンドを解釈/実行することでひとつの画面を表示する仕組みになっています。そしてエンジンに実行させるDLの内容を更新することで、表示内容を更新することができます。上記のコードではFt_Gpu_CoCmd_Dlstart()で、DLの先頭にポインタをセットし、つづく一連のAPIを使って、DLに書き込むコマンド列を生成していきます。 Ft_Gpu_CoCmd_Swap()でエンジンが参照するDLを入れ替えをおこなうので、通常コマンド列の最後にはこのコマンドが入ります。

SAM3Sには充分なメモリがあるので、FT800のDLに書き込むコマンド列はいったんマイコンのSRAM内に書き溜めておいて、最後に Ft_App_Flush_Co_Buffer()を読んで実際にFT800のDLにまとめてダウンロードしています。

DLについては、また次の記事で説明しようかと思います。