Sim's blog

電子工作はじめてみました

CyFi

2008-10-31 01:06:15 | PSoC
CypressのメールマガジンにCyFiというのが載っていました。PSoCを使った無線ソリューションのようです(CyFiのページ)。PSoC FirstTouch Kitの先端につながるキットCY3271が販売になっています。
販売ページには「FREE WORLDWIDE SHIPPING UNTIL 1 NOV 08」と書いてあります。11/1までは送料無料みたいです。
2.4GHz帯を使って1Mbps出るみたいです。無線のことはよく分かりませんが日本国内だと法律の問題(ギテキ?)とか大丈夫なんでしょうか?

液晶市場のコネクタピッチ変換基板

2008-10-30 23:26:43 | 電子工作
トラ技にも広告が出ている液晶モジュール激安通販の液晶市場さんで、コネクタピッチ変換基板の販売がはじまりました。とにかく安い上に、液晶モジュール用の特殊なコネクタにあわせたソケットもついてくるうれしいショップですが、ソケット自体が0.5mmピッチだったり、特殊な形状だったりして基盤を作らないといけないこともありました。とりあえず試してみるのにコネクタピッチ変換基板があると便利です。

MAX II マイクロキット

2008-10-30 22:54:56 | FPGA
トラ技来月号(12月号)の特集は「特集◆作りながら学ぶマイクロコンピュータ」です(関連記事)。この特集で使うCPLDボード「MAX II マイクロキット」の取扱いがCQ出版web shopで始まります(11/4発売予定)。ソリトンウェーブでも買えます(製品ページ)。価格はどちらも10290円ですが、CQ出版で買うと送料無料になりそうな雰囲気があります(要確認)。

また、関連してCQ出版では「HDLでマイコンを作ろう」というセミナーを開催します。記事を書かれたソリトンウェーブの方自ら講演されるみたいです。

ネギミクを大きくしてみました。

2008-10-29 23:26:54 | AVR
ATMega88でもネギミクの続きです。

前回までの絵のサイズは56x48でした。今回は128x109にしてみました。

56x48のはちゅね


128x109のはちゅね


前回と同じ方法で圧縮してみましたが、サイズは18kバイトくらいになってATMega168の16kには入りませんでした。そこで秘密兵器の登場です。秋月で売っている「シリアルI2C EEPROM 24LC512」(通販番号I-02524)を使いました。200円なのに64kバイト(8x64kバイト=512kビット)もデータをいれておけます。動画の部分(10kバイト)は高速アクセスしたいのでATMega168のROMに、最初の静止画の部分(8kバイト)だけを24LC512に格納しました。

24LC512です

8pinのDIPパッケージです。ピン機能です。

1 A0 A2~A0でI2Cのアドレスを決める
2 A1
3 A2
4 GND
5 SDA I2Cデータポート (2.2kΩでプルアップ)
6 SCL I2Cクロックポート、400kbps (2.2kΩでプルアップ)
7 WP ライトプロテクト(GND接続で書き込み可)
8 Vcc 2.5~5.5V

A2~A0でアドレスを変えれるので同じバス上に8個まで24LC512を接続することができます。書き込みはバイト単位とページ単位(128バイト)ができ、読み出しはランダム読み出しとシーケンシャル読み出しができます。ページ書き込みのときは、一旦バッファで受信してから書き込むので、データを送り終わったあと、ビジーの間待たないと次のコマンドを送ることができません。今回はまったところです。I2Cのstartを送るとNAKが返ってきて最初のブロックしか書き込めませんでした。startを送ってACKが返ってくるまで何度もstartを送ってビジーでなくなるのをポーリングして待ちます。

24LC512の書き込みのコードです。
// ATMega168のROMの内容を24LC512に書き込む
void flash_writep(uint16_t addr, const uint8_t *data, uint16_t size)
{
    uint8_t i, s;

    while(size >= 128){
        // 1ブロック書き込む
        i2c_start(LC512 | TW_WRITE);
        i2c_write(addr >> 8); // アドレス上位	
        i2c_write(addr);        // アドレス下位
        for(i = 0; i < 128; i++) i2c_write(pgm_read_byte(data++));
        i2c_stop();

        // ビジーウェイト
        while((s = i2c_startx(LC512 | TW_WRITE)) == TW_MT_SLA_NACK) ;
        if(s != TW_MT_SLA_ACK) i2c_error();
        i2c_stop();
        addr += 128;
        size -= 128;
    }

    // 最後のページを書き込む
    i2c_start(LC512 | TW_WRITE);
    i2c_write(addr >> 8); // アドレス上位
    i2c_write(addr);        // アドレス下位
    for(i = 0; i < size; i++) i2c_write(pgm_read_byte(data++));
//  for(i = size; i < 128; i++) i2c_write(0xff);
    i2c_stop();

    // ビジーウェイト
    while((s = i2c_startx(LC512 | TW_WRITE)) == TW_MT_SLA_NACK) ;
    if(s != TW_MT_SLA_ACK) i2c_error();
    i2c_stop();
}

最後のページは128バイト全部そろってなくても書き込めるみたいでした。その場合は、元々書いてあったデータがそのまま残っていました。

24LC512のシーケンシャルリード(連続読み出し)を使った画像解凍ルーチンです。最初にアドレスを贈っておけば、後は読み出すだけでアドレスが自動的に増えてくれるので、2度目以降はアドレスを送る必要がなく効率的です。
// 24LC512から読み出してNokia 6100液晶に解凍&表示する
void flash_unpack(uint16_t addr, uint16_t size)
{
    uint8_t c0, c1, n;

    i2c_start(LC512 | TW_WRITE);
    i2c_write(addr >> 8); // アドレス上位
    i2c_write(addr);        // アドレス下位
    i2c_start(LC512 | TW_READ);
    lcd_cmd(RAMWR);
    do {
        c0 = i2c_read_ack();
        n = c0 >> 4;
        c1 = i2c_read_ack();
        do {
            lcd_data(c0);
            lcd_data(c1);
        } while(n--);
    } while(--size);
    i2c_read_nak();     // NAKを送るためにダミーで読む
    i2c_stop();
}

高速化するには、今のデータを受け取っている間に一つ前のデータを表示することが考えられます。

実験の様子です。

Nokia 6100液晶はピンを立てればブレッドボードでも遊べるのでいいです。

ATMega16のROM使用サイズは10544バイトで6kバイトくらい空いています。圧縮しないと167,424バイト(= 6x128x109x2)だったのが18kバイトちょいに収まりました。もう少しがんばればATMega168単体でもいけたのかもしれません。外付けROMを試せたのでよしとします。

半導体娘計画

2008-10-27 22:13:25 | FPGA
ニコニコ動画を見ていたらFPGAで遊んでいる人がいました。つないでいるのは秋月の300円液晶ですね。基板設計からFPGA回路設計、グラフィックから音楽まですごい人がいるものです。ビデオの編集もすごいです。以前、液晶パネルをFPGAつないでた人ですね。

自作音源で自作曲を演奏してみた/半導体娘計画【新番組】


作っている方のページ「『Utchy by 砂肝』の館
MAKE TOKYO MEETING02にも出品されるそうです。

FPGAではXilinxとAlteraはどっちが攻めでどっちが受けなんでしょうか?

Interface 12月号

2008-10-27 21:48:16 | その他のマイコン
特集はμITRON

付録基板関連は7つ
- ARM Cortex-M3基板へのTOPPERS/ASPカーネルの移植
- TOPPERS/ASP for ColdFireの実装
- FRマイコン基板でμT-Kernel仕様準拠RTOSを使ってみよう
- USB接続可能なBDM互換デバッガの作成
- Silent Cを消さずにCodeWarriorを活用する方法
- UPnP機能を使ったマイコン・プログラムの設計
- Cコンパイラによるユーザ・ドライバの作成と呼び出し方法

ARM Cortex-M3基板はDesignWave付録のARM基板です。特集記事に付録基板を活用しているのはとてもいいと思いました。

USB版BDMデバッガはTBLCFで、JB16を使っています。今後若松から発売予定だそうですが、まだ値段等は出ていません。
来栖川電工のページにも情報が載るみたいです。

SilentCの活用に関してはのりたんさんのページ参照


ATMega88でもネギミク

2008-10-23 01:55:56 | AVR
Nokia6100液晶でネギミクの続きです。

前回はフラッシュが16kあるATMega168を使いました。今回はもうちょっとがんばってATMega88に載せてみました。

前回はランレングス圧縮を使って32256バイト(6x5376)を8004バイトまで縮めました。今回は、4524バイトまで縮めています。やったことは、そうたいしたことではなく、変化部分を3つの長方形で囲むようにして、各長方形毎に圧縮しただけです。

前回は長方形1つでした。下の図で、oは変化したドット、.と-は変化しなかったドットです。.は白で-は白以外です。左上の方はネギで右の方に髪の毛があります。変化した部分(o)が2箇所に分断されているせいで、囲む長方形が大きくなっています。
(2,1)-(42,32) → 1152バイト
..........o....o.........................  o 変化したドット
.........ooo.ooo.........................  . 変化しなかったドット(白)
.........oooooooo.............-------....  - 変化しなかったドット(白以外)
.........oooooooo............------------
..........ooooooo...........-------------
...........oooooo..........--------------
............oooooo........---------------
.............ooooo........---------------
...............ooo.......----------------
..oo............ooo......----------------
.ooooo...........ooo.....----------------
oooooooo.........ooo....-----------------
.ooooooooo........oo.oooo----------------
....oooooooo......ooooooo----------------
...ooooooooooooooooooooooo---------------
...oooooooooooooooooo---ooo--------------
...oo........oooooooo----oo--------------
...................o------ooo------------
...................ooo-------oo---------.
...................oooooo---oooo------.--
....................oooooooooooo-------..
....................oooooooooo----------.
....................--------.o----------.
....................---------------------
...................----------------------
...................----------------------
...................----------------------
...................----------------------
..................--------------------oo-
..................--------------------oo-
..................--------------------oo-
..................---------------------oo

変化部分(o)を3つの領域に分けます。具体的には以下のようにしました。
(2,1)-(28,22) → 294バイト
..........o....o...........
.........ooo.ooo...........
.........oooooooo..........
.........oooooooo..........
..........ooooooo..........
...........oooooo..........
............oooooo........-
.............ooooo........-
...............ooo.......--
..oo............ooo......--
.ooooo...........ooo.....--
oooooooo.........ooo....---
.ooooooooo........oo.oooo--
....oooooooo......ooooooo--
...ooooooooooooooooooooooo-
...oooooooooooooooooo---ooo
...oo........oooooooo----oo
...................o------o
...................ooo-----
...................oooooo--
....................ooooooo
....................ooooooo

(29,18)-(33,23) → 60バイト
oo---
--oo-
-oooo
ooooo
ooo--
-.o--

(40,29)-(42,32) → 22バイト
oo-
oo-
oo-
-oo

元々が1152バイトだったものが376バイト(= 294+60+22)に縮みました。
白以外の部分(-)は色変化があって圧縮しずらいので、白い外の部分があまり含まれないような分割の方が圧縮率が高くなります。
長方形の決め方は、3つの長方形のx座標が重ならないような組み合わせを全て試して一番縮んだものにしています。透明色が使えれば変化しない部分は全て透明色になるのでランレングスが効くのですが、そうはなっていません。一旦RAMに展開して重ね合わせ処理をしてから、再度転送しないといけないと思います。全画面展開できるだけのメモリがないので、作るのは難しそうです。

もっとちゃんとした方法を使えばもっと縮みそうですが、ちゃんとした方法を実装するのに必要なメモリがありません。原始的な方法ですが、そこそこ効果がありました。プログラムコード込みで5550バイトでした。

Nokia6100液晶でネギミク

2008-10-21 02:13:11 | AVR
Nokia6100液晶って130x130なんですけどの続きです。

お約束というか、ネギ振りをさせてみました。
問題なのは画像のサイズです。とりあえずATMega88からATMega168に変えました(ROMサイズ8k→16k)。130x130のデータは大きすぎるので56x48の縮小版です。絵の縮小はubuntuのGIMPを使いました。
56x48でもドットの数は2688(= 56x48)です。1ドット2バイトとして5376バイト、1.5バイトでも4032バイトです。ネギ振りの絵は6枚あるので、16kでもROMの容量が足りません。しかたがないので圧縮することにしました。難しいことはできないので、ランレングスで圧縮します。1ドット2バイト表現の上位4ビットが余っているので、ここにランレングスをいれることにしました。4ビットなので、0~Fで1~16を表すことにします。結果として5376バイトが2170バイトに縮みました。
また、アニメーション部分は変化したところだけ書き直すようにしました。結局、元画像+変化分6枚を8004バイトに圧縮できました。コードが1kくらいなので全部で9002バイトです。もっとがんばればATMega88に入れれるかもしれません。

表示部分のソースです。
// 解凍&描画
// データ形式はLLLLRRRRGGGGBBBBでLLLLが長さ
void lcd_unpack(const uint8_t *p, uint16_t size)
{
    uint8_t c0, c1, n;

    lcd_cmd(RAMWR);
    do {
        c0 = pgm_read_byte(p++);
        n = c0 >> 4;
        c1 = pgm_read_byte(p++);
        do {
            lcd_data(c0);
            lcd_data(c1);
        } while(n--);
    } while(--size);
}


// 外部変数
extern const uint8_t miku[2170] PROGMEM;   // (0,0)-(55,47)
extern const uint8_t miku01[1152] PROGMEM; // (2,1)-(42,32)
extern const uint8_t miku12[1156] PROGMEM; // (2,10)-(41,39)
extern const uint8_t miku23[826] PROGMEM;  // (5,21)-(42,39)
extern const uint8_t miku34[684] PROGMEM;  // (2,20)-(40,36)
extern const uint8_t miku45[926] PROGMEM;  // (2,5)-(40,29)
extern const uint8_t miku50[1090] PROGMEM; // (6,1)-(41,32)

#define X0 36
#define Y0 40
#define DELAY 100

int main()
{
    lcd_init();

    // 最初の絵
    lcd_window(X0, Y0, X0 + 55, Y0 + 47);
    lcd_unpack(miku, sizeof(miku) / 2);

    while(1){
        // 0→1
        _delay_ms(DELAY);
        lcd_window(X0 + 2, Y0 + 1, X0 + 42, Y0 + 32);
        lcd_unpack(miku01, sizeof(miku01) / 2);

        // 1→2
        _delay_ms(DELAY);
        lcd_window(X0 + 2, Y0 + 10, X0 + 41, Y0 + 39);
        lcd_unpack(miku12, sizeof(miku12) / 2);

        // 2→3
        _delay_ms(DELAY);
        lcd_window(X0 + 5, Y0 + 21, X0 + 42, Y0 + 39);
        lcd_unpack(miku23, sizeof(miku23) / 2);

        // 3→4
        _delay_ms(DELAY);
        lcd_window(X0 + 2, Y0 + 20, X0 + 40, Y0 + 36);
        lcd_unpack(miku34, sizeof(miku34) / 2);

        // 4→5
        _delay_ms(DELAY);
        lcd_window(X0 + 2, Y0 + 5, X0 + 40, Y0 + 29);
        lcd_unpack(miku45, sizeof(miku45) / 2);

        // 5→0
        _delay_ms(DELAY);
        lcd_window(X0 + 6, Y0 + 1, X0 + 41, Y0 + 32);
        lcd_unpack(miku50, sizeof(miku50) / 2);

    }
}

圧縮するプログラムのバグがなかなか取れなくて苦労しました。
変化部分は、変化する部分を囲む四角にしていますが、複数の四角にした方が効率は良さそうです。複数の四角を組み合わせて変化部分を囲むアルゴリズムは思いつきませんでした。変化部分は、透明色ありにすればランが伸びるのでもっと圧縮できそうです。ただし、この液晶モジュールはデータの読み出しができないので、座標をスキップすることになります。

携帯のヒンジが壊れたらしく、表示される色が変です。そろそろ買い換え時なのかな。

秋月でATMEL AT90USBKeyを扱いはじめました。

2008-10-20 22:41:34 | AVR
秋月でAT90USBKEYというAtmel純正のボードの扱いがはじまりました(通販コードM-2646)。Atmelのページはこちら。CPUのAT90USB1287のページ。ROMが128kもあります。PORTがA~Fまでありますが、1.27mmピッチなのでブレッドボードには向きません。
10/21追記 タイトル変更ATME→ATMEL

同時発売のネットワーク電子辞書DB-J260って、ぐぐったら以前は29800円で売っていたものみたいです。今はどこも売り切れです。

2010/9/4 追記 AT90USBKEYのリンク先を訂正。この記事を書いた後で秋月のホームページがリニューアルしたのでリンク切れになっていました。もっともこれを書いている時点では在庫切れになっています。

ソフトSPIの実力

2008-10-19 14:23:52 | AVR
Nokia6100液晶はソフトウェアSPIで通信しています。どのくらい速度が出ているのかオシロで見てみました。



上の赤がCSで、下の黄色がSCKです。1us per DIV(横方向は1マス1us)です。9つのパルスが見えるので9ビットなことが分かります。最初だけちょっと狭くて後の8つがちょっと広いです。広い方は0.75usです。1.33Mbpsですね。AVRは8MHzの内蔵クロックを使っているので6クロックで1ビット送信していることになります。このあたりは8bitとはいえ、さすがRISCといったところでしょうか。

ちなみにどんなコードだったかというと以下になります。
// マクロ
#define sck() do { PORTB |= _BV(SCK); PORTB &= ~_BV(SCK); } while(0)
#define cs1() PORTB |= _BV(CS)
#define cs0() PORTB &= ~_BV(CS)

// S1D15G10にデータを送る
void lcd_data(uint8_t c)
{
    register uint8_t t = c;
    cs0();
    PORTD = _BV(MOSI); sck();    // 1
    PORTD = t; sck(); t <<= 1; // bit7
    PORTD = t; sck(); t <<= 1; // bit6
    PORTD = t; sck(); t <<= 1; // bit5
    PORTD = t; sck(); t <<= 1; // bit4
    PORTD = t; sck(); t <<= 1; // bit3
    PORTD = t; sck(); t <<= 1; // bit2
    PORTD = t; sck(); t <<= 1; // bit1
    PORTD = t; sck();            // bit0
    cs1();
}

本来ならPORTDのMSBだけを変化させるようなコードを書くべきなのですが、8bit全部書いているのでPORTDを他の用途に使うことはできません(入力は可?)。PORTDを犠牲にして速度をかせいでいることになります。

それにしても、ソフトだけなのに結構出るもんなんですね。

Nokia6100液晶って130x130なんですけど

2008-10-19 01:33:08 | AVR
Nokia6100液晶を使ってみるの続きです。

その後、すんさんがPIC24Fにソースを移植してくださいました。猫の写真を表示されています。

この液晶の名前ですが、元々使われていたNokia社の携帯電話の名前(Nokia6100とかNokia6610)を使って呼ばれているようです。なのでNokia6100液晶というのは正式な型番ではなく通称みたいです。また、複数の携帯で使われていたらしく呼ばれる名前も多少ゆれています。

液晶パネルですが、TFTではなくSTNです。TFTはドット毎の制御ですが、STNはライン毎の制御なので、にじみが出ます。背景が黒だと目立つので背景は白とかの方がにじみが目立たなくていいです。電子ボリュームの値を大きくすると、画面が明るくなってにじみが目立たなくなりますが、その分諧調が減ってしまいます。明るい方の色が全部飽和して一番明るい色になります。見やすいように電子ボリュームの値を調節できるようにするのがよさそうです。

SRAMは132x132あるみたいです。この液晶モジュールはSRAMの読み出しができないのでそれ以上存在するかどうかは確認できません。写真では(2,2)-(129,129)に赤い四角を描いています。左側と下側にすきまが見えますがここにも描画できます。実際に表示できる座標は(0,2)-(129,131)の130x130です。前回のコードでy座標に2を足しているのは、y座標が0と1は表示されなかったからです。

初期化のときのDATCTLコマンドの第1パラメータを変えることで表示の向きを変えることができます。このとき表示できる座標も変わります。例えばモード0では(0,2)-(129,131)ですが、モード1だと(0,0)-(129,129)を表示できて、その外側は表示できません。モードによって表示される座標が違うので補正も変える必要があります。モード0ではy座標を2増やしますが同じ補正をモード1で行うと下の2行が欠けてしまうことになります。
どのモードでも表示されるのは(2,2)-(129,129)の128x128の領域です。この領域だけを使うことにすれば補正はx, y座標とも2を増やせばいいことになり、モードによって補正を変える必要はなくなります。

DATCTLコマンドの第1パラメータを変えるとどうなるか具体的に見てみます。描画ルーチンは全て同じものを使っています。ここでx座標といっているのはCASETコマンドで設定するColumn Addressです。またy座標はPASETコマンドで設定するPage Addressのことです。

モード0

上と右が欠けているので表示される座標は(0, 2)-(129,131)です。

モード1

モード0と較べてy座標(Page Address)が反転しています。
表示される座標は(0, 0)-(129, 129)です。

モード2

モード0と較べてx座標(Column Address)が反転しています。
表示される座標は(2, 2)-(131, 131)です。

モード3

モード0と較べてx, y座標共に反転しています。
モード0から180度回転していることになるので、写真上側から見ると正常に見えることになります。
表示される座標は(2,0)-(131, 129)です。

モード4

今度はx座標とy座標が入れ替わっています。
表示される座標は(2,0)-(131,129)です。

モード5

モード4と較べてx座標(画面で見ると縦方向)が反転しています。
表示される座標は(0,0)-(129,129)です。
写真右方向から見ると正常に見えます。

モード6

モード4と較べてy座標(画面で見ると横方向)が反転しています。
表示される座標は(2,2)-(131,131)です。
写真左方向から見ると正常に見えます。

モード7

モード4と較べてx, y座標共に反転しています。
表示される座標は(0,2)-(129,131)です。

結構複雑ですね。実際は液晶モジュールのコントローラがCPU側から送られてきた座標をどのように解釈しているかという話になります。

今回の表示のコードです。
void plot(uint8_t x, uint8_t y, uint16_t c)
{
    lcd_cmd(CASET);
    lcd_data(x);
    lcd_data(x);
    lcd_cmd(PASET);
    lcd_data(y);
    lcd_data(y);

    lcd_cmd(RAMWR);
    lcd_data(c >> 8);
    lcd_data(c);
}

void test1(void)
{
    uint8_t i;

    lcd_puts_p(2, 2, PSTR("Hello, world"));
    lcd_puts_p(5, 8, PSTR("MODE"));
    lcd_putc(10, 8, '0' + MODE);

    for(i = 0; i < 132; i++){
        plot(2, i, 0xf00);
        plot(129, i, 0xf00);
        plot(i, 2, 0xf00);
        plot(i, 129, 0xf00);
    }
}


Nokia6100液晶を使ってみる

2008-10-16 23:58:36 | AVR
以前、買うだけ買ってほったらかしにしていたNokia6100液晶を使ってみました。きっかけはトラ技11月号にこの液晶を使った製作記事が載っていたからです(この記事について以前書いたもの)。トラ技11月号のソースプログラムはダウンロードできるのですが、PIC用なのでAVRではそのまま使えません。PICのソースを見ながら初期化の手順や描画手順のエッセンスを抜き出してAVRに移植しはじめました。

ということで一応動くものができたのですが、ちょっと疑問がわいてきたので別のソースも参考にして比較検討をはじめました(参考にしたNokia LCD Driver for 4096 Colors by Loren Blaney)。

SparkFunのColor LCDモジュールです。

キャリーボードは液晶が必要とする電圧を全て作ってくれます。必要なのは3.3Vだけです。

SparkFunColor LCDモジュールのコントローラはPhilipsのPCF8833とEPSONのS1D15G10があります。うちのはEPSONのでした。EPSONにはS1D15G00という似た名前(最後が00か10かの違い)のコントローラがあって、たとえばストロベリーリナックスでダウンロードできるデータシートは10ではなくて00の方のデータシートです。00と10は微妙にコマンドが違っています。一番大きな違いは10の方では4096色モードが2種類あることです。

4096色モードの一つ目(00互換)は、2ドットを3バイトで表します。4096色は12ビットで表現できます。2つで24ビットなので3バイトです。

1バイト目 RRRRGGGG (左のドットのRとG)
2バイト目 BBBBRRRR (左のドットのBと右のドットのR)
3バイト目 GGGGBBBB (右のドットのGとB)

のように2つのドットの情報をまぜて3バイトを作ります。必ず2つのドットを変更しないといけないようです(要確認)。

4096色モードの二つ目(10専用?)は、1ドットを2バイトで表します。

1バイト目 0000RRRR (R)
2バイト目 GGGGBBBB (GとB)

のように表します。
どちらのモードがいいかというと互いに得意と不得意があります。その1の方はデータがコンパクトに表現できるので、ROMサイズが少なかったり、通信量が少なかったりします。そのかわり細かいドットの操作が面倒です。その2の方は逆でサイズは大きくなりますが、2つのドットを組み合わせたりとか面倒なことがいりません。

モードの切り替えはDATCTLコマンドの3つ目のパラメータで選択します。
- 1のとき、256色モード
- 2のとき、4096色モードその1
- 4のとき、4096色モードその2
です。

トラ技のソースでは4096色モードその1を使っています。Blaneyさんのソースはその2を使っています。また、トラ技のソースはy座標が連続しない設定になっているのと、windowを使った高速転送を使っていません。

このコントローラには矩形転送のためのおもしろいコマンドがあります。通常四角い絵を描くときは、左上のドットをうって、その右隣のドットをうって、順にうっていって右上までいったら、y座標を一つ増やしてx座標を左に戻してといった折り返しの処理が必要になります。このコントローラはあらかじめ周りを囲む四角の座標を設定しておけば、折り返しの処理を自動で行ってくれるので、CPU側はデータを必要な分だけ送れば四角い絵を描けることになります。8x8ドットのフォントを描きたいときは、文字を囲む四角を設定してから、フォントのデータを左上から順に送るだけで画面上は勝手に折り返してくれます。

動かした結果です。

黒、赤、緑、青の4色のHello, worldと、中央の4096色を64x64の四角にしたものを描いています。横方向に4つの繰り返しが見えます。これはBが0~F、0~F、0~F、0~Fと4回動いているからです(その間にGは4つ増える)。

8x8のフォントを16x16(= 256)で表示しています。


ソースです。
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

// ピン接続
#define MOSI PD7 // 動かせない
#define SCK  PB2
#define RST  PB1 // pull-down
#define CS   PB0 // pull-up

// S1D15G10コマンド
#define DISON  0xaf // 1. Display on P0
#define DISOFF 0xae // 2. Display off P0
#define DISINV 0xa7 // 4. Inverse display P0
#define COMSCN 0xbb // 5. Common scan direction P1
#define DISCTL 0xca // 6. Display control P4 (データシートp.30のP3は間違い)
#define SLPOUT 0x94 // 8. Sleep out P0
#define PASET  0x75 // 9. Page Address set P2
#define CASET  0x15 // 10.Column Address set P2
#define DATCTL 0xbc // 11.Data scan direction, etc. P3
#define RAMWR  0x5c // 13.Writing to memory P:data
#define OSCON  0xd1 // 21.Internal oscillation on P0
#define PWRCTR 0x20 // 23.Power control P1
#define VOLCTR 0x81 // 24.Electronic colume control P2

// マクロ
#define sck() do { PORTB |= _BV(SCK); PORTB &= ~_BV(SCK); } while(0)
#define cs1() PORTB |= _BV(CS)
#define cs0() PORTB &= ~_BV(CS)

// 外部変数
extern const uint8_t font[256][8] PROGMEM;

// データ形式は0000rrrrggggbbbbの16bit
uint16_t fgcolor = 0x000;
uint16_t bgcolor = 0xfff;

// S1D15G10にコマンドを送る
void lcd_cmd(uint8_t c)
{
    register uint8_t t = c;
    cs0();
    PORTD = 0; sck();            // 0
    PORTD = t; sck(); t <<= 1; // bit7
    PORTD = t; sck(); t <<= 1; // bit6
    PORTD = t; sck(); t <<= 1; // bit5
    PORTD = t; sck(); t <<= 1; // bit4
    PORTD = t; sck(); t <<= 1; // bit3
    PORTD = t; sck(); t <<= 1; // bit2
    PORTD = t; sck(); t <<= 1; // bit1
    PORTD = t; sck();            // bit0
    cs1();
}

// S1D15G10にデータを送る
void lcd_data(uint8_t c)
{
    register uint8_t t = c;
    cs0();
    PORTD = _BV(MOSI); sck();    // 1
    PORTD = t; sck(); t <<= 1; // bit7
    PORTD = t; sck(); t <<= 1; // bit6
    PORTD = t; sck(); t <<= 1; // bit5
    PORTD = t; sck(); t <<= 1; // bit4
    PORTD = t; sck(); t <<= 1; // bit3
    PORTD = t; sck(); t <<= 1; // bit2
    PORTD = t; sck(); t <<= 1; // bit1
    PORTD = t; sck();            // bit0
    cs1();
}

// S1D15G10のwindow設定
void lcd_window(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1)
{
    lcd_cmd(CASET);
    lcd_data(x0);
    lcd_data(x1);
    lcd_cmd(PASET);
    lcd_data(y0 + 2);
    lcd_data(y1 + 2);
}

// S1D15G10初期化
void lcd_init(void)
{
    uint16_t i;

    // ポート設定とリセット
    PORTB = _BV(CS);    // CS=1, RST=0
    DDRB  = _BV(SCK) | _BV(RST) | _BV(CS);
    DDRD  = _BV(MOSI);
    _delay_ms(10);      // トラ技は200ms
    PORTB |= _BV(RST);
    _delay_ms(10);      // トラ技は100ms

    // S1D15G10の初期化
    lcd_cmd(DISCTL);    // display control
    lcd_data(0);        // - (トラ技は3)
    lcd_data(32);       // - 132line / 4 - 1 = 32 (31にすると126ライン)
    lcd_data(11);       // - (トラ技は12)
    lcd_data(0);        // - P4はS1D15G00にはない。0:dispersion 1:non-dispersion
    lcd_cmd(COMSCN);    // コモンのスキャン方向
    lcd_data(1);        // - 1:1→68, 132←69 (トラ技は0)
    lcd_cmd(OSCON);     // 内部発振器ON
    lcd_cmd(SLPOUT);    // sleep out
    lcd_cmd(PWRCTR);    // power control
    lcd_data(15);       // - 15固定
    lcd_cmd(DISINV);    // inverse display (0:暗 - f:明)
    lcd_cmd(DATCTL);    // data control
    lcd_data(0);        // - 描画方向
    lcd_data(0);        // -
    lcd_data(4);        // - トラ技は2 (S1D16G00のデータシートにはないモード)
    lcd_cmd(VOLCTR);    // 電子ボリューム制御
    lcd_data(33);       // - トラ技は31 ← コントラスト
    lcd_data(3);        // - 3固定

    lcd_cmd(CASET);     // SRAMクリア
    lcd_data(0);
    lcd_data(131);
    lcd_cmd(PASET);
    lcd_data(0);
    lcd_data(131);
    lcd_cmd(RAMWR);     // メモリ書き込み (RAM初期化)
    for(i = 0; i < 132 * 132; i++){
        lcd_data(bgcolor >> 8);
        lcd_data(bgcolor);
    }
    _delay_ms(100);
    lcd_cmd(DISON);     // ディスプレイON
}

// 8x8文字出力
void lcd_putc(uint8_t x, uint8_t y, uint8_t c)
{
    uint8_t i, j, w;
    uint16_t dot;

    // 最初にwindowをセットしてやれば後はデータを送るだけ
    lcd_window(x * 8, y * 8, x * 8 + 7, y * 8 + 7);
    lcd_cmd(RAMWR);
    for(i = 0; i < 8; i++){
        w = pgm_read_byte(&font[c][i]);
        for(j = 0; j < 8; j++){
            dot = (w & 0x80) ? fgcolor : bgcolor;
            lcd_data(dot >> 8);
            lcd_data(dot);
            w <<= 1;
        }
    }
}

// 文字列出力(ROM文字列)
void lcd_puts_p(uint8_t x, uint8_t y, char *s)
{
    uint8_t c;

    while((c = pgm_read_byte(s++)) != 0) lcd_putc(x++, y, c);
}

void lcd_pset(uint8_t x, uint8_t y, uint16_t c)
{
    lcd_window(x, y, x, y);
    lcd_cmd(RAMWR);
    lcd_data(c >> 8);
    lcd_data(c);
}

int main()
{
    uint16_t i;

    lcd_init();

    // 文字列出力
    lcd_puts_p(0, 0, PSTR("Hello, world"));
    fgcolor = 0xf00;
    lcd_puts_p(0, 1, PSTR("Hello, world"));
    fgcolor = 0x0f0;
    lcd_puts_p(0, 2, PSTR("Hello, world"));
    fgcolor = 0x00f;
    lcd_puts_p(0, 3, PSTR("Hello, world"));

    // 64x64の色変化四角を描く
    lcd_window(32, 32, 95, 95);
    lcd_cmd(RAMWR);
    for(i = 0; i < 4096; i++){
        lcd_data(i >> 8);
        lcd_data(i);
    }

    while(1) ; // 無限ループ
}

このコントローラは9ビットのSPIを使います。AVRのSPIハードは8ビットSPIしかできないので、ソフトで実現しています。1ビット目がコマンド(0)またはデータ(1)の区別を表していて、残りの8ビットがコマンドまたはデータになります。データシートを見ると1サイクルが50nsなので20MHzまで平気です。ソフトだともっと遅くなります。

初期化は結構めんどうです。たくさんコマンドを送らないといけません。結局、ほとんどBlaneyさんのコードと同じようなものになりました。BlaneyさんのコードはSRAMの初期化の部分に間違いがあるので修正してあります。

もうちょっと書きたいことがあるのですが、長くなってきたので続きます。


RTC-8564NBを使ってみる

2008-10-13 03:06:11 | AVR
このところI2CでつなげるアイテムとAVRで遊んできました。
- 温度センサーLM73
- カラーセンサーADJD-S371-QR999

今回は秋月で売っているリアルタイムクロックモジュールRTC-8564NBを使ってみました(通販番号I-00233)。このICはある意味定番のようで、ぐぐってみると、いろいろな方が使われています。
メーカーのサイトです。データシートとアプリケーションノートがダウンロードできます。

秋月のモジュールは変換基板上に実装されているので、そのままブレッドボードに挿して使えます。

デフォルトではI2Cのラインはプルアップされていないので、自分でプルアップしてやる必要があります。もしくはジャンパをはんだで盛って接続してやります。プルアップ抵抗は2.2kΩにしています。

ピン配置です(写真と合わせてあります)。
 5 SDA  4 Vss
 6 SCL  3 INT
 7 NC   2 CLKOUT
 8 Vdd  1 CLKOE

CLKOUTは設定で周期を変えれますが、今回は1Hzのパルスを出力しています。CLKOUTをAVRの外部割込み入力INT0につないでいます。割り込みが起きるたびにRTC-8564NBから時間を読み取ってLCDに表示しています。


ソースです。
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/twi.h>
#include <util/delay.h>
#include "lcd.h"
#include "i2c.h"

#define RTC8564 (0x51 << 1)

#define CTRL1 0x00
#define CTRL2 0x01
#define SEC   0x02
#define MIN   0x03
#define HOUR  0x04
#define DAY   0x05
#define WDAY  0x06
#define MONTH 0x07
#define YEAR  0x08
#define CKOUT 0x0d // CLKOUT frequency 7:FE 1:FD1 0:FD0
#define CTRLT 0x0e // Timer Control 7:TE 1:TD1 0:TD0

// RTC8564にコマンドを送る
void write(uint8_t address, uint8_t data)
{
    i2c_start(RTC8564 | TW_WRITE);
    i2c_write(address);
    i2c_write(data);
    i2c_stop();
}

// 曜日の名前
const char *wdname[7] = {
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
};

// 割り込み処理ルーチン (see iomx8.h)
ISR(INT0_vect)
{
    uint8_t year, month, day, wday, hour, min, sec;

    // RTC-8564の読み込み
    i2c_start(RTC8564 | TW_WRITE);
    i2c_write(SEC);
    i2c_stop();
    i2c_start(RTC8564 | TW_READ);
    sec   = i2c_read_ack() & 0x7f; // 02 秒
    min   = i2c_read_ack() & 0x7f; // 03 分
    hour  = i2c_read_ack() & 0x3f; // 04 時
    day   = i2c_read_ack() & 0x3f; // 05 日
    wday  = i2c_read_ack() & 0x07; // 06 曜日
    month = i2c_read_ack() & 0x1f; // 07 月
    year  = i2c_read_nak();        // 08 年
    i2c_stop();

    // 表示
    lcd_locate(0, 0);
    lcd_hex2(0x20);
    lcd_hex2(year);
    lcd_putc('/');
    lcd_hex2(month);
    lcd_putc('/');
    lcd_hex2(day);

    lcd_locate(0, 1);
    lcd_hex2(hour);
    lcd_putc(':');
    lcd_hex2(min);
    lcd_putc(':');
    lcd_hex2(sec);

    lcd_locate(0, 2);
    lcd_puts(wdname[wday]);
}

int main()
{
    lcd_init();
    i2c_init();
    _delay_ms(1000); // 1秒待つ

    // 外部割込み初期化
    EICRA = _BV(ISC01) | _BV(ISC00); // INT0立ち上がりエッジ
    EIMSK = _BV(INT0);  // INT0割り込み許可

    // RTC8564初期化
    write(CTRL1, 0x20); // stop
    write(CTRL2, 0x00);
    write(CTRLT, 0x00);
    write(CKOUT, 0x83); // 7:FE=1 10:FD=11 (1Hz)
    write(YEAR,  0x08); // 年 (下位2桁)
    write(MONTH, 0x10); // 月
    write(DAY,   0x12); // 日
    write(HOUR,  0x23); // 時
    write(MIN,   0x59); // 分
    write(SEC,   0x50); // 秒
    write(WDAY,  0x00); // 曜日
    write(CTRL1, 0x00); // start

    sei();              // 割り込み許可
    while(1){
        sleep_enable(); // CPUスリープ
        sleep_cpu();
        sleep_disable();
    }
}

時間の調整機能がないので、時計としては不完全です。
データはBCDなので、表示するのに割り算がいりません。読み出したデータの上位にはゴミがついているのでちゃんとマスクしてやらないといけません。

今回はじめて使ったのは外部割込みとCPUのスリープです。

勝手にカウントアップしてくれるし、60進数のこととか考えなくても読み出すだけで時計になっているので簡単です。