Ethernet-SPI IC ENC28J60 のデータシートを読んでみただけ

:::note warn
実機動作確認は何もしておりません。解釈に誤りがあるかもしれません。
:::

## きっかけ

なんとなく秋月電子のページを覗いていたとき、この IC が目に入りました。商品説明を軽く見てみると、SPI の使えるマイコンで Ethernet に繋げられるらしい。

「そういや [Arduino の Ethernet シールド](https://www.marutsu.co.jp/pc/i/604591/)地味に値段するよなぁ･･･この IC 使えれば安めに Ethernet デバイス作れるんじゃね？」なんていう思いつきからデータシートを読んでみた。

せっかくなので、メモがてら記事に起こすことにしたのでした。

## この IC でできること

https://akizukidenshi.com/catalog/g/gI-01948/

* この IC と SPI 通信すると、IC から Ethernet 信号を入出力することができる
  * つまり、SPI インターフェイスのあるマイコンで Ethernet 通信できる
* ただしスピードは 10base-T
  * 映像とか大規模なものは厳しい

## とりあえずさっさと動かしたいなら

すでに先人がライブラリを作ってくれてあるようです。それを使いましょう。

https://www.arduino.cc/reference/en/libraries/ethercard/

ライブラリ抜きで動かすなら、下に示す前提知識に加え、ネットワークパケットの知識まで必要になってきます。本記事はフルスクラッチ実装を試みる猛者な人に向けて贈るものになります。そうでない方はブラウザバック推奨です。(この言葉ってもう古い？)

~~でも猛者だったら自分でデータシートぜんぶ読んでいける･･･？~~

## 前提知識

* マイコンの基礎は OK
* 2進数が分かる, 10進数-2進数変換ができる
* SPI 通信のやり方が分かる
* レジスタ、およびレジスタマップが何なのか分かる
  * 「IC のレジスタに値を書くことで、IC の各種設定ができる」というのを知っている

## 必要なパーツ

データシートから抜粋

![enc28j60-sample-scheme.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/70fd4db5-1205-d9b5-a7ed-d7fefa7ee18e.png)

主なパーツとしては:

* この Ethernet IC - 600円
* [パルストランス内蔵 RJ45 コネクタ](https://akizukidenshi.com/catalog/g/gK-09374/) - 300円
  * ユニバーサル基盤やブレッドボード上の実装を想定して DIP 化キットを選定
* 他、レベルシフト用パーツや抵抗など

:::note warn
価格はいずれも記事執筆時点
:::

だいたい 1000円程度の見積もりになりそうですね。配線もそこまで難しい要素もなさそうです。回路図右側のトランスは、RJ45 コネクタに内蔵されているタイプを選べば心配なし。

あるいは、このように基盤実装済みのモジュールを使ってしまえばより確実。

https://www.akiba-led.jp/product/1506

## SPI 通信のフォーマット

IC のレジスタの書き方などを確かめます。

またデータシート抜粋

![enc28j60-spi-list.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/291f5e52-54ac-7296-7f3c-b3ca84ade249.png)

主に使いそうなものをチェック。

### IC リセット

![enc28j60-spi-reset.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/794c6d64-3132-4f82-acb5-87d48ac70e6c.png)

全て 1 を 1byte、つまり `0xff` を送れば全設定やレジスタをリセットできます。

### 設定レジスタの書き込み

![enc28j60-spi-ctrl-write.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/e5733b17-4bd4-0382-d72c-ff3fdc9f6813.png)

Opcode は固定、Address は設定する対象 (レジスタ) によって決まり、Data Byte はレジスタにセットする値になります。1byte目は C コード的に書くと `0b01000000 & address` というようになります。

Address がどうなるか、詳しくは[後述](#%E8%A8%AD%E5%AE%9A%E3%83%AC%E3%82%B8%E3%82%B9%E3%82%BF%E3%81%AE%E6%A7%8B%E9%80%A0)。

### 送信バッファの書き込み

![enc28j60-spi-buf-write.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/97a8e133-77e3-4d34-5629-df62fd9f2f40.png)

1byte目は固定で `0b01111010`。それ以降は、実際に送りたい byte 列を書き続けていきます。最後まで書いたら、CS の立ち上がりによって書き込み完了となります。

### 受信バッファの読み込み

データシートにタイミングチャートの記載はありませんが、送信バッファ書き込みの流れと TABLE 4-1 から次のようになると思われます。

```plantuml
@startuml
robust "SI" as SI
robust "SO" as SO

SI is 0
SO is 0

@SI
1 is 0
3 is 1
6 is 0
7 is 1
8 is 0
9 is 0
SI@9 <-> @17 : Don't care

@SO
9 is {0,1}
@16

highlight 1 to 4 #white : Opcode
highlight 4 to 9 #white : Address
highlight 9 to 16 #white : Data Byte 0
@enduml
```

`0b00111010` を送ったあとは、CS が立ち上がるまでデータが送られてきます。

## 設定レジスタの構造

上の「設定レジスタの書き込み」のタイミングチャートの通り、5bit のレジスタアドレスで、どこのレジスタに書き込むかを指定します。ところで、設定レジスタの構造を見てみると？

![enc28j60-register-map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/82df79af-a675-2ab8-0de3-728eb90a2a01.png)

レジスタいっぱいありますね。あと Bank というのに分かれています。

この IC のレジスタは:

1. Bank を選択する
2. レジスタアドレスを指定して書き込む

という 2段階でレジスタを選択することになっています。

画像横方向 Bank 0~3 の選択について、ECON1 というレジスタに注目すると･･･

![enc28j60-econ1-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/49a407ea-fcb9-bb0e-562a-9e1426560242.png)

![enc28j60-econ1-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/efb5b992-3992-3573-ca65-35c6f45ea379.png)

`BSEL1` `BSEL0` でバンクの指定ができるのがわかります。また全バンクに ECON1 があるのもわかります。まずはこれによって「1. Bank を選択する」ができます。

ところで「[設定レジスタの書き込み](#%E8%A8%AD%E5%AE%9A%E3%83%AC%E3%82%B8%E3%82%B9%E3%82%BF%E3%81%AE%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF)」で 5bit のアドレス指定がありました。(画像再掲)

![enc28j60-spi-ctrl-write.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/e5733b17-4bd4-0382-d72c-ff3fdc9f6813.png)

これによって「2. レジスタアドレスを指定して書き込む」ができます。

## 送信・受信バッファの構造

またデータシート抜粋

![enc28j60-eth-buffer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/17136134-39c9-d36a-53a0-2e015b231253.png)

送信バッファと受信バッファは共用で、どこからどこまでがそれぞれの領域かをレジスタで設定するようです。

「[送信バッファの書き込み](#%E9%80%81%E4%BF%A1%E3%83%90%E3%83%83%E3%83%95%E3%82%A1%E3%81%AE%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF)」と「[受信バッファの読み込み](#%E5%8F%97%E4%BF%A1%E3%83%90%E3%83%83%E3%83%95%E3%82%A1%E3%81%AE%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF)」を見て分かる通り、通信自体にはどこから書く・読むかのアドレス指定がありません。(画像再掲)

![enc28j60-spi-buf-write.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/97a8e133-77e3-4d34-5629-df62fd9f2f40.png)

通信をすると、毎回 'Buffer Write Pointer' 'Buffer Read Pointer' から書き込み・読み込みが始まるという形のようです。

## 受信検知

`INT` というピンがあり、そこから割り込み信号を出す (指定のイベント発生通知をする) ことができるようです。

![enc28j60-interrupt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/18acafad-a44e-bb18-1e4b-ab644990814c.png)

`PKTIF` `PKTIE` という信号に注目してみると？

受信があると `PKTIF` というレジスタが `1` になるようです。

![enc28j60-interrupt-pktif-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/b493f83d-d9b9-16e2-9bcd-15186c531cf1.png)

![enc28j60-interrupt-pktif-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/adfac9cd-de08-cf86-0f7a-21ca1a4f11bc.png)

上の回路図と合わせて見ると、`PKTIE` を `1` にセットすることで、受信時に `INT` が立ち下がるようにできるようです。

![enc28j60-interrupt-pktie-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/43d425d6-f3d1-4eed-4a39-f5bedca57131.png)

![enc28j60-interrupt-pktie-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/5839d33a-e75f-d413-1b64-0571affa6d90.png)

それから、`INTIE` というレジスタが出力の大元となっているので、これを `1` にセットするのを忘れず。

## おわり

すごくざっくりと、必要そうなところを探してつまみました。何気にここまでしっかりデータシート読み込んだのは初めてかもしれん･･･。

~~まぁ初めに貼ったライブラリ使えばこれも読まずに済むんですけど。~~

そのうち実機チェックやれたらやりたい。(典型的なやらないフラグ)