MSP430F5529 - イーサネット (W5500)

2025年12月21日 (日) 19:28時点におけるWiki (トーク | 投稿記録)による版 (ページの作成:「== 概要 == MSP430F5529は、USB機能を内蔵した16ビットRISCマイコンである。<br> しかし、イーサネット機能は内蔵していないため、TCP/IP通信を行うには、外付けのイーサネットコントローラICが必要となる。<br> <br> W5500は、WIZnet社が開発したハードウェアTCP/IPスタック内蔵型イーサネットコントローラで、TCP/IPプロトコルの処理を全てIC内部で実行する。<br>…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)

概要

MSP430F5529は、USB機能を内蔵した16ビットRISCマイコンである。
しかし、イーサネット機能は内蔵していないため、TCP/IP通信を行うには、外付けのイーサネットコントローラICが必要となる。

W5500は、WIZnet社が開発したハードウェアTCP/IPスタック内蔵型イーサネットコントローラで、TCP/IPプロトコルの処理を全てIC内部で実行する。
このため、マイコン側ではソフトウェアTCP/IPスタック (lwIPやuIP等) を用意する必要がなく、シンプルなSPI通信によるソケットAPI呼び出しだけでTCP/IP通信が実現できる。

主な構成は以下の通りである。

  • MSP430F5529 (マイコン)
  • W5500 (ハードウェアTCP/IPスタック内蔵イーサネットコントローラ)
  • SPIインターフェース


この構成により、MSP430F5529の限られたRAM (8[KB]) でも、複雑なTCP/IPスタックを実装することなく、Webサーバ、HTTPクライアント、TCPソケット通信、UDPアプリケーション等を容易に実装できる。


ENC28J60 と W5500

イーサネット通信を実現するには、大きく分けて2つの階層がある。

  • 上位層
    ネットワーク層以上で、TCP/IPプロトコルの処理を行う。
  • 下位層
    物理層 および データリンク層 で、イーサネットフレームの送受信を担当する。


ENC28J60は下位層のみを持つICである。
イーサネットフレームの送受信はできるが、TCP/IPプロトコルの処理機能は持たない。
これを イーサネットMAC/PHYコントローラ と呼ぶ。
このため、ENC28J60を使用する場合は、マイコン側でuIPやlwIP等のソフトウェアTCP/IPスタックを実装する必要がある。

一方、W5500は、上位層および下位層の両方をIC内に持つため、TCP/IPの複雑な処理も含めて全てW5500が自動的に処理する。
これを ハードウェアTCP/IPスタック内蔵型 と呼ぶ。
マイコンは、SPIインターフェースを通じてソケットレジスタを操作するだけで、TCP/IP通信が可能となる。

W5500の主なメリット

  • マイコンのRAM/ROM使用量が大幅に削減される。
    ソフトウェアTCP/IPスタック (uIP、lwIP) が不要
    通常、uIPで15〜25[KB]のROM、4〜8[KB]のRAMが必要だが、W5500では数[KB]のドライバコードのみで済む。
  • CPU負荷が大幅に軽減される。
    TCP/IPプロトコル処理がハードウェアで実行されるため、マイコンはアプリケーション処理に集中できる。
  • 実装が容易
    ソケットAPIが提供されており、Berkeley Socketに似たインターフェースで開発できる。
    プロトコルスタックのデバッグやチューニングが不要
  • 高速通信
    100Mbps対応 (ENC28J60は10Mbpsのみ)
    SPIクロックは最大80[MHz] (ENC28J60は最大20[MHz])
  • 複数同時接続
    8つの独立したソケットを同時使用可能
    Webサーバ、クライアント、UDPを同時に動作させることも可能


W5500のデメリット

  • 価格がやや高い
    ENC28J60と比較して部品コストが高い。
  • 柔軟性の制限
    プロトコルスタックがハードウェア実装のため、カスタマイズに制限がある。
    ただし、一般的なアプリケーションでは問題にならない。


総合的に見ると、MSP430F5529のようなRAM容量が限られたマイコンでは、W5500を使用することで開発期間の短縮、安定性の向上、メモリ使用量の削減が期待できる。


W5500イーサネットコントローラ

WIZnet社のW5500は、ハードウェアTCP/IPスタック内蔵型イーサネットコントローラで、組み込みシステムにおけるイーサネット実装を大幅に簡素化する。

  • IEEE 802.3準拠の10/100BASE-TX Ethernetコントローラ
  • ハードウェアTCP/IPプロトコルスタック内蔵
    TCP、UDP、IPv4、ICMP、ARP、IGMP、PPPoE対応
  • SPIインターフェース (最大80[MHz])
  • 内蔵32[KB]のバッファメモリ
    各ソケットに独立したバッファを割り当て可能
  • 8つの独立したハードウェアソケット
    TCP、UDP、MACRAW、PPPoEモードに対応
  • MACおよびPHY機能を統合
  • パケットフィルタリング機能
    ブロードキャスト、マルチキャスト、ユニキャストフィルタ
  • Wake-on-LAN機能
  • 低消費電力設計
    パワーダウンモード、省電力モード対応
  • 3.3[V]単一電源動作
  • 48ピンLQFP、QFNパッケージ


メリット

  • ソフトウェアTCP/IPスタック不要
    マイコンのRAM/ROM使用量を大幅に削減
  • SPIインターフェースのため、配線が簡単
    4本の信号線 (MOSI、MISO、SCLK、CS)
  • 100Mbps対応で高速通信が可能
  • 豊富なサンプルコードとライブラリ
    WIZnet公式ライブラリ (ioLibrary_Driver) が利用可能
  • MSP430F5529のUSART (SPI) で容易に接続可能
  • 複数のソケットを同時使用可能
    1つのICで複数のTCP/UDP接続を処理


デメリット

  • ENC28J60と比較して価格が高い
  • パッケージがやや大きい (48ピン)
    ただし、外付け部品は少ないため、実装面積は大きくならない


W5500は、その使いやすさと高機能性から、産業機器、IoTデバイス、ホームオートメーション等、幅広い分野で採用されている。
特に、MSP430F5529のようなRAMが限られたマイコンとの組み合わせでは、W5500のハードウェアTCP/IPスタックが大きなアドバンテージとなる。


W5500とuIP/lwIPの比較

下表に、W5500を使用する場合と、ENC28J60 + uIP/lwIPを使用する場合の比較を示す。

W5500 vs ENC28J60 + uIP/lwIP
項目 W5500 ENC28J60 + uIP ENC28J60 + lwIP
ROM使用量 5〜10KB 15〜25KB 40〜60KB
RAM使用量 1〜3KB 4〜8KB 20〜40KB
CPU負荷
実装難易度
通信速度 100Mbps 10Mbps 10Mbps
同時接続数 8 1〜10 2〜50
部品コスト
カスタマイズ性


MSP430F5529のような限られたRAM (8[KB]) を持つマイコンでは、W5500を使用することで以下のメリットが得られる。

  • RAM/ROM使用量の大幅な削減
    ソフトウェアTCP/IPスタックが不要なため、アプリケーション処理に多くのメモリを使用できる。
  • 開発期間の短縮
    ソケットAPIを使用するだけで済むため、TCP/IPスタックの実装やデバッグが不要。
  • CPU負荷の軽減
    プロトコル処理がハードウェアで実行されるため、マイコンはアプリケーション処理に集中できる。
  • 安定性の向上
    ハードウェアTCP/IPスタックは、長年の実績があり、バグが少ない。


一方、ENC28J60 + uIP/lwIPを使用する場合は、以下のメリットがある。

  • 低コスト
    部品コストが低く、大量生産に適している。
  • 高いカスタマイズ性
    プロトコルスタックを自由にカスタマイズできる。
    独自プロトコルの実装や、特殊な通信要件に対応可能。


用途に応じて、適切なソリューションを選択することが重要である。


MSP430F5529の特徴

MSP430F5529は、Texas Instruments社の16ビットRISCマイコンで、USB機能を内蔵した上位モデルである。

MSP430F5529の特徴を以下に示す。

  • 128[KB] フラッシュROM
  • 8[KB] RAM
  • 最大25[MHz]動作
  • USB 2.0フルスピード (12[Mbps]) 対応
  • USCI (UART、SPI、I2C対応)×4モジュール
  • 12ビットADC
  • タイマA/B
  • 80ピン (LQFP)
  • DMAコントローラ
  • 低消費電力設計
  • 内蔵LDO (USB用3.3[V]電源回路)


W5500実装に必要なリソース要件は以下の通りである。

  • フラッシュROM
    約5〜10[KB] (W5500ドライバ+アプリケーション)
    uIP実装 (15〜25[KB]) と比較して大幅に削減
  • RAM
    約1〜3[KB] (バッファ+変数)
    uIP実装 (4〜8[KB]) と比較して大幅に削減


MSP430F5529のリソースは、W5500を使用する場合、十分な余裕を持って実装できる。
ソフトウェアTCP/IPスタックが不要なため、RAMの大部分をアプリケーション処理に使用できる点が大きなメリットである。


ハードウェア接続

MSP430F5529とW5500は、SPIインターフェースで接続する。

下表にMSP430F5529のUSCI_B0をSPIモードで使用する接続例を示す。

ピン接続表 (W5500)
MSP430F5529 W5500 機能 説明
P3.0 MOSI SPI マスター出力/スレーブ入力
P3.1 MISO SPI マスター入力/スレーブ出力
P3.2 SCLK SPI シリアルクロック
P2.7 SCSn 制御信号 チップセレクト
(アクティブLow)
P2.6 RSTn 制御信号 リセット信号
(アクティブLow)
P2.0 INTn 割り込み 割り込み信号
(アクティブLow、オプション)
VCC AVDD
DVDD
電源 3.3V
GND AGND
DGND
GND グランド


※注意

  • 水晶振動子
    W5500には25MHzの水晶振動子が必要 (ピン1、2に接続)
    負荷容量は18pFを推奨
  • デカップリングコンデンサ
    各電源ピン (AVDD、DVDD) 近くに0.1[uF]を配置
    さらに、10[uF]の電解コンデンサを電源ライン全体に配置することを推奨
  • RJ45コネクタ
    トランス内蔵型を使用 (Hanrun HR911105A、Pulse J0011D21BNL等)
    センタータップは、W5500のデータシートに従って適切に接続
  • LEDインジケータ
    W5500はリンクステータスLED、アクティビティLEDの制御ピンを持つ
    必要に応じてLEDを接続 (推奨抵抗値:510[Ω])
  • リセット回路
    W5500のRSTnピンには外部プルアップ抵抗 (10[kΩ]) を接続
    電源投入時の安定動作のため、RCリセット回路を追加することを推奨


SPIインターフェースは、MSP430F5529のP3ポートに配置されているUSCI_B0モジュールを使用する。
このモジュールはハードウェアSPIをサポートしており、効率的な通信が可能である。

W5500のSPIは最大80[MHz]に対応しているが、MSP430F5529の最大動作周波数が25[MHz]であることを考慮すると、実用的なSPIクロックは10〜12.5[MHz]程度 となる。
これでも十分な通信速度が得られるため、問題にはならない。

制御信号 (SCSn、RSTn) および割り込み信号 (INTn) は任意のGPIOピンに接続できる。
割り込み機能を使用する場合は、割り込み対応ピンを選択する必要があるが、ポーリング方式でも十分に動作する。

W5500の電源は3.3[V]であり、MSP430F5529と同じ電圧で動作するため、レベル変換回路は不要である。
ただし、電源の安定性を確保するため、各電源ピン近くに適切なデカップリングコンデンサを配置することが重要である。


W5500ドライバの構成

W5500を使用する場合、WIZnet社が提供する公式ドライバライブラリ (ioLibrary_Driver) を使用することができる。
ただし、MSP430F5529向けには、ハードウェア依存部分 (SPI通信、遅延関数等) を実装する必要がある。

W5500ドライバは、以下に示すような階層構造を持っている。

  • アプリケーション層
    ユーザアプリケーション (Webサーバ、HTTPクライアント等)
  • ソケットAPI層
    socket()、connect()、listen()、send()、recv()等のソケット関数
    Berkeley Socketに似たインターフェース
  • W5500ドライバ層
    レジスタアクセス関数
    ソケットレジスタ、共通レジスタの読み書き
  • SPI通信層
    SPIバイト送受信関数 (ハードウェア依存)
    MSP430F5529のUSCI_B0を使用


主要なソースファイルを以下に示す。

  • w5500.c / w5500.h
    W5500レジスタアクセス関数
    ソケット制御、ネットワーク設定
  • socket.c / socket.h
    ソケットAPI実装
    TCP/UDP通信用の高レベル関数
  • wizchip_conf.c / wizchip_conf.h
    W5500設定と初期化
    バッファサイズ設定、SPI関数登録
  • msp430_spi.c / msp430_spi.h (開発者が作成する部分)
    MSP430F5529用SPI通信実装 (ハードウェア依存)


この階層構造により、W5500のコア機能とハードウェア依存部分が明確に分離されている。
MSP430F5529で使用するには、主にSPI通信の箇所 (msp430_spi.c) を開発する必要がある。

W5500の公式ライブラリは以下から入手可能である。



W5500 : サンプルコード

以下の例では、W5500を使用して、簡単なTCPサーバ (Webサーバ) を構築してHTTPリクエストに応答している。

W5500を使用する場合、ソフトウェアTCP/IPスタック (uIP、lwIP) は不要であり、ソケットAPIを直接呼び出すだけでTCP/IP通信が可能となる。

SPI通信層 (msp430_spi.c)

まず、MSP430F5529のSPI通信を実装する。

この層では、W5500とのSPI通信に必要な基本的な関数を記述する。
W5500のレジスタアクセスは、チップセレクト (SCSn) の制御、コマンド送信、データ送受信の3つのステップで構成される。

 #include <msp430f5529.h>
 #include "msp430_spi.h"
 #include <stdint.h>
 
 // チップセレクト制御
 #define W5500_CS_LOW()   (P2OUT &= ~BIT7)
 #define W5500_CS_HIGH()  (P2OUT |= BIT7)
 
 // SPI初期化 (USCI_B0をSPIモードで使用)
 void spi_init(void)
 {
    // ポート設定
    P3SEL |= BIT0 | BIT1 | BIT2;  // P3.0 = MOSI, P3.1 = MISO, P3.2 = SCLK
    P2DIR |= BIT7;                // P2.7 = CS (出力)
    P2OUT |= BIT7;                // CS初期値をHigh
    P2DIR |= BIT6;                // P2.6 = RST (出力)
    P2OUT |= BIT6;                // RST初期値をHigh
 
    // USCI_B0をSPIマスターモードで初期化
    UCB0CTL1 |= UCSWRST;                         // ソフトウェアリセット有効
    UCB0CTL0 = UCMST | UCSYNC | UCCKPH | UCMSB;  // マスター、同期、位相、MSBファースト
    UCB0CTL1 = UCSSEL_2 | UCSWRST;               // SMCLKを使用
    UCB0BR0 = 2;                                 // クロック分周 (25[MHz] / 2 = 12.5[MHz])
    UCB0BR1 = 0;
    UCB0CTL1 &= ~UCSWRST;                        // ソフトウェアリセット解除
 }
 
 // SPIバイト送受信
 uint8_t spi_transfer(uint8_t data)
 {
    while (!(UCB0IFG & UCTXIFG));  // 送信バッファが空になるまで待機
    UCB0TXBUF = data;              // データ送信
    while (!(UCB0IFG & UCRXIFG));  // 受信完了まで待機
    return UCB0RXBUF;              // 受信データを返す
 }
 
 // W5500チップセレクト (開始)
 void wizchip_select(void)
 {
    W5500_CS_LOW();
 }
 
 // W5500チップセレクト (終了)
 void wizchip_deselect(void)
 {
    W5500_CS_HIGH();
 }
 
 // W5500バイト読み出し
 uint8_t wizchip_read(void)
 {
    return spi_transfer(0x00);
 }
 
 // W5500バイト書き込み
 void wizchip_write(uint8_t wb)
 {
    spi_transfer(wb);
 }
 
 // W5500バースト読み出し
 void wizchip_read_burst(uint8_t* pBuf, uint16_t len)
 {
    while (len--) {
       *pBuf++ = spi_transfer(0x00);
    }
 }
 
 // W5500バースト書き込み
 void wizchip_write_burst(uint8_t* pBuf, uint16_t len)
 {
    while (len--) {
       spi_transfer(*pBuf++);
    }
 }


W5500ドライバ (w5500.c)

次に、W5500の基本的なレジスタアクセス関数を実装する。

W5500のレジスタは、共通レジスタ (ネットワーク設定等) とソケットレジスタ (各ソケット固有の設定) に分かれている。
レジスタアクセスは、SPI経由で行われ、アドレスと制御バイトを送信した後、データを読み書きする。

以下の実装では、簡略化のため、必要最小限の関数のみを示している。
完全な実装には、WIZnet公式ライブラリを参照することを推奨する。

 #include "w5500.h"
 #include "msp430_spi.h"
 #include <string.h>
 
 // W5500共通レジスタアドレス
 #define MR         0x0000  // モードレジスタ
 #define GAR        0x0001  // ゲートウェイアドレス
 #define SUBR       0x0005  // サブネットマスク
 #define SHAR       0x0009  // ソースHWアドレス (MAC)
 #define SIPR       0x000F  // ソースIPアドレス
 #define PHYCFGR    0x002E  // PHY設定レジスタ
 
 // W5500ソケットレジスタアドレス
 #define Sn_MR      0x0000  // ソケットモードレジスタ
 #define Sn_CR      0x0001  // ソケットコマンドレジスタ
 #define Sn_IR      0x0002  // ソケット割り込みレジスタ
 #define Sn_SR      0x0003  // ソケットステータスレジスタ
 #define Sn_PORT    0x0004  // ソケットポート番号
 #define Sn_TX_FSR  0x0020  // TX空きサイズ
 #define Sn_TX_WR   0x0024  // TX書き込みポインタ
 #define Sn_RX_RSR  0x0026  // RX受信サイズ
 #define Sn_RX_RD   0x0028  // RX読み出しポインタ
 
 // ソケットコマンド
 #define SOCK_OPEN      0x01
 #define SOCK_LISTEN    0x02
 #define SOCK_CONNECT   0x04
 #define SOCK_DISCON    0x08
 #define SOCK_CLOSE     0x10
 #define SOCK_SEND      0x20
 #define SOCK_RECV      0x40
 
 // ソケットステータス
 #define SOCK_CLOSED      0x00
 #define SOCK_INIT        0x13
 #define SOCK_LISTEN      0x14
 #define SOCK_ESTABLISHED 0x17
 #define SOCK_CLOSE_WAIT  0x1C
 
 // 制御バイト生成マクロ
 #define WIZCHIP_COMMON_REG_RWB   0x00  // 共通レジスタアクセス
 #define WIZCHIP_SOCK_REG_RWB(sn) (0x08 + (sn << 5))  // ソケットレジスタアクセス
 #define WIZCHIP_TX_BUF(sn)       (0x10 + (sn << 5))  // TX バッファアクセス
 #define WIZCHIP_RX_BUF(sn)       (0x18 + (sn << 5))  // RX バッファアクセス
 
 // 共通レジスタ書き込み
 void WIZCHIP_WRITE_COMMON(uint16_t addr, uint8_t data)
 {
    wizchip_select();
    wizchip_write((addr >> 8) & 0xFF);     // アドレス上位
    wizchip_write(addr & 0xFF);            // アドレス下位
    wizchip_write(WIZCHIP_COMMON_REG_RWB | 0x04);  // 制御バイト (書き込み)
    wizchip_write(data);
    wizchip_deselect();
 }
 
 // 共通レジスタ読み出し
 uint8_t WIZCHIP_READ_COMMON(uint16_t addr)
 {
    uint8_t data;
    wizchip_select();
    wizchip_write((addr >> 8) & 0xFF);
    wizchip_write(addr & 0xFF);
    wizchip_write(WIZCHIP_COMMON_REG_RWB);  // 制御バイト (読み出し)
    data = wizchip_read();
    wizchip_deselect();
    return data;
 }
 
 // 共通レジスタバースト書き込み
 void WIZCHIP_WRITE_COMMON_BUF(uint16_t addr, uint8_t* pBuf, uint16_t len)
 {
    wizchip_select();
    wizchip_write((addr >> 8) & 0xFF);
    wizchip_write(addr & 0xFF);
    wizchip_write(WIZCHIP_COMMON_REG_RWB | 0x04);
    wizchip_write_burst(pBuf, len);
    wizchip_deselect();
 }
 
 // ソケットレジスタ書き込み
 void WIZCHIP_WRITE_SOCK(uint8_t sn, uint16_t addr, uint8_t data)
 {
    wizchip_select();
    wizchip_write((addr >> 8) & 0xFF);
    wizchip_write(addr & 0xFF);
    wizchip_write(WIZCHIP_SOCK_REG_RWB(sn) | 0x04);
    wizchip_write(data);
    wizchip_deselect();
 }
 
 // ソケットレジスタ読み出し
 uint8_t WIZCHIP_READ_SOCK(uint8_t sn, uint16_t addr)
 {
    uint8_t data;
    wizchip_select();
    wizchip_write((addr >> 8) & 0xFF);
    wizchip_write(addr & 0xFF);
    wizchip_write(WIZCHIP_SOCK_REG_RWB(sn));
    data = wizchip_read();
    wizchip_deselect();
    return data;
 }
 
 // ソケットレジスタ16ビット読み出し
 uint16_t WIZCHIP_READ_SOCK_16(uint8_t sn, uint16_t addr)
 {
    uint16_t data;
    data = (WIZCHIP_READ_SOCK(sn, addr) << 8);
    data |= WIZCHIP_READ_SOCK(sn, addr + 1);
    return data;
 }
 
 // TXバッファ書き込み
 void WIZCHIP_WRITE_TX_BUF(uint8_t sn, uint16_t addr, uint8_t* pBuf, uint16_t len)
 {
    wizchip_select();
    wizchip_write((addr >> 8) & 0xFF);
    wizchip_write(addr & 0xFF);
    wizchip_write(WIZCHIP_TX_BUF(sn) | 0x04);
    wizchip_write_burst(pBuf, len);
    wizchip_deselect();
 }
 
 // RXバッファ読み出し
 void WIZCHIP_READ_RX_BUF(uint8_t sn, uint16_t addr, uint8_t* pBuf, uint16_t len)
 {
    wizchip_select();
    wizchip_write((addr >> 8) & 0xFF);
    wizchip_write(addr & 0xFF);
    wizchip_write(WIZCHIP_RX_BUF(sn));
    wizchip_read_burst(pBuf, len);
    wizchip_deselect();
 }
 
 // W5500初期化
 void w5500_init(void)
 {
    uint8_t i;
    
    // ソフトウェアリセット
    WIZCHIP_WRITE_COMMON(MR, 0x80);
    __delay_cycles(100000);  // 2ms待機 (50MHz想定)
    
    // バッファサイズ設定 (各ソケット2KB TX/RX)
    for (i = 0; i < 8; i++) {
       WIZCHIP_WRITE_SOCK(i, 0x001E, 2);  // Sn_TXBUF_SIZE = 2KB
       WIZCHIP_WRITE_SOCK(i, 0x001F, 2);  // Sn_RXBUF_SIZE = 2KB
    }
 }
 
 // ネットワーク設定
 void w5500_set_network(uint8_t* mac, uint8_t* ip, uint8_t* subnet, uint8_t* gateway)
 {
    WIZCHIP_WRITE_COMMON_BUF(SHAR, mac, 6);      // MACアドレス
    WIZCHIP_WRITE_COMMON_BUF(SIPR, ip, 4);       // IPアドレス
    WIZCHIP_WRITE_COMMON_BUF(SUBR, subnet, 4);   // サブネットマスク
    WIZCHIP_WRITE_COMMON_BUF(GAR, gateway, 4);   // ゲートウェイ
 }
 
 // ソケットオープン (TCPサーバ)
 int8_t socket_open(uint8_t sn, uint16_t port)
 {
    // ソケットが既にオープンされているか確認
    if (WIZCHIP_READ_SOCK(sn, Sn_SR) != SOCK_CLOSED) {
       return -1;
    }
 
    // TCPモード設定
    WIZCHIP_WRITE_SOCK(sn, Sn_MR, 0x01);  // TCP mode
    
    // ポート番号設定
    WIZCHIP_WRITE_SOCK(sn, Sn_PORT, (port >> 8) & 0xFF);
    WIZCHIP_WRITE_SOCK(sn, Sn_PORT + 1, port & 0xFF);
    
    // ソケットオープンコマンド
    WIZCHIP_WRITE_SOCK(sn, Sn_CR, SOCK_OPEN);
    while (WIZCHIP_READ_SOCK(sn, Sn_CR));  // コマンド完了待ち
    
    return 0;
 }
 
 // ソケットリッスン (TCPサーバ)
 int8_t socket_listen(uint8_t sn)
 {
    if (WIZCHIP_READ_SOCK(sn, Sn_SR) != SOCK_INIT) {
       return -1;
    }
    
    WIZCHIP_WRITE_SOCK(sn, Sn_CR, SOCK_LISTEN);
    while (WIZCHIP_READ_SOCK(sn, Sn_CR));
    
    return 0;
 }
 
 // データ受信
 int32_t socket_recv(uint8_t sn, uint8_t* buf, uint16_t len)
 {
    uint16_t recv_size;
    uint16_t ptr;
 
    // 受信データサイズ確認
    recv_size = WIZCHIP_READ_SOCK_16(sn, Sn_RX_RSR);
    if (recv_size == 0) {
       return 0;
    }
 
    // 受信サイズ制限
    if (recv_size > len) {
       recv_size = len;
    }
 
    // 読み出しポインタ取得
    ptr = WIZCHIP_READ_SOCK_16(sn, Sn_RX_RD);
 
    // データ読み出し
    WIZCHIP_READ_RX_BUF(sn, ptr, buf, recv_size);
 
    // 読み出しポインタ更新
    ptr += recv_size;
    WIZCHIP_WRITE_SOCK(sn, Sn_RX_RD, (ptr >> 8) & 0xFF);
    WIZCHIP_WRITE_SOCK(sn, Sn_RX_RD + 1, ptr & 0xFF);
 
    // RECVコマンド
    WIZCHIP_WRITE_SOCK(sn, Sn_CR, SOCK_RECV);
    while (WIZCHIP_READ_SOCK(sn, Sn_CR));
 
    return recv_size;
 }
 
 // データ送信
 int32_t socket_send(uint8_t sn, uint8_t* buf, uint16_t len)
 {
    uint16_t ptr;
    uint16_t free_size;
 
    // 送信バッファ空きサイズ確認
    free_size = WIZCHIP_READ_SOCK_16(sn, Sn_TX_FSR);
    if (free_size < len) {
       return 0;  // バッファ不足
    }
 
    // 書き込みポインタ取得
    ptr = WIZCHIP_READ_SOCK_16(sn, Sn_TX_WR);
 
    // データ書き込み
    WIZCHIP_WRITE_TX_BUF(sn, ptr, buf, len);
 
    // 書き込みポインタ更新
    ptr += len;
    WIZCHIP_WRITE_SOCK(sn, Sn_TX_WR, (ptr >> 8) & 0xFF);
    WIZCHIP_WRITE_SOCK(sn, Sn_TX_WR + 1, ptr & 0xFF);
 
    // SENDコマンド
    WIZCHIP_WRITE_SOCK(sn, Sn_CR, SOCK_SEND);
    while (WIZCHIP_READ_SOCK(sn, Sn_CR));
 
    return len;
 }
 
 // ソケットクローズ
 void socket_close(uint8_t sn)
 {
    WIZCHIP_WRITE_SOCK(sn, Sn_CR, SOCK_CLOSE);
    while (WIZCHIP_READ_SOCK(sn, Sn_CR));
 }
 
 // ソケットステータス取得
 uint8_t socket_status(uint8_t sn)
 {
    return WIZCHIP_READ_SOCK(sn, Sn_SR);
 }


メイン処理

メイン処理では、MSP430F5529の初期化、W5500の初期化、その他実行等を行う。

初期化では、マイコンのクロック設定、SPI通信の設定、W5500のハードウェアリセット、W5500の初期化、ネットワーク設定を実行する。
その後、HTTPサーバ処理を繰り返し実行する。

HTTPサーバ処理は、ソケットのステータスに応じて以下の動作を行う。

  • ソケットがクローズ状態の場合
    ソケットをオープンし、リッスン状態に移行する。
  • ソケットが確立状態の場合
    データ受信を確認し、GETリクエストを受信した場合はHTMLレスポンスを送信する。
    送信後、ソケットをクローズする。
  • ソケットがクローズ待ち状態の場合
    クライアント側からの切断要求に応じてソケットをクローズする。


W5500を使用する場合、TCP/IPプロトコルの処理は全てW5500が自動的に行うため、アプリケーション側ではソケットAPIを呼び出すだけでよい。
これにより、uIPやlwIPを使用する場合と比較して、ソースコードが大幅に簡素化される。

 #include <msp430f5529.h>
 #include "w5500.h"
 #include "msp430_spi.h"
 #include <string.h>
 #include <stdint.h>
 
 // ネットワーク設定
 #define MAC_ADDR   {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56}
 #define IP_ADDR    {192, 168, 1, 100}
 #define SUBNET     {255, 255, 255, 0}
 #define GATEWAY    {192, 168, 1, 1}
 
 // ソケット番号
 #define SOCK_HTTP  0
 
 // HTTPポート
 #define HTTP_PORT  80
 
 // HTTPレスポンス
 const char http_response[] = 
     "HTTP/1.1 200 OK\r\n"
     "Content-Type: text/html\r\n"
     "Connection: close\r\n"
     "\r\n"
     "<!DOCTYPE html>"
     "<html>"
     "<head><title>MSP430F5529 W5500 Server</title></head>"
     "<body>"
     "<h1>Welcome to MSP430F5529!</h1>"
     "<p>This is a TCP server running on MSP430F5529 with W5500.</p>"
     "<p>Hardware TCP/IP Stack</p>"
     "</body>"
     "</html>";
 
 // クロック初期化 (25[MHz])
 void clock_init(void)
 {
    UCSCTL3 = SELREF_2;            // FLLリファレンス = REFO
    UCSCTL4 |= SELA_2;             // ACLK = REFO
 
    __bis_SR_register(SCG0);       // FLLを無効化
    UCSCTL0 = 0x0000;              // DCO、MOD初期化
    UCSCTL1 = DCORSEL_6;           // DCO周波数範囲選択
    UCSCTL2 = FLLD_0 + 762;        // 25[MHz]設定 (762 + 1) × 32768[Hz]
    __bic_SR_register(SCG0);       // FLLを有効化
 
    __delay_cycles(250000);        // 安定化待機
 }
 
 // W5500ハードウェアリセット
 void w5500_reset(void)
 {
    P2OUT &= ~BIT6;                // RST = Low
    __delay_cycles(50000);         // 1[ms]待機
    P2OUT |= BIT6;                 // RST = High
    __delay_cycles(5000000);       // 100[ms]待機 (W5500起動待ち)
 }
 
 // HTTPサーバ処理
 void http_server_process(uint8_t sn)
 {
    uint8_t status;
    uint8_t buf[256];
    int32_t recv_len;
 
    status = socket_status(sn);
 
    switch (status) {
       case SOCK_CLOSED:
          // ソケットオープン
          socket_open(sn, HTTP_PORT);
          socket_listen(sn);
          break;
       case SOCK_ESTABLISHED:
          // データ受信確認
          recv_len = socket_recv(sn, buf, sizeof(buf));
 
          if (recv_len > 0) {
             // GETリクエストをチェック
             if (strncmp((char*)buf, "GET ", 4) == 0) {
                // HTTPレスポンス送信
                socket_send(sn, (uint8_t*)http_response, strlen(http_response));
 
                // 切断
                socket_close(sn);
             }
          }
          break;
       case SOCK_CLOSE_WAIT:
          // クライアント側から切断要求
          socket_close(sn);
          break;
       default:
          break;
    }
 }
 
 // メイン関数
 int main(void)
 {
    uint8_t mac[6] = MAC_ADDR;
    uint8_t ip[4] = IP_ADDR;
    uint8_t subnet[4] = SUBNET;
    uint8_t gateway[4] = GATEWAY;
 
    WDTCTL = WDTPW | WDTHOLD;  // ウォッチドッグ停止
 
    // システム初期化
    clock_init();              // クロック設定 (25[MHz])
    spi_init();                // SPI初期化
 
    // W5500リセット
    w5500_reset();
 
    // W5500初期化
    w5500_init();
 
    // ネットワーク設定
    w5500_set_network(mac, ip, subnet, gateway);
 
    // メインループ
    while (1) {
       // HTTPサーバ処理
       http_server_process(SOCK_HTTP);
 
       // 適度な遅延を入れてCPU負荷を軽減
       __delay_cycles(10000);  // 約0.4ms
    }
 }



その他の注意事項

W5500を使用したTCP/IP通信を使用する場合の注意事項を以下に示す。

  • ソケット管理
    W5500は8つの独立したソケットを持つが、各ソケットのバッファサイズは合計32[KB]を超えないように設定する必要がある。
    例えば、全てのソケットを使用する場合、各ソケットのバッファは2[KB] (TX) + 2[KB] (RX) = 4[KB]となり、合計32[KB]となる。
  • SPI通信速度
    W5500のSPIは最大80[MHz]に対応しているが、MSP430F5529の制約により、実用的には10〜12.5[MHz]程度となる。
    これでも十分な通信速度が得られるため、問題にはならない。
  • リセットタイミング
    W5500は、電源投入後およびハードウェアリセット後、約100[ms]の起動時間が必要である。
    この期間中はレジスタアクセスを行わないように注意する。
  • バッファポインタ管理
    TXバッファとRXバッファの読み書きポインタは、アプリケーション側で適切に更新する必要がある。
    ポインタの更新を忘れると、データの送受信が正常に動作しない。
  • 同時接続数
    W5500は8つのソケットを持つため、最大8つの同時接続が可能である。
    ただし、各接続のデータ転送量やタイミングに応じて、適切なバッファサイズを設定する必要がある。
  • 割り込み使用
    W5500は、データ受信、接続確立、切断等のイベントに対して割り込みを発生できる。
    ポーリング方式でも十分に動作するが、CPU負荷を削減したい場合は割り込みの使用を検討する。


※注意
よくあるトラブルシューティングを以下に示す。

  • W5500が応答しない
    ハードウェアリセットのタイミング、SPI配線、電源電圧を確認する。
    SPIクロック速度を下げて試してみる。
  • ネットワークに接続できない
    IPアドレス、サブネットマスク、ゲートウェイの設定を確認する。
    MACアドレスが重複していないか確認する。
    PHYリンクステータスを確認する (PHYCFGR レジスタ) 。
  • データが送受信できない
    ソケットのステータスを確認し、ESTABLISHED状態であることを確認する。
    バッファポインタの更新が正しく行われているか確認する。
    送信バッファに十分な空きがあるか確認する。
  • 接続が切断される
    TCPキープアライブの設定を確認する。
    タイムアウト設定を確認する。


SPI通信の最適化

W5500との通信効率を向上させるため、SPIクロック速度を可能な限り高く設定することを推奨する。
ただし、配線長や基板レイアウトによっては、信号品質が劣化する可能性があるため、実環境で動作確認を行う必要がある。

また、W5500はバーストモードをサポートしており、連続したレジスタアドレスにアクセスする場合、アドレスを再送信せずにデータを連続して送受信できる。
これにより、SPI通信のオーバーヘッドを削減できる。

W5500のリンクステータス確認

W5500が正常にネットワークに接続されているかを確認するため、PHYCFGRレジスタを読み出してリンクステータスを確認することを推奨する。

 // PHYリンクステータス確認
 uint8_t phycfgr = WIZCHIP_READ_COMMON(PHYCFGR);
 if (phycfgr & 0x01) {
    // リンクUP
 }
 else {
    // リンクDOWN
 }


リンクが確立していない場合、イーサネットケーブルの接続、RJ45コネクタの配線、PHYの設定を確認する必要がある。


参考資料