はじめに
Amazonで購入したEthernetコントローラ基板をSTM32F4Discovery + NuttXで使用できるようにする。
このコントローラ基板はENC28J60
が載っている。ENC28J60はMACとPHYの機能を持っているため、ホストとなるボードはSPIで通信できればEthernetが使えるようになる。
ちなみにSTM32F4Discoveryに載っているSTM32F407VGT6
はMACの機能を持っているのでPHYだけのコントローラでも使用できる。
ENC28J60のドライバ
NuttXにはENC28J60のドライバは存在するが、STM32F4Discoveryの初期化処理でこのドライバを呼び出すようになっていないため、 初期化処理を修正する。
初期化処理の修正
次のファイルを修正している。
- configs/stm32f4discovery/src/stm32_netinit.c
- configs/stm32f4discovery/src/stm32_spi.c
- configs/stm32f4discovery/src/stm32f4discovery.h
diff --git a/configs/stm32f4discovery/src/stm32_netinit.c b/configs/stm32f4discovery/src/stm32_netinit.c index 36ee755f93..47a450ad5c 100644 --- a/configs/stm32f4discovery/src/stm32_netinit.c +++ b/configs/stm32f4discovery/src/stm32_netinit.c @@ -39,6 +39,134 @@ #include <nuttx/config.h> +#ifdef CONFIG_ENC28J60 +#include <stdint.h> +#include <stdio.h> +#include <debug.h> + +#include <nuttx/spi/spi.h> +#include <nuttx/net/enc28j60.h> + +#include <arch/board/board.h> + +#include "chip.h" +#include "up_arch.h" +#include "up_internal.h" +#include "stm32_spi.h" + +#include "stm32f4discovery.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ +/* ENC28J60 + * + * --- ------ -------------- ----------------------------------------------------- + * PIN NAME SIGNAL NOTES + * --- ------ -------------- ----------------------------------------------------- + * + * 29 PA4 PA4-SPI1-NSS 10Mbit ENC28J60 + * 30 PA5 PA5-SPI1-SCK 10Mbit ENC28J60 + * 31 PA6 PA6-SPI1-MISO 10Mbit ENC28J60 + * 32 PA7 PA7-SPI1-MOSI 10Mbit ENC28J60 + * 34 PE4 (no name) 10Mbps ENC28J60 Interrupt + */ + +/* ENC28J60 is on SPI1 */ + +#ifndef CONFIG_STM32_SPI1 +# error "Need CONFIG_STM32_SPI1 in the configuration" +#endif + +/* SPI Assumptions **********************************************************/ + +#define ENC28J60_SPI_PORTNO 1 /* On SPI1 */ +#define ENC28J60_DEVNO 0 /* Only one ENC28J60 */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_lower_s +{ + const struct enc_lower_s lower; /* Low-level MCU interface */ + xcpt_t handler; /* ENC28J60 interrupt handler */ + FAR void *arg; /* Argument that accompanies the interrupt */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int up_attach(FAR const struct enc_lower_s *lower, xcpt_t handler, + FAR void *arg); +static void up_enable(FAR const struct enc_lower_s *lower); +static void up_disable(FAR const struct enc_lower_s *lower); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The ENC28J60 normal provides interrupts to the MCU via a GPIO pin. The + * following structure provides an MCU-independent mechanixm for controlling + * the ENC28J60 GPIO interrupt. + */ + +static struct stm32_lower_s g_enclower = +{ + .lower = + { + .attach = up_attach, + .enable = up_enable, + .disable = up_disable + }, + .handler = NULL, + .arg = NULL +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: struct enc_lower_s methods + ****************************************************************************/ + +static int up_attach(FAR const struct enc_lower_s *lower, xcpt_t handler, + FAR void *arg) +{ + FAR struct stm32_lower_s *priv = (FAR struct stm32_lower_s *)lower; + + /* Just save the handler for use when the interrupt is enabled */ + + priv->handler = handler; + priv->arg = arg; + return OK; +} + +static void up_enable(FAR const struct enc_lower_s *lower) +{ + FAR struct stm32_lower_s *priv = (FAR struct stm32_lower_s *)lower; + + DEBUGASSERT(priv->handler); + (void)stm32_gpiosetevent(GPIO_ENC28J60_INTR, false, true, true, + priv->handler, priv->arg); +} + +/* REVISIT: Since the interrupt is completely torn down, not just disabled, + * in interrupt requests that occurs while the interrupt is disabled will be + * lost. + */ + +static void up_disable(FAR const struct enc_lower_s *lower) +{ + (void)stm32_gpiosetevent(GPIO_ENC28J60_INTR, false, true, true, + NULL, NULL); +} +#endif /* CONFIG_ENC28J60 */ + + /************************************************************************************ * Public Functions ************************************************************************************/ @@ -50,6 +178,35 @@ #if defined(CONFIG_NET) && !defined(CONFIG_NETDEV_LATEINIT) void up_netinitialize(void) { +#ifdef CONFIG_ENC28J60 + FAR struct spi_dev_s *spi; + int ret; + + /* Assumptions: + * 1) ENC28J60 pins were configured in up_spi.c early in the boot-up phase. + * 2) Clocking for the SPI1 peripheral was also provided earlier in boot-up. + */ + + spi = stm32_spibus_initialize(ENC28J60_SPI_PORTNO); + if (!spi) + { + nerr("ERROR: Failed to initialize SPI port %d\n", ENC28J60_SPI_PORTNO); + return; + } + + /* Bind the SPI port to the ENC28J60 driver */ + + ret = enc_initialize(spi, &g_enclower.lower, ENC28J60_DEVNO); + if (ret < 0) + { + nerr("ERROR: Failed to bind SPI port %d ENC28J60 device %d: %d\n", + ENC28J60_SPI_PORTNO, ENC28J60_DEVNO, ret); + return; + } + + ninfo("Bound SPI port %d to ENC28J60 device %d\n", + ENC28J60_SPI_PORTNO, ENC28J60_DEVNO); +#endif /* CONFIG_ENC28J60 */ } #endif diff --git a/configs/stm32f4discovery/src/stm32_spi.c b/configs/stm32f4discovery/src/stm32_spi.c index a9be4c1d2d..0cafcc5aa0 100644 --- a/configs/stm32f4discovery/src/stm32_spi.c +++ b/configs/stm32f4discovery/src/stm32_spi.c @@ -70,7 +70,12 @@ void weak_function stm32_spidev_initialize(void) { #ifdef CONFIG_STM32_SPI1 +# ifdef CONFIG_ENC28J60 + (void)stm32_configgpio(GPIO_ENC28J60_CS); /* ENC28J60 chip select */ + (void)stm32_configgpio(GPIO_ENC28J60_INTR); +# else (void)stm32_configgpio(GPIO_CS_MEMS); /* MEMS chip select */ +# endif #endif #if defined(CONFIG_STM32_SPI2) && defined(CONFIG_SENSORS_MAX31855) (void)stm32_configgpio(GPIO_MAX31855_CS); /* MAX31855 chip select */ @@ -126,6 +131,14 @@ void stm32_spi1select(FAR struct spi_dev_s *dev, uint32_t devid, bool selected) { spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); +#ifdef CONFIG_ENC28J60 + if (devid == SPIDEV_ETHERNET(0)) + { + /* Set the GPIO low to select and high to de-select */ + + stm32_gpiowrite(GPIO_ENC28J60_CS, !selected); + } +#endif #ifdef CONFIG_LCD_ST7567 if (devid == SPIDEV_DISPLAY(0)) { diff --git a/configs/stm32f4discovery/src/stm32f4discovery.h b/configs/stm32f4discovery/src/stm32f4discovery.h index 80c269039c..c673ce1b76 100644 --- a/configs/stm32f4discovery/src/stm32f4discovery.h +++ b/configs/stm32f4discovery/src/stm32f4discovery.h @@ -286,6 +286,17 @@ #define GPIO_CS_XEN1210 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\ GPIO_OUTPUT_SET|GPIO_PORTA|GPIO_PIN4) +/* ENC28J60 Ethernet PHY */ +#ifdef CONFIG_ENC28J60 +/* PA1 */ +# define GPIO_ENC28J60_CS (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\ + GPIO_OUTPUT_SET|GPIO_PORTA|GPIO_PIN4) +/* PE4 */ +# define GPIO_ENC28J60_INTR (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI| \ + GPIO_OPENDRAIN|GPIO_PORTE|GPIO_PIN4) +#endif + + /* USB OTG FS * * PA9 OTG_FS_VBUS VBUS sensing (also connected to the green LED)
コンフィグレーション
次のコンフィグレーションを全て有効化する。
CONFIG_NET
Networking Support -> Networking Support
CONFIG_NET_TCP
Networking Support -> Networking support -> TCP/IP Networking
CONFIG_NET_UDP
Networking Support -> Networking support -> UDP Networking
CONFIG_NET_BROADCAST
Networking Support -> Networking support -> UDP Networking -> Disable UDP/IP Stack -> UDP broadcast Rx support
CONFIG_NETDEV_PHY_IOCTL
これを忘れるとifup/ifdownコマンドが効かない
Networking Support -> Networking support -> Network Device Operations -> Enable PHY ioctl()
CONFIG_NET_ICMP
Networking Support -> Networking support -> ICMP Networking Support
CONFIG_NET_ICMP_SOCKET
Networking Support -> Networking support -> ICMP Networking Support -> IPPROTO_ICMP socket support
CONFIG_NETUTILS_PING
Application Configuration -> Network Utilities -> ICMP ping support
CONFIG_SYSTEM_PING
Application Configuration -> System Libraries and NSH Add-Ons -> ICMP 'ping' command
CONFIG_NSH_IPADDR
192.168.10.100
に設定
Application Configuration -> NSH Library -> NSH Library -> Networking Configuration -> Network initialization -> IP Address Configuration -> Target IPv4 address
0xc0a80a64
に設定
CONFIG_NSH_DRIPADDR
192.168.10.1
に設定
Application Configuration -> NSH Library -> NSH Library -> Networking Configuration -> Network initialization -> IP Address Configuration -> Router IPv4 address
0xc0a80a01
に設定
CONFIG_NSH_NOMAC
Application Configuration -> NSH Library -> NSH Library -> Networking Configuration -> Network initialization -> ardware has no MAC address
CONFIG_DISABLE_POLL=n
POLLの無効化を解除する。
Device Drivers -> Disable driver poll interfaces
CONFIG_NETDEVICES
Device Drivers -> Network Device/PHY Support
CONFIG_ENC28J60
Device Drivers -> Network Device/PHY Support -> ENC28J60
CONFIG_SCHED_HPWORK
RTOS Features -> Work queue support -> High priority (kernel) worker thread
CONFIG_SCHED_LPWORK
RTOS Features -> Work queue support -> Low priority (kernel) worker thread
コンフィグの作成
毎回これを手動で設定するのは面倒なのでコンフィグを作成する。
今回は予めnshを使用していることを想定して作業する。
defconfigの作成
$ make savedefconfig
stm32f4discovery/nsh_enc28j60の作成
これをconfigs/stm32f4discovery/nsh_enc28j60
において、tools/configure.sh
で指定できるようにする。
$ mkdir -p configs/stm32f4discovery/nsh_enc28j60 $ cp defconfig configs/stm32f4discovery/nsh_enc28j60/
コンフィグの使用
作成したコンフィグ使う場合には次のようにする。
$ make distclean $ tools/configure.sh -l stm32f4discovery/nsh_enc28j60
ボードとの接続
ENC28J60 | STM32F4DISCOVERY |
---|---|
5V | 5V |
LNT(INT) | PE4 |
SO | PA6 |
ST(SI) | PA7 |
SCK | PA5 |
CS | PA4 |
GND | GND |
RSTなどいくつか使用していないピンがあるが、これで問題なく使用できる。
動作確認
対抗となるPCのIPアドレスを192.168.10.1
などに設定しpingを実行する。
次のようになればOK。
$ ping 192.168.10.100 PING 192.168.10.100 (192.168.10.100) 56(84) bytes of data. 64 bytes from 192.168.10.100: icmp_seq=2 ttl=64 time=1.00 ms 64 bytes from 192.168.10.100: icmp_seq=3 ttl=64 time=0.567 ms 64 bytes from 192.168.10.100: icmp_seq=4 ttl=64 time=0.596 ms
反対に、STM32F4Discovery側からは次のようになればOK。
nsh> ping 192.168.10.1 PING 192.168.10.1 56 bytes of data 56 bytes from 192.168.10.1: icmp_seq=0 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=1 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=2 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=3 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=4 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=5 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=6 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=7 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=8 time=0 ms 56 bytes from 192.168.10.1: icmp_seq=9 time=0 ms 10 packets transmitted, 10 received, 0% packet loss, time 10100 ms
リポジトリのクローン
この環境を組み込んだリポジトリのクローンをGithub
に作成した。
使用する場合はnuttxのリポジトリとして次のものを使用する。
$ git clone https://github.com/mickey-happygolucky/nuttx.git -b enc28j60 $ git clone https://bitbucket.org/nuttx/apps.git $ cd nuttx $ tools/configure.sh -l configs/stm32f4discovery/nsh_enc28j60 $ make -j4 $ st-flash write nuttx.bin 0x8000000