秋月300円液晶は、解像度400×96、18bit(RGB各6bit)カラーというスペックで、 液晶モジュール単体の値段が300円と非常に安価であることから、 インターネット上でも様々な人がこれを使った作品を公開しています。 現在では、なる研さん を始め多くの方が情報を公開してくださっているので、 かなり敷居が低くなりました。
自分も液晶モジュールを購入したのですが、これといったアプリケーションがなく、 いまさら感が漂っていたので、ずっと放置していました。 しかし、このまま放置しておくのもかわいそうなので、 グラフィック時計にしてあげることにしました。
すでに、多くの方が300円液晶を使って作品をつくられているので、 今回はSTM32Fの勉強をかねて粛々と300円液晶を食べることにします。 とはいえ、一応方針を決めておきます。
item | subitem | information |
---|---|---|
CPU | Core | Cortex-M3 |
Freq | 72MHz | |
Memory | ROM | 64kB |
RAM | 20kB | |
Timer | General | 3 |
Advanced | 1 | |
RTC | 1 | |
DMAC | 7ch×2 | |
Package | LQFP48 |
言うまでもありませんが、300円液晶はグラフィックコントローラを持たないので、 同期信号と共に常にピクセルデータを送りつづけなければなりません。 300円液晶をマイコンでそれなりに動かすのであれば、DMA機能は必須です。 ということで、STM32Fを使います。なるべく小さいパッケージのものを使いたかったので、 デバイスはSTM32F103C8Tにしました。STM32F103C8Tのスペックは右に示したとおりです。
STM32F103C8T6を使ったコントローラの構成を見積もって見ます。 画面のリフレッシュレートは60Hzはちょっと厳しいかもしれないので、 30Hzを目標にします。またポートが16bit幅なのでカラーは16bitに収まるよう、 15bit(RGB各5bit)とします。
まず始めに問題点を洗っておきます。スペックに書いたとおり、 STM32F103C8T6にはRAMが20kBも搭載されていますが、これでも1画面分の 400×96×16bit = 76.8kBには届きません。 また、ROMに関しても64kBしかないので足りません。 そこで、画像は256色(パレット)のランレングス圧縮でROMに格納します。 またRAMも足らないので、1ライン分をリアルタイムでROMから展開することで対処します。 CPUの負担はなるべく減らすためにRAMからGPIOへの転送はDMACを使い、 水平・垂直同期信号及びクロックもタイマモジュールで生成します。
なる研さんのホームページを参考に、 おおよその同期信号とディスプレイ表示タイミングを決定しました。 ブランキングを含む画面幅は、きりよく512ドットとしました。
この同期信号のタイミングを基にSTM32Fのタイマモジュールの割り当てを考えます。 右の図に各種信号の生成ツリーを示します。 STM32Fのタイマモジュールは、マスタとなるタイマのイベント出力を スレーブとなるタイマのトリガとして使うことができます。 そこで、ドットクロックの生成にTIM1を用いて、そのオーバーフロー出力を 水平同期信号と垂直同期信号を生成するTIM3及びTIM2に接続します。 これにより、各種同期信号の自動生成が可能となります。 また、TIM1のオーバーフローでDMAリクエストすることで、データ転送も行います。
ここまでの話を基にグラフィック時計の回路図と基板パターンを作成します。
まず電源回路を考えます。液晶モジュールには、ロジック用5V、液晶ドライブ用±9V、 バックライト用高電圧の3種類が必要です。さらに、STM32Fは3.3Vで動作します。 部品点数と値段を考えると、あまりDC-DCコンバータを増やしたくないので、 主電源を5Vとして、シリーズレギュレータで3.3Vを生成します。 液晶用の±9V(12V)は、どうにもならないので秋月のDC-DCコンバータモジュールを使用します。 また、STM32Fにはバッテリバックアップ可能なRTC(Read Time Clock)が内蔵されているので、 時計機能にはそれを使います。
電圧 | 用途 |
---|---|
5V | 主電源、液晶ロジック用 |
±9V(±12V) | 液晶ドライブ用 |
数100V | 液晶バックライト用 |
3.3V | STM32マイコン用 |
耐圧等の問題はありそうですが、バックライト用のインバータについては、 snj_roseさんのページを参考に自作することとしました。 (snj_roseさんに感謝)そのうち、ちゃんと計算して設計したいですね。
今回は、mikan基板CADを使ってFusionPCBに基板を発注してみました。 1箇所GNDに接続し忘れた点と、チップ部品のパッドが小さい点以外は、問題なさそうです(シルクは適当です)。 10枚発注して、約3000円くらいでした。これだけ安いと手軽に注文できてしまいます。
今回は板金加工機を使って、アルミケースを作ってみました。 左のアルミ板を折り曲げて組み立てたものが、右の写真です。 液晶モジュールは、左右のアルミ板に挟むようにして固定しています。
前述のとおり、画像は256色パレットのランレングス圧縮して内蔵ROMに格納しています。 画像は、最大127の連続するピクセルを格納したブロックを並べたフォーマットとなっています。 ブロックは、1バイトのヘッダと可変長のデータ部から構成されており、 ヘッダ部分に圧縮の有無を示す1ビットと、格納されているピクセル数が記載されています。 非圧縮ブロックの場合、2バイト目からピクセル数分の色情報が格納されます。 圧縮ブロックの場合、ヘッダに記載されているピクセル数分、 2バイト目に記載された色が連続していることを意味しています。
今回、15ビットカラー、かつ配線の関係で、ビットがMSBから順に並んでいないこともあり、 画像を専用のフォーマットに変換するプログラムを作成しました。 また、複数の画像でパレットを共有できるように、メディアンカットによるパレット生成も行っています。 変換すると、構造体の形式でヘッダファイルが作成されます。
// ビットマップ情報構造体 typedef struct _BitmapStructure { uint16_t width; uint16_t height; uint16_t length; const uint16_t* palette; const uint8_t* data; } BitmapStructureT;