AVRで動かすWebサーバ


はじめに

パソコン関係のジャンク屋に行くと,中古のPCMCIA型(正式名称はPCカードですが,ここではPCMCIAと呼ぶことにします)のLANカードがしばしば売られています.たいていは,10BASE-T用でしかも専用ケーブル(カプラ)が付属していませんが,1枚10~100円という安値で出ていたりします.
10BASE-T(10Mbps)という時代遅れの性能でしかもカプラがないので普通の人にとってはゴミかもしれませんが,電子工作でAVR等のマイコンと組み合わせて使うにはとても便利なデバイスのように見えます.
10Mbpsという速度は8ビットのマイコンにとっては十分な速度で,さらにイーサネットの優れた互換性のおかげで,スイッチングHUBを介せば100BASE-Tや1000BASE-TのNICを持った機器とそのまま通信できます.PCMCIAのLANカードはノートPC用に設計されているため,組み込みに便利な小さなサイズで,また消費電流もカードによりますが数十mA程度です.通常マイコンをLANにつなごうとすると,コントローラIC,パルストランス,MACアドレス書き込み済みのEEPROM等を購入して配線する必要がありますが,それらが全て1つに収まっています.PCMCIAカードは,カードにもよりますが,8本のデータバスと少数のアドレスバスや信号線をつなげばマイコンから制御することができます.カプラが付いてなくても,分解して自分でケーブルをつなげば使うことができます.
マイコンをLANにつなぐと色々と面白いことができそうです.Webサーバの機能を持たせることでWebブラウザを使ってデータの入出力や機器の設定を行えるようにしたり,センサ情報によりメールを自動送信したり,NTPサーバを利用して正確な時刻を取得したり,Webラジオを作ったり,など様々な応用が考えられます.

ただしネットワークを利用する各種のサーバやクライアントを作るには,TCP/IPの面倒なプロトコルを実装する必要があります.プロトコルスタックを自作するとなると大変ですが,このページuIPというフリーの実装があることを知りました.このuIPは8ビットのマイコンでも動かすことができるコンパクトなオープンソースのソフトウェアです.
今回は,フラッシュROMが8kバイト,RAMが1kバイトというLANにつなぐには比較的小規模なマイコンといえるATmega8の上で,uIPを使ってWWWサーバを動かし,WebブラウザからLEDのON/OFFを制御したりスイッチのON/OFFの状態を読み取ることができるシステムを作ってみました.LEDやスイッチをリレーやセンサに変える応用も容易にできると思います.

LANカード(PCMCIA)のカプラの修理

もしLANカードに専用の接続ケーブル(カプラ)が付いていない場合は,LANカードを改造します.10BASE-Tの信号はTX+,TX-,RX+,RX-の4本あるので,この4つの信号をカードから引っ張り出してきて,RJ-45コネクタに配線します.
LANカードの改造はWeb上にいくつか情報があり,例えば3ComのEhterLink IIIというカードの場合はこちらのページでピン配置等の情報が解説されています.
基本的に,まずPCMCIAカードを分解して,基板上から4つの信号をリード線に半田付けして引き出します.分解するときにケースを変形させてしまうと,ケースに収めた後でノートPCのカードスロットに入らなくなったりするので注意が必要です(PCで使う予定がなければ気にしないでよいですが).
カードからリード線のまま引き出してもよいのですが,自分はコネクタ(SILタイプのICソケット)を取り付けました.カプラを取り付けた状態でケーブルを引っ張ってもすぐに抜けるので,コネクタ側に力が加わってダメージを受けることはないと思います.
LANカードの改造例 LANカードの改造例  カプラ カプラ
このコネクタの結線は,誤挿入を防止するためピン1つをつぶして非対称の配置としました.ただし後から考えると,わざと対称的で逆挿しできる配置にしておいて,逆に挿入した場合はストレートケーブルではなくクロスケーブル結線となるようにしても良かったかもしれません.

カードの型番が違えばピン配置も異なっているので信号を引き出す方法が変わってきますが,LANカードの場合は4つしか信号が無いので改造は簡単だと思います.パルストランスを見つけてそこからの配線をたどると信号を見分けやすくなります.

ハードウェア

今回,PCMCIAのLANカードとしては,3Com社の3C589D(EhterLink III)というカードを使いました.このカードは5Vで動作します.ジャンク屋で安く売っていた時に買い込みました.
大量に買い込んだLANカード 大量に買い込んだLANカード
一般的に,LANカードが違えばその制御方法は異なるので,専用のデバイスドライバを書く必要があります.例えば,NE2000互換の仕様を持つLANカードなどには,今回作成したデバイスドライバは使えません.
3Com社のカードでも,3C589Bや3C589Cは3C589Dと互換性があるので利用可能だと思いますが,無印の3C589は8ビットのアクセスモードを備えていないので使えないと思います.
このLANカードの制御方法については,このドキュメント(3c589d.zip)が参考になりました.

回路図は次のとおりです:
回路図 回路図(PDF)
このPCMCIAカードは,D0-7,A0-3,CE1#,WE#,IORD#,IOWR#の16本の信号をマイコンとつなげば制御できます.データのやりとりは8ビットバスモードを利用して行うので,必要なデータバスは8本だけです.IOポートアドレスは4ビットしかないので,マイコンとつなぐアドレスバスはA0-3の4本だけです.ただし,A16はプルアップ,A4-10はプルダウンしておく必要があります.それ以外のアドレスバスのピンは,このカードではデコードされていないようだったので何も接続していません.
PCMCIAカードとの接続は,今回はソケット使わずリード線を直接カードの端子に差し込むという簡易的な方法を用いました.
AVRの余った端子には,4つのLEDと,2つのスイッチ(ジャンパピン用のピン端子)をつなぎました.
完成写真  完成写真
上が完成した写真です.
この小さい基板の上でWebサーバを動かします.

ソフトウェア

前述したとおり,uIPというソフトを利用してTCP/IPを扱います.
uIPはバージョン1.0が最新版ですが,ここではコードサイズの小さなバージョン0.6を使いました.
uIPでは,TCP/IPのパケットを処理する基本的な部分はCで書かれているのでavr-gccでそのままコンパイルできますが,デバイス依存部分,つまりLANカードの制御を行うデバイスドライバの部分は自分で書く必要があります.またWebサーバにする場合,静的なデータを表示するだけであれば付属のhttpdプログラムがそのまま使えますが,CGIを使って動的な振る舞いを実現したい場合はやはり自分でコードを書く必要があります.

LANカードの制御を行うために実際に書かなければいけないのは,次の3つの関数です:

初期化関数
デバイスの初期化を行います.例えば,LANカードのMACアドレスをEEPROMから読み込んで設定したりします.AVRのポートの設定等もここで行いました.
MACフレーム受信関数
送られてきたMACフレームを受信して返します.0.5秒経過してもデータを受信していない場合は,受信したデータ数が0バイトだったとして親関数に戻らなければなりません.uIPでは割り込みを使わずにポーリングで処理するので,このように一定時間が経ったら処理を別の関数に移す必要があります.
MACフレーム送信関数
与えられたMACフレームを送信します.

上記のMACフレーム受信関数や送信関数で扱うMACフレームは,uIPでは先頭のプリアンブルと末尾のFCSは無い状態でやりとりされます.3C589Dの場合,それらの情報はハードウェア側で付与されるのでデバイスドライバでの処理は必要ありません.ただし,3C589Dの送信時のチェックサム自動生成機能と受信時のチェックサム自動検証機能を有効にする必要があります.

PCMCIAの基本的な扱い方(ピン配置や信号の意味,タイミング等)は「PCカード/CFカードの徹底研究(CQ出版社)」という本が参考になりました.
3C589Dの場合は,0x10000番地からCCR(カードコンフィギュレーションレジスタ)が配置されていて,0x10000番地がCCOR(カードコンフィグレーションオプションレジスタ),0x10002番地がCCSR(カードコンフィグレーションステータスレジスタ)となっています.つまり,PCMCIAのレジスタにアクセスする際は,A16ピンをHにして,A1ピンで書き込むレジスタを選択して,それ以外のアドレス線はLにしておきます.
このLANカードの場合,まずCCORの7ビット目を1にした後で0にしてカードのリセットを行います.次にCCSRの5ビット目を1にして8ビットでのデータ転送を有効にして,CCORの0ビット目を1にしてI/Oアクセスを有効にします.
その後は,LANカード固有のレジスタにアクセスして設定を行いますが,詳細は前述の資料を参考にしてください.
デバイスドライバのMACフレーム受信関数では,0.5秒経った場合は処理を中止すると書きましたが,そのためには0.5秒の時間を測定する必要があります.AVR内蔵のタイマーを使っても良かったのですが,クロック周波数によりパラメータが変わるのと,AVR以外のマイコンを使った場合の移植性などを考え,LANカードに内蔵のタイマーを使うことにしました.
3C589Dは3.2usの分解能を持った255カウントのタイマを持っていますので,これを利用します.このタイマは何らかの割り込みが発生すると0からカウントを始めるので,"Interrupt Reqested"というユーザーが自由に使える割り込みだけを許可してタイマの制御に利用しています.3.2usの分解能だと2バイトの符号無し整数では0.5秒をカウントできないので(0.5s/2.3us>65535),タイマのカウント数を4で割って処理しています.

デバッグ

今回はデバッグにとても苦労しました.
もともとこの作品は2年ほど前に一度作りかけて,ハードはそのとき完成しましたが,ソフトがうまく動作せずそのまま放置していました.
AVRのUART端子をデバッグ用に残しておかなかったため,ハードウェアの状態をモニタできずデバッグが不可能でしたが,これは設計ミスでした.
幸いスイッチ入力用に用意しておいた2つのポートがあったので,片方をソフトウェアUARTの送信用,もう片方をPCMCIAのOE#信号用として使いました.PCMCIAのアトリビュートメモリを読む必要はないのでOE#端子はマイコンに接続していませんでしたが,デバッグ時にPCMCIAとマイコンとのやりとりが正しく行われているかどうかをチェックするためにはあると便利です.
ソフトウェアUARTと並んで大変重宝したのがパケットモニタソフトです(linuxのtcpdumpやWindowsのMicrosoft Network Monitor).これらの2つがなければデバッグは不可能でした.
デバッグは以下の手順で行い順番に動くようにしていきました:

  1. PCMCIAレベルでの動作確認
    PCMCIAのアトリビュートメモリにあるCISの読み込みが正常にできるかの確認.CCRへの書き込みを行い,書き込んだ値が正常に読み出せるかの確認.
  2. LANカードのレベルでの動作確認
    ベンダーIDや各種ステータスのチェック.特にMACアドレスがちゃんとEEPROMから読み出せて,正しく設定できているかの確認.
  3. ARPの動作確認
    ブロードキャストや自分のMACアドレス宛のパケットを読み込めているかどうか(ここまではuIP以前のハードウェアとデバイスドライバ側の責任).正しい応答を返して他のマシンからアドレスを解決できているかの確認.
  4. PING(ICMP)に応答するかの確認
  5. http(TCP)で通信できるかの確認
    コネクションの確立,データやACKのやりとり等の確認.
    uIPがアクセスしてきたホストからのACKを認識しないという問題があって,はまりました.原因は,MACフレームの最低長は46オクテットと定められており,これより小さいデータはパディングされて46オクテット以上の長さになりますが,その場合にデバイス側が受信したデータサイズとIPパケットに書かれたデータサイズが一致しないため,エラーが起きたとみなしてパケットを捨てていたためでした.

ファームウェア

このファームウェアを使う場合,ファイルを展開したらまずavr/というディレクトリに移り,そこにあるuipopt.hで指定されているMACアドレスを実際に使用するものに書き換えます.必要に応じてIPアドレス等も変更します.その後makeを実行するとコンパイルされて,uip.hexというバイナリファイルが生成されます.
ファームのサイズは7,826バイトです.

完成

今回実装したWebサーバでは,2つのページにアクセスできます.
"/test.html"は,テスト用のページです.
テスト用ページ  テスト用ページ

"/io.cgi"は,LEDの制御やスイッチの状態表示を行うページです."/index.html"へアクセスしてもこのページにジャンプします.
LEDのON/OFFを設定してフォームを送ると,基板上のLEDに設定が反映されます.また,スイッチの状態も表示されます.
IO制御用ページ  IO制御用ページ IO制御の様子  LED制御の様子

RAMとフラッシュの容量を節約するため最低限必要なHTMLタグしか使用していないので,ブラウザによってはうまく表示できない可能性があります(FirefoxとIEでは動作確認しています).

回路全体の消費電流は,通信を行っていない状態で38mAでした.
次は,何かTCP/IPのクライアントを作ってみたいと思います.


[戻る]
2009-01-16 ページ作成
(2009-01 製作)
T. Nakagawa