これまでブレッドボードに載せていたGLCDをユニバーサル基板を使ってArduinoのシールド調にしてみました。
構成
以下の部品を載せるために、ちょっと大きめですがサンハヤト ICB-97GHD(95x135mm)を使用しました。GLCDは8-bitパラレルバスで必要なピン数が多いため、Arduino Megaを使用しています。
Arduino純正のEthernet Shieldでよく指摘されていますが(Under Power 研究所さんの参考記事)、ArduinoのRESETピンは5VにプルアップしてあるだけでEthernet Shieldで使用しているW5100 TCP/IP処理チップのパワーオンリセット(2μs以上Lowレベルに落とす必要がある)に使えません。そのため、リセットICを使用して3.3V系のパワーオンリセットリセットパルスを作っています。
回路図を以下に示します。
外観の写真はこんな感じです(漢字表示を行っていますが、こちらは別途続編を投稿します)。W5100チップと3端子レギュレータは熱対策としてヒートシンクを貼り付けています(W5100はそれほど気にする必要はないのでしょうが気休めです)。
裏面の写真です。ピンヘッダを使って、Arduinoを裏側から接続します。
工作テクの限界で配線が汚いです。リセットIC(米粒大)の半田付けは結構苦戦。半田の熱でチップのパッケージが溶けそうになりました、、本当に動いているのか怪しいところがありますが、WIZ812MJが正しく起動しているので多分大丈夫でしょう。
AT45DB161D Data Flashの接続
前回の記事「Arduinoで漢字表示(3)」からの変更点を以下に記載します。WIZ812MJとSPI接続を共有するための変更です。
1)SPIのSlave Select(SS)ピンをPL0に移動しました。ATMega1280のSSピン(PB0)はWIZ812MJ(Ethernet Module)で使用します。SS信号はソフト制御のためどのピンに移動してもよいと思ったのですが、単にピンアサインをPL0に変更しただけでは動いてくれませんでした。Arduino Forumを漁ってみると、オリジナルのPB0をHighに設定してからSPIレジスタ(SPCR)の設定をせよとあります。確かにこの方法で動作します。
2)オリジナルのat45db161dライブラリでは、SPI mode 3を使っています。Ethernetライブラリにあわせてmode 0に変更しました。(WIZ812MJはEthernetライブラリがそのまま使えます)
3)オリジナルのat45db161dライブラリでは、各関数の入り口でSS信号をHigh→Low(active)にトグルしてデバイスをたたいた後、SSをLowのままで放置しています。他のSPIデバイスと共存するためには、アクセス終了後SS信号をHigh(inactive)にする必要があります。
at45db161dライブラリでは、ContinuousArrayRead()関数などでデバイスにコマンドを送った後、引き続きspi_transfer()を呼び出すことでデーターの転送を行うため、この一連のアクセスの中でデバイスをactive状態に保持する必要があります。すなわち、ContinuousArrayRead()関数の出口でSSをinactiveにできません。
at45db161dライブラリには、EndAndWait()という関数があり一連のアクセス終了後この関数を呼んでSSをinactiveにすればよさそうです。EndAndWait()でもSSをactiveのまま放置しているため、inactiveにするコードを追加しました。
4) ComparePageToBuffer()のバグ修正(前回と同様の内容を再掲)
at45db161dライブラリ(オリジナルはBlokoSさんのブログ)の変更点を以下に示します。
<at45db161d.h>
#ifndef AT45DB161D_H
#define AT45DB161D_H
#include <WProgram.h>
extern "C" {
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "WConstants.h"
};
#include "at45db161d_commands.h"
#ifndef SPI
#if defined(__AVR_ATmega1280__)
#define DATAOUT 51
#define DATAIN 50
#define SPICLOCK 52
#define SLAVESELECT 49 // 53(PB0)→ 49(PL0)に変更
#define SPI_SS 53
#define RESET 8
#define WP 7
#else
#define DATAOUT 11
#define DATAIN 12
#define SPICLOCK 13
#define SLAVESELECT 10
#define RESET 8
#define WP 7
#endif
・・以下略・・
<at45db161d.cpp>
void ATD45DB161D::Init()
{
uint8_t clr;
/* Initialize pinout */
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK, OUTPUT);
pinMode(SLAVESELECT, OUTPUT);
pinMode(DATAIN, INPUT);
/* Disable device */
DF_CS_inactive; // Deactivate alternative SS
#if defined(__AVR_ATmega1280__)
digitalWrite(SPI_SS, HIGH); // Deactivate original SS (Must be HIGH befor SPCR set)
#endif
/* Setup SPI mode=0 */
//SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPOL) | (1 << CPHA);
SPCR = (1 << SPE) | (1 << MSTR);
/* Cleanup registers */
clr = SPSR;
clr = SPDR;
}
void ATD45DB161D::EndAndWait()
{
DF_CS_inactive; /* End current operation */
DF_CS_active; /* Some internal operation may occur
* (buffer to page transfer, page erase, etc... ) */
/* Wait for the chip to be ready */
while(!(ReadStatusRegister() & READY_BUSY))
{}
DF_CS_inactive; /* Release SPI Bus */
}
int8_t ATD45DB161D::ComparePageToBuffer(uint16_t page, uint8_t bufferNum)
{
uint8_t status;
DF_CS_inactive; /* Make sure to toggle CS signal in order */
DF_CS_active; /* to reset Dataflash command decoder */
/* Send opcode */
spi_transfer((bufferNum == 1) ? AT45DB161D_COMPARE_PAGE_TO_BUFFER_1 :
AT45DB161D_COMPARE_PAGE_TO_BUFFER_2);
/* Page address */
spi_transfer((uint8_t)(page >> 6));
spi_transfer((uint8_t)(page << 2));
spi_transfer(0x00);
DF_CS_inactive; /* Start comparaison */
DF_CS_active;
/* Wait for the end of the comparaison and get the result */
while(!((status = ReadStatusRegister()) & READY_BUSY))
{}
if ( status & COMPARE )
return 0; /* If (COMPARE bit = 1) then Error */
else
return 1;
}
WIZ812MJの接続
1)電源系統
WIZ812MJは3.3V動作かつ200mA弱電力を喰うため、3.3Vボルテージレギュレーターから給電します。ボルテージレギュレーターの入力は、VIN(ACアダプターからの給電)を使用します。5Vから電源を取るとArduino本体のボルテージレギュレーターに結構電流が流れて発熱が大きくなるためです。Data Flashも3.3V動作のためこの電源を使います。
2)MISO信号の接続
AT45DB161Dと同様に、入力は5V許容のため、SCLK, MOSI, SCS(SS)はArduinoと直結します。悩んだのはMISO(WIZ812MJ側は3.3V出力)です。Ethernet shieldではMCUと直結になっています。また、Ethernetライブラリを見ると、MISOをつなぐ入力ピンの内蔵プルアップ抵抗を有効にしています。以下に、hardware\libraries\Ethernet\utility\spi.hの抜粋を示します(コメントは私が追記)
// Set DDR -> SS:output, SCLK: output, MOSI: output. Others: input (default)
// Set PORTB -> SS_HIGH
// Set PORTB -> Input pin pull-up (MISO)
#define SPI0_Init() DDRB |= SPI0_SS_BIT|SPI0_SCLK_BIT|SPI0_MOSI_BIT;\
PORTB |= SPI0_SS_BIT; PORTB &= ~(SPI0_SCLK_BIT|SPI0_MOSI_BIT);\
SPCR = 0x50
MCUのMISOピンを5VにプルアップしてしまえばHighレベルの閾値電圧の問題はなくなりますが、SPIデバイス側で問題が出ないかです。WIZ812MJに載っているW5100 TCP/IPチップはVIN(DC Input Voltage)を5.5Vまで許容しているため、MISOを5Vにプルアップしても問題なさそうです。
一方AT45DB161D Data Flashの方は、出力を5Vにプルアップできるという記載はありませんでした。そのため、AT45DB161Dはレベルシフターをかませて3.3V→5V変換を行っています。レベルシフターのピンが余っていたのでWIZ812MJ側もレベルシフターを介して接続しています(W5100チップの5V入力許容に気がつく前に、レベルシフター経由で配線してしまったというのが実情ですが、、)。
(2009/7/22追記)
複数のSPIデバイスが存在する場合、MISO信号をMCUの手前で束ねる必要があります。このために、レベルシフターの5V出力側で、WIZ812MJとAT45DB161DのMISO信号をワイヤードオアしています。レベルシフターの5V出力側はMOS FETを使っており、オープンドレインのワイヤードオア回路相当になっているので出力をぶつけても問題ないかと思っています。AT45DB161DはSSがinactiveになるとMISOをtri-state(ハイインピーダンス)にするという記述がデーターシートにあります。複数デバイスのの出力を接続する場合は以下の方法がありますが、SPIは(b)の形態になるようです(I2Cはaでしょうか);
(a)各デバイスのオープンドレインをワイヤードオア(プルアップ要)
(b)各デバイスで出力ピンのハイインピーダンス制御を行ってバス接続
今にして思えば、WIZ812MJとAT45DB161DのMISOを3.3V側で束ねてやるのが正しい設計のように思えます。とりあえず動いていますが、どこかで修正して動作確認してみます。
(2009/7/31追記)
WIZ812MJとAT45DB161DのMISOを3.3V側で束ねる形態に回路を変更。問題なく動いています。回路図も修正しました。
(2009/8/15追記)
Arduino Forumにて次のスレッドを見つけました。
Arduino + Ethernet Shield + Another SPI device
W5100チップにてMISOをハイインピーダンスにするためには、SS(SCS)の制御でなくSEN(SPI enable)信号をLowレベルに落としてやる必要があるとのことです(データーシートには記載がないのですが)。WIZ812MJはSS信号をモジュール内で反転してSENに入力しているため、MCUからSSをInactive(High)にすると自動的にSENがLowに落ちてくれます。そのため、WIZ812MJではSS信号に連動してMISOをハイインピーダンスにすることができるため、他のSPIデバイスとの混在が可能です。
Official Ethernet ShieldではSENをプルアップしているのみです。そのため、Official Ethernet ShieldではMISOをハイインピーダンスにすることができず、他のSPIデバイスと混在して使用する際は注意が必要です。
3)Ethernet Libraryの修正
Arduino IDE添付のライブラリはATMega168/328用です。Arduino Megaが使用しているATMega1280はSPI関連のピン収容が異なるためそのままでは動作しません。hardware\libraries\Ethernet\utility\spi.hを以下のように修正することでArduino Megaでも動作するようになります。
//------------------------------------------------------------------------
//AVR Mega168/1280 SPI HAL
#define BIT0 0x01
#define BIT1 0x02
#define BIT2 0x04
#define BIT3 0x08
#define BIT4 0x10
#define BIT5 0x20
#define BIT6 0x40
#define BIT7 0x80
#if defined(__AVR_ATmega1280__) //Arduino Mega用ピン収容
#define SPI0_SS_BIT BIT0
#define SPI0_SS_DDR DDRB
#define SPI0_SS_PORT PORTB
#define SPI0_SCLK_BIT BIT1
#define SPI0_MOSI_BIT BIT2
#define SPI0_MISO_BIT BIT3
#else
#define SPI0_SS_BIT BIT2
#define SPI0_SS_DDR DDRB
#define SPI0_SS_PORT PORTB
#define SPI0_SCLK_BIT BIT5
#define SPI0_MOSI_BIT BIT3
#define SPI0_MISO_BIT BIT4
#endif
#define SPI0_SCLK_DDR DDRB
#define SPI0_SCLK_PORT PORTB
#define SPI0_MOSI_DDR DDRB
#define SPI0_MOSI_PORT PORTB
#define SPI0_MISO_DDR DDRB
#define SPI0_MISO_PORT PORTB
#define SPI0_WaitForReceive()
#define SPI0_RxData() (SPDR)
#define SPI0_TxData(Data) (SPDR = Data)
#define SPI0_WaitForSend() while( (SPSR & 0x80)==0x00 )
#define SPI0_SendByte(Data) SPI0_TxData(Data);SPI0_WaitForSend()
#define SPI0_RecvBute() SPI0_RxData()
// PB4(MISO), PB3(MOSI), PB5(SCK), PB2(/SS) // CS=1, waiting for SPI start // SPI mode 0, 4MHz
// Set DDR -> SS:output, SCLK: output, MOSI: output. Others: input (default)
// Set PORTB -> SS_HIGH
// Set PORTB -> Input pin pull-up (MISO)
#define SPI0_Init() DDRB |= SPI0_SS_BIT|SPI0_SCLK_BIT|SPI0_MOSI_BIT;\
PORTB |= SPI0_SS_BIT; PORTB &= ~(SPI0_SCLK_BIT|SPI0_MOSI_BIT);\
SPCR = 0x50
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//IInChip SPI HAL
#define IINCHIP_SpiInit SPI0_Init
#define IINCHIP_SpiSendData SPI0_SendByte
#define IINCHIP_SpiRecvData SPI0_RxData
#if defined(__AVR_ATmega1280__)
#define IINCHIP_CS_BIT BIT0
#define IINCHIP_CS_DDR DDRB
#define IINCHIP_CS_PORT PORTB
#else
#define IINCHIP_CS_BIT BIT2
#define IINCHIP_CS_DDR DDRB
#define IINCHIP_CS_PORT PORTB
#endif
#define IINCHIP_CSInit() (IINCHIP_CS_DDR |= IINCHIP_CS_BIT)
#define IINCHIP_CSon() (IINCHIP_CS_PORT |= IINCHIP_CS_BIT)
#define IINCHIP_CSoff() (IINCHIP_CS_PORT &= ~IINCHIP_CS_BIT)
//------------------------------------------------------------------------
その他
at45db161d, Ethernet libraryの両方を使って、漢字表示(SPI Data Flash memoryへのアクセス)とTCP/IP接続の併用ができるようになりました。
GLCDはピンソケットを使いユニバーサル基板面から持ち上げて実装しているため、GLCDの下側に広大な敷地があります。この敷地に生AVR(ATMega644あたり)を載せてしまえば、高価なArduino Megaを占有せずに回路が組めそうです。最終目的のRSSリーダー用ファームができるまでは頻繁にライブラリやスケッチの更新を行うためMegaを使うとして、安定版ができたら、ATMega644 + SanguinoにスイッチしてMegaを浮かせるという道もありそうです。
あと、コストパフォーマンスを考えると、ストロベリー・リナックスさんで販売しているWIZ200Webという選択肢もおもしろそうです。W5300とATMEGA128,フラッシュROM(4Mビット),外部RAM(256Kビット)がついて4300円と大変お得です。TCP/IPチップがW5300のため、Arduino Ethernet libraryはそのままでは使えないと思いますが。
2009/7/22追記:
MISO信号のバス接続に関する記述を追加。
2009/7/31追記:
MISO信号の接続を変更。
最近のコメント