diff --git a/Makefile b/Makefile index d6698614..d086e182 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ VCFLAGS = /nologo /W3 /O2 /MD /I. $(DEFS) $(TFLAGS) IPV6 ?= 1 ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common ASAN_OPTIONS ?= detect_leaks=1 -EXAMPLES := $(dir $(wildcard examples/*/Makefile)) $(wildcard examples/stm32/nucleo-*) +EXAMPLES := $(dir $(wildcard examples/*/Makefile)) $(wildcard examples/stm32/nucleo-*) $(wildcard examples/rp2040/*) PREFIX ?= /usr/local VERSION ?= $(shell cut -d'"' -f2 src/version.h) COMMON_CFLAGS ?= $(C_WARN) $(WARN) $(INCS) $(DEFS) -DMG_ENABLE_IPV6=$(IPV6) $(TFLAGS) diff --git a/examples/rp2040/pico-w5500/CMakeLists.txt b/examples/rp2040/pico-w5500/CMakeLists.txt new file mode 100644 index 00000000..707e8c92 --- /dev/null +++ b/examples/rp2040/pico-w5500/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.13) +include(pico-sdk/pico_sdk_init.cmake) + +project(example) +pico_sdk_init() + +add_executable(example main.c mongoose.c) +target_link_libraries(example pico_stdlib hardware_spi) +pico_add_extra_outputs(example) + +# Enable USB output. Comment out in order to use UART +pico_enable_stdio_usb(example 1) +pico_enable_stdio_uart(example 0) + +# Mongoose build flags +add_definitions(-DMG_ARCH=MG_ARCH_NEWLIB) +add_definitions(-DMG_ENABLE_MIP=1) +add_definitions(-DMG_ENABLE_CUSTOM_MILLIS=1) diff --git a/examples/rp2040/pico-w5500/Makefile b/examples/rp2040/pico-w5500/Makefile new file mode 100644 index 00000000..3c375c5e --- /dev/null +++ b/examples/rp2040/pico-w5500/Makefile @@ -0,0 +1,13 @@ +SDK_VERSION ?= 1.4.0 +SDK_REPO ?= https://github.com/raspberrypi/pico-sdk + +all example build: pico-sdk + test -d build || mkdir build + cd build && cmake .. && make + +pico-sdk: + git clone --depth 1 -b $(SDK_VERSION) $(SDK_REPO) $@ + cd $@ && git submodule update --init + +clean: + rm -rf pico-sdk build diff --git a/examples/rp2040/pico-w5500/README.md b/examples/rp2040/pico-w5500/README.md new file mode 100644 index 00000000..061975b3 --- /dev/null +++ b/examples/rp2040/pico-w5500/README.md @@ -0,0 +1,57 @@ +# RP2040 with W5500 Ethernet module + +This repository demonstrates how to use RP2040 with W5500 Ethernet module, +to implement networking. In this example, a very simple HTTP server is +implemented. See [other examples](../..) for more functionality. + +## RP2040 Pico wiring + +The W5500 module uses SPI for communication. The following pins are used +by this example (modify `main.c` if you use a different pinout): + +```c +enum { LED = 25, SPI_CS = 17, SPI_CLK = 18, SPI_TX = 19, SPI_RX = 16 }; +``` + +On a breadboard, it might look like this: + +![](images/wiring.png) + +## Pinout reference + +![](images/pinout.png) + +## Build and run + +Clone Mongoose repo, go to this example, and build it: + +```sh +git clone https://github.com/cesanta/mongoose +cd mongoose/examples/ro2040/pico-w5500 +make +``` + +The above will make a firmware in `build/example.uf2`. Reboot your Pico +board in bootloader mode, and copy `example.uf2` to the RPI disk. + +Attach serial console. Then, plug in Ethernet cable: + +``` +$ cu -l /dev/cu.usb* -s 115200 +4653 2 main.c:79:main Ethernet: down +520b 2 main.c:79:main Ethernet: down +5dc3 2 main.c:79:main Ethernet: down +6593 1 mongoose.c:6757:onstatechange Link up +659a 3 mongoose.c:6840:tx_dhcp_discover DHCP discover sent +667b 3 mongoose.c:6723:arp_cache_add ARP cache: added 192.168.0.1 @ 90:5c:44:55:19:8b +667d 2 mongoose.c:6749:onstatechange READY, IP: 192.168.0.24 +667e 2 mongoose.c:6750:onstatechange GW: 192.168.0.1 +6680 2 mongoose.c:6752:onstatechange Lease: 86062 sec +697b 2 main.c:79:main Ethernet: up +7533 2 main.c:79:main Ethernet: up +``` + +Note the aquired IP address printed. Run a browser, and type that IP address +in the address field. You should see an "ok" message in a browser: + +![](images/browser.png) diff --git a/examples/rp2040/pico-w5500/images/browser.png b/examples/rp2040/pico-w5500/images/browser.png new file mode 100644 index 00000000..0afaf7a2 Binary files /dev/null and b/examples/rp2040/pico-w5500/images/browser.png differ diff --git a/examples/rp2040/pico-w5500/images/pinout.png b/examples/rp2040/pico-w5500/images/pinout.png new file mode 100644 index 00000000..f3d85dca Binary files /dev/null and b/examples/rp2040/pico-w5500/images/pinout.png differ diff --git a/examples/rp2040/pico-w5500/images/wiring.png b/examples/rp2040/pico-w5500/images/wiring.png new file mode 100644 index 00000000..3ef38d99 Binary files /dev/null and b/examples/rp2040/pico-w5500/images/wiring.png differ diff --git a/examples/rp2040/pico-w5500/main.c b/examples/rp2040/pico-w5500/main.c new file mode 100644 index 00000000..0a12e89e --- /dev/null +++ b/examples/rp2040/pico-w5500/main.c @@ -0,0 +1,85 @@ +// Copyright (c) 2023 Cesanta Software Limited +// All rights reserved + +#include +#include +#include "hardware/spi.h" +#include "pico/stdlib.h" + +#include "mongoose.h" + +enum { LED = 25, SPI_CS = 17, SPI_CLK = 18, SPI_TX = 19, SPI_RX = 16 }; // Pins +enum { STATUS_TIMER_MS = 3000, BLINK_TIMER_MS = 250 }; // Timeouts + +static void spi_begin(void *spi) { + gpio_put(SPI_CS, 0); +} + +static void spi_end(void *spi) { + gpio_put(SPI_CS, 1); +} + +static uint8_t spi_txn(void *spi, uint8_t byte) { + uint8_t result = 0; + spi_write_read_blocking(spi0, &byte, &result, 1); + // MG_INFO(("%x -> %x", byte, result)); + return result; +} + +uint64_t mg_millis(void) { + return to_ms_since_boot(get_absolute_time()); +} + +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + if (ev == MG_EV_HTTP_MSG) { + mg_http_reply(c, 200, "", "ok\n"); + } +} + +int main(void) { + stdio_init_all(); + MG_INFO(("Starting ...")); + + // Init LED + gpio_init(LED); + gpio_set_dir(LED, GPIO_OUT); + + // Init SPI pins + uint r = spi_init(spi0, 500 * 1000); + MG_INFO(("r = %x\n", r)); + gpio_set_function(SPI_RX, GPIO_FUNC_SPI); // MISO + gpio_set_function(SPI_TX, GPIO_FUNC_SPI); // MOSI + gpio_set_function(SPI_CLK, GPIO_FUNC_SPI); // CLK + gpio_init(SPI_CS); // CS + gpio_set_dir(SPI_CS, GPIO_OUT); // Set CS it to output + gpio_put(SPI_CS, 1); // And drive CS high (inactive) + + // Init Mongoose + struct mip_spi spi = {NULL, spi_begin, spi_end, spi_txn}; + struct mip_if mif = {.mac = {2, 0, 1, 2, 3, 5}, + .driver = &mip_driver_w5500, + .driver_data = &spi}; + struct mg_mgr mgr; // Declare event manager + mg_mgr_init(&mgr); // Init event manager + mg_log_set(MG_LL_DEBUG); // Set DEBUG log level + mip_init(&mgr, &mif); // Init TCP/IP stack + mg_http_listen(&mgr, "http://0.0.0.0", fn, NULL); // HTTP listener + + bool led_on = false; // Initial LED state + uint64_t status_timer = 0, blink_timer = 0; // Initial timer expirations + + // Infinite event manager loop + for (;;) { + if (mg_timer_expired(&blink_timer, BLINK_TIMER_MS, mg_millis())) { + led_on = !led_on; // Flip LED state + if (mip_driver_w5500.up(&mif)) led_on = true; // Always on if Eth up + gpio_put(LED, led_on); // Set LED + } + if (mg_timer_expired(&status_timer, STATUS_TIMER_MS, mg_millis())) { + MG_INFO(("Ethernet: %s", mip_driver_w5500.up(&mif) ? "up" : "down")); + } + mg_mgr_poll(&mgr, 1); + } + + return 0; +} diff --git a/examples/rp2040/pico-w5500/mongoose.c b/examples/rp2040/pico-w5500/mongoose.c new file mode 120000 index 00000000..5e522bbc --- /dev/null +++ b/examples/rp2040/pico-w5500/mongoose.c @@ -0,0 +1 @@ +../../../mongoose.c \ No newline at end of file diff --git a/examples/rp2040/pico-w5500/mongoose.h b/examples/rp2040/pico-w5500/mongoose.h new file mode 120000 index 00000000..ee4ac823 --- /dev/null +++ b/examples/rp2040/pico-w5500/mongoose.h @@ -0,0 +1 @@ +../../../mongoose.h \ No newline at end of file