📢 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>© 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> | ||