├── .gitignore ├── .gitmodules ├── .vscode ├── settings.json └── launch.json ├── src ├── config.h ├── main.c ├── lwip │ ├── sys_arch.c │ ├── arch │ │ └── cc.h │ └── lwipopts.h ├── lan8720.h ├── lan8720_rx.pio ├── lan8720_tx.pio └── lan8720.c ├── README.md ├── CMakeLists.txt ├── pico_sdk_import.cmake └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/lwip"] 2 | path = lib/lwip 3 | url = https://github.com/lwip-tcpip/lwip.git 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "dma.h": "c", 4 | "pico.h": "c", 5 | "lan8720_rx.pio.h": "c", 6 | "pio.h": "c", 7 | "assert.h": "c", 8 | "init.h": "c" 9 | } 10 | } -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #define CPU_FREQ 250000000 5 | 6 | #define LAN8720_PIO pio1 7 | #define LAN8720_SM 0 8 | #define LAN8720_PIN_RX 16 9 | #define LAN8720_PIN_TX 19 10 | #define LAN8720_PIN_MDIO 26 11 | 12 | #endif // CONFIG_H -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "lan8720.h" 3 | #include "lwip/apps/httpd.h" 4 | #include "lwip/timeouts.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main() 14 | { 15 | // it is necessary to overclock RPi to 16 | // at least 250MHz 17 | set_sys_clock_khz(CPU_FREQ / KHZ, true); 18 | 19 | stdio_init_all(); 20 | 21 | struct lan8720 lan8720; 22 | 23 | lan8720_init(&lan8720, LAN8720_PIO, LAN8720_SM, LAN8720_PIN_MDIO, LAN8720_PIN_RX, LAN8720_PIN_TX); 24 | httpd_init(); 25 | 26 | for (;;) 27 | sys_check_timeouts(); 28 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Cortex Debug", 9 | "cwd": "${workspaceRoot}", 10 | "executable": "${workspaceRoot}/build/LAN8720.elf", 11 | "request": "launch", 12 | "type": "cortex-debug", 13 | "servertype": "openocd", 14 | "device": "Pico2040", 15 | "runToMain": true, 16 | "configFiles": ["interface/picoprobe.cfg", "target/rp2040.cfg"], 17 | "searchDir": ["D:\\_Moje\\openocd_picoprobe\\tcl"], 18 | "svdFile": "D:\\_Moje\\pico-sdk\\src\\rp2040\\hardware_regs\\rp2040.svd", 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /src/lwip/sys_arch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "lwip/init.h" 12 | 13 | //auto_init_mutex(lwip_mutex); 14 | extern struct critical_section sys_critical_section; 15 | 16 | /* lwip has provision for using a mutex, when applicable */ 17 | sys_prot_t sys_arch_protect(void) 18 | { 19 | critical_section_enter_blocking(&sys_critical_section); 20 | //mutex_enter_blocking(&lwip_mutex); 21 | 22 | return 0; 23 | } 24 | 25 | void sys_arch_unprotect(sys_prot_t pval) 26 | { 27 | (void) pval; 28 | 29 | critical_section_exit(&sys_critical_section); 30 | //mutex_exit(&lwip_mutex); 31 | } 32 | 33 | /* lwip needs a millisecond time source, and the TinyUSB board support code has one available */ 34 | uint32_t sys_now(void) 35 | { 36 | return to_ms_since_boot(get_absolute_time()); 37 | } 38 | -------------------------------------------------------------------------------- /src/lan8720.h: -------------------------------------------------------------------------------- 1 | #ifndef LAN8720_H 2 | #define LAN8720_H 3 | 4 | #include "lwip/netif.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif // __cplusplus 13 | 14 | #define LAN8720_PREAMBLE_SFD_SIZE 8 15 | #define LAN8720_FRAME_SIZE 1518 16 | #define LAN8720_CRC_SIZE sizeof(uint32_t) 17 | 18 | struct lan8720 19 | { 20 | uint32_t lastupdatetime; 21 | 22 | uint8_t mdio_pin; 23 | uint8_t mdc_pin; 24 | uint8_t rx_pin; 25 | uint8_t tx_pin; 26 | 27 | uint8_t phy_address; 28 | PIO pio; 29 | 30 | uint rx_sm; 31 | uint rx_sm_offset; 32 | uint rx_dma_channel; 33 | 34 | uint tx_sm; 35 | uint tx_sm_offset; 36 | uint tx_dma_channel; 37 | 38 | dma_channel_config rx_dma_channel_config; 39 | dma_channel_config tx_dma_channel_config; 40 | 41 | struct netif netif; 42 | uint8_t *mac_addr; 43 | 44 | struct lan8720_framedata *curr_packetdata; 45 | uint8_t txbuffer[LAN8720_PREAMBLE_SFD_SIZE + LAN8720_FRAME_SIZE + LAN8720_CRC_SIZE]; 46 | 47 | bool rxfull; 48 | 49 | struct lan8720 *next; 50 | }; 51 | 52 | 53 | void lan8720_init(struct lan8720 *lan8720, PIO pio, int sm, uint8_t mdio_pin, uint8_t rx_pin, uint8_t tx_pin); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif // __cplusplus 58 | 59 | #endif // LAN8720_H -------------------------------------------------------------------------------- /src/lan8720_rx.pio: -------------------------------------------------------------------------------- 1 | .program lan8720_rx 2 | 3 | ; pin mapping: 4 | ; pin+0: RX0 5 | ; pin+1: RX1 6 | ; pin+2: RXEN 7 | 8 | .wrap_target 9 | wait 0 pin 2 ; Wait while TX0, TX1 and TXEN are nonzero 10 | wait 0 pin 0 ; 11 | wait 0 pin 1 ; 12 | 13 | wait 1 pin 2 ; Detect preamble 14 | wait 1 pin 0 ; 15 | 16 | wait 1 pin 1 ; Detect start frame delimiter 17 | 18 | wait 0 gpio 22 ; Wait single clock 19 | wait 1 gpio 22 ; 20 | 21 | recvloop: 22 | wait 0 gpio 22 ; wait for clock LO 23 | wait 1 gpio 22 ; wait for clock HI 24 | in pins 2 ; sample data 25 | jmp pin recvloop ; if data valid, repeat 26 | 27 | irq wait 0 rel ; let the interrupt handler to process packet 28 | .wrap 29 | 30 | % c-sdk { 31 | 32 | static inline void lan8720_rx_init(PIO pio, uint sm, uint offset, uint pin) { 33 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 3, false); 34 | 35 | pio_sm_config c = lan8720_rx_program_get_default_config(offset); 36 | sm_config_set_in_pins(&c, pin); 37 | 38 | pio_gpio_init(pio, pin); 39 | pio_gpio_init(pio, pin + 1); 40 | pio_gpio_init(pio, pin + 2); 41 | 42 | sm_config_set_in_shift(&c, true, true, 8); 43 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); 44 | sm_config_set_jmp_pin(&c, pin + 2); 45 | 46 | sm_config_set_clkdiv(&c, 1); 47 | 48 | pio_sm_init(pio, sm, offset, &c); 49 | pio_sm_set_enabled(pio, sm, true); 50 | } 51 | %} 52 | -------------------------------------------------------------------------------- /src/lan8720_tx.pio: -------------------------------------------------------------------------------- 1 | .program lan8720_tx 2 | .side_set 1 3 | 4 | ; pin mapping: 5 | ; pin+0: TX0 6 | ; pin+1: TX1 7 | ; pin+2: TXEN 8 | ; 9 | ; side_set pin is TX_EN 10 | 11 | .wrap_target 12 | idle: 13 | ; this loops while TXEN is inactive 14 | jmp !osre inittx side 0x00 ; if there are data in output shift register, go to inittx 15 | jmp idle side 0x00 ; repeat 16 | 17 | inittx: 18 | ; wait single clock 19 | wait 1 gpio 22 side 0x00 ; wait for clock HI 20 | wait 0 gpio 22 side 0x00 ; wait for clock LO 21 | 22 | sendloop: 23 | ; this loops until there no data in TX fifo 24 | out pins 2 side 0x01 ; out 2 bits 25 | wait 1 gpio 22 side 0x01 ; wait for clock HI 26 | wait 0 gpio 22 side 0x01 ; wait for clock LO 27 | jmp !osre sendloop side 0x01 ; if there are data in output shift register, loop 28 | set pins 0 side 0x00 29 | .wrap 30 | 31 | % c-sdk { 32 | 33 | static inline void lan8720_tx_init(PIO pio, uint sm, uint offset, uint pin) { 34 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 3, true); 35 | 36 | pio_sm_config c = lan8720_tx_program_get_default_config(offset); 37 | sm_config_set_out_pins(&c, pin, 2); 38 | 39 | pio_gpio_init(pio, pin); 40 | pio_gpio_init(pio, pin + 1); 41 | pio_gpio_init(pio, pin + 2); 42 | 43 | sm_config_set_out_shift(&c, true, true, 8); 44 | sm_config_set_set_pins(&c, pin, 2); 45 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 46 | 47 | sm_config_set_clkdiv(&c, 1); 48 | 49 | sm_config_set_sideset_pins(&c, pin + 2); 50 | 51 | pio_sm_init(pio, sm, offset, &c); 52 | pio_sm_set_enabled(pio, sm, true); 53 | } 54 | %} 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pico-lan8720 2 | 3 | This was only an experiment so code is not very clean. 4 | 5 | This is an attempt to use RPi Pico with LAN8720. This project was inspired by https://github.com/sandeepmistry/pico-rmii-ethernet. Drawbacks of pico-rmii-ethernet are that it underclocks RPi Pico to 50MHz and ethernet only works at 10MBit. I wanted to use 100MBit ethernet and not to underclock my RPi Pico, so I decided to go another way. 6 | 7 | This project requires overclocking RPi Pico to at least 250MHz. According to various sources the RPi Pico should work well overclocked to this freqnency but I cannot guarantee it to you - use it at your own risk. 8 | 9 | ## Build 10 | 11 | Clone this repository and init submodules 12 | 13 | git submodule init 14 | git submodule update 15 | 16 | Use cmake or VisualStudio Code to build the project. 17 | 18 | ## Configuration 19 | 20 | there are some constants in file config.h. You can configure all pins but nINT/RETCLK is hardcoded to GPIO22. 21 | 22 | ``` 23 | #define LAN8720_PIN_RX 16 24 | #define LAN8720_PIN_TX 19 25 | #define LAN8720_PIN_MDIO 26 26 | 27 | RX0 = LAN8720_PIN_RX + 0 28 | RX1 = LAN8720_PIN_RX + 1 29 | CRS = LAN8720_PIN_RX + 2 30 | 31 | TX0 = LAN8720_PIN_TX + 0 32 | TX1 = LAN8720_PIN_TX + 1 33 | TXEN = LAN8720_PIN_TX + 2 34 | 35 | MDIO = LAN8720_PIN_MDIO + 0 36 | MDC = LAN8720_PIN_MDIO + 1 37 | 38 | 22 = nINT/RETCLK 39 | 40 | ``` 41 | 42 | ## How does it work 43 | 44 | Because RPi Pico runs at higher speed (250MHz) and TX and RX loop executes in 4 cycles so it is possible to catch changes of the clock and capture 50MHz signal by PIO program. 45 | 46 | ### RX 47 | 48 | There is a little hack inspired by pico_rmii_ethernet to detect preamble and start frame delimiter (SFD) on RX0 and RX1 pins. After SFD, data are captured while CRS is HI and written through DMA to memory. When CRS gets LO, interrupt is called and length of the packet is calculated by write cursor of DMA. Interrupt handler should run as short as possible so packet processing is passed to event loop. 49 | 50 | ### TX 51 | 52 | Packet data are constructed in TX fifo including preamble, SFD and CRC (https://en.wikipedia.org/wiki/Ethernet_frame). TX PIO program waits in loop while there are no data in TX fifo. When data are in fifo, PIO program detects falling edge and pushes bits to TX0/TX1 and sets TXEN to HI. When there are no data in TX fifo, TX0/TX1 and TXEN signals go LO. 53 | 54 | ## No guarantee 55 | 56 | I did not have a logic analyzer so this this code was created by method of trial and error. I tested it by ping (1ms interval) and by connecting to http server. There can be bugs in the code. 57 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(name LAN8720) 2 | cmake_minimum_required(VERSION 3.13) 3 | 4 | set(LWIP_PATH ${CMAKE_CURRENT_LIST_DIR}/lib/lwip) 5 | 6 | include(pico_sdk_import.cmake) 7 | 8 | project(${name} C CXX) 9 | set(CMAKE_C_STANDARD 11) 10 | set(CMAKE_CXX_STANDARD 17) 11 | 12 | #set(PICO_DEOPTIMIZED_DEBUG 1) 13 | #set(CMAKE_BUILD_TYPE Debug) 14 | set(CMAKE_BUILD_TYPE RelWithDebInfo) 15 | 16 | pico_sdk_init() 17 | 18 | add_library(pico_lwip INTERFACE) 19 | 20 | target_sources(pico_lwip INTERFACE 21 | ${LWIP_PATH}/src/core/altcp.c 22 | ${LWIP_PATH}/src/core/altcp_alloc.c 23 | ${LWIP_PATH}/src/core/altcp_tcp.c 24 | ${LWIP_PATH}/src/core/def.c 25 | ${LWIP_PATH}/src/core/dns.c 26 | ${LWIP_PATH}/src/core/inet_chksum.c 27 | ${LWIP_PATH}/src/core/init.c 28 | ${LWIP_PATH}/src/core/ip.c 29 | ${LWIP_PATH}/src/core/mem.c 30 | ${LWIP_PATH}/src/core/memp.c 31 | ${LWIP_PATH}/src/core/netif.c 32 | ${LWIP_PATH}/src/core/pbuf.c 33 | ${LWIP_PATH}/src/core/raw.c 34 | ${LWIP_PATH}/src/core/stats.c 35 | ${LWIP_PATH}/src/core/sys.c 36 | ${LWIP_PATH}/src/core/tcp.c 37 | ${LWIP_PATH}/src/core/tcp_in.c 38 | ${LWIP_PATH}/src/core/tcp_out.c 39 | ${LWIP_PATH}/src/core/timeouts.c 40 | ${LWIP_PATH}/src/core/udp.c 41 | ${LWIP_PATH}/src/core/ipv4/acd.c 42 | ${LWIP_PATH}/src/core/ipv4/autoip.c 43 | ${LWIP_PATH}/src/core/ipv4/dhcp.c 44 | ${LWIP_PATH}/src/core/ipv4/etharp.c 45 | ${LWIP_PATH}/src/core/ipv4/icmp.c 46 | ${LWIP_PATH}/src/core/ipv4/igmp.c 47 | ${LWIP_PATH}/src/core/ipv4/ip4.c 48 | ${LWIP_PATH}/src/core/ipv4/ip4_addr.c 49 | ${LWIP_PATH}/src/core/ipv4/ip4_frag.c 50 | ${LWIP_PATH}/src/netif/ethernet.c 51 | 52 | ${LWIP_PATH}/src/apps/http/httpd.c 53 | ${LWIP_PATH}/src/apps/http/fs.c 54 | 55 | ${CMAKE_CURRENT_LIST_DIR}/src/lwip/sys_arch.c 56 | ) 57 | 58 | target_include_directories(pico_lwip INTERFACE 59 | ${LWIP_PATH}/src/include 60 | ${CMAKE_CURRENT_LIST_DIR}/src/lwip 61 | ) 62 | 63 | set(srcs 64 | src/main.c 65 | src/lan8720.c 66 | src/lan8720.h 67 | ) 68 | 69 | add_executable(${name} ${srcs}) 70 | pico_generate_pio_header(${name} ${CMAKE_CURRENT_SOURCE_DIR}/src/lan8720_rx.pio) 71 | pico_generate_pio_header(${name} ${CMAKE_CURRENT_SOURCE_DIR}/src/lan8720_tx.pio) 72 | 73 | target_link_libraries(pico_lwip INTERFACE pico_stdlib hardware_sync) 74 | target_link_libraries(${name} pico_stdlib pico_mem_ops pico_unique_id hardware_sync pico_sync_mutex hardware_pio hardware_dma hardware_irq hardware_spi hardware_adc hardware_uart pico_lwip) 75 | 76 | # enable usb output, disable uart output 77 | pico_enable_stdio_usb(${name} 1) 78 | pico_enable_stdio_uart(${name} 0) 79 | 80 | pico_add_extra_outputs(${name}) 81 | 82 | -------------------------------------------------------------------------------- /src/lwip/arch/cc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * 31 | */ 32 | #ifndef __CC_H__ 33 | #define __CC_H__ 34 | 35 | //#include "cpu.h" 36 | 37 | typedef int sys_prot_t; 38 | 39 | 40 | 41 | /* define compiler specific symbols */ 42 | #if defined (__ICCARM__) 43 | 44 | #define PACK_STRUCT_BEGIN 45 | #define PACK_STRUCT_STRUCT 46 | #define PACK_STRUCT_END 47 | #define PACK_STRUCT_FIELD(x) x 48 | #define PACK_STRUCT_USE_INCLUDES 49 | 50 | #elif defined (__CC_ARM) 51 | 52 | #define PACK_STRUCT_BEGIN __packed 53 | #define PACK_STRUCT_STRUCT 54 | #define PACK_STRUCT_END 55 | #define PACK_STRUCT_FIELD(x) x 56 | 57 | #elif defined (__GNUC__) 58 | 59 | #define PACK_STRUCT_BEGIN 60 | #define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) 61 | #define PACK_STRUCT_END 62 | #define PACK_STRUCT_FIELD(x) x 63 | 64 | #elif defined (__TASKING__) 65 | 66 | #define PACK_STRUCT_BEGIN 67 | #define PACK_STRUCT_STRUCT 68 | #define PACK_STRUCT_END 69 | #define PACK_STRUCT_FIELD(x) x 70 | 71 | #endif 72 | 73 | #define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0) 74 | 75 | #endif /* __CC_H__ */ 76 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /src/lwip/lwipopts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Simon Goldschmidt 30 | * 31 | */ 32 | #ifndef __LWIPOPTS_H__ 33 | #define __LWIPOPTS_H__ 34 | 35 | /* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ 36 | #define NO_SYS 1 37 | #define MEM_ALIGNMENT 4 38 | #define LWIP_RAW 1 39 | #define LWIP_NETCONN 0 40 | #define LWIP_SOCKET 0 41 | #define LWIP_DHCP 1 42 | #define LWIP_ICMP 1 43 | #define LWIP_UDP 1 44 | #define LWIP_TCP 1 45 | #define ETH_PAD_SIZE 0 46 | #define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67)) 47 | 48 | #define LWIP_NETIF_LINK_CALLBACK 1 49 | #define LWIP_NETIF_STATUS_CALLBACK 1 50 | 51 | #define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/) 52 | #define TCP_SND_BUF (2 * TCP_MSS) 53 | 54 | #define LWIP_HTTPD_CGI 0 55 | #define LWIP_HTTPD_SSI 0 56 | #define LWIP_HTTPD_SSI_INCLUDE_TAG 0 57 | 58 | #if 0 59 | #define LWIP_DEBUG 1 60 | #define TCP_DEBUG LWIP_DBG_ON 61 | #define ETHARP_DEBUG LWIP_DBG_ON 62 | #define PBUF_DEBUG LWIP_DBG_ON 63 | #define IP_DEBUG LWIP_DBG_ON 64 | #define TCPIP_DEBUG LWIP_DBG_ON 65 | #define DHCP_DEBUG LWIP_DBG_ON 66 | #define UDP_DEBUG LWIP_DBG_ON 67 | #endif 68 | 69 | 70 | #endif /* __LWIPOPTS_H__ */ 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/lan8720.c: -------------------------------------------------------------------------------- 1 | #include "lan8720.h" 2 | #include "config.h" 3 | #include "lan8720_rx.pio.h" 4 | #include "lan8720_tx.pio.h" 5 | #include "lwip/dhcp.h" 6 | #include "lwip/init.h" 7 | #include "lwip/timeouts.h" 8 | #include "lwip/sys.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define _BV(n) (1 << (n)) 18 | 19 | #define LAN8720_REG0_SPEED100 0x2000 20 | #define LAN8720_REG0_AUTONEG 0x1000 21 | #define LAN8720_REG0_FULLDUPLEX 0x0100 22 | 23 | #define LAN8720_REG4_TXABLE 0x0080 24 | #define LAN8720_REG4_10FULLDUPLEX 0x0040 25 | #define LAN8720_REG4_10ABLE 0x0020 26 | 27 | #define LAN8720_RX_FRAME_COUNT 2 28 | #define LAN8720_UPDATE_INTERVAL 1000 29 | 30 | #define LAN8720_CRC_POLY 0xedb88320 31 | 32 | //#define ENABLE_LOG 33 | #ifdef ENABLE_LOG 34 | #define LOG_FMT(fmt, ...) do { printf(fmt "\n", __VA_ARGS__); } while (false) 35 | #define LOG(text) do { printf(text); } while (false) 36 | #else // ENABLE_LOG 37 | #define LOG_FMT(fmt, ...) do { } while (false) 38 | #define LOG(text) do { } while (false) 39 | #endif // ENABLE_LOG 40 | 41 | struct critical_section sys_critical_section; 42 | 43 | struct lan8720_framedata 44 | { 45 | struct pbuf_custom p; 46 | struct lan8720 *lan8720; 47 | uint16_t size; 48 | uint8_t data[LAN8720_FRAME_SIZE]; 49 | }; 50 | 51 | LWIP_MEMPOOL_DECLARE(lan8720_rx_pool, LAN8720_RX_FRAME_COUNT, sizeof(struct lan8720_framedata), "rx_pool"); 52 | 53 | static struct lan8720 *g_lan8720_first; 54 | 55 | volatile static int dbg; 56 | 57 | static bool lan8720_rx_trystart(struct lan8720 *lan8720); 58 | 59 | void lan8720_link_callback(struct netif *netif) 60 | { 61 | LOG_FMT("netif link status changed %s\n", netif_is_link_up(netif) ? "up" : "down"); 62 | } 63 | 64 | void lan8720_status_callback(struct netif *netif) 65 | { 66 | LOG_FMT("netif status changed %s\n", ip4addr_ntoa(netif_ip4_addr(netif))); 67 | } 68 | 69 | static uint32_t lan8720_frame_crc(const uint8_t *data, int length) 70 | { 71 | uint32_t crc = 0xffffffff; /* Initial value. */ 72 | 73 | while(--length >= 0) 74 | { 75 | uint8_t current_octet = *data++; 76 | 77 | for (int bit = 8; --bit >= 0; current_octet >>= 1) 78 | { 79 | if ((crc ^ current_octet) & 1) 80 | { 81 | crc >>= 1; 82 | crc ^= LAN8720_CRC_POLY; 83 | } 84 | else 85 | { 86 | crc >>= 1; 87 | } 88 | } 89 | } 90 | 91 | return ~crc; 92 | } 93 | 94 | static void lan8720_free_framedata(struct lan8720_framedata *framedata) 95 | { 96 | struct lan8720 *lan8720 = framedata->lan8720; 97 | 98 | LWIP_MEMPOOL_FREE(lan8720_rx_pool, framedata); 99 | if (lan8720->rxfull) 100 | { 101 | if (lan8720_rx_trystart(lan8720)) 102 | { 103 | lan8720_rx_init(lan8720->pio, lan8720->rx_sm, lan8720->rx_sm_offset, lan8720->rx_pin); 104 | pio_sm_set_enabled(lan8720->pio, lan8720->rx_sm, true); 105 | } 106 | } 107 | } 108 | 109 | static void lan8720_free_pbuf(struct pbuf *p) 110 | { 111 | struct lan8720_framedata *framedata = (struct lan8720_framedata*)p; // same address 112 | lan8720_free_framedata(framedata); 113 | } 114 | 115 | static void lan8720_rx_process_frame(void *userdata) 116 | { 117 | struct lan8720_framedata *framedata = (struct lan8720_framedata*)userdata; 118 | struct lan8720 *lan8720 = framedata->lan8720; 119 | 120 | if (framedata->size > sizeof(uint32_t)) 121 | { 122 | uint32_t payload_size = framedata->size - sizeof(uint32_t); 123 | uint32_t crc = lan8720_frame_crc(framedata->data, payload_size); 124 | uint8_t *frame_crc = framedata->data + payload_size; 125 | if (memcmp(&crc, frame_crc, sizeof(uint32_t)) == 0) 126 | { 127 | framedata->p.custom_free_function = lan8720_free_pbuf; 128 | struct pbuf *p = pbuf_alloced_custom( 129 | PBUF_RAW, 130 | framedata->size, 131 | PBUF_REF, 132 | &framedata->p, 133 | framedata->data, 134 | sizeof(framedata->data) 135 | ); 136 | 137 | err_t res = lan8720->netif.input(p, &lan8720->netif); 138 | if (res != ERR_OK) 139 | { 140 | pbuf_free(p); 141 | } 142 | else 143 | { 144 | LOG("valid packet received\n"); 145 | } 146 | } 147 | else 148 | { 149 | lan8720_free_framedata(framedata); 150 | LOG("invalid packet received (crc)\n"); 151 | } 152 | } 153 | else 154 | { 155 | lan8720_free_framedata(framedata); 156 | LOG("invalid packet received (short)\n"); 157 | } 158 | } 159 | 160 | static void lan8720_rx_start(struct lan8720_framedata *framedata) 161 | { 162 | struct lan8720 *lan8720 = framedata->lan8720; 163 | 164 | dma_channel_set_write_addr(lan8720->rx_dma_channel, framedata->data, false); 165 | dma_channel_set_trans_count(lan8720->rx_dma_channel, sizeof(framedata->data), true); 166 | } 167 | 168 | static bool lan8720_rx_trystart(struct lan8720 *lan8720) 169 | { 170 | struct lan8720_framedata *framedata = (struct lan8720_framedata *)LWIP_MEMPOOL_ALLOC(lan8720_rx_pool); 171 | 172 | if (framedata == NULL) 173 | return false; 174 | 175 | framedata->lan8720 = lan8720; 176 | lan8720->curr_packetdata = framedata; 177 | lan8720_rx_start(framedata); 178 | return true; 179 | } 180 | 181 | static void lan8720_rx_finish(struct lan8720 *lan8720) 182 | { 183 | dma_channel_abort(lan8720->rx_dma_channel); 184 | 185 | struct lan8720_framedata *framedata = lan8720->curr_packetdata; 186 | assert(framedata != NULL); 187 | 188 | lan8720->curr_packetdata = NULL; 189 | 190 | // get address of write pointer of DMA 191 | uint8_t *wr = (uint8_t *)dma_channel_hw_addr(lan8720->rx_dma_channel)->write_addr; 192 | // frame length is calculated by difference of dma_write pointer and frame begin pointer 193 | framedata->size = (uint16_t)(wr - framedata->data); 194 | sys_timeout(0, lan8720_rx_process_frame, framedata); 195 | } 196 | 197 | static void lan8720_rx_handle(struct lan8720 *lan8720) 198 | { 199 | if (lan8720->pio->ints0 & (PIO_IRQ0_INTS_SM0_BITS << lan8720->rx_sm)) 200 | { 201 | lan8720_rx_finish(lan8720); 202 | 203 | if (!lan8720_rx_trystart(lan8720)) 204 | { 205 | // STOP PIO! 206 | pio_sm_set_enabled(lan8720->pio, lan8720->rx_sm, false); 207 | lan8720->rxfull = true; 208 | } 209 | 210 | pio_interrupt_clear(lan8720->pio, lan8720->rx_sm); 211 | } 212 | } 213 | 214 | static void lan8720_rx_irq_handler() 215 | { 216 | struct lan8720 *lan8720 = g_lan8720_first; 217 | while (lan8720 != NULL) 218 | { 219 | lan8720_rx_handle(lan8720); 220 | lan8720 = lan8720->next; 221 | } 222 | } 223 | 224 | static err_t lan8720_tx_start(struct netif *netif, struct pbuf *p) 225 | { 226 | struct lan8720 *lan8720 = (struct lan8720 *)netif->state; 227 | 228 | // wait 229 | dma_channel_wait_for_finish_blocking(lan8720->tx_dma_channel); // production 230 | 231 | uint len = LAN8720_PREAMBLE_SFD_SIZE; 232 | for (struct pbuf *q = p; q != NULL; q = q->next) 233 | { 234 | memcpy(lan8720->txbuffer + len, q->payload, q->len); 235 | 236 | len += q->len; 237 | 238 | if (q->len == q->tot_len) 239 | { 240 | break; 241 | } 242 | } 243 | 244 | if (len < 60 + LAN8720_PREAMBLE_SFD_SIZE) 245 | { 246 | // pad 247 | len = 60 + LAN8720_PREAMBLE_SFD_SIZE; 248 | } 249 | 250 | uint32_t crc = lan8720_frame_crc(lan8720->txbuffer + LAN8720_PREAMBLE_SFD_SIZE, len - LAN8720_PREAMBLE_SFD_SIZE); 251 | 252 | memcpy(lan8720->txbuffer + len, &crc, sizeof(crc)); 253 | len += 4; 254 | 255 | dma_channel_set_read_addr(lan8720->tx_dma_channel, lan8720->txbuffer, false); 256 | dma_channel_set_trans_count(lan8720->tx_dma_channel, len, true); 257 | 258 | LOG("sending packet\n"); 259 | 260 | return ERR_OK; 261 | } 262 | 263 | static inline void lan8720_rmii_mdio_dir_out(struct lan8720 *lan8720) 264 | { 265 | gpio_set_dir(lan8720->mdio_pin, GPIO_OUT); 266 | } 267 | 268 | static inline void lan8720_rmii_mdio_dir_in(struct lan8720 *lan8720) 269 | { 270 | gpio_set_dir(lan8720->mdio_pin, GPIO_IN); 271 | } 272 | 273 | static inline void lan8720_rmii_mdio_clk_lo(struct lan8720 *lan8720) 274 | { 275 | gpio_put(lan8720->mdc_pin, 0); 276 | } 277 | 278 | static inline void lan8720_rmii_mdio_clk_hi(struct lan8720 *lan8720) 279 | { 280 | gpio_put(lan8720->mdc_pin, 1); 281 | } 282 | 283 | static void lan8720_rmii_mdio_out_bit(struct lan8720 *lan8720, uint8_t bit) 284 | { 285 | lan8720_rmii_mdio_clk_lo(lan8720); 286 | 287 | sleep_us(1); 288 | 289 | gpio_put(lan8720->mdio_pin, bit); 290 | 291 | lan8720_rmii_mdio_clk_hi(lan8720); 292 | 293 | sleep_us(1); 294 | } 295 | 296 | static uint8_t lan8720_rmii_mdio_in_bit(struct lan8720 *lan8720) 297 | { 298 | uint8_t res; 299 | lan8720_rmii_mdio_clk_lo(lan8720); 300 | 301 | sleep_us(1); 302 | 303 | lan8720_rmii_mdio_clk_hi(lan8720); 304 | res = gpio_get(lan8720->mdio_pin); 305 | 306 | sleep_us(1); 307 | 308 | return res ? 0x01 : 0x00; 309 | } 310 | 311 | static uint16_t lan8720_rmii_mdio_read(struct lan8720 *lan8720, uint8_t addr, uint8_t reg) 312 | { 313 | lan8720_rmii_mdio_dir_out(lan8720); 314 | 315 | // PRE_32 316 | for (int i = 0; i < 32; i++) 317 | lan8720_rmii_mdio_out_bit(lan8720, 1); 318 | 319 | // ST 320 | lan8720_rmii_mdio_out_bit(lan8720, 0); 321 | lan8720_rmii_mdio_out_bit(lan8720, 1); 322 | 323 | // OP 324 | lan8720_rmii_mdio_out_bit(lan8720, 1); 325 | lan8720_rmii_mdio_out_bit(lan8720, 0); 326 | 327 | // PA5 328 | for (int i = 0; i < 5; i++) 329 | { 330 | uint bit = (addr >> (4 - i)) & 0x01; 331 | lan8720_rmii_mdio_out_bit(lan8720, bit); 332 | } 333 | 334 | // RA5 335 | for (int i = 0; i < 5; i++) 336 | { 337 | uint bit = (reg >> (4 - i)) & 0x01; 338 | lan8720_rmii_mdio_out_bit(lan8720, bit); 339 | } 340 | 341 | // TA 342 | lan8720_rmii_mdio_dir_in(lan8720); 343 | lan8720_rmii_mdio_out_bit(lan8720, 0); 344 | lan8720_rmii_mdio_out_bit(lan8720, 0); 345 | 346 | uint16_t data = 0; 347 | for (int i = 0; i < 16; i++) 348 | { 349 | data <<= 1; 350 | 351 | data |= lan8720_rmii_mdio_in_bit(lan8720); 352 | } 353 | 354 | return data; 355 | } 356 | 357 | static void lan8720_rmii_mdio_write(struct lan8720 *lan8720, uint8_t addr, uint8_t reg, uint16_t value) 358 | { 359 | lan8720_rmii_mdio_dir_out(lan8720); 360 | 361 | // PRE_32 362 | for (int i = 0; i < 32; i++) 363 | lan8720_rmii_mdio_out_bit(lan8720, 1); 364 | 365 | // ST 366 | lan8720_rmii_mdio_out_bit(lan8720, 0); 367 | lan8720_rmii_mdio_out_bit(lan8720, 1); 368 | 369 | // OP 370 | lan8720_rmii_mdio_out_bit(lan8720, 0); 371 | lan8720_rmii_mdio_out_bit(lan8720, 1); 372 | 373 | // PA5 374 | for (int i = 0; i < 5; i++) 375 | { 376 | uint bit = (addr >> (4 - i)) & 0x01; 377 | lan8720_rmii_mdio_out_bit(lan8720, bit); 378 | } 379 | 380 | // RA5 381 | for (int i = 0; i < 5; i++) 382 | { 383 | uint bit = (reg >> (4 - i)) & 0x01; 384 | lan8720_rmii_mdio_out_bit(lan8720, bit); 385 | } 386 | 387 | // TA 388 | lan8720_rmii_mdio_out_bit(lan8720, 1); 389 | lan8720_rmii_mdio_out_bit(lan8720, 0); 390 | 391 | uint16_t data = 0; 392 | for (int i = 0; i < 16; i++) 393 | { 394 | uint bit = (value >> (15 - i)) & 0x01; 395 | 396 | lan8720_rmii_mdio_out_bit(lan8720, bit); 397 | } 398 | } 399 | 400 | void lan8720_updatestate(struct lan8720 *lan8720) 401 | { 402 | volatile uint16_t link_status = (lan8720_rmii_mdio_read(lan8720, lan8720->phy_address, 1) & 0x04) >> 2; 403 | if (netif_is_link_up(&lan8720->netif) ^ link_status) 404 | { 405 | if (link_status) 406 | { 407 | // LOG("netif_set_link_up\n"); 408 | netif_set_link_up(&lan8720->netif); 409 | } 410 | else 411 | { 412 | // LOG("netif_set_link_down\n"); 413 | netif_set_link_down(&lan8720->netif); 414 | } 415 | } 416 | } 417 | 418 | void lan8720_idlehandler(void *userdata) 419 | { 420 | struct lan8720 *lan8720 = (struct lan8720 *)userdata; 421 | 422 | sys_check_timeouts(); 423 | 424 | u32_t now = sys_now(); 425 | if (now - lan8720->lastupdatetime >= LAN8720_UPDATE_INTERVAL) 426 | { 427 | lan8720->lastupdatetime = now; 428 | lan8720_updatestate(lan8720); 429 | } 430 | 431 | sys_timeout(0, lan8720_idlehandler, lan8720); 432 | } 433 | 434 | err_t lan8720_netif_init(struct netif *netif) 435 | { 436 | struct lan8720 *lan8720 = (struct lan8720 *)netif->state; 437 | 438 | netif->linkoutput = lan8720_tx_start; 439 | netif->output = etharp_output; 440 | netif->mtu = 1500; 441 | netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6; 442 | 443 | // generate one for unique board id 444 | pico_unique_board_id_t board_id; 445 | 446 | pico_get_unique_board_id(&board_id); 447 | 448 | netif->hwaddr[0] = 0xb8; 449 | netif->hwaddr[1] = 0x27; 450 | netif->hwaddr[2] = 0xeb; 451 | memcpy(&netif->hwaddr[3], &board_id.id[5], 3); 452 | 453 | netif->hwaddr_len = ETH_HWADDR_LEN; 454 | 455 | uint8_t iodir; 456 | 457 | gpio_init(lan8720->mdio_pin); 458 | gpio_init(lan8720->mdc_pin); 459 | gpio_set_dir(lan8720->mdc_pin, GPIO_OUT); 460 | 461 | lan8720_rmii_mdio_clk_lo(lan8720); 462 | 463 | for (int i = 0; i < 32; i++) 464 | { 465 | uint16_t aux = lan8720_rmii_mdio_read(lan8720, i, 0); 466 | if (aux != 0xffff) 467 | { 468 | lan8720->phy_address = i; 469 | LOG_FMT("detected LAN8720 with address %d\n", i); 470 | 471 | break; 472 | } 473 | } 474 | 475 | // 100MBit 476 | lan8720_rmii_mdio_write(lan8720, lan8720->phy_address, 0, LAN8720_REG0_SPEED100 | LAN8720_REG0_AUTONEG | LAN8720_REG0_FULLDUPLEX); // 100MBit + AutoNegotiation 477 | lan8720_rmii_mdio_write(lan8720, lan8720->phy_address, 4, 0x00c1); // SPEED 100Mbps, 100 Full + TX ability 478 | 479 | lan8720->rx_sm_offset = pio_add_program(lan8720->pio, &lan8720_rx_program); 480 | lan8720->rx_dma_channel = dma_claim_unused_channel(true); 481 | lan8720->rx_dma_channel_config = dma_channel_get_default_config(lan8720->rx_dma_channel); 482 | 483 | channel_config_set_read_increment(&lan8720->rx_dma_channel_config, false); 484 | channel_config_set_write_increment(&lan8720->rx_dma_channel_config, true); 485 | channel_config_set_dreq(&lan8720->rx_dma_channel_config, pio_get_dreq(lan8720->pio, lan8720->rx_sm, false)); 486 | channel_config_set_transfer_data_size(&lan8720->rx_dma_channel_config, DMA_SIZE_8); 487 | dma_channel_set_config(lan8720->rx_dma_channel, &lan8720->rx_dma_channel_config, false); 488 | dma_channel_set_read_addr(lan8720->rx_dma_channel, ((uint8_t*)&lan8720->pio->rxf[lan8720->rx_sm]) + 3, false); 489 | 490 | lan8720->tx_sm_offset = pio_add_program(lan8720->pio, &lan8720_tx_program); 491 | lan8720->tx_dma_channel = dma_claim_unused_channel(true); 492 | lan8720->tx_dma_channel_config = dma_channel_get_default_config(lan8720->tx_dma_channel); 493 | 494 | channel_config_set_read_increment(&lan8720->tx_dma_channel_config, true); 495 | channel_config_set_write_increment(&lan8720->tx_dma_channel_config, false); 496 | channel_config_set_dreq(&lan8720->tx_dma_channel_config, pio_get_dreq(lan8720->pio, lan8720->tx_sm, true)); 497 | channel_config_set_transfer_data_size(&lan8720->tx_dma_channel_config, DMA_SIZE_8); 498 | dma_channel_set_config(lan8720->tx_dma_channel, &lan8720->tx_dma_channel_config, false); 499 | dma_channel_set_write_addr(lan8720->tx_dma_channel, ((uint8_t*)&lan8720->pio->txf[lan8720->tx_sm]) + 0, false); 500 | 501 | for (int i = 0; i < 7; i++) 502 | lan8720->txbuffer[i] = 0x55; 503 | lan8720->txbuffer[7] = 0xd5; 504 | 505 | lan8720->rxfull = false; 506 | 507 | // netif_set_link_up will tx packet 508 | lan8720_tx_init(lan8720->pio, lan8720->tx_sm, lan8720->tx_sm_offset, lan8720->tx_pin); 509 | 510 | irq_set_exclusive_handler(lan8720->pio == pio0 ? PIO0_IRQ_0 : PIO1_IRQ_0, lan8720_rx_irq_handler); 511 | pio_set_irq0_source_enabled(lan8720->pio, pis_interrupt0, true); 512 | irq_set_enabled(lan8720->pio == pio0 ? PIO0_IRQ_0 : PIO1_IRQ_0, true); 513 | 514 | if (lan8720_rx_trystart(lan8720)) 515 | { 516 | lan8720_rx_init(lan8720->pio, lan8720->rx_sm, lan8720->rx_sm_offset, lan8720->rx_pin); 517 | pio_sm_set_enabled(lan8720->pio, lan8720->rx_sm, true); 518 | } 519 | 520 | sys_timeout(0, lan8720_idlehandler, lan8720); 521 | 522 | return ERR_OK; 523 | } 524 | 525 | void lan8720_init(struct lan8720 *lan8720, PIO pio, int sm, uint8_t mdio_pin, uint8_t rx_pin, uint8_t tx_pin) 526 | { 527 | lan8720->mdio_pin = mdio_pin; 528 | lan8720->mdc_pin = mdio_pin + 1; 529 | lan8720->pio = pio; 530 | lan8720->rx_sm = sm; 531 | lan8720->tx_sm = sm + 1; 532 | lan8720->rx_pin = rx_pin; 533 | lan8720->tx_pin = tx_pin; 534 | lan8720->next = g_lan8720_first; 535 | g_lan8720_first = lan8720; 536 | 537 | static bool sys_critical_section_inited = false; 538 | if (!sys_critical_section_inited) 539 | { 540 | critical_section_init(&sys_critical_section); 541 | sys_critical_section_inited = true; 542 | } 543 | 544 | static bool pool_initialized = false; 545 | if (!pool_initialized) 546 | { 547 | LWIP_MEMPOOL_INIT(lan8720_rx_pool); 548 | pool_initialized = true; 549 | } 550 | 551 | lwip_init(); 552 | netif_add(&lan8720->netif, IP4_ADDR_ANY4, IP4_ADDR_ANY, IP4_ADDR_ANY, lan8720, lan8720_netif_init, netif_input); 553 | 554 | netif_set_link_callback(&lan8720->netif, lan8720_link_callback); 555 | netif_set_status_callback(&lan8720->netif, lan8720_status_callback); 556 | 557 | netif_set_default(&lan8720->netif); 558 | netif_set_up(&lan8720->netif); 559 | dhcp_start(&lan8720->netif); 560 | } 561 | --------------------------------------------------------------------------------