「MSPM0G3519 - イーサネット (ENC28J60)」の版間の差分

📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

編集の要約なし
 
1,069行目: 1,069行目:
* fsdata.cが正しく生成されているかを確認する。
* fsdata.cが正しく生成されているかを確認する。
* makefsdata.pyを使用して、HTMLファイルを正しく変換する。
* makefsdata.pyを使用して、HTMLファイルを正しく変換する。
<br><br>
== uIP : サンプルコード ==
以下の例では、ENC28J60およびuIPを使用して、Webサーバを構築してHTTPリクエストに応答している。<br>
uIPは軽量であるため、MSPM0G3519の豊富なリソースを考慮すると軽量すぎる可能性があるが、簡単な実装や学習目的には適している。<br>
<br>
==== uIP設定ファイル (uip-conf.h) ====
まず、uIPの動作パラメータを設定する。<br>
<br>
この設定ファイルでは、デバイスのネットワーク設定 (IPアドレス、ネットマスク、ゲートウェイ) を定義している。<br>
これらの値は、使用するネットワーク環境に合わせて変更する必要がある。<br>
MSPM0G3519は128[KB]のRAMを持つため、バッファサイズや接続数を比較的大きく設定できる。<br>
<br>
* バッファサイズ
*: 標準的なイーサネットフレームのMTU (Maximum Transmission Unit) である1500バイトに設定している。
* TCP接続数
*: 8に設定しているが、これはRAM容量と必要な同時接続数に応じて調整できる。
*: MSPM0G3519の豊富なRAMを活かし、MSP430F5529の設定 (4接続) より多めに設定している。
*: 接続数を増やすと、より多くのクライアントを同時に処理できるが、RAM使用量も増加する点に注意が必要である。
<br>
<syntaxhighlight lang="c">
#ifndef __UIP_CONF_H__
#define __UIP_CONF_H__
#include <stdint.h>
// uIP設定パラメータ
typedef uint8_t u8_t;        // 8ビット符号なし整数型
typedef uint16_t u16_t;      // 16ビット符号なし整数型
// IPアドレス設定
#define UIP_IPADDR0  192    // IPアドレス 192.168.1.100
#define UIP_IPADDR1  168
#define UIP_IPADDR2  1
#define UIP_IPADDR3  100
// ネットマスク設定
#define UIP_NETMASK0  255    // ネットマスク 255.255.255.0
#define UIP_NETMASK1  255
#define UIP_NETMASK2  255
#define UIP_NETMASK3  0
// デフォルトゲートウェイ
#define UIP_DRIPADDR0 192    // ゲートウェイ 192.168.1.1
#define UIP_DRIPADDR1 168
#define UIP_DRIPADDR2 1
#define UIP_DRIPADDR3 1
// uIPバッファ設定
#define UIP_CONF_BUFFER_SIZE    1500  // イーサネットMTUサイズ
#define UIP_CONF_RECEIVE_WINDOW  1460  // TCP受信ウィンドウサイズ
// TCP/UDP接続数 (MSPM0G3519の豊富なRAMを活用)
#define UIP_CONF_MAX_CONNECTIONS 8    // 最大TCP接続数 (MSP430より多め)
#define UIP_CONF_MAX_LISTENPORTS 8    // 最大TCP待受ポート数
#define UIP_CONF_UDP_CONNS      4    // 最大UDP接続数
// プロトコル有効化
#define UIP_CONF_UDP            1    // UDPを有効化
#define UIP_CONF_UDP_CHECKSUMS  1    // UDPチェックサムを有効化
#define UIP_CONF_BROADCAST      1    // ブロードキャストを有効化
// 統計情報 (開発時は有効化、製品版では無効化してRAM節約)
#define UIP_CONF_STATISTICS      0    // 統計情報を無効化
// アプリケーション名
#define UIP_APPCALL  httpd_appcall    // HTTPサーバーアプリケーション
#endif /* __UIP_CONF_H__ */
</syntaxhighlight>
<br>
==== ENC28J60ドライバ (enc28j60.c) ====
ENC28J60との通信を行うドライバ制御 (SPIを使用したレジスタアクセスやパケット送受信等) を記述する。<br>
<br>
このドライバでは、ENC28J60の内部構造が重要である。<br>
ENC28J60は、8[KB]の内部RAMを持ち、これを受信バッファと送信バッファに分割して使用する。<br>
<br>
以下の例では、受信バッファを0x0000から0x19FFまで (約6.5[KB])、送信バッファを0x1A00から0x1FFFまで (約1.5[KB]) に設定している。<br>
この分割比率は、アプリケーションの特性に応じて調整可能である。<br>
<br>
また、ENC28J60のレジスタは4つのバンク (Bank0〜Bank3) に分かれており、アクセスする前に適切なバンクを選択する必要がある。<br>
バンク選択は、ECON1レジスタの下位2ビットで制御される。<br>
ドライバでは、現在のバンクを追跡し、必要な場合のみバンク切り替えを行うことにより、不要なSPI通信を削減している。<br>
<br>
MSPM0G3519のSPIペリフェラルは、DL_SPI関数を使用して制御する。<br>
最大80[MHz]の高速動作により、SPIクロックも高速に設定できるが、ENC28J60の最大速度 (20[MHz]) を超えないように注意する必要がある。<br>
<br>
<syntaxhighlight lang="c">
// enc28j60.c : ENC28J60ドライバ (MSPM0G3519 uIP用)
#include "ti_msp_dl_config.h"  // MSPM0G3519 SDK設定
#include "enc28j60.h"
#include <stdint.h>
// ENC28J60レジスタアドレス定義
#define ERDPTL    0x00
#define ERDPTH    0x01
#define EWRPTL    0x02
#define EWRPTH    0x03
#define ETXSTL    0x04
#define ETXSTH    0x05
#define ETXNDL    0x06
#define ETXNDH    0x07
#define ERXSTL    0x08
#define ERXSTH    0x09
#define ERXNDL    0x0A
#define ERXNDH    0x0B
#define ERXRDPTL  0x0C
#define ERXRDPTH  0x0D
// EIE : イーサネット割り込み有効レジスタ
#define EIE      0x1B
#define INTIE    0x80
#define PKTIE    0x40
// EIR : イーサネット割り込み要求レジスタ
#define EIR      0x1C
#define PKTIF    0x40
#define TXIF      0x08
// ECON1 : イーサネット制御レジスタ1
#define ECON1    0x1F
#define TXRTS    0x08
#define RXEN      0x04
// ECON2 : イーサネット制御レジスタ2
#define ECON2    0x1E
#define AUTOINC  0x80
#define PKTDEC    0x40
// ESTAT : イーサネットステータスレジスタ
#define ESTAT    0x1D
#define CLKRDY    0x01
// MACON1 : MACコントロールレジスタ1
#define MACON1    0x00 | 0x40
#define MARXEN    0x01
#define TXPAUS    0x08
#define RXPAUS    0x04
// MACON3 : MACコントロールレジスタ3
#define MACON3    0x02 | 0x40
#define PADCFG0  0x20
#define TXCRCEN  0x10
#define FRMLNEN  0x02
#define FULDPX    0x01
// MAMXFLL/MAMXFLH : 最大フレーム長レジスタ
#define MAMXFLL  0x0A | 0x40
#define MAMXFLH  0x0B | 0x40
// MABBIPG : バックツーバックパケット間隙レジスタ
#define MABBIPG  0x04 | 0x40
// MAIPGL/MAIPGH : 非バックツーバックパケット間隙レジスタ
#define MAIPGL    0x06 | 0x40
#define MAIPGH    0x07 | 0x40
// SPIコマンド定義
#define ENC28J60_READ_CTRL_REG  0x00
#define ENC28J60_WRITE_CTRL_REG 0x40
#define ENC28J60_BIT_FIELD_SET  0x80
#define ENC28J60_BIT_FIELD_CLR  0xA0
#define ENC28J60_READ_BUF_MEM  0x3A
#define ENC28J60_WRITE_BUF_MEM  0x7A
#define ENC28J60_SOFT_RESET    0xFF
// バンク選択
#define BANK0  0x00
#define BANK1  0x01
#define BANK2  0x02
#define BANK3  0x03
// チップセレクト制御 (実際のピン定義に応じて変更)
#define ENC28J60_CS_LOW()  DL_GPIO_clearPins(GPIO_ENC_CS_PORT, GPIO_ENC_CS_PIN)
#define ENC28J60_CS_HIGH()  DL_GPIO_setPins(GPIO_ENC_CS_PORT, GPIO_ENC_CS_PIN)
static uint8_t current_bank = 0;
// 遅延関数 (80[MHz]駆動の場合)
static void delay_us(uint32_t us)
{
    // 80[MHz] = 80 cycles/us
    // 簡易的な遅延実装のため、精度が必要な場合はタイマの割り込みを使用する
    volatile uint32_t cycles = us * 80;
    while (cycles--);
}
// SPIバイト送受信
static uint8_t spi_transfer(uint8_t data)
{
    // SPIにデータ送信
    DL_SPI_transmitData8(SPI_0_INST, data);
    // 送信完了待ち
    while (DL_SPI_isBusy(SPI_0_INST));
    // 受信データ取得
    return DL_SPI_receiveData8(SPI_0_INST);
}
// バンク選択
static void enc28j60_set_bank(uint8_t address)
{
    uint8_t bank = (address & 0x60) >> 5;
    if (bank != current_bank) {
      // EIE、EIR、ESTAT、ECON1、ECON2は全バンク共通
      enc28j60_write_op(ENC28J60_BIT_FIELD_CLR, ECON1, 0x03);
      enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, bank);
      current_bank = bank;
    }
}
// レジスタ読み出し
uint8_t enc28j60_read(uint8_t address)
{
    uint8_t data;
    enc28j60_set_bank(address);
    ENC28J60_CS_LOW();
    spi_transfer(ENC28J60_READ_CTRL_REG | (address & 0x1F));
    // MACおよびMIIレジスタの場合、ダミーバイトを読む
    if (address & 0x80) {
      spi_transfer(0x00);
    }
    data = spi_transfer(0x00);
    ENC28J60_CS_HIGH();
    return data;
}
// レジスタ書き込み
void enc28j60_write(uint8_t address, uint8_t data)
{
    enc28j60_set_bank(address);
    ENC28J60_CS_LOW();
    spi_transfer(ENC28J60_WRITE_CTRL_REG | (address & 0x1F));
    spi_transfer(data);
    ENC28J60_CS_HIGH();
}
// ビット操作 (OR)
void enc28j60_write_op(uint8_t op, uint8_t address, uint8_t data)
{
    ENC28J60_CS_LOW();
    spi_transfer(op | (address & 0x1F));
    spi_transfer(data);
    ENC28J60_CS_HIGH();
}
// バッファメモリ読み出し
void enc28j60_read_buffer(uint8_t *buffer, uint16_t len)
{
    ENC28J60_CS_LOW();
    spi_transfer(ENC28J60_READ_BUF_MEM);
    while (len--) {
      *buffer++ = spi_transfer(0x00);
    }
    ENC28J60_CS_HIGH();
}
// バッファメモリ書き込み
void enc28j60_write_buffer(const uint8_t *buffer, uint16_t len)
{
    ENC28J60_CS_LOW();
    spi_transfer(ENC28J60_WRITE_BUF_MEM);
    while (len--) {
      spi_transfer(*buffer++);
    }
    ENC28J60_CS_HIGH();
}
// ENC28J60初期化
void enc28j60_init(const uint8_t *mac_address)
{
    // ソフトウェアリセット
    ENC28J60_CS_LOW();
    spi_transfer(ENC28J60_SOFT_RESET);
    ENC28J60_CS_HIGH();
    delay_us(1000);  // 1[ms]待機
    // クロックが安定するまで待機
    while (!(enc28j60_read(ESTAT) & CLKRDY));
    // 受信バッファ設定 (0x0000 - 0x19FF)
    enc28j60_write(ERXSTL, 0x00);
    enc28j60_write(ERXSTH, 0x00);
    enc28j60_write(ERXNDL, 0xFF);
    enc28j60_write(ERXNDH, 0x19);
    enc28j60_write(ERXRDPTL, 0x00);
    enc28j60_write(ERXRDPTH, 0x00);
    // 送信バッファ設定 (0x1A00 - 0x1FFF)
    enc28j60_write(ETXSTL, 0x00);
    enc28j60_write(ETXSTH, 0x1A);
    enc28j60_write(ETXNDL, 0xFF);
    enc28j60_write(ETXNDH, 0x1F);
    // MAC初期化
    enc28j60_write(MACON1, MARXEN);                      // MAC受信有効
    enc28j60_write(MACON3, PADCFG0 | TXCRCEN | FRMLNEN);  // パディング、CRC、フレーム長チェック有効
    enc28j60_write(MAMXFLL, 0xEE);                        // 最大フレーム長1518バイト
    enc28j60_write(MAMXFLH, 0x05);
    enc28j60_write(MABBIPG, 0x12);                        // バックツーバック間隔
    enc28j60_write(MAIPGL, 0x12);                        // 非バックツーバック間隔
    enc28j60_write(MAIPGH, 0x0C);
    // MACアドレス設定
    enc28j60_write(0x00 | 0x60, mac_address[0]);  // MAADR1
    enc28j60_write(0x01 | 0x60, mac_address[1]);  // MAADR2
    enc28j60_write(0x02 | 0x60, mac_address[2]);  // MAADR3
    enc28j60_write(0x03 | 0x60, mac_address[3]);  // MAADR4
    enc28j60_write(0x04 | 0x60, mac_address[4]);  // MAADR5
    enc28j60_write(0x05 | 0x60, mac_address[5]);  // MAADR6
    // 割り込み有効化
    enc28j60_write(EIE, INTIE | PKTIE);
    // 受信有効化
    enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, RXEN);
}
// パケット送信
void enc28j60_packet_send(const uint8_t *buffer, uint16_t len)
{
    // 送信バッファポインタ設定
    enc28j60_write(EWRPTL, 0x00);
    enc28j60_write(EWRPTH, 0x1A);
    // 制御バイト書き込み (0x00 = デフォルト設定)
    enc28j60_write_buffer((uint8_t[]){0x00}, 1);
    // パケットデータ書き込み
    enc28j60_write_buffer(buffer, len);
    // 送信終了ポインタ設定
    enc28j60_write(ETXNDL, (0x1A00 + len) & 0xFF);
    enc28j60_write(ETXNDH, ((0x1A00 + len) >> 8) & 0xFF);
    // 送信開始
    enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, TXRTS);
    // 送信完了待ち (オプション)
    while (enc28j60_read(ECON1) & TXRTS);
}
// パケット受信
uint16_t enc28j60_packet_receive(uint8_t *buffer, uint16_t max_len)
{
    uint16_t len = 0;
    uint8_t header[6];
    // 受信パケットがあるかどうかを確認
    if (enc28j60_read(0x19) == 0) {  // EPKTCNT
      return 0;
    }
    // ヘッダ読み出し (次のパケットポインタ、ステータス、長さ)
    enc28j60_read_buffer(header, 6);
    // パケット長取得 (ヘッダ内の長さフィールド)
    len = (header[3] << 8) | header[2];
    len -= 4;  // CRCを除く
    // バッファに収まる場合のみデータを読み出し
    if (len <= max_len) {
      enc28j60_read_buffer(buffer, len);
    }
    // 次のパケットポインタ更新
    uint16_t next_packet = (header[1] << 8) | header[0];
    enc28j60_write(ERXRDPTL, next_packet & 0xFF);
    enc28j60_write(ERXRDPTH, (next_packet >> 8) & 0xFF);
    // パケットデクリメント
    enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON2, PKTDEC);
    return (len <= max_len) ? len : 0;
}
</syntaxhighlight>
<br>
==== メイン処理 ====
メイン処理では、MSPM0G3519の初期化、uIPの初期化等を記述する。<br>
<br>
MSPM0G3519のタイマペリフェラルを使用して、uIPに必要な周期的なタイマイベントを生成する。<br>
uIPは、TCPの再送タイマやARPのタイムアウト管理のために、定期的なタイマ処理を必要とする。<br>
一般的には、TCPタイマは500[ms]周期、ARPタイマは10秒周期で処理する。<br>
<br>
MSPM0G3519の80MHz動作により、uIPのTCP/IP処理は非常に高速に実行できる。<br>
また、128[KB]のRAMにより、uIPのバッファやスタック変数に十分なメモリを割り当てることができる。<br>
<br>
<syntaxhighlight lang="c">
// main.c : メイン処理 (MSPM0G3519 uIP)
#include "ti_msp_dl_config.h"
#include "uip.h"
#include "uip_arp.h"
#include "enc28j60.h"
#include <stdint.h>
// MACアドレス定義
#define MAC_ADDR0  0x02
#define MAC_ADDR1  0x00
#define MAC_ADDR2  0x00
#define MAC_ADDR3  0x00
#define MAC_ADDR4  0x00
#define MAC_ADDR5  0x01
// グローバルバッファ (uIP用)
// MSPM0G3519は128KB RAMを持つため、余裕を持ったバッファサイズを確保可能
uint8_t uip_buf[UIP_BUFSIZE + 2];
// タイマ管理用
volatile uint16_t periodic_timer = 0;
volatile uint16_t arp_timer = 0;
// タイマ割り込みハンドラ (10ms周期想定)
void TIMER_0_INST_IRQHandler(void)
{
    periodic_timer++;
    arp_timer++;
}
// uIP周期処理
void uip_periodic_handler(void)
{
    int i;
    // TCP接続の周期処理 (500[ms] = 50 × 10[ms])
    if (periodic_timer >= 50) {
      periodic_timer = 0;
      for (i = 0; i < UIP_CONNS; i++) {
          uip_periodic(i);
          if (uip_len > 0) {
            uip_arp_out();  // ARPテーブル参照してイーサネットヘッダ追加
            enc28j60_packet_send(uip_buf, uip_len);
          }
      }
      // UDP接続の周期処理
#if UIP_UDP
      for (i = 0; i < UIP_UDP_CONNS; i++) {
          uip_udp_periodic(i);
          if (uip_len > 0) {
            uip_arp_out();
            enc28j60_packet_send(uip_buf, uip_len);
          }
      }
#endif
    }
    // ARPタイマー処理 (10秒 = 1000 × 10ms)
    if (arp_timer >= 1000) {
      arp_timer = 0;
      uip_arp_timer();
    }
}
// メイン関数
int main(void)
{
    uip_ipaddr_t ipaddr;
    uint8_t mac_address[6] = {MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5};
    // MSPM0G3519ペリフェラル初期化
    SYSCFG_DL_init();
    // タイマ割り込み有効化
    NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
    // ENC28J60初期化
    enc28j60_init(mac_address);
    // uIP初期化
    uip_init();
    // IPアドレス設定
    uip_ipaddr(ipaddr, UIP_IPADDR0, UIP_IPADDR1, UIP_IPADDR2, UIP_IPADDR3);
    uip_sethostaddr(ipaddr);
    // ネットマスク設定
    uip_ipaddr(ipaddr, UIP_NETMASK0, UIP_NETMASK1, UIP_NETMASK2, UIP_NETMASK3);
    uip_setnetmask(ipaddr);
    // デフォルトゲートウェイ設定
    uip_ipaddr(ipaddr, UIP_DRIPADDR0, UIP_DRIPADDR1, UIP_DRIPADDR2, UIP_DRIPADDR3);
    uip_setdraddr(ipaddr);
    // ARPテーブル初期化
    uip_arp_init();
    // HTTPサーバ起動 (ポート80)
    uip_listen(HTONS(80));
    // グローバル割り込み有効化
    __enable_irq();
    // メインループ
    while (1) {
      // パケット受信チェック
      uip_len = enc28j60_packet_receive(uip_buf, UIP_BUFSIZE);
      if (uip_len > 0) {
          // イーサネットヘッダ解析
          struct uip_eth_hdr *eth_hdr = (struct uip_eth_hdr *)&uip_buf[0];
          if (eth_hdr->type == htons(UIP_ETHTYPE_IP)) {
            // IPパケット処理
            uip_arp_ipin();    // ARPテーブル更新
            uip_input();      // uIPへ入力
            if (uip_len > 0) {
                uip_arp_out(); // ARPテーブル参照
                enc28j60_packet_send(uip_buf, uip_len);
            }
          }
          else if (eth_hdr->type == htons(UIP_ETHTYPE_ARP)) {
            // ARPパケット処理
            uip_arp_arpin();
            if (uip_len > 0) {
                enc28j60_packet_send(uip_buf, uip_len);
            }
          }
      }
      // 周期処理 (TCP再送、ARPタイムアウト等)
      uip_periodic_handler();
    }
}
</syntaxhighlight>
<br>
初期化フェーズでは、MSPM0G3519のペリフェラル設定、SPI通信の設定、タイマの設定、ENC28J60およびuIPの初期化を実行する。<br>
SYSCFG_DL_init関数は、TI MSPM0 SDKによって自動生成される設定関数であり、クロック、GPIO、SPI、タイマ等のペリフェラルを一括で初期化する。<br>
<br>
メイン処理では、受信パケットの確認と周期処理を繰り返し実行する。<br>
受信パケットがある場合、イーサネットヘッダを解析してIPパケットかARPパケットかを判断し、それぞれに適した処理を行う。<br>
* 受信パケットの確認
** IPパケットの場合
**: uIPスタックに渡して、TCP/UDP処理を行う。
**: MSPM0G3519の80MHz動作により、TCP/IP処理は非常に高速に実行される。
** ARPパケットの場合
**: ARPテーブルの更新や応答を行う。
*: <br>
* 周期処理
*: TCPの再送タイマやARPエントリのタイムアウト管理を行う。(必須)
*: TCP接続は500[ms]周期、ARPタイマは10秒周期で処理する。
*: これらのタイミングは、uIPの仕様に基づいて設定されており、変更する場合は慎重に検討する必要がある。
<br>
==== HTTPサーバの構築 ====
HTTPリクエストに応答する簡単なWebサーバを構築する。<br>
<br>
uIPはイベント駆動型のアーキテクチャを採用しており、アプリケーション関数 (httpd_appcall関数) は、様々なイベントが発生したときに呼び出される。<br>
主なイベントは以下の通りである。<br>
* 新規接続が確立された場合
*: uip_connected
* クライアントからデータを受信した場合
*: uip_newdata
* データの再送が必要な場合
*: uip_rexmit
* 接続がクローズされた場合
*: uip_closed
*: uip_aborted
*: uip_timedout
*: その他
<br>
以下の例では、GETリクエストを受信した時、予め用意されたHTMLページを送信する。<br>
MSPM0G3519の豊富なフラッシュメモリ (512[KB]) により、複数のHTMLページや画像データを格納することも可能である。<br>
実務では、URLに応じて異なるコンテンツを返したり、ADCで取得したセンサデータを含む動的なページを生成することも可能である。<br>
<br>
uIPは限られたRAMで動作するように設計されているが、MSPM0G3519の128[KB] RAMにより、比較的大きなコンテンツも扱うことができる。<br>
ただし、非常に大きなコンテンツを扱う場合は、分割送信やフラッシュROMからの直接読み出し等の工夫が推奨される。<br>
<br>
<syntaxhighlight lang="c">
// httpd.c : HTTPサーバ実装
#include "uip.h"
#include <string.h>
// HTTPレスポンス (HTMLページ)
// MSPM0G3519のフラッシュROM (512[KB]) により、複数のページを格納可能
const char http_response[] =
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Connection: close\r\n"
    "\r\n"
    "<!DOCTYPE html>"
    "<html>"
    "<head>"
    "  <title>MSPM0G3519 uIP Server</title>"
    "  <meta charset=\"UTF-8\">"
    "  <style>"
    "    body { font-family: Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }"
    "    .container { max-width: 800px; margin: 0 auto; background-color: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }"
    "    h1 { color: #c00; border-bottom: 2px solid #c00; padding-bottom: 10px; }"
    "    .specs { background-color: #f9f9f9; padding: 15px; border-left: 4px solid #c00; margin: 20px 0; }"
    "    .specs h2 { margin-top: 0; color: #333; }"
    "    ul { line-height: 1.8; }"
    "    .footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; text-align: center; color: #666; font-size: 0.9em; }"
    "  </style>"
    "</head>"
    "<body>"
    "  <div class=\"container\">"
    "    <h1>Welcome to MSPM0G3519 uIP Server!</h1>"
    "    <div class=\"specs\">"
    "      <h2>System Specifications</h2>"
    "      <ul>"
    "        <li><strong>MCU:</strong> MSPM0G3519 (ARM Cortex-M0+)</li>"
    "        <li><strong>Clock Speed:</strong> 80 MHz</li>"
    "        <li><strong>Flash Memory:</strong> 512 KB (Dual-bank with ECC)</li>"
    "        <li><strong>SRAM:</strong> 128 KB</li>"
    "        <li><strong>Ethernet Controller:</strong> ENC28J60 (10 Mbps)</li>"
    "        <li><strong>TCP/IP Stack:</strong> uIP (Lightweight Implementation)</li>"
    "      </ul>"
    "    </div>"
    "    <p>This embedded web server demonstrates the power of the MSPM0G3519 microcontroller combined with the uIP TCP/IP stack.</p>"
    "    <p>The MSPM0G3519 offers excellent performance with its 80 MHz ARM Cortex-M0+ core and generous memory resources, making it ideal for IoT and embedded networking applications.</p>"
    "    <div class=\"footer\">"
    "      <p>&copy; 2025 MSPM0G3519 Embedded Web Server</p>"
    "    </div>"
    "  </div>"
    "</body>"
    "</html>";
// HTTPアプリケーション処理
void httpd_appcall(void)
{
    // 新規接続時
    if (uip_connected()) {
        // 特に処理なし (GETリクエスト待ち)
    }
    // データ受信時
    if (uip_newdata()) {
        // 簡易的にGETリクエストをチェック
        // より高度な実装では、HTTPヘッダの完全なパースを行う
        if (strncmp((char *)uip_appdata, "GET ", 4) == 0) {
          // HTTPレスポンス送信
          uip_send((void *)http_response, sizeof(http_response) - 1);
        }
    }
    // 再送要求時
    if (uip_rexmit()) {
        uip_send((void *)http_response, sizeof(http_response) - 1);
    }
    // 接続クローズ時
    if (uip_closed() || uip_aborted() || uip_timedout()) {
        // 特に処理なし
        // 必要に応じてクリーンアップ処理を追加
    }
}
</syntaxhighlight>
<br>
MSPM0G3519の豊富なリソースを活かすことにより、高度な機能を追加することも可能である。<br>
例えば、以下のような拡張が考えられる。<br>
* URLルーティング
*: リクエストされたURLに応じて、異なるHTMLページを返す。
*: 複数のページを用意し、ナビゲーション機能を実装する。
*: <br>
* 動的コンテンツ生成
*: ADCで取得したセンサデータをリアルタイムでWebページに表示する。
*: GPIO状態を読み取り、LEDの点灯状態等をWebページに反映する。
*: <br>
* フォーム処理
*: POSTリクエストを処理し、Webブラウザから設定値を変更する。
*: LEDの制御、PWMのデューティ比設定等をWebインターフェースから行う。
*: <br>
* JSON API
*: REST APIを実装し、JSON形式でデータを送受信する。
*: IoTデバイスとしての機能を強化する。
<br><br>
<br><br>