「MSPM0G3519 - イーサネット (ENC28J60)」の版間の差分
細 Wiki がページ「MSP430F5529 - イーサネット (ENC28J60)」を「MSPM0G3519 - イーサネット (ENC28J60)」に、リダイレクトを残さずに移動しました |
編集の要約なし |
||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
MSPM0G3519は、Texas Instruments社の32ビットARM Cortex-M0+コアを搭載したマイコンである。<br> | |||
しかし、イーサネット機能は内蔵していないため、TCP/IP通信を行うには、外付けのイーサネットコントローラIC (ENC28J60等) | 最大80[MHz]で動作し、512[KB]のフラッシュROMと128[KB]のSRAMを搭載している。<br> | ||
しかし、イーサネット機能は内蔵していないため、TCP/IP通信を行うには、外付けのイーサネットコントローラIC (ENC28J60等) とlwIPまたはuIPライブラリを組み合わせて使用する。<br> | |||
<br> | <br> | ||
lwIPは、スウェーデンのAdam Dunkels氏が開発した軽量TCP/IPスタックで、uIPの後継として位置付けられている。<br> | |||
MSPM0G3519は十分なRAM容量 (128[KB]) を持つため、lwIPの使用が推奨される。<br> | |||
<br> | |||
lwIPは、より高速で安定したTCP/IP通信を実現し、多数の同時接続に対応できる。<br> | |||
<br> | <br> | ||
主な構成は以下の通りである。<br> | 主な構成は以下の通りである。<br> | ||
* | * MSPM0G3519 (マイコン) | ||
* ENC28J60 (イーサネットコントローラ) | * ENC28J60 (イーサネットコントローラ) | ||
* SPIインターフェース | |||
* lwIPライブラリ (TCP/IPスタック) | |||
*: <u>推奨</u> | |||
* uIPライブラリ (TCP/IPスタック) | * uIPライブラリ (TCP/IPスタック) | ||
* | *: 軽量版 : 非推奨 (メンテナンスされていない) | ||
<br> | <br> | ||
この構成により、MSPM0G3519を使用したWebサーバ、HTTPクライアント、Telnetサーバ、UDPアプリケーション、DHCPクライアント、DNSクライアント等の構築が可能となる。<br> | |||
<br><br> | <br><br> | ||
| 46行目: | 53行目: | ||
* マルチスレッド対応 | * マルチスレッド対応 | ||
*: RTOSと組み合わせた使用が可能 | *: RTOSと組み合わせた使用が可能 | ||
*: | *: ただし、MSPM0G3519では通常はシングルスレッド構成 (NO_SYS=1) で使用する | ||
* 移植性 | * 移植性 | ||
*: C言語で記述されており、様々なマイコンに移植可能 | *: C言語で記述されており、様々なマイコンに移植可能 | ||
*: ハードウェア依存部分が明確に分離されている | *: ハードウェア依存部分が明確に分離されている | ||
<br> | <br> | ||
<u> | <u>※MSPM0G3519でのlwIP使用について</u><br> | ||
<u> | <u>MSPM0G3519は、512[KB]のフラッシュと128[KB]のSRAMを持つため、lwIPを標準構成で十分に使用できる。</u><br> | ||
<u>これにより、以下に示すメリットが得られる。</u><br> | |||
* メリット | * メリット | ||
*: より多くの同時接続に対応 (通常10〜50接続以上) | *: より多くの同時接続に対応 (通常10〜50接続以上) | ||
| 58行目: | 66行目: | ||
*: DHCP、DNS等の上位機能を標準サポート | *: DHCP、DNS等の上位機能を標準サポート | ||
*: より安定したTCP実装 | *: より安定したTCP実装 | ||
*: パケットの分割送信と再送処理が効率的 | |||
*: ゼロコピー機能によるメモリ効率の向上 | |||
*: <br> | *: <br> | ||
* デメリット | * デメリット | ||
*: | *: uIPと比較して、初期設定がやや複雑 | ||
*: | *: コードサイズが大きい (ただし、MSPM0G3519のフラッシュ容量で十分に収まる) | ||
<br> | <br> | ||
MSPM0G3519では、lwIPの使用を推奨する。<br> | |||
RAMとフラッシュROMに十分な余裕があるため、lwIPの高機能性を最大限活用できる。<br> | |||
軽量なアプリケーションにはuIPも使用可能であるが、将来的な機能拡張を考慮すると、lwIPの採用がより適切である。<br> | |||
<br> | |||
<br><br> | <br><br> | ||
== uIPライブラリの特徴 == | == uIPライブラリの特徴 == | ||
<u>uIPは既にメンテナンスされていないため、使用は非推奨である。</u><br> | |||
<br> | |||
uIPは、組み込みシステム向けに最適化された軽量TCP/IPプロトコルスタックである。<br> | uIPは、組み込みシステム向けに最適化された軽量TCP/IPプロトコルスタックである。<br> | ||
<br> | <br> | ||
| 94行目: | 101行目: | ||
<u>同時接続数が限られており (通常1〜10接続程度)、スループットよりも省メモリを優先した設計となっている。</u><br> | <u>同時接続数が限られており (通常1〜10接続程度)、スループットよりも省メモリを優先した設計となっている。</u><br> | ||
<u>高速通信や多数の同時接続が必要な場合は、lwIP等のより高機能なスタックの使用を検討する必要がある。</u><br> | <u>高速通信や多数の同時接続が必要な場合は、lwIP等のより高機能なスタックの使用を検討する必要がある。</u><br> | ||
<br> | |||
<u>MSPM0G3519は128[KB]の十分なRAMを持つため、特別な理由がない限りlwIPの使用を推奨する。</u><br> | |||
<br><br> | <br><br> | ||
== ENC28J60イーサネットコントローラ == | == ENC28J60イーサネットコントローラ == | ||
MSPM0G3519はイーサネットコントローラを内蔵していないため、外付けのイーサネットコントローラチップが必要となる。<br> | |||
<br> | <br> | ||
Microchip社のENC28J60は、SPI接続のイーサネットコントローラで、組み込みシステムで広く使用されている。<br> | |||
<br> | <br> | ||
* IEEE 802.3準拠の10BASE-T Ethernetコントローラ | * IEEE 802.3準拠の10BASE-T Ethernetコントローラ | ||
* SPIインターフェース ( | * SPIインターフェース (最大20[MHz]) | ||
* 内蔵8KBのバッファRAM | * 内蔵8KBのバッファRAM | ||
* MACおよびPHY機能を統合 | * MACおよびPHY機能を統合 | ||
| 114行目: | 123行目: | ||
* 入手性が良好で、価格も比較的安価 | * 入手性が良好で、価格も比較的安価 | ||
* 豊富なサンプルコードとドキュメント | * 豊富なサンプルコードとドキュメント | ||
* | * MSPM0G3519のSPIペリフェラルで容易に接続可能 | ||
<br> | <br> | ||
<u>デメリット</u><br> | <u>デメリット</u><br> | ||
* | * 10[Mbps]のみの対応 (100[Mbps]非対応) | ||
* SPIのオーバーヘッドによりスループットが制限される。 | * SPIのオーバーヘッドによりスループットが制限される。 | ||
<br> | <br> | ||
| 124行目: | 133行目: | ||
<br><br> | <br><br> | ||
== | == MSPM0G3519の特徴 == | ||
MSPM0G3519は、Texas Instruments社の32ビットARM Cortex-M0+コアを搭載したマイコンで、MSPM0ファミリの上位モデルである。<br> | |||
<br> | <br> | ||
MSPM0G3519の主な特徴を以下に示す。<br> | |||
<br> | <br> | ||
* 128[KB] | * コア | ||
* | *: ARM 32ビット Cortex-M0+ CPU (最大80[MHz]) | ||
* | *: メモリ保護ユニット (MPU) 搭載 | ||
* | * メモリ | ||
* | *: 512[KB] フラッシュROM (デュアルバンク、ECC付き) | ||
* | *: 128[KB] SRAM (合計) | ||
* | **: SRAM バンク0: 64[KB] (ECC保護またはハードウェアパリティ、スタンバイモードまで保持) | ||
* | **: SRAM バンク1: 64[KB] (STOPモードまで保持) | ||
* | *: 16[KB] データフラッシュバンク (ECC保護) | ||
* | * 高性能アナログペリフェラル | ||
* | *: 2個の同時サンプリング12ビット4[Msps] ADC (最大27外部チャネル) | ||
*: 1個の12ビット1Msps DAC (出力バッファ内蔵) | |||
*: 3個の高速コンパレータ (8ビットリファレンスDAC内蔵) | |||
*: 内部共有電圧リファレンス (1.4[V] または 2.5[V]) | |||
*: 温度センサ内蔵 | |||
* 通信インターフェース | |||
*: 7個のUARTインターフェース | |||
**: 2個はLIN、IrDA、DALI、スマートカード、マンチェスターをサポート | |||
**: 3個はスタンバイモードでの低消費電力動作をサポート | |||
*: 3個のI2Cインターフェース (FM+ 1[Mbit/s]対応、STOP モードからのウェークアップ対応) | |||
*: 3個のSPIインターフェース (1個は最大32[Mbit/s]) | |||
*: 2個のCAN-FDインターフェース (CAN 2.0 A/B、CAN-FD対応) | |||
* タイマ / DMA | |||
*: 9個のタイマ (最大28個のPWMチャネル対応) | |||
*: 12チャネルDMAコントローラ | |||
* セキュリティ機能 | |||
*: AES-128/256アクセラレータ (GCM/GMAC、CCM/CBC-MAC、CBC、CTR対応) | |||
*: セキュアキーストレージ (最大4個のAESキー) | |||
*: 真性乱数生成器 (TRNG) | |||
*: フレキシブルなファイアウォール | |||
* その他 | |||
*: 最大94個のGPIO | |||
*: RTC (アラームおよびカレンダーモード付き) | |||
*: ウォッチドッグタイマ | |||
*: 低消費電力モード対応 | |||
*: 動作電圧: 1.62[V]〜3.6[V] | |||
*: 動作温度範囲: -40[℃]〜125[℃] | |||
<br> | <br> | ||
lwIP実装に必要なリソース要件は以下の通りである。<br> | |||
* フラッシュROM | * フラッシュROM | ||
*: | *: 約50〜80[KB] (lwIP本体+ドライバ+アプリケーション) | ||
* RAM | * RAM | ||
*: | *: 約20〜40[KB] (バッファ+メモリプール+スタック変数) | ||
<br> | <br> | ||
MSPM0G3519のリソースは、これらの要件を十分に満たしているため、lwIPの実装に適している。<br> | |||
MSP430F5529と比較して、以下の点で大幅に優位性がある。<br> | |||
* RAMが16倍 (8[KB] → 128[KB]) で、多数の同時接続とバッファを確保可能 | |||
* フラッシュが4倍 (128[KB] → 512[KB]) で、より多くの機能を実装可能 | |||
* 32ビットアーキテクチャにより、TCP/IP処理が高速 | |||
* 高速クロック (80[MHz]) により、SPIスループットが向上 | |||
* 12チャネルDMAコントローラにより、CPUを介さずにSPIデータ転送が可能 | |||
* CAN-FD対応により、車載やFA分野での応用が可能 | |||
<br><br> | <br><br> | ||
== ハードウェア接続 == | == ハードウェア接続 == | ||
MSPM0G3519とENC28J60は、SPIインターフェースで接続する。<br> | |||
<br> | <br> | ||
下表にMSPM0G3519のSPI0モジュールを使用する接続例を示す。<br> | |||
<br> | <br> | ||
<center> | <center> | ||
{| class="wikitable" | {| class="wikitable" | ||
|+ ピン接続表 (ENC28J60) | |+ ピン接続表 (ENC28J60) | ||
! | ! MSPM0G3519 !! ENC28J60 !! 機能 !! 説明 | ||
|- | |- | ||
| style="text-align:center;" | | | style="text-align:center;" | PB8 (SPI0_PICO) || style="text-align:center;" | MOSI (SI) || SPI || コントローラ出力/ペリフェラル入力 | ||
|- | |- | ||
| style="text-align:center;" | | | style="text-align:center;" | PB7 (SPI0_POCI) || style="text-align:center;" | MISO (SO) || SPI || コントローラ入力/ペリフェラル出力 | ||
|- | |- | ||
| style="text-align:center;" | | | style="text-align:center;" | PB9 (SPI0_SCLK) || style="text-align:center;" | SCLK (SCK) || SPI || シリアルクロック | ||
|- | |- | ||
| style="text-align:center;" | | | style="text-align:center;" | PB6 (GPIO) || style="text-align:center;" | CS# || 制御信号 || チップセレクト<br> (アクティブLow) | ||
|- | |- | ||
| style="text-align:center;" | | | style="text-align:center;" | PA18 (GPIO) || style="text-align:center;" | RESET# || 制御信号 || リセット信号<br> (アクティブLow) | ||
|- | |- | ||
| style="text-align:center;" | | | style="text-align:center;" | PA17 (GPIO) || style="text-align:center;" | INT || 割り込み || 割り込み信号<br> (アクティブLow) | ||
|- | |- | ||
| style="text-align:center;" | | | style="text-align:center;" | VDD || style="text-align:center;" | VCC || 電源 || 3.3V | ||
|- | |- | ||
| style="text-align:center;" | | | style="text-align:center;" | VSS || style="text-align:center;" | VSS || GND || グランド | ||
|} | |} | ||
</center> | </center> | ||
| 187行目: | 229行目: | ||
* LEDインジケータ | * LEDインジケータ | ||
*: LEDA (ピン15)、LEDB (ピン14) に接続可能 | *: LEDA (ピン15)、LEDB (ピン14) に接続可能 | ||
* ピン配置 | |||
*: 上記のピン配置は一例であり、MSPM0G3519の他のSPIペリフェラルやGPIOピンも使用可能 | |||
*: 使用するパッケージに応じて適切なピンを選択する | |||
*: MSPM0G3519は最大100ピンのパッケージがあり、ピン配置の自由度が高い | |||
<br> | <br> | ||
SPIインターフェースは、MSPM0G3519のSPI0ペリフェラルを使用する。<br> | |||
このペリフェラルはハードウェアSPIをサポートしており、DMAと組み合わせることで効率的な通信が可能である。<br> | |||
MSPM0G3519のSPI0は最大32[Mbit/s]に対応しているが、ENC28J60の最大速度は20[MHz]であるため、実際の動作速度はENC28J60の制限となる。<br> | |||
<br> | <br> | ||
制御信号 (CS#、RESET#) および 割り込み信号 (INT) は任意のGPIOピンに接続できるが、割り込み機能を使用する場合は、割り込み対応ピンを選択する必要がある。<br> | 制御信号 (CS#、RESET#) および 割り込み信号 (INT) は任意のGPIOピンに接続できるが、割り込み機能を使用する場合は、割り込み対応ピンを選択する必要がある。<br> | ||
<br> | <br> | ||
ENC28J60の電源は3.3[V] | ENC28J60の電源は3.3[V]であり、MSPM0G3519と同じ電圧で動作するため、レベル変換回路は不要である。<br> | ||
ただし、電源の安定性を確保するため、各電源ピン近くに適切なデカップリングコンデンサを配置することが重要である。<br> | ただし、電源の安定性を確保するため、各電源ピン近くに適切なデカップリングコンデンサを配置することが重要である。<br> | ||
<br><br> | <br><br> | ||
| 203行目: | 250行目: | ||
*: ユーザアプリケーション (Webサーバ、HTTPクライアント等) | *: ユーザアプリケーション (Webサーバ、HTTPクライアント等) | ||
* API層 | * API層 | ||
*: Sequential API (netconn API) | *: Sequential API (netconn API) : ※RTOS使用時 | ||
*: Raw API (コールバックベース、低レベルAPI) | *: Raw API (コールバックベース、低レベルAPI) : ※NO_SYS=1の時 | ||
* lwIPコア層 | * lwIPコア層 | ||
*: TCP、UDP、IP、ICMP、ARPプロトコル実装 | *: TCP、UDP、IP、ICMP、ARPプロトコル実装 | ||
| 217行目: | 264行目: | ||
*: tcp.c、udp.c、ip.c、icmp.c、arp.c等 | *: tcp.c、udp.c、ip.c、icmp.c、arp.c等 | ||
* lwip/src/api/ | * lwip/src/api/ | ||
*: Sequential API実装 | *: Sequential API実装 (RTOS使用時) | ||
*: netconn.c、api_lib.c、api_msg.c等 | *: netconn.c、api_lib.c、api_msg.c等 | ||
* lwip/src/netif/ | * lwip/src/netif/ | ||
| 224行目: | 271行目: | ||
* lwip/src/include/lwip/ | * lwip/src/include/lwip/ | ||
*: lwIPヘッダファイル群 | *: lwIPヘッダファイル群 | ||
* lwip/src/apps/ | |||
*: アプリケーション層実装 | |||
*: httpd.c、mqtt.c、snmp.c等 | |||
* arch/ | * arch/ | ||
*: アーキテクチャ依存部分 (移植先マイコン依存) | *: アーキテクチャ依存部分 (移植先マイコン依存) | ||
| 233行目: | 283行目: | ||
<br> | <br> | ||
この階層構造により、lwIPの中核部分とハードウェア依存部分が明確に分離されている。<br> | この階層構造により、lwIPの中核部分とハードウェア依存部分が明確に分離されている。<br> | ||
MSPM0G3519では、主にアーキテクチャ依存部分 (arch/)、ネットワークインターフェース層 (enc28j60_lwip.c)、設定ファイル (lwipopts.h) を実装または修正する必要がある。<br> | |||
<br> | |||
MSPM0G3519では、一般的にRaw APIを使用して実装する。(NO_SYS=1時)<br> | |||
Raw APIは、コールバック関数ベースの低レベルAPIで、RTOSを必要とせずに動作する。<br> | |||
Sequential APIは、よりシンプルな記述が可能であるが、RTOSが必要となる。<br> | |||
<br> | <br> | ||
MSPM0G3519のリソースは十分であるため、将来的にRTOSの導入を検討する場合は、Sequential APIへの移行も容易である。<br> | |||
<br> | <br> | ||
* lwIPの公式Webサイト | * lwIPの公式Webサイト | ||
| 243行目: | 295行目: | ||
* lwIPのGithub | * lwIPのGithub | ||
*: https://github.com/lwip-tcpip/lwip | *: https://github.com/lwip-tcpip/lwip | ||
* lwIP Documentation | |||
*: http://www.nongnu.org/lwip/2_1_x/index.html | |||
<br><br> | <br><br> | ||
| 280行目: | 334行目: | ||
<br> | <br> | ||
この階層構造により、uIPの中核部分とハードウェア依存部分が明確に分離されている。<br> | この階層構造により、uIPの中核部分とハードウェア依存部分が明確に分離されている。<br> | ||
MSPM0G3519への移植では、主にドライバ層 (enc28j60.c)、タイマ管理 (clock-arch.c)、設定ファイル (uip-conf.h) を追加または修正する必要がある。<br> | |||
また、多くの場合、uIPコア層はそのまま使用できる。<br> | また、多くの場合、uIPコア層はそのまま使用できる。<br> | ||
<br> | <br> | ||
| 289行目: | 343行目: | ||
<br><br> | <br><br> | ||
== | == lwIP : サンプルコード == | ||
以下の例では、ENC28J60およびlwIPを使用して、簡単なWebサーバを構築してHTTPリクエストに応答している。<br> | |||
MSPM0G3519の豊富なリソースを活用し、lwIPのRaw APIを使用している。<br> | |||
<br> | <br> | ||
==== | ==== lwIP設定ファイル (lwipopts.h) ==== | ||
まず、lwIPの動作パラメータを設定する。<br> | |||
<br> | <br> | ||
この設定ファイルでは、NO_SYS=1によりRTOSを使用しないスタンドアロン構成を指定している。<br> | |||
MSPM0G3519の128[KB] RAMを活用し、複数の同時接続とDHCPクライアント機能を有効化している。<br> | |||
<br> | <br> | ||
* | * メモリプール設定 | ||
*: | *: MSPM0G3519の豊富なRAM容量に応じて、メモリプールサイズを最適化している。 | ||
*: pbufプールやヒープサイズは、アプリケーションの要件に応じて柔軟に変更可能である。 | |||
* TCP接続数 | * TCP接続数 | ||
*: | *: 16接続に設定しているが、これはアプリケーションの要件に応じて調整できる。 | ||
*: | *: MSPM0G3519では、20〜30接続以上も十分に対応可能である。 | ||
* DHCP機能 | |||
*: DHCPクライアント機能を有効化することで、IPアドレスの自動取得が可能となる。 | |||
<br> | <br> | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
#ifndef | #ifndef __LWIPOPTS_H__ | ||
#define | #define __LWIPOPTS_H__ | ||
# | // NO_SYS==1: RTOSを使用しないスタンドアロン構成 | ||
#define NO_SYS 1 | |||
// メモリ管理設定 (MSPM0G3519の128KB RAMを活用) | |||
#define MEM_ALIGNMENT 4 | |||
#define MEM_SIZE (32*1024) // 32KB ヒープ (十分なRAMがあるため大きめに設定) | |||
#define MEMP_NUM_PBUF 32 | |||
#define MEMP_NUM_TCP_PCB 16 | |||
#define MEMP_NUM_TCP_PCB_LISTEN 8 | |||
#define MEMP_NUM_TCP_SEG 32 | |||
#define MEMP_NUM_SYS_TIMEOUT 16 | |||
// pbuf設定 | |||
#define PBUF_POOL_SIZE 32 | |||
#define PBUF_POOL_BUFSIZE 512 | |||
// TCP設定 | |||
#define LWIP_TCP 1 | |||
#define TCP_MSS 1460 | |||
#define TCP_WND (8 * TCP_MSS) // 8 MSS ウィンドウサイズ | |||
#define TCP_SND_BUF (8 * TCP_MSS) // 8 MSS 送信バッファ | |||
#define TCP_SND_QUEUELEN (4 * TCP_SND_BUF/TCP_MSS) | |||
// UDP設定 | |||
#define LWIP_UDP 1 | |||
#define MEMP_NUM_UDP_PCB 8 | |||
// ICMP設定 | |||
#define LWIP_ICMP 1 | |||
// | // DHCP設定 | ||
#define LWIP_DHCP 1 | |||
#define LWIP_NETIF_HOSTNAME 1 | |||
// | // DNS設定 | ||
#define | #define LWIP_DNS 1 // DNS有効化 | ||
#define | #define DNS_TABLE_SIZE 4 | ||
#define DNS_MAX_SERVERS 2 | |||
#define | |||
// | // ARP設定 | ||
#define | #define LWIP_ARP 1 | ||
#define ARP_TABLE_SIZE 10 | |||
#define | #define ARP_QUEUEING 1 | ||
#define | |||
// | // IP設定 | ||
#define | #define LWIP_IPV4 1 | ||
#define | #define LWIP_IPV6 0 | ||
#define | #define IP_REASSEMBLY 1 // IPフラグメント再構築を有効化 | ||
#define | #define IP_FRAG 1 // IPフラグメント分割を有効化 | ||
// | // チェックサム設定 | ||
#define | #define CHECKSUM_GEN_IP 1 | ||
#define | #define CHECKSUM_GEN_UDP 1 | ||
#define CHECKSUM_GEN_TCP 1 | |||
#define CHECKSUM_CHECK_IP 1 | |||
#define CHECKSUM_CHECK_UDP 1 | |||
#define CHECKSUM_CHECK_TCP 1 | |||
// | // 統計情報 (開発時は有効化、製品版では無効化してRAM節約) | ||
#define | #define LWIP_STATS 1 | ||
#define | #define LWIP_STATS_DISPLAY 1 | ||
// | // デバッグ設定 (開発時は有効化可能) | ||
#define | #define LWIP_DEBUG 0 | ||
// | // その他 | ||
#define | #define LWIP_NETCONN 0 // Raw API使用のため無効化 | ||
#define LWIP_SOCKET 0 // ソケットAPI不使用 | |||
#define LWIP_NETIF_LINK_CALLBACK 1 | |||
#define LWIP_NETIF_STATUS_CALLBACK 1 | |||
// | // プラットフォーム依存の型定義 | ||
#define | #define U16_F "hu" | ||
#define S16_F "hd" | |||
#define X16_F "hx" | |||
#define U32_F "u" | |||
#define S32_F "d" | |||
#define X32_F "x" | |||
#endif /* | #endif /* __LWIPOPTS_H__ */ | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
| 358行目: | 451行目: | ||
ENC28J60との通信を行うドライバ制御 (SPIを使用したレジスタアクセスやパケット送受信等) を記述する。<br> | ENC28J60との通信を行うドライバ制御 (SPIを使用したレジスタアクセスやパケット送受信等) を記述する。<br> | ||
<br> | <br> | ||
このドライバでは、MSPM0G3519のSPIペリフェラルを使用してENC28J60を制御する。<br> | |||
MSPM0G3519のSPI通信速度は最大32[Mbit/s]であり、ENC28J60の最大速度である20[MHz]に近い動作が可能である。<br> | |||
<br> | <br> | ||
以下の例では、基本的なレジスタアクセスとパケット送受信機能を提供している。<br> | |||
必要に応じて、DMAを使用した高速データ転送や、割り込み駆動の受信処理を追加することも可能である。<br> | |||
<br> | <br> | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
#include | // enc28j60.c : ENC28J60ドライバ | ||
#include "ti_msp_dl_config.h" // MSPM0G3519 SDK設定 | |||
#include "enc28j60.h" | #include "enc28j60.h" | ||
#include <stdint.h> | #include <stdint.h> | ||
| 388行目: | 479行目: | ||
#define ERXRDPTL 0x0C | #define ERXRDPTL 0x0C | ||
#define ERXRDPTH 0x0D | #define ERXRDPTH 0x0D | ||
#define EIE 0x1B | #define EIE 0x1B | ||
#define EIR 0x1C | #define EIR 0x1C | ||
#define | #define ESTAT 0x1D | ||
#define | #define ECON2 0x1E | ||
#define ECON1 0x1F | #define ECON1 0x1F | ||
// | // MACレジスタ | ||
#define MAIPGL 0x06 | #define MACON1 0x00 | 0x40 | ||
#define MAIPGH 0x07 | #define MACON3 0x02 | 0x40 | ||
#define MAMXFLL 0x0A | 0x40 | |||
#define MAMXFLH 0x0B | 0x40 | |||
#define MABBIPG 0x04 | 0x40 | |||
#define MAIPGL 0x06 | 0x40 | |||
#define MAIPGH 0x07 | 0x40 | |||
// SPIコマンド定義 | // SPIコマンド定義 | ||
| 446行目: | 503行目: | ||
#define ENC28J60_SOFT_RESET 0xFF | #define ENC28J60_SOFT_RESET 0xFF | ||
// | // ビット定義 | ||
#define | #define ECON1_TXRTS 0x08 | ||
#define | #define ECON1_RXEN 0x04 | ||
#define | #define ECON2_AUTOINC 0x80 | ||
#define | #define ECON2_PKTDEC 0x40 | ||
#define ESTAT_CLKRDY 0x01 | |||
#define MACON1_MARXEN 0x01 | |||
#define MACON3_PADCFG0 0x20 | |||
#define MACON3_TXCRCEN 0x10 | |||
#define MACON3_FRMLNEN 0x02 | |||
// チップセレクト制御 | // チップセレクト制御 (実際のピン定義に応じて変更) | ||
#define ENC28J60_CS_LOW() ( | #define ENC28J60_CS_LOW() DL_GPIO_clearPins(GPIO_ENC_CS_PORT, GPIO_ENC_CS_PIN) | ||
#define ENC28J60_CS_HIGH() ( | #define ENC28J60_CS_HIGH() DL_GPIO_setPins(GPIO_ENC_CS_PORT, GPIO_ENC_CS_PIN) | ||
static uint8_t current_bank = 0; | 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バイト送受信 | // SPIバイト送受信 | ||
static uint8_t spi_transfer(uint8_t data) | static uint8_t spi_transfer(uint8_t data) | ||
{ | { | ||
// SPIにデータ送信 | |||
DL_SPI_transmitData8(SPI_0_INST, data); | |||
while ( | |||
return | // 送信完了待ち | ||
while (DL_SPI_isBusy(SPI_0_INST)); | |||
// 受信データ取得 | |||
return DL_SPI_receiveData8(SPI_0_INST); | |||
} | } | ||
| 473行目: | 548行目: | ||
if (bank != current_bank) { | if (bank != current_bank) { | ||
enc28j60_write_op(ENC28J60_BIT_FIELD_CLR, ECON1, 0x03); | enc28j60_write_op(ENC28J60_BIT_FIELD_CLR, ECON1, 0x03); | ||
enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, bank); | enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, bank); | ||
| 512行目: | 586行目: | ||
} | } | ||
// ビット操作 | // ビット操作 | ||
void enc28j60_write_op(uint8_t op, uint8_t address, uint8_t data) | void enc28j60_write_op(uint8_t op, uint8_t address, uint8_t data) | ||
{ | { | ||
| 555行目: | 629行目: | ||
ENC28J60_CS_HIGH(); | ENC28J60_CS_HIGH(); | ||
delay_us(1000); // 1ms待機 | |||
// クロックが安定するまで待機 | // クロックが安定するまで待機 | ||
while (!(enc28j60_read(ESTAT) & | while (!(enc28j60_read(ESTAT) & ESTAT_CLKRDY)); | ||
// 受信バッファ設定 (0x0000 - 0x19FF) | // 受信バッファ設定 (0x0000 - 0x19FF) | ||
| 574行目: | 648行目: | ||
enc28j60_write(ETXNDH, 0x1F); | enc28j60_write(ETXNDH, 0x1F); | ||
// MAC初期化 | // MAC初期化 | ||
enc28j60_write(MACON1, | enc28j60_write(MACON1, MACON1_MARXEN); | ||
enc28j60_write(MACON3, | enc28j60_write(MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); | ||
enc28j60_write(MAMXFLL, 0xEE); | enc28j60_write(MAMXFLL, 0xEE); | ||
enc28j60_write(MAMXFLH, 0x05); | enc28j60_write(MAMXFLH, 0x05); | ||
enc28j60_write(MABBIPG, 0x12); | enc28j60_write(MABBIPG, 0x12); | ||
enc28j60_write(MAIPGL, 0x12); | enc28j60_write(MAIPGL, 0x12); | ||
enc28j60_write(MAIPGH, 0x0C); | enc28j60_write(MAIPGH, 0x0C); | ||
// MACアドレス設定 | // MACアドレス設定 | ||
enc28j60_write(0x00 | 0x60, mac_address[0]); // MAADR1 | enc28j60_write(0x00 | 0x60, mac_address[0]); // MAADR1 | ||
enc28j60_write(0x01 | 0x60, mac_address[1]); // MAADR2 | enc28j60_write(0x01 | 0x60, mac_address[1]); // MAADR2 | ||
| 592行目: | 666行目: | ||
// 割り込み有効化 | // 割り込み有効化 | ||
enc28j60_write(EIE, INTIE | PKTIE | enc28j60_write(EIE, 0xC0); // INTIE | PKTIE | ||
// 受信有効化 | // 受信有効化 | ||
enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, | enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); | ||
} | } | ||
| 605行目: | 679行目: | ||
enc28j60_write(EWRPTH, 0x1A); | enc28j60_write(EWRPTH, 0x1A); | ||
// 制御バイト書き込み | // 制御バイト書き込み | ||
enc28j60_write_buffer((uint8_t[]){0x00}, 1); | enc28j60_write_buffer((uint8_t[]){0x00}, 1); | ||
| 616行目: | 690行目: | ||
// 送信開始 | // 送信開始 | ||
enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, | enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); | ||
// 送信完了待ち (オプション: タイムアウト処理を追加することを推奨) | |||
while (enc28j60_read(ECON1) & ECON1_TXRTS); | |||
} | } | ||
| 625行目: | 702行目: | ||
uint8_t header[6]; | uint8_t header[6]; | ||
// | // 受信パケット数確認 | ||
if (enc28j60_read(0x19) == 0) { // EPKTCNT | if (enc28j60_read(0x19) == 0) { // EPKTCNT | ||
return 0; | return 0; | ||
} | } | ||
// ヘッダ読み出し | // ヘッダ読み出し | ||
enc28j60_read_buffer(header, 6); | enc28j60_read_buffer(header, 6); | ||
// パケット長取得 | // パケット長取得 | ||
len = (header[3] << 8) | header[2]; | len = (header[3] << 8) | header[2]; | ||
len -= 4; // CRCを除く | len -= 4; // CRCを除く | ||
| 648行目: | 725行目: | ||
// パケットデクリメント | // パケットデクリメント | ||
enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON2, | enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); | ||
return (len <= max_len) ? len : 0; | return (len <= max_len) ? len : 0; | ||
| 654行目: | 731行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
==== | ==== lwIPネットワークインターフェース (netif_enc28j60.c) ==== | ||
lwIPとENC28J60ドライバを接続するネットワークインターフェース層を実装する。<br> | |||
この層は、lwIPのpbuf構造体とENC28J60のパケットバッファ間でデータを変換する役割を持つ。<br> | |||
<br> | <br> | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
#include | // netif_enc28j60.c : lwIPネットワークインターフェース | ||
#include " | |||
#include " | #include "lwip/opt.h" | ||
#include "lwip/def.h" | |||
#include "lwip/mem.h" | |||
#include "lwip/pbuf.h" | |||
#include "lwip/sys.h" | |||
#include "lwip/stats.h" | |||
#include "netif/etharp.h" | |||
#include "enc28j60.h" | #include "enc28j60.h" | ||
#include < | #include <string.h> | ||
#define IFNAME0 'e' | |||
#define IFNAME1 'n' | |||
#define | |||
#define | |||
// | // パケット送信 | ||
uint8_t | static err_t low_level_output(struct netif *netif, struct pbuf *p) | ||
{ | |||
struct pbuf *q; | |||
uint8_t buffer[1518]; | |||
uint16_t len = 0; | |||
// pbufチェーンからバッファにコピー | |||
for (q = p; q != NULL; q = q->next) { | |||
if (len + q->len > sizeof(buffer)) { | |||
return ERR_BUF; | |||
} | |||
memcpy(&buffer[len], q->payload, q->len); | |||
len += q->len; | |||
} | |||
// ENC28J60で送信 | |||
enc28j60_packet_send(buffer, len); | |||
// | |||
return ERR_OK; | |||
} | } | ||
// | // パケット受信 | ||
static struct pbuf *low_level_input(struct netif *netif) | |||
{ | { | ||
struct pbuf *p, *q; | |||
uint8_t buffer[1518]; | |||
uint16_t len; | |||
// ENC28J60からパケット受信 | |||
len = enc28j60_packet_receive(buffer, sizeof(buffer)); | |||
if (len == 0) { | |||
return NULL; | |||
} | |||
// pbuf割り当て | |||
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); | |||
if (p != NULL) { | |||
uint16_t offset = 0; | |||
// pbufチェーンにデータをコピー | |||
for (q = p; q != NULL; q = q->next) { | |||
memcpy(q->payload, &buffer[offset], q->len); | |||
offset += q->len; | |||
} | |||
} | |||
return p; | |||
} | } | ||
// | // ネットワークインターフェース初期化 | ||
err_t netif_enc28j60_init(struct netif *netif) | |||
{ | { | ||
netif->name[0] = IFNAME0; | |||
netif->name[1] = IFNAME1; | |||
netif->output = etharp_output; | |||
netif->linkoutput = low_level_output; | |||
netif->mtu = 1500; | |||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; | |||
// | // MACアドレス設定 | ||
netif->hwaddr_len = 6; | |||
netif->hwaddr[0] = 0x02; | |||
netif->hwaddr[1] = 0x00; | |||
netif->hwaddr[2] = 0x00; | |||
netif->hwaddr[3] = 0x00; | |||
netif->hwaddr[4] = 0x00; | |||
netif->hwaddr[5] = 0x01; | |||
// ENC28J60初期化 | |||
enc28j60_init(netif->hwaddr); | |||
return ERR_OK; | |||
} | |||
// パケット受信処理 (メインループから定期的に呼び出す) | |||
void netif_enc28j60_input(struct netif *netif) | |||
{ | |||
struct pbuf *p; | |||
// 受信可能なパケットを全て処理 | |||
while ((p = low_level_input(netif)) != NULL) { | |||
if (netif->input(p, netif) != ERR_OK) { | |||
pbuf_free(p); | |||
} | } | ||
} | } | ||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== メイン処理 ==== | |||
メイン処理では、MSPM0G3519の初期化、lwIPの初期化、HTTPサーバの起動を記述する。<br> | |||
<br> | |||
<syntaxhighlight lang="c"> | |||
// main.c : メイン処理 | |||
#include "ti_msp_dl_config.h" | |||
#include "lwip/init.h" | |||
#include "lwip/netif.h" | |||
#include "lwip/timeouts.h" | |||
#include "lwip/dhcp.h" | |||
#include "netif/etharp.h" | |||
#include "netif_enc28j60.h" | |||
#include "lwip/apps/httpd.h" | |||
// lwIP用タイマ (1[ms]周期) | |||
volatile uint32_t lwip_timer_ms = 0; | |||
// タイマ割り込みハンドラ (1[ms]周期) | |||
void TIMER_0_INST_IRQHandler(void) | |||
{ | |||
lwip_timer_ms++; | |||
} | } | ||
int main(void) | int main(void) | ||
{ | { | ||
struct netif netif; | |||
ip4_addr_t ipaddr, netmask, gw; | |||
// MSPM0G3519ペリフェラル初期化 | |||
SYSCFG_DL_init(); | |||
// | // タイマ割り込み有効化 | ||
NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN); | |||
// | // lwIP初期化 | ||
lwip_init(); | |||
// | // 静的IPアドレス設定 (DHCPを使用しない場合) | ||
IP4_ADDR(&ipaddr, 192, 168, 1, 100); | |||
IP4_ADDR(&netmask, 255, 255, 255, 0); | |||
IP4_ADDR(&gw, 192, 168, 1, 1); | |||
// | // ネットワークインターフェース追加 | ||
netif_add(&netif, &ipaddr, &netmask, &gw, NULL, netif_enc28j60_init, ethernet_input); | |||
netif_set_default(&netif); | |||
netif_set_up(&netif); | |||
// | // DHCPクライアント起動 (オプション) | ||
// dhcp_start(&netif); | |||
// | // HTTPサーバ初期化 | ||
httpd_init(); | |||
// | // メインループ | ||
while (1) { | |||
// パケット受信処理 | |||
netif_enc28j60_input(&netif); | |||
// lwIPタイムアウト処理 (周期的なタイマイベント処理) | |||
sys_check_timeouts(); | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== 簡易HTTPサーバの構築 ==== | |||
lwIPには、HTTPサーバ実装 (httpd.c) が含まれているため、これを利用することで簡単にWebサーバを構築できる。<br> | |||
以下の例では、カスタムCGIハンドラを追加して、動的なコンテンツを生成している。<br> | |||
<br> | |||
<syntaxhighlight lang="c"> | |||
// httpd_cgi.c - CGIハンドラ | |||
#include "lwip/apps/httpd.h" | |||
#include "ti_msp_dl_config.h" | |||
#include <string.h> | |||
// LED状態取得CGI | |||
const char *led_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) | |||
{ | |||
// LEDの状態を確認 | |||
uint32_t led_state = DL_GPIO_readPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN); | |||
if (led_state) { | |||
/ | return "/led_on.ssi"; | ||
} | |||
else { | |||
return "/led_off.ssi"; | |||
} | |||
} | |||
// LED制御CGI | |||
const char *led_control_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) | |||
{ | |||
int i; | |||
for (i = 0; i < iNumParams; i++) { | |||
if (strcmp(pcParam[i], "led") == 0) { | |||
if (strcmp(pcValue[i], "on") == 0) { | |||
DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN); | |||
} | } | ||
else if ( | else if (strcmp(pcValue[i], "off") == 0) { | ||
DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN); | |||
} | } | ||
} | } | ||
} | |||
return "/index.html"; | |||
} | |||
// CGIハンドラテーブル | |||
static const tCGI cgi_handlers[] = { | |||
{ "/led_status.cgi", led_cgi_handler }, | |||
{ "/led_control.cgi", led_control_cgi_handler } | |||
}; | |||
// CGI初期化 | |||
void httpd_cgi_init(void) | |||
{ | |||
http_set_cgi_handlers(cgi_handlers, sizeof(cgi_handlers) / sizeof(tCGI)); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
lwIPのHTTPサーバは、ファイルシステムを持たないため、Webページのコンテンツはプログラムに埋め込む必要がある。<br> | |||
lwIPには、makefsdataツールが付属しており、HTMLファイルをC言語のヘッダファイルに変換することができる。<br> | |||
<br> | |||
<syntaxhighlight lang="html"> | |||
<!-- index.html --> | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<title>MSPM0G3519 Web Server</title> | |||
<style> | |||
body { font-family: Arial, sans-serif; margin: 40px; } | |||
h1 { color: #333; } | |||
.info { background-color: #f0f0f0; padding: 20px; border-radius: 5px; } | |||
.controls { margin-top: 20px; } | |||
button { padding: 10px 20px; margin: 5px; } | |||
</style> | |||
</head> | |||
<body> | |||
<h1>MSPM0G3519 Web Server</h1> | |||
<div class="info"> | |||
<p>This is running on lwIP stack with ENC28J60 Ethernet controller.</p> | |||
<ul> | |||
<li>MCU: MSPM0G3519 (ARM Cortex-M0+, 80MHz)</li> | |||
<li>Flash: 512KB</li> | |||
<li>RAM: 128KB</li> | |||
<li>Ethernet: ENC28J60 (10Mbps)</li> | |||
</ul> | |||
</div> | |||
<div class="controls"> | |||
<h2>LED Control</h2> | |||
<form action="/led_control.cgi" method="get"> | |||
<button type="submit" name="led" value="on">LED ON</button> | |||
<button type="submit" name="led" value="off">LED OFF</button> | |||
</form> | |||
</div> | |||
</body> | |||
</html> | |||
</syntaxhighlight> | |||
<br><br> | |||
== その他の注意事項 == | |||
lwIPを使用したTCP/IP通信を実装する場合の注意事項を以下に示す。<br> | |||
<br> | |||
==== タイマ管理 ==== | |||
lwIPは、sys_check_timeouts関数を定期的に呼び出す必要がある。<br> | |||
メインループ内で適切な間隔 (推奨 : 数[ms]〜数十[ms]) で呼び出すことを推奨する。<br> | |||
<br> | |||
タイマの精度は、TCP通信の品質に影響するため、正確なタイマ実装が重要である。<br> | |||
<br> | |||
==== メモリ管理 ==== | |||
lwIPは、メモリプール方式を採用しているため、lwipopts.hで適切なプールサイズを設定する必要がある。<br> | |||
メモリ不足が発生すると、パケットのドロップや接続の失敗が生じる可能性がある。<br> | |||
<br> | <br> | ||
MSPM0G3519は128[KB]のRAMを持つため、メモリプールサイズは柔軟に調整できる。<br> | |||
<br> | |||
==== pbuf処理 ==== | |||
pbuf構造体は、参照カウント方式でメモリ管理されている。<br> | |||
pbuf_free関数を適切に呼び出さないと、メモリリークが発生する。<br> | |||
<br> | |||
特に、パケット受信処理やエラー処理時には注意が必要である。<br> | |||
<br> | |||
==== 割り込みコンテキスト ==== | |||
lwIPの関数は、基本的に割り込みコンテキストから呼び出すべきではない。<br> | |||
パケット受信処理は、メインループで行うことを推奨する。<br> | |||
<br> | |||
ENC28J60の割り込み信号を使用する場合は、割り込みハンドラ内ではフラグを立てるのみとし、実際の処理はメインループで行う。<br> | |||
<br> | <br> | ||
==== | ==== DHCPの使用 ==== | ||
DHCPクライアントを使用する場合、IPアドレスの取得に時間がかかる場合がある。<br> | |||
netif_set_link_callback関数を使用して、リンクアップ時にDHCPを起動することを推奨する。<br> | |||
<br> | <br> | ||
DHCPによるIPアドレス取得中は、TCP/UDP通信ができないため、アプリケーションの実装時に考慮する必要がある。<br> | |||
<br> | <br> | ||
==== エンディアン変換 ==== | |||
ネットワークバイトオーダー (ビッグエンディアン) とホストバイトオーダーの変換に注意する。<br> | |||
<br> | <br> | ||
lwIPは、htons()、ntohs()、htonl()、ntohl()等のマクロを提供している。<br> | |||
<br> | <br> | ||
ARM Cortex-M0+はリトルエンディアンであるため、適切な変換が必要である。<br> | |||
<br><br> | <br><br> | ||
== | == トラブルシューティング == | ||
==== パケットが受信できない ==== | |||
* ENC28J60の初期化、MACアドレス設定、ネットワークインターフェースの設定を確認する。 | |||
* netif_set_up()が呼ばれているか、NETIF_FLAG_LINK_UPが設定されているかを確認する。 | |||
* SPIの通信速度が高すぎる場合、通信が不安定になる可能性があるため、速度を下げてみる。 | |||
<br> | |||
==== ARPが応答しない ==== | |||
*: MACアドレスが正しく設定されているか、etharp_output()が正しく設定されているかを確認する。 | |||
* ARP_TABLE_SIZEが適切に設定されているかを確認する。 | |||
<br> | |||
==== TCP接続が確立しない ==== | |||
* ファイアウォールやルータの設定を確認する。 | |||
* sys_check_timeouts()が定期的に呼ばれているかを確認する。 | |||
* TCPの3ウェイハンドシェイクが正常に行われているか、パケットキャプチャで確認する。 | |||
<br> | <br> | ||
* | ==== メモリ不足エラー ==== | ||
* | * lwipopts.hのメモリプールサイズを増やす。 | ||
* | * LWIP_STATS=1を有効化して、メモリ使用状況を確認する。 | ||
* MSPM0G3519は128[KB]のRAMを持つため、十分なメモリを割り当てることができる。 | |||
<br> | <br> | ||
< | ==== HTTPサーバが応答しない ==== | ||
* httpd_init関数が呼ばれているか、または、ポート80がリッスンされているかを確認する。 | |||
* fsdata.cが正しく生成されているかを確認する。 | |||
* makefsdata.pyを使用して、HTMLファイルを正しく変換する。 | |||
<br><br> | |||
== SPI通信の速度 == | |||
MSPM0G3519のSPI0は最大32[Mbit/s]に対応しているが、ENC28J60の最大速度は20[MHz]である。<br> | |||
そのため、実際の動作速度はENC28J60の制限となる。<br> | |||
<br> | <br> | ||
SPIクロック周波数は、MSPM0G3519のクロック設定により調整可能である。<br> | |||
通信が不安定な場合は、SPIクロック周波数を下げて (例: 10[MHz]、5[MHz]) 通信の安定性を確認することを推奨する。<br> | |||
配線長やノイズの影響により、実際の動作速度は制限される場合がある。<br> | |||
<br><br> | |||
== DMAの活用 == | |||
MSPM0G3519の12チャネルDMAコントローラを使用することで、CPUの負荷を軽減しつつ、高速なデータ転送が可能となる。<br> | |||
特に、大きなパケットを連続的に送受信する場合、DMAの活用により大幅なパフォーマンス向上が期待できる。<br> | |||
<br> | <br> | ||
== | DMAを使用する場合は、以下の点に注意する必要がある。<br> | ||
* DMAチャネルの割り当て | |||
*: 他のペリフェラルとDMAチャネルが競合しないように設定する。 | |||
*: MSPM0G3519は12チャネルのDMAを持つため、柔軟に割り当てが可能である。 | |||
* バッファアライメント | |||
*: DMA転送用のバッファは、適切なアライメント (4バイト境界等) に配置する。 | |||
* 割り込みハンドリング | |||
*: DMA転送完了割り込みを適切に処理する。 | |||
*: DMA転送完了後に、次の処理 (パケット受信処理等) を行う。 | |||
<br><br> | |||
== CAN-FDインターフェースの活用 == | |||
MSPM0G3519は2個のCAN-FDインターフェースを搭載しているため、イーサネット通信とCAN通信を組み合わせたゲートウェイ応用が可能である。<br> | |||
例えば、以下のような応用が考えられる。<br> | |||
* CANバスとイーサネットを接続するゲートウェイ | |||
*: 車載ネットワークやFA機器のデータをイーサネット経由でクラウドに送信 | |||
* CANデータのロギングとWeb表示 | |||
*: CANバスのデータをリアルタイムでWebブラウザに表示 | |||
* イーサネット経由でのCAN制御 | |||
*: Webブラウザから車載機器やFA機器を制御 | |||
<br><br> | <br><br> | ||
== 参考資料 == | == 参考資料 == | ||
* | * MSPM0G3519データシート | ||
*: https://www.ti.com/product/ja-jp/ | *: https://www.ti.com/product/ja-jp/MSPM0G3519 | ||
* MSPM0 SDK | |||
*: https://www.ti.com/tool/MSPM0-SDK | |||
* MSPM0 G-Series 80-MHz Microcontrollers Technical Reference Manual | |||
*: https://www.ti.com/lit/pdf/SLAU846 | |||
* ENC28J60データシート | * ENC28J60データシート | ||
*: https://www.microchip.com/en-us/product/ENC28J60 | *: https://www.microchip.com/en-us/product/ENC28J60 | ||
* | * lwIP公式ドキュメント | ||
*: | *: http://www.nongnu.org/lwip/2_1_x/index.html | ||
*: https://github.com/ | * lwIP Github | ||
*: https://github.com/lwip-tcpip/lwip | |||
* [https://www.rfc-editor.org/rfc/rfc793 RFC 793] | * [https://www.rfc-editor.org/rfc/rfc793 RFC 793] | ||
*: Transmission Control Protocol (TCP) | *: Transmission Control Protocol (TCP) | ||
* [https://www.rfc-editor.org/rfc/rfc826 RFC 826] | * [https://www.rfc-editor.org/rfc/rfc826 RFC 826] | ||
*: An Ethernet Address Resolution Protocol (ARP) | *: An Ethernet Address Resolution Protocol (ARP) | ||
* [https://www.rfc-editor.org/rfc/rfc2131 RFC 2131] | |||
*: Dynamic Host Configuration Protocol (DHCP) | |||
<br><br> | <br><br> | ||
{{#seo: | {{#seo: | ||
|title={{PAGENAME}} : | |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki | ||
|keywords=MochiuWiki,MSP430, | |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 | ||
|description={{PAGENAME}} - | |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux | ||
|image=/resources/assets/MochiuLogo_Single_Blue.png | |||
}} | }} | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:MSP430]] | [[カテゴリ:MSP430]] | ||
2025年12月22日 (月) 14:42時点における版
概要
MSPM0G3519は、Texas Instruments社の32ビットARM Cortex-M0+コアを搭載したマイコンである。
最大80[MHz]で動作し、512[KB]のフラッシュROMと128[KB]のSRAMを搭載している。
しかし、イーサネット機能は内蔵していないため、TCP/IP通信を行うには、外付けのイーサネットコントローラIC (ENC28J60等) とlwIPまたはuIPライブラリを組み合わせて使用する。
lwIPは、スウェーデンのAdam Dunkels氏が開発した軽量TCP/IPスタックで、uIPの後継として位置付けられている。
MSPM0G3519は十分なRAM容量 (128[KB]) を持つため、lwIPの使用が推奨される。
lwIPは、より高速で安定したTCP/IP通信を実現し、多数の同時接続に対応できる。
主な構成は以下の通りである。
- MSPM0G3519 (マイコン)
- ENC28J60 (イーサネットコントローラ)
- SPIインターフェース
- lwIPライブラリ (TCP/IPスタック)
- 推奨
- uIPライブラリ (TCP/IPスタック)
- 軽量版 : 非推奨 (メンテナンスされていない)
この構成により、MSPM0G3519を使用したWebサーバ、HTTPクライアント、Telnetサーバ、UDPアプリケーション、DHCPクライアント、DNSクライアント等の構築が可能となる。
ENC28J60 と W5500
イーサネット通信を実現するには、大きく分けて2つの階層がある。
- 上位層
- ネットワーク層以上で、TCP/IPプロトコルの処理を行う。
- 下位層
- 物理層 および データリンク層 で、イーサネットフレームの送受信を担当する。
ENC28J60は下位層のみを持つICである。
イーサネットフレームの送受信はできるが、TCP/IPプロトコルの処理機能は持たない。
これを イーサネットMAC/PHYコントローラ と呼ぶ。
一方、W5500は、上位層および下位層の両方をIC内に持つため、TCP/IPの複雑な処理も含めて全てW5500が自動的に処理する。
これを ハードウェアTCP/IPスタック内蔵型 と呼ぶ。
lwIPライブラリの特徴
lwIP (Lightweight IP) は、スウェーデンのAdam Dunkels氏が開発したTCP/IPプロトコルスタックで、uIPの後継として位置付けられている。
lwIPは、uIPよりも高機能であり、より多くのメモリを使用するが、その分、高いスループットと多数の同時接続に対応できる設計となっている。
lwIPの特徴を以下に示す。
- 高機能なTCP/IP実装
- 完全なTCP/IP実装で約40〜60[KB]程度のコードサイズ
- uIPと比較して、より高速で安定した通信が可能
- 適度なRAM使用量
- 通常構成で約20〜40[KB]程度のRAM使用量
- メモリプール方式による効率的なバッファ管理
- RFC準拠
- TCP、UDP、IP、ICMP、ARP、DHCP、DNSプロトコルに対応
- HTTP、SNMP、MQTT等の上位プロトコルも実装可能
- マルチスレッド対応
- RTOSと組み合わせた使用が可能
- ただし、MSPM0G3519では通常はシングルスレッド構成 (NO_SYS=1) で使用する
- 移植性
- C言語で記述されており、様々なマイコンに移植可能
- ハードウェア依存部分が明確に分離されている
※MSPM0G3519でのlwIP使用について
MSPM0G3519は、512[KB]のフラッシュと128[KB]のSRAMを持つため、lwIPを標準構成で十分に使用できる。
これにより、以下に示すメリットが得られる。
- メリット
- より多くの同時接続に対応 (通常10〜50接続以上)
- 高速なデータ転送が可能
- DHCP、DNS等の上位機能を標準サポート
- より安定したTCP実装
- パケットの分割送信と再送処理が効率的
- ゼロコピー機能によるメモリ効率の向上
- デメリット
- uIPと比較して、初期設定がやや複雑
- コードサイズが大きい (ただし、MSPM0G3519のフラッシュ容量で十分に収まる)
MSPM0G3519では、lwIPの使用を推奨する。
RAMとフラッシュROMに十分な余裕があるため、lwIPの高機能性を最大限活用できる。
軽量なアプリケーションにはuIPも使用可能であるが、将来的な機能拡張を考慮すると、lwIPの採用がより適切である。
uIPライブラリの特徴
uIPは既にメンテナンスされていないため、使用は非推奨である。
uIPは、組み込みシステム向けに最適化された軽量TCP/IPプロトコルスタックである。
uIPの特徴を以下に示す。
- 小さなコードサイズ
- 完全なTCP/IP実装で約10〜20[KB]程度のコードサイズ
- 低RAM使用量
- 最小構成で数[KB]程度のRAM使用量
- バッファ管理が効率的に設計されている
- RFC準拠
- TCP、UDP、IP、ICMP、ARPプロトコルに対応
- HTTP 1.0サーバ実装を含む
- 移植性
- C言語で記述されており、様々なマイコンに移植可能
- ハードウェア依存部分が明確に分離されている
※注意
uIPは軽量化のため、いくつかの制約がある。
同時接続数が限られており (通常1〜10接続程度)、スループットよりも省メモリを優先した設計となっている。
高速通信や多数の同時接続が必要な場合は、lwIP等のより高機能なスタックの使用を検討する必要がある。
MSPM0G3519は128[KB]の十分なRAMを持つため、特別な理由がない限りlwIPの使用を推奨する。
ENC28J60イーサネットコントローラ
MSPM0G3519はイーサネットコントローラを内蔵していないため、外付けのイーサネットコントローラチップが必要となる。
Microchip社のENC28J60は、SPI接続のイーサネットコントローラで、組み込みシステムで広く使用されている。
- IEEE 802.3準拠の10BASE-T Ethernetコントローラ
- SPIインターフェース (最大20[MHz])
- 内蔵8KBのバッファRAM
- MACおよびPHY機能を統合
- パケットフィルタリング機能
- 低消費電力設計 (スリープモード対応)
- 3.3V単一電源動作
- 28ピンSSOP、QFNパッケージ
メリット
- SPIインターフェースのため、配線が簡単 (4本の信号線)
- 入手性が良好で、価格も比較的安価
- 豊富なサンプルコードとドキュメント
- MSPM0G3519のSPIペリフェラルで容易に接続可能
デメリット
- 10[Mbps]のみの対応 (100[Mbps]非対応)
- SPIのオーバーヘッドによりスループットが制限される。
上記のデメリットはあるものの、多くの組み込みアプリケーションでは10[Mbps]で十分であり、
配線の簡便性と入手性の良さからENC28J60は組み込みイーサネット実装において人気の高い選択肢となっている。
MSPM0G3519の特徴
MSPM0G3519は、Texas Instruments社の32ビットARM Cortex-M0+コアを搭載したマイコンで、MSPM0ファミリの上位モデルである。
MSPM0G3519の主な特徴を以下に示す。
- コア
- ARM 32ビット Cortex-M0+ CPU (最大80[MHz])
- メモリ保護ユニット (MPU) 搭載
- メモリ
- 512[KB] フラッシュROM (デュアルバンク、ECC付き)
- 128[KB] SRAM (合計)
- SRAM バンク0: 64[KB] (ECC保護またはハードウェアパリティ、スタンバイモードまで保持)
- SRAM バンク1: 64[KB] (STOPモードまで保持)
- 16[KB] データフラッシュバンク (ECC保護)
- 高性能アナログペリフェラル
- 2個の同時サンプリング12ビット4[Msps] ADC (最大27外部チャネル)
- 1個の12ビット1Msps DAC (出力バッファ内蔵)
- 3個の高速コンパレータ (8ビットリファレンスDAC内蔵)
- 内部共有電圧リファレンス (1.4[V] または 2.5[V])
- 温度センサ内蔵
- 通信インターフェース
- 7個のUARTインターフェース
- 2個はLIN、IrDA、DALI、スマートカード、マンチェスターをサポート
- 3個はスタンバイモードでの低消費電力動作をサポート
- 3個のI2Cインターフェース (FM+ 1[Mbit/s]対応、STOP モードからのウェークアップ対応)
- 3個のSPIインターフェース (1個は最大32[Mbit/s])
- 2個のCAN-FDインターフェース (CAN 2.0 A/B、CAN-FD対応)
- タイマ / DMA
- 9個のタイマ (最大28個のPWMチャネル対応)
- 12チャネルDMAコントローラ
- セキュリティ機能
- AES-128/256アクセラレータ (GCM/GMAC、CCM/CBC-MAC、CBC、CTR対応)
- セキュアキーストレージ (最大4個のAESキー)
- 真性乱数生成器 (TRNG)
- フレキシブルなファイアウォール
- その他
- 最大94個のGPIO
- RTC (アラームおよびカレンダーモード付き)
- ウォッチドッグタイマ
- 低消費電力モード対応
- 動作電圧: 1.62[V]〜3.6[V]
- 動作温度範囲: -40[℃]〜125[℃]
lwIP実装に必要なリソース要件は以下の通りである。
- フラッシュROM
- 約50〜80[KB] (lwIP本体+ドライバ+アプリケーション)
- RAM
- 約20〜40[KB] (バッファ+メモリプール+スタック変数)
MSPM0G3519のリソースは、これらの要件を十分に満たしているため、lwIPの実装に適している。
MSP430F5529と比較して、以下の点で大幅に優位性がある。
- RAMが16倍 (8[KB] → 128[KB]) で、多数の同時接続とバッファを確保可能
- フラッシュが4倍 (128[KB] → 512[KB]) で、より多くの機能を実装可能
- 32ビットアーキテクチャにより、TCP/IP処理が高速
- 高速クロック (80[MHz]) により、SPIスループットが向上
- 12チャネルDMAコントローラにより、CPUを介さずにSPIデータ転送が可能
- CAN-FD対応により、車載やFA分野での応用が可能
ハードウェア接続
MSPM0G3519とENC28J60は、SPIインターフェースで接続する。
下表にMSPM0G3519のSPI0モジュールを使用する接続例を示す。
| MSPM0G3519 | ENC28J60 | 機能 | 説明 |
|---|---|---|---|
| PB8 (SPI0_PICO) | MOSI (SI) | SPI | コントローラ出力/ペリフェラル入力 |
| PB7 (SPI0_POCI) | MISO (SO) | SPI | コントローラ入力/ペリフェラル出力 |
| PB9 (SPI0_SCLK) | SCLK (SCK) | SPI | シリアルクロック |
| PB6 (GPIO) | CS# | 制御信号 | チップセレクト (アクティブLow) |
| PA18 (GPIO) | RESET# | 制御信号 | リセット信号 (アクティブLow) |
| PA17 (GPIO) | INT | 割り込み | 割り込み信号 (アクティブLow) |
| VDD | VCC | 電源 | 3.3V |
| VSS | VSS | GND | グランド |
※注意
- 水晶振動子
- ENC28J60には25MHzの水晶振動子が必要 (ピン23、24に接続)
- デカップリングコンデンサ
- 各電源ピン近くに0.1[uF]を配置
- RJ45コネクタ
- トランス内蔵型を使用 (Pulse社 J0011D21BNL等)
- LEDインジケータ
- LEDA (ピン15)、LEDB (ピン14) に接続可能
- ピン配置
- 上記のピン配置は一例であり、MSPM0G3519の他のSPIペリフェラルやGPIOピンも使用可能
- 使用するパッケージに応じて適切なピンを選択する
- MSPM0G3519は最大100ピンのパッケージがあり、ピン配置の自由度が高い
SPIインターフェースは、MSPM0G3519のSPI0ペリフェラルを使用する。
このペリフェラルはハードウェアSPIをサポートしており、DMAと組み合わせることで効率的な通信が可能である。
MSPM0G3519のSPI0は最大32[Mbit/s]に対応しているが、ENC28J60の最大速度は20[MHz]であるため、実際の動作速度はENC28J60の制限となる。
制御信号 (CS#、RESET#) および 割り込み信号 (INT) は任意のGPIOピンに接続できるが、割り込み機能を使用する場合は、割り込み対応ピンを選択する必要がある。
ENC28J60の電源は3.3[V]であり、MSPM0G3519と同じ電圧で動作するため、レベル変換回路は不要である。
ただし、電源の安定性を確保するため、各電源ピン近くに適切なデカップリングコンデンサを配置することが重要である。
lwIPライブラリの構成
lwIPライブラリは、以下に示すような階層構造を持っている。
- アプリケーション層
- ユーザアプリケーション (Webサーバ、HTTPクライアント等)
- API層
- Sequential API (netconn API) : ※RTOS使用時
- Raw API (コールバックベース、低レベルAPI) : ※NO_SYS=1の時
- lwIPコア層
- TCP、UDP、IP、ICMP、ARPプロトコル実装
- ネットワークインターフェース層
- イーサネットコントローラドライバ (ENC28J60)
- メモリ管理層
- メモリプール、バッファ管理 (pbuf)
lwIPの主要なソースファイルを以下に示す。
- lwip/src/core/
- lwIPコア実装ファイル群
- tcp.c、udp.c、ip.c、icmp.c、arp.c等
- lwip/src/api/
- Sequential API実装 (RTOS使用時)
- netconn.c、api_lib.c、api_msg.c等
- lwip/src/netif/
- ネットワークインターフェース
- ethernet.c、etharp.c等
- lwip/src/include/lwip/
- lwIPヘッダファイル群
- lwip/src/apps/
- アプリケーション層実装
- httpd.c、mqtt.c、snmp.c等
- arch/
- アーキテクチャ依存部分 (移植先マイコン依存)
- cc.h、perf.h、sys_arch.h等
- lwipopts.h
- lwIP設定ファイル (移植先マイコン依存)
- enc28j60_lwip.c / enc28j60_lwip.h
- ENC28J60ドライバ (移植先マイコン依存)
この階層構造により、lwIPの中核部分とハードウェア依存部分が明確に分離されている。
MSPM0G3519では、主にアーキテクチャ依存部分 (arch/)、ネットワークインターフェース層 (enc28j60_lwip.c)、設定ファイル (lwipopts.h) を実装または修正する必要がある。
MSPM0G3519では、一般的にRaw APIを使用して実装する。(NO_SYS=1時)
Raw APIは、コールバック関数ベースの低レベルAPIで、RTOSを必要とせずに動作する。
Sequential APIは、よりシンプルな記述が可能であるが、RTOSが必要となる。
MSPM0G3519のリソースは十分であるため、将来的にRTOSの導入を検討する場合は、Sequential APIへの移行も容易である。
- lwIPの公式Webサイト
- lwIPのGithub
- lwIP Documentation
uIPライブラリの構成
uIPライブラリは、以下に示すような階層構造を持っている。
- アプリケーション層
- ユーザアプリケーション (Webサーバ、HTTPクライアント等)
- uIPコア層
- TCP、UDP、IP、ICMP、ARPプロトコル実装
- ドライバ層
- イーサネットコントローラドライバ (ENC28J60)
- タイマ管理
- ARPタイマ、TCP再送タイマ等
uIPの主要なソースファイルを以下に示す。
- uip.c / uip.h
- uIPライブラリ
- uIPコア実装
- uip_arp.c / uip_arp.h
- uIPライブラリ
- ARPプロトコル実装
- timer.c / timer.h / timer-arch.h
- uIPライブラリ
- タイマ管理
- clock.c / clock-arch.c / clock-arch.h
- uIPライブラリ
- システムクロック実装 (移植先マイコン依存)
- tapdev.c / tapdev.h
- uIPライブラリ
- ネットワークデバイスインターフェース
- uip-conf.h
- uIPライブラリ
- uIP設定ファイル (移植先マイコン依存)
- enc28j60.c / enc28j60.h
- ENC28J60ドライバ (移植先マイコン依存)
この階層構造により、uIPの中核部分とハードウェア依存部分が明確に分離されている。
MSPM0G3519への移植では、主にドライバ層 (enc28j60.c)、タイマ管理 (clock-arch.c)、設定ファイル (uip-conf.h) を追加または修正する必要がある。
また、多くの場合、uIPコア層はそのまま使用できる。
- uIPの公式Webサイト
- 閉鎖
- uIPのGithub
lwIP : サンプルコード
以下の例では、ENC28J60およびlwIPを使用して、簡単なWebサーバを構築してHTTPリクエストに応答している。
MSPM0G3519の豊富なリソースを活用し、lwIPのRaw APIを使用している。
lwIP設定ファイル (lwipopts.h)
まず、lwIPの動作パラメータを設定する。
この設定ファイルでは、NO_SYS=1によりRTOSを使用しないスタンドアロン構成を指定している。
MSPM0G3519の128[KB] RAMを活用し、複数の同時接続とDHCPクライアント機能を有効化している。
- メモリプール設定
- MSPM0G3519の豊富なRAM容量に応じて、メモリプールサイズを最適化している。
- pbufプールやヒープサイズは、アプリケーションの要件に応じて柔軟に変更可能である。
- TCP接続数
- 16接続に設定しているが、これはアプリケーションの要件に応じて調整できる。
- MSPM0G3519では、20〜30接続以上も十分に対応可能である。
- DHCP機能
- DHCPクライアント機能を有効化することで、IPアドレスの自動取得が可能となる。
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
// NO_SYS==1: RTOSを使用しないスタンドアロン構成
#define NO_SYS 1
// メモリ管理設定 (MSPM0G3519の128KB RAMを活用)
#define MEM_ALIGNMENT 4
#define MEM_SIZE (32*1024) // 32KB ヒープ (十分なRAMがあるため大きめに設定)
#define MEMP_NUM_PBUF 32
#define MEMP_NUM_TCP_PCB 16
#define MEMP_NUM_TCP_PCB_LISTEN 8
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_SYS_TIMEOUT 16
// pbuf設定
#define PBUF_POOL_SIZE 32
#define PBUF_POOL_BUFSIZE 512
// TCP設定
#define LWIP_TCP 1
#define TCP_MSS 1460
#define TCP_WND (8 * TCP_MSS) // 8 MSS ウィンドウサイズ
#define TCP_SND_BUF (8 * TCP_MSS) // 8 MSS 送信バッファ
#define TCP_SND_QUEUELEN (4 * TCP_SND_BUF/TCP_MSS)
// UDP設定
#define LWIP_UDP 1
#define MEMP_NUM_UDP_PCB 8
// ICMP設定
#define LWIP_ICMP 1
// DHCP設定
#define LWIP_DHCP 1
#define LWIP_NETIF_HOSTNAME 1
// DNS設定
#define LWIP_DNS 1 // DNS有効化
#define DNS_TABLE_SIZE 4
#define DNS_MAX_SERVERS 2
// ARP設定
#define LWIP_ARP 1
#define ARP_TABLE_SIZE 10
#define ARP_QUEUEING 1
// IP設定
#define LWIP_IPV4 1
#define LWIP_IPV6 0
#define IP_REASSEMBLY 1 // IPフラグメント再構築を有効化
#define IP_FRAG 1 // IPフラグメント分割を有効化
// チェックサム設定
#define CHECKSUM_GEN_IP 1
#define CHECKSUM_GEN_UDP 1
#define CHECKSUM_GEN_TCP 1
#define CHECKSUM_CHECK_IP 1
#define CHECKSUM_CHECK_UDP 1
#define CHECKSUM_CHECK_TCP 1
// 統計情報 (開発時は有効化、製品版では無効化してRAM節約)
#define LWIP_STATS 1
#define LWIP_STATS_DISPLAY 1
// デバッグ設定 (開発時は有効化可能)
#define LWIP_DEBUG 0
// その他
#define LWIP_NETCONN 0 // Raw API使用のため無効化
#define LWIP_SOCKET 0 // ソケットAPI不使用
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_STATUS_CALLBACK 1
// プラットフォーム依存の型定義
#define U16_F "hu"
#define S16_F "hd"
#define X16_F "hx"
#define U32_F "u"
#define S32_F "d"
#define X32_F "x"
#endif /* __LWIPOPTS_H__ */
ENC28J60ドライバ (enc28j60.c)
ENC28J60との通信を行うドライバ制御 (SPIを使用したレジスタアクセスやパケット送受信等) を記述する。
このドライバでは、MSPM0G3519のSPIペリフェラルを使用してENC28J60を制御する。
MSPM0G3519のSPI通信速度は最大32[Mbit/s]であり、ENC28J60の最大速度である20[MHz]に近い動作が可能である。
以下の例では、基本的なレジスタアクセスとパケット送受信機能を提供している。
必要に応じて、DMAを使用した高速データ転送や、割り込み駆動の受信処理を追加することも可能である。
// enc28j60.c : ENC28J60ドライバ
#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
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
// MACレジスタ
#define MACON1 0x00 | 0x40
#define MACON3 0x02 | 0x40
#define MAMXFLL 0x0A | 0x40
#define MAMXFLH 0x0B | 0x40
#define MABBIPG 0x04 | 0x40
#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 ECON1_TXRTS 0x08
#define ECON1_RXEN 0x04
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define ESTAT_CLKRDY 0x01
#define MACON1_MARXEN 0x01
#define MACON3_PADCFG0 0x20
#define MACON3_TXCRCEN 0x10
#define MACON3_FRMLNEN 0x02
// チップセレクト制御 (実際のピン定義に応じて変更)
#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) {
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();
}
// ビット操作
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); // 1ms待機
// クロックが安定するまで待機
while (!(enc28j60_read(ESTAT) & 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, MACON1_MARXEN);
enc28j60_write(MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN);
enc28j60_write(MAMXFLL, 0xEE);
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, 0xC0); // INTIE | PKTIE
// 受信有効化
enc28j60_write_op(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
}
// パケット送信
void enc28j60_packet_send(const uint8_t *buffer, uint16_t len)
{
// 送信バッファポインタ設定
enc28j60_write(EWRPTL, 0x00);
enc28j60_write(EWRPTH, 0x1A);
// 制御バイト書き込み
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, ECON1_TXRTS);
// 送信完了待ち (オプション: タイムアウト処理を追加することを推奨)
while (enc28j60_read(ECON1) & 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, ECON2_PKTDEC);
return (len <= max_len) ? len : 0;
}
lwIPネットワークインターフェース (netif_enc28j60.c)
lwIPとENC28J60ドライバを接続するネットワークインターフェース層を実装する。
この層は、lwIPのpbuf構造体とENC28J60のパケットバッファ間でデータを変換する役割を持つ。
// netif_enc28j60.c : lwIPネットワークインターフェース
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/stats.h"
#include "netif/etharp.h"
#include "enc28j60.h"
#include <string.h>
#define IFNAME0 'e'
#define IFNAME1 'n'
// パケット送信
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *q;
uint8_t buffer[1518];
uint16_t len = 0;
// pbufチェーンからバッファにコピー
for (q = p; q != NULL; q = q->next) {
if (len + q->len > sizeof(buffer)) {
return ERR_BUF;
}
memcpy(&buffer[len], q->payload, q->len);
len += q->len;
}
// ENC28J60で送信
enc28j60_packet_send(buffer, len);
return ERR_OK;
}
// パケット受信
static struct pbuf *low_level_input(struct netif *netif)
{
struct pbuf *p, *q;
uint8_t buffer[1518];
uint16_t len;
// ENC28J60からパケット受信
len = enc28j60_packet_receive(buffer, sizeof(buffer));
if (len == 0) {
return NULL;
}
// pbuf割り当て
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL) {
uint16_t offset = 0;
// pbufチェーンにデータをコピー
for (q = p; q != NULL; q = q->next) {
memcpy(q->payload, &buffer[offset], q->len);
offset += q->len;
}
}
return p;
}
// ネットワークインターフェース初期化
err_t netif_enc28j60_init(struct netif *netif)
{
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
netif->output = etharp_output;
netif->linkoutput = low_level_output;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
// MACアドレス設定
netif->hwaddr_len = 6;
netif->hwaddr[0] = 0x02;
netif->hwaddr[1] = 0x00;
netif->hwaddr[2] = 0x00;
netif->hwaddr[3] = 0x00;
netif->hwaddr[4] = 0x00;
netif->hwaddr[5] = 0x01;
// ENC28J60初期化
enc28j60_init(netif->hwaddr);
return ERR_OK;
}
// パケット受信処理 (メインループから定期的に呼び出す)
void netif_enc28j60_input(struct netif *netif)
{
struct pbuf *p;
// 受信可能なパケットを全て処理
while ((p = low_level_input(netif)) != NULL) {
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
}
}
}
メイン処理
メイン処理では、MSPM0G3519の初期化、lwIPの初期化、HTTPサーバの起動を記述する。
// main.c : メイン処理
#include "ti_msp_dl_config.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/timeouts.h"
#include "lwip/dhcp.h"
#include "netif/etharp.h"
#include "netif_enc28j60.h"
#include "lwip/apps/httpd.h"
// lwIP用タイマ (1[ms]周期)
volatile uint32_t lwip_timer_ms = 0;
// タイマ割り込みハンドラ (1[ms]周期)
void TIMER_0_INST_IRQHandler(void)
{
lwip_timer_ms++;
}
int main(void)
{
struct netif netif;
ip4_addr_t ipaddr, netmask, gw;
// MSPM0G3519ペリフェラル初期化
SYSCFG_DL_init();
// タイマ割り込み有効化
NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
// lwIP初期化
lwip_init();
// 静的IPアドレス設定 (DHCPを使用しない場合)
IP4_ADDR(&ipaddr, 192, 168, 1, 100);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 1, 1);
// ネットワークインターフェース追加
netif_add(&netif, &ipaddr, &netmask, &gw, NULL, netif_enc28j60_init, ethernet_input);
netif_set_default(&netif);
netif_set_up(&netif);
// DHCPクライアント起動 (オプション)
// dhcp_start(&netif);
// HTTPサーバ初期化
httpd_init();
// メインループ
while (1) {
// パケット受信処理
netif_enc28j60_input(&netif);
// lwIPタイムアウト処理 (周期的なタイマイベント処理)
sys_check_timeouts();
}
}
簡易HTTPサーバの構築
lwIPには、HTTPサーバ実装 (httpd.c) が含まれているため、これを利用することで簡単にWebサーバを構築できる。
以下の例では、カスタムCGIハンドラを追加して、動的なコンテンツを生成している。
// httpd_cgi.c - CGIハンドラ
#include "lwip/apps/httpd.h"
#include "ti_msp_dl_config.h"
#include <string.h>
// LED状態取得CGI
const char *led_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
{
// LEDの状態を確認
uint32_t led_state = DL_GPIO_readPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);
if (led_state) {
return "/led_on.ssi";
}
else {
return "/led_off.ssi";
}
}
// LED制御CGI
const char *led_control_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
{
int i;
for (i = 0; i < iNumParams; i++) {
if (strcmp(pcParam[i], "led") == 0) {
if (strcmp(pcValue[i], "on") == 0) {
DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);
}
else if (strcmp(pcValue[i], "off") == 0) {
DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);
}
}
}
return "/index.html";
}
// CGIハンドラテーブル
static const tCGI cgi_handlers[] = {
{ "/led_status.cgi", led_cgi_handler },
{ "/led_control.cgi", led_control_cgi_handler }
};
// CGI初期化
void httpd_cgi_init(void)
{
http_set_cgi_handlers(cgi_handlers, sizeof(cgi_handlers) / sizeof(tCGI));
}
lwIPのHTTPサーバは、ファイルシステムを持たないため、Webページのコンテンツはプログラムに埋め込む必要がある。
lwIPには、makefsdataツールが付属しており、HTMLファイルをC言語のヘッダファイルに変換することができる。
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>MSPM0G3519 Web Server</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
h1 { color: #333; }
.info { background-color: #f0f0f0; padding: 20px; border-radius: 5px; }
.controls { margin-top: 20px; }
button { padding: 10px 20px; margin: 5px; }
</style>
</head>
<body>
<h1>MSPM0G3519 Web Server</h1>
<div class="info">
<p>This is running on lwIP stack with ENC28J60 Ethernet controller.</p>
<ul>
<li>MCU: MSPM0G3519 (ARM Cortex-M0+, 80MHz)</li>
<li>Flash: 512KB</li>
<li>RAM: 128KB</li>
<li>Ethernet: ENC28J60 (10Mbps)</li>
</ul>
</div>
<div class="controls">
<h2>LED Control</h2>
<form action="/led_control.cgi" method="get">
<button type="submit" name="led" value="on">LED ON</button>
<button type="submit" name="led" value="off">LED OFF</button>
</form>
</div>
</body>
</html>
その他の注意事項
lwIPを使用したTCP/IP通信を実装する場合の注意事項を以下に示す。
タイマ管理
lwIPは、sys_check_timeouts関数を定期的に呼び出す必要がある。
メインループ内で適切な間隔 (推奨 : 数[ms]〜数十[ms]) で呼び出すことを推奨する。
タイマの精度は、TCP通信の品質に影響するため、正確なタイマ実装が重要である。
メモリ管理
lwIPは、メモリプール方式を採用しているため、lwipopts.hで適切なプールサイズを設定する必要がある。
メモリ不足が発生すると、パケットのドロップや接続の失敗が生じる可能性がある。
MSPM0G3519は128[KB]のRAMを持つため、メモリプールサイズは柔軟に調整できる。
pbuf処理
pbuf構造体は、参照カウント方式でメモリ管理されている。
pbuf_free関数を適切に呼び出さないと、メモリリークが発生する。
特に、パケット受信処理やエラー処理時には注意が必要である。
割り込みコンテキスト
lwIPの関数は、基本的に割り込みコンテキストから呼び出すべきではない。
パケット受信処理は、メインループで行うことを推奨する。
ENC28J60の割り込み信号を使用する場合は、割り込みハンドラ内ではフラグを立てるのみとし、実際の処理はメインループで行う。
DHCPの使用
DHCPクライアントを使用する場合、IPアドレスの取得に時間がかかる場合がある。
netif_set_link_callback関数を使用して、リンクアップ時にDHCPを起動することを推奨する。
DHCPによるIPアドレス取得中は、TCP/UDP通信ができないため、アプリケーションの実装時に考慮する必要がある。
エンディアン変換
ネットワークバイトオーダー (ビッグエンディアン) とホストバイトオーダーの変換に注意する。
lwIPは、htons()、ntohs()、htonl()、ntohl()等のマクロを提供している。
ARM Cortex-M0+はリトルエンディアンであるため、適切な変換が必要である。
トラブルシューティング
パケットが受信できない
- ENC28J60の初期化、MACアドレス設定、ネットワークインターフェースの設定を確認する。
- netif_set_up()が呼ばれているか、NETIF_FLAG_LINK_UPが設定されているかを確認する。
- SPIの通信速度が高すぎる場合、通信が不安定になる可能性があるため、速度を下げてみる。
ARPが応答しない
- MACアドレスが正しく設定されているか、etharp_output()が正しく設定されているかを確認する。
- ARP_TABLE_SIZEが適切に設定されているかを確認する。
TCP接続が確立しない
- ファイアウォールやルータの設定を確認する。
- sys_check_timeouts()が定期的に呼ばれているかを確認する。
- TCPの3ウェイハンドシェイクが正常に行われているか、パケットキャプチャで確認する。
メモリ不足エラー
- lwipopts.hのメモリプールサイズを増やす。
- LWIP_STATS=1を有効化して、メモリ使用状況を確認する。
- MSPM0G3519は128[KB]のRAMを持つため、十分なメモリを割り当てることができる。
HTTPサーバが応答しない
- httpd_init関数が呼ばれているか、または、ポート80がリッスンされているかを確認する。
- fsdata.cが正しく生成されているかを確認する。
- makefsdata.pyを使用して、HTMLファイルを正しく変換する。
SPI通信の速度
MSPM0G3519のSPI0は最大32[Mbit/s]に対応しているが、ENC28J60の最大速度は20[MHz]である。
そのため、実際の動作速度はENC28J60の制限となる。
SPIクロック周波数は、MSPM0G3519のクロック設定により調整可能である。
通信が不安定な場合は、SPIクロック周波数を下げて (例: 10[MHz]、5[MHz]) 通信の安定性を確認することを推奨する。
配線長やノイズの影響により、実際の動作速度は制限される場合がある。
DMAの活用
MSPM0G3519の12チャネルDMAコントローラを使用することで、CPUの負荷を軽減しつつ、高速なデータ転送が可能となる。
特に、大きなパケットを連続的に送受信する場合、DMAの活用により大幅なパフォーマンス向上が期待できる。
DMAを使用する場合は、以下の点に注意する必要がある。
- DMAチャネルの割り当て
- 他のペリフェラルとDMAチャネルが競合しないように設定する。
- MSPM0G3519は12チャネルのDMAを持つため、柔軟に割り当てが可能である。
- バッファアライメント
- DMA転送用のバッファは、適切なアライメント (4バイト境界等) に配置する。
- 割り込みハンドリング
- DMA転送完了割り込みを適切に処理する。
- DMA転送完了後に、次の処理 (パケット受信処理等) を行う。
CAN-FDインターフェースの活用
MSPM0G3519は2個のCAN-FDインターフェースを搭載しているため、イーサネット通信とCAN通信を組み合わせたゲートウェイ応用が可能である。
例えば、以下のような応用が考えられる。
- CANバスとイーサネットを接続するゲートウェイ
- 車載ネットワークやFA機器のデータをイーサネット経由でクラウドに送信
- CANデータのロギングとWeb表示
- CANバスのデータをリアルタイムでWebブラウザに表示
- イーサネット経由でのCAN制御
- Webブラウザから車載機器やFA機器を制御
参考資料
- MSPM0G3519データシート
- MSPM0 SDK
- MSPM0 G-Series 80-MHz Microcontrollers Technical Reference Manual
- ENC28J60データシート
- lwIP公式ドキュメント
- lwIP Github
- RFC 793
- Transmission Control Protocol (TCP)
- RFC 826
- An Ethernet Address Resolution Protocol (ARP)
- RFC 2131
- Dynamic Host Configuration Protocol (DHCP)