From a9caeed59dab2767c8f99ef39072c61d612ea5a0 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Sun, 7 Feb 2021 14:33:12 +0100 Subject: [PATCH 1/5] [board] Add NUCLEO-F767ZI BSP --- .circleci/config.yml | 2 +- README.md | 7 +- examples/nucleo_f767zi/blink/main.cpp | 40 ++++++ examples/nucleo_f767zi/blink/project.xml | 9 ++ src/modm/board/nucleo_f767zi/board.hpp | 151 +++++++++++++++++++++++ src/modm/board/nucleo_f767zi/board.xml | 16 +++ src/modm/board/nucleo_f767zi/module.lb | 44 +++++++ 7 files changed, 265 insertions(+), 4 deletions(-) create mode 100755 examples/nucleo_f767zi/blink/main.cpp create mode 100755 examples/nucleo_f767zi/blink/project.xml create mode 100755 src/modm/board/nucleo_f767zi/board.hpp create mode 100755 src/modm/board/nucleo_f767zi/board.xml create mode 100755 src/modm/board/nucleo_f767zi/module.lb diff --git a/.circleci/config.yml b/.circleci/config.yml index 03b4d6cc0e..766b497548 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -102,7 +102,7 @@ jobs: name: Examples STM32F7 Series when: always command: | - (cd examples && ../tools/scripts/examples_compile.py stm32f746g_discovery stm32f769i_discovery nucleo_f746zg) + (cd examples && ../tools/scripts/examples_compile.py stm32f746g_discovery stm32f769i_discovery nucleo_f746zg nucleo_f767zi) - run: name: Examples STM32G0 Series when: always diff --git a/README.md b/README.md index 325ab0d85c..ea75c6e0fd 100644 --- a/README.md +++ b/README.md @@ -153,20 +153,21 @@ documentation. NUCLEO-F446ZE NUCLEO-F746ZG +NUCLEO-F767ZI NUCLEO-G071RB NUCLEO-G431KB -NUCLEO-G431RB +NUCLEO-G431RB NUCLEO-G474RE NUCLEO-L152RE NUCLEO-L432KC -NUCLEO-L476RG +NUCLEO-L476RG OLIMEXINO-STM32 Raspberry Pi SAMD21-MINI -STM32-F4VE +STM32-F4VE STM32F030F4P6-DEMO diff --git a/examples/nucleo_f767zi/blink/main.cpp b/examples/nucleo_f767zi/blink/main.cpp new file mode 100755 index 0000000000..205bc44743 --- /dev/null +++ b/examples/nucleo_f767zi/blink/main.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016-2017, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +using namespace Board; + +int +main() +{ + Board::initialize(); + Leds::setOutput(); + + // Use the logging streams to print some messages. + // Change MODM_LOG_LEVEL above to enable or disable these messages + MODM_LOG_DEBUG << "debug" << modm::endl; + MODM_LOG_INFO << "info" << modm::endl; + MODM_LOG_WARNING << "warning" << modm::endl; + MODM_LOG_ERROR << "error" << modm::endl; + + uint32_t counter(0); + + while (true) + { + Leds::write(1 << (counter % (Leds::width+1) )); + modm::delay(Button::read() ? 100ms : 500ms); + + MODM_LOG_INFO << "loop: " << counter++ << modm::endl; + } + + return 0; +} diff --git a/examples/nucleo_f767zi/blink/project.xml b/examples/nucleo_f767zi/blink/project.xml new file mode 100755 index 0000000000..13d0f39050 --- /dev/null +++ b/examples/nucleo_f767zi/blink/project.xml @@ -0,0 +1,9 @@ + + modm:nucleo-f767zi + + + + + modm:build:scons + + diff --git a/src/modm/board/nucleo_f767zi/board.hpp b/src/modm/board/nucleo_f767zi/board.hpp new file mode 100755 index 0000000000..00ae3e6879 --- /dev/null +++ b/src/modm/board/nucleo_f767zi/board.hpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2020, Mike Wolfram + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_NUCLEO_F767ZI_HPP +#define MODM_STM32_NUCLEO_F767ZI_HPP + +#include +#include +#include +/// @ingroup modm_board_nucleo_f767zi +#define MODM_BOARD_HAS_LOGGER + +using namespace modm::platform; + +/// @ingroup modm_board_nucleo_f767zi +namespace Board +{ + using namespace modm::literals; + +/// STM32F7 running at 216MHz from the external 8MHz clock +struct SystemClock +{ + static constexpr uint32_t Frequency = 216_MHz; + static constexpr uint32_t Apb1 = Frequency / 4; + static constexpr uint32_t Apb2 = Frequency / 2; + static constexpr uint32_t Apb1Timer = Apb1 * 2; + static constexpr uint32_t Apb2Timer = Apb2 * 2; + + static constexpr uint32_t Ethernet = Frequency; + + static constexpr uint32_t Adc1 = Apb2; + static constexpr uint32_t Adc2 = Apb2; + static constexpr uint32_t Adc3 = Apb2; + + static constexpr uint32_t Dac = Apb1; + + static constexpr uint32_t Spi1 = Apb2; + static constexpr uint32_t Spi2 = Apb1; + static constexpr uint32_t Spi3 = Apb1; + static constexpr uint32_t Spi4 = Apb2; + static constexpr uint32_t Spi5 = Apb2; + static constexpr uint32_t Spi6 = Apb2; + + static constexpr uint32_t Usart1 = Apb2; + static constexpr uint32_t Usart2 = Apb1; + static constexpr uint32_t Usart3 = Apb1; + static constexpr uint32_t Uart4 = Apb1; + static constexpr uint32_t Uart5 = Apb1; + static constexpr uint32_t Usart6 = Apb2; + static constexpr uint32_t Uart7 = Apb1; + static constexpr uint32_t Uart8 = Apb1; + + static constexpr uint32_t Can1 = Apb1; + static constexpr uint32_t Can2 = Apb1; + + static constexpr uint32_t I2c1 = Apb1; + static constexpr uint32_t I2c2 = Apb1; + static constexpr uint32_t I2c3 = Apb1; + static constexpr uint32_t I2c4 = Apb1; + + static constexpr uint32_t Timer1 = Apb2Timer; + static constexpr uint32_t Timer2 = Apb1Timer; + static constexpr uint32_t Timer3 = Apb1Timer; + static constexpr uint32_t Timer4 = Apb1Timer; + static constexpr uint32_t Timer5 = Apb1Timer; + static constexpr uint32_t Timer6 = Apb1Timer; + static constexpr uint32_t Timer7 = Apb1Timer; + static constexpr uint32_t Timer8 = Apb2Timer; + static constexpr uint32_t Timer9 = Apb2Timer; + static constexpr uint32_t Timer10 = Apb2Timer; + static constexpr uint32_t Timer11 = Apb2Timer; + static constexpr uint32_t Timer12 = Apb1Timer; + static constexpr uint32_t Timer13 = Apb1Timer; + static constexpr uint32_t Timer14 = Apb1Timer; + + static bool inline + enable() + { + Rcc::enableExternalClock(); // 8 MHz + const Rcc::PllFactors pllFactors{ + .pllM = 4, // 8MHz / M=4 -> 2MHz + .pllN = 216, // 2MHz * N=216 -> 432MHz + .pllP = 2 // 432MHz / P=2 -> 216MHz = F_cpu + //.pllQ = 9 // 432MHz / P=2 -> 48MHz (USB, etc.) + }; + Rcc::enablePll(Rcc::PllSource::ExternalClock, pllFactors); + + // Required for 216 MHz operation + Rcc::enableOverdriveMode(); + Rcc::setFlashLatency(); + Rcc::enableSystemClock(Rcc::SystemClockSource::Pll); + // APB1 is running at 54MHz, since AHB / 4 = 54MHz (= limit) + Rcc::setApb1Prescaler(Rcc::Apb1Prescaler::Div4); + // APB2 is running at 108MHz, since AHB / 2 = 108MHz (= limit) + Rcc::setApb2Prescaler(Rcc::Apb2Prescaler::Div2); + Rcc::updateCoreFrequency(); + + return true; + } +}; + +// Arduino Footprint +#include "nucleo144_arduino.hpp" + +using Button = GpioInputC13; + +using LedGreen = GpioOutputB0; // LED1 [Green] +using LedBlue = GpioOutputB7; // LED2 [Blue] +using LedRed = GpioOutputB14; // LED3 [Red] +using Leds = SoftwareGpioPort< LedRed, LedBlue, LedGreen >; + +namespace stlink +{ +using Tx = GpioOutputD8; +using Rx = GpioInputD9; +using Uart = Usart3; +} + +using LoggerDevice = modm::IODeviceWrapper< stlink::Uart, modm::IOBuffer::BlockIfFull >; + + +inline void +initialize() +{ + SystemClock::enable(); + SysTickTimer::initialize(); + + stlink::Uart::connect(); + stlink::Uart::initialize(); + + LedGreen::setOutput(modm::Gpio::Low); + LedBlue::setOutput(modm::Gpio::Low); + LedRed::setOutput(modm::Gpio::Low); + + Button::setInput(); + Button::setInputTrigger(Gpio::InputTrigger::RisingEdge); + Button::enableExternalInterrupt(); +// Button::enableExternalInterruptVector(12); +} + +} + +#endif // MODM_STM32_NUCLEO_F767ZI_HPP diff --git a/src/modm/board/nucleo_f767zi/board.xml b/src/modm/board/nucleo_f767zi/board.xml new file mode 100755 index 0000000000..9d548c88f6 --- /dev/null +++ b/src/modm/board/nucleo_f767zi/board.xml @@ -0,0 +1,16 @@ + + + + ../../../../repo.lb + + + + + + + + + + modm:board:nucleo-f767zi + + diff --git a/src/modm/board/nucleo_f767zi/module.lb b/src/modm/board/nucleo_f767zi/module.lb new file mode 100755 index 0000000000..856ae2dff2 --- /dev/null +++ b/src/modm/board/nucleo_f767zi/module.lb @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Mike Wolfram +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":board:nucleo-f767zi" + module.description = """\ +# NUCLEO-F767ZI + +[Nucleo kit for STM32F767ZI](https://www.st.com/en/evaluation-tools/nucleo-f767zi.html) +""" + +def prepare(module, options): + if not options[":target"].partname.startswith("stm32f767zi"): + return False + + module.depends( + ":architecture:clock", + ":debug", + ":platform:clock", + ":platform:core", + ":platform:gpio", + ":platform:uart:3") + + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } + env.template("../board.cpp.in", "board.cpp") + env.copy('.') + env.copy("../nucleo144_arduino.hpp", "nucleo144_arduino.hpp") + env.collect(":build:openocd.source", "board/st_nucleo_f7.cfg"); From f2ee8678d4d6bf6873c0ad9ef4f29a4450910c5f Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Sun, 7 Feb 2021 15:33:00 +0100 Subject: [PATCH 2/5] [driver] Add LAN8720a driver --- README.md | 13 ++++--- src/modm/driver/ethernet/lan8720a.hpp | 55 +++++++++++++++++++++++++++ src/modm/driver/ethernet/lan8720a.lb | 28 ++++++++++++++ 3 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 src/modm/driver/ethernet/lan8720a.hpp create mode 100644 src/modm/driver/ethernet/lan8720a.lb diff --git a/README.md b/README.md index ea75c6e0fd..c57acccc64 100644 --- a/README.md +++ b/README.md @@ -221,48 +221,49 @@ can easily configure them for you specific needs. ITG3200 L3GD20 +LAN8720A LAWICEL LIS302DL LIS3DSH LIS3MDL LM75 -LP503X +LP503X LSM303A LSM6DS33 LTC2984 MAX6966 MAX7219 -MCP23X17 +MCP23X17 MCP2515 MMC5603 NOKIA5110 NRF24 TFT-DISPLAY -PAT9125EL +PAT9125EL PCA8574 PCA9535 PCA9548A PCA9685 SIEMENS-S65 -SIEMENS-S75 +SIEMENS-S75 SK6812 SK9822 SSD1306 SX1276 TCS3414 -TCS3472 +TCS3472 TLC594X TMP102 TMP175 VL53L0 VL6180 -WS2812 +WS2812 diff --git a/src/modm/driver/ethernet/lan8720a.hpp b/src/modm/driver/ethernet/lan8720a.hpp new file mode 100644 index 0000000000..c2e2e02788 --- /dev/null +++ b/src/modm/driver/ethernet/lan8720a.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020, Mike Wolfram + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef MODM_LAN8720A_HPP +#define MODM_LAN8720A_HPP + +namespace modm +{ + +struct Lan8720a +{ + static constexpr uint32_t Address = 0x00; + + struct Register { + static constexpr uint16_t BCR = 0x0000; + static constexpr uint16_t BSR = 0x0001; + static constexpr uint16_t AN = 0x0004; + static constexpr uint16_t SR = 0x001f; + static constexpr uint16_t ISFR = 0x001d; + static constexpr uint16_t ISFR_INT4 = 0x000B; + }; + + static constexpr uint32_t ResetDelay = 0x000000FF; + static constexpr uint32_t ConfigDelay = 0x00000FFF; + static constexpr int ReadTimeout = 0xffff; + static constexpr int WriteTimeout = 0xffff; + + // TODO: use modm::Register for these bits + static constexpr uint16_t Reset = 0x8000; + static constexpr uint16_t LoopBack = 0x4000; + static constexpr uint16_t FullDuplex100M = 0x2100; + static constexpr uint16_t HalfDuplex100M = 0x2000; + static constexpr uint16_t FullDuplex10M = 0x0100; + static constexpr uint16_t HalfDuplex10M = 0x0000; + static constexpr uint16_t AutoNegotiation = 0x1000; + static constexpr uint16_t RestartAutoNegotiation = 0x0200; + + static constexpr uint16_t LinkedStatus = 0x0004; + static constexpr uint16_t DuplexStatus = 0x0010; + static constexpr uint16_t SpeedStatus = 0x0004; + static constexpr uint16_t AutoNegotiationComplete = 0x0020; + static constexpr uint16_t JabberDetection = 0x0002; +}; + +} + +#endif // MODM_LAN8720A_HPP + diff --git a/src/modm/driver/ethernet/lan8720a.lb b/src/modm/driver/ethernet/lan8720a.lb new file mode 100644 index 0000000000..5b5242f9c9 --- /dev/null +++ b/src/modm/driver/ethernet/lan8720a.lb @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Mike Wolfram +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + + +def init(module): + module.name = ":driver:lan8720a" + module.description = """\ +# LAN8720A Ethernet Transceiver + +Microchip's LAN8720A/LAN8720Ai are high-performance, small-footprint, +low-power 10BASE-T/100BASE-TX transceiver connected via an RMII interface. +""" + +def prepare(module, options): + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/ethernet" + env.copy("lan8720a.hpp") From e45977b1e618fd1e6cb08ac9327ae902bef93073 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Sun, 7 Feb 2021 15:33:20 +0100 Subject: [PATCH 3/5] [ethernet] Add STM32 peripheral driver --- src/modm/platform/eth/stm32/eth.hpp | 696 +++++++++++++++++++++++ src/modm/platform/eth/stm32/eth_impl.hpp | 138 +++++ src/modm/platform/eth/stm32/module.lb | 43 ++ 3 files changed, 877 insertions(+) create mode 100644 src/modm/platform/eth/stm32/eth.hpp create mode 100644 src/modm/platform/eth/stm32/eth_impl.hpp create mode 100644 src/modm/platform/eth/stm32/module.lb diff --git a/src/modm/platform/eth/stm32/eth.hpp b/src/modm/platform/eth/stm32/eth.hpp new file mode 100644 index 0000000000..ea5a448560 --- /dev/null +++ b/src/modm/platform/eth/stm32/eth.hpp @@ -0,0 +1,696 @@ +/* + * Copyright (c) 2020, Mike Wolfram + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_ETH_HPP +#define MODM_ETH_HPP + +#include +#include +#include +#include "../device.hpp" + +#include +#include + +namespace modm +{ +namespace platform +{ + +struct eth +{ + enum class + MediaInterface : uint32_t { + MII = 0x00, + RMII = SYSCFG_PMC_MII_RMII_SEL + }; + + enum class + DuplexMode { + Half = 0, + Full = 1, + }; + + enum class + Speed { + Speed10M = 0, + Speed100M = 1, + }; + + enum class + LinkStatus { + Down = 0, + Up, + }; + + enum class + MacAddressIndex : uint32_t { + Index0 = 0x00000000, + Index1 = 0x00000008, + Index2 = 0x00000010, + Index3 = 0x00000018, + }; + + enum class + Interrupt : uint32_t { + NormalIrqSummary = modm::Bit16, + AbnormalIrqSummary = modm::Bit15, + EarlyReceive = modm::Bit14, + FatalBusError = modm::Bit13, + EarlyTransmit = modm::Bit10, + ReceiveWatchdog = modm::Bit9, + ReceiveStopped = modm::Bit8, + ReceiveBufferUnavailable = modm::Bit7, + Receive = modm::Bit6, + TransmitUnderflow = modm::Bit5, + ReceiveOverflow = modm::Bit4, + TransmitJabberTimeout = modm::Bit3, + TransmitBufferUnavailable = modm::Bit2, + TransmitStopped = modm::Bit1, + Transmit = modm::Bit0, + }; + MODM_FLAGS32(Interrupt); + + enum class + InterruptFlags : uint32_t { + TimeStampTrigger = modm::Bit29, + Pmt = modm::Bit28, + Mmc = modm::Bit27, + ErrorBitStatus2 = modm::Bit25, + ErrorBitStatus1 = modm::Bit24, + ErrorBitStatus0 = modm::Bit23, + TransmitProcessState2 = modm::Bit22, + TransmitProcessState1 = modm::Bit21, + TransmitProcessState0 = modm::Bit20, + ReceiveProcessState2 = modm::Bit19, + ReceiveProcessState1 = modm::Bit18, + ReceiveProcessState0 = modm::Bit17, + NormalIrqSummary = modm::Bit16, + AbnormalIrqSummary = modm::Bit15, + EarlyReceive = modm::Bit14, + FatalBusError = modm::Bit13, + EarlyTransmit = modm::Bit10, + ReceiveWatchdog = modm::Bit9, + ReceiveStopped = modm::Bit8, + ReceiveBufferUnavailable = modm::Bit7, + Receive = modm::Bit6, + TransmitUnderflow = modm::Bit5, + ReceiveOverflow = modm::Bit4, + TransmitJabberTimeout = modm::Bit3, + TransmitBufferUnavailable = modm::Bit2, + TransmitStopped = modm::Bit1, + Transmit = modm::Bit0, + }; + MODM_FLAGS32(InterruptFlags); + + enum class + Event : uint32_t { + None = 0x00, + Receive = 0x01, + Transmit = 0x02, + Error = 0x04, + }; + MODM_FLAGS32(Event); + +protected: + static constexpr uint32_t i(MediaInterface interface) { return uint32_t(interface); } +}; + +template +class Eth : public eth +{ + enum class + MacConfiguration : uint32_t { + WatchDogDisable = modm::Bit23, + JabberDisable = modm::Bit22, + InterframeGap2 = modm::Bit19, + InterframeGap1 = modm::Bit18, + InterframeGap0 = modm::Bit17, + CarrierSense = modm::Bit16, + EthernetSpeed = modm::Bit14, + ReceiveOwn = modm::Bit13, + LoopBack = modm::Bit12, + DuplexMode = modm::Bit11, + Ipv4ChecksumOffLoad = modm::Bit10, + RetryDisable = modm::Bit9, + AutomaticPad = modm::Bit7, + BackOffLimit1 = modm::Bit6, + BackOffLimit0 = modm::Bit5, + DeferalCheck = modm::Bit4, + TransmitterEnable = modm::Bit3, + ReceiveEnable = modm::Bit2, + }; + MODM_FLAGS32(MacConfiguration); + + static constexpr uint32_t MacCrClearMask { 0xFF20810F }; + + enum class + Watchdog : uint32_t { + Enable = 0b0, + Disable = 0b1, + }; + using Watchdog_t = modm::Configuration; + + enum class + Jabber : uint32_t { + Enable = 0b0, + Disable = 0b1, + }; + using Jabber_t = modm::Configuration; + + enum class + InterframeGap : uint32_t { + Gap96BitTimes = 0b000, + Gap88BitTimes = 0b001, + Gap80BitTimes = 0b010, + Gap72BitTimes = 0b011, + Gap64BitTimes = 0b100, + Gap56BitTimes = 0b101, + Gap48BitTimes = 0b110, + Gap40BitTimes = 0b111, + }; + using InterframeGap_t = modm::Configuration; + + enum class + CarrierSense : uint32_t { + Enable = 0b0, + Disable = 0b1, + }; + using CarrierSense_t = modm::Configuration; + + using Speed_t = modm::Configuration; + using DuplexMode_t = modm::Configuration; + + enum class + MacFrameFilter : uint32_t { + ReceiveAll = modm::Bit31, + HashOrPerfect = modm::Bit10, + SourceAddress = modm::Bit9, + SourceAddressInverse = modm::Bit8, + PassControl1 = modm::Bit7, + PassControl0 = modm::Bit6, + BroadcastDisable = modm::Bit5, + PassAllMulticast = modm::Bit4, + DestinationAddressInverse = modm::Bit3, + HashMulticast = modm::Bit2, + HasUnicast = modm::Bit1, + PromiscuousMode = modm::Bit0, + }; + MODM_FLAGS32(MacFrameFilter); + + enum class + PassControlFrame : uint32_t { + BlockAll = 0b00, + AllExceptPause = 0b01, + AllAddressFilterFail = 0b10, + AllAddressFilterPass = 0b11, + }; + using PassControlFrame_t = modm::Configuration; + + enum class + MacFlowControl : uint32_t { + PauseTime15 = modm::Bit31, + PauseTime14 = modm::Bit30, + PauseTime13 = modm::Bit29, + PauseTime12 = modm::Bit28, + PauseTime11 = modm::Bit27, + PauseTime10 = modm::Bit26, + PauseTime9 = modm::Bit25, + PauseTime8 = modm::Bit24, + PauseTime7 = modm::Bit23, + PauseTime6 = modm::Bit22, + PauseTime5 = modm::Bit21, + PauseTime4 = modm::Bit20, + PauseTime3 = modm::Bit19, + PauseTime2 = modm::Bit18, + PauseTime1 = modm::Bit17, + PauseTime0 = modm::Bit16, + ZeroQuantaPauseDisable = modm::Bit7, + PauseLowThreshold1 = modm::Bit5, + PauseLowThreshold0 = modm::Bit4, + UnicastPauseDetect = modm::Bit3, + ReceiveFlowControlEnable = modm::Bit2, + TransmitFlowControlEnable = modm::Bit1, + FlowControlBusy = modm::Bit0, + }; + MODM_FLAGS32(MacFlowControl); + + static constexpr uint32_t MacFcrClearMask { 0x0000FF41 }; + + enum class + PauseLowThreshold : uint32_t { + Minus4SlotTimes = 0b00, + Minus28SlotTimes = 0b01, + Minus144SlotTimes = 0b10, + Minus256SlotTimes = 0b11, + }; + using PauseLowThreshold_t = modm::Configuration; + + enum class + DmaOperationMode : uint32_t { + DropCrcErrorFrameDisable = modm::Bit26, + ReceiveStoreAndForward = modm::Bit25, + DisableFlushReceivedFrames = modm::Bit24, + TransmitStoreAndForward = modm::Bit21, + FlushTransmitFifo = modm::Bit20, + TransmitThreshold2 = modm::Bit16, + TransmitThreshold1 = modm::Bit15, + TransmitThreshold0 = modm::Bit14, + StartTransmission = modm::Bit13, + ForwardErrorFrames = modm::Bit7, + ForwardUndersizedGoodFrames = modm::Bit6, + ReceiveThreshold1 = modm::Bit4, + ReceiveThreshold0 = modm::Bit3, + OperateOnSecondFrame = modm::Bit2, + StartReceive = modm::Bit1, + }; + MODM_FLAGS32(DmaOperationMode); + + static constexpr uint32_t DmaOmrClearMask { 0xF8DE3F23 }; + + enum class + TransmitThreshold : uint32_t { + Bytes64 = 0b000, + Bytes128 = 0b001, + Bytes192 = 0b010, + Bytes256 = 0b011, + Bytes40 = 0b100, + Bytes32 = 0b101, + Bytes24 = 0b110, + Bytes16 = 0b111, + }; + using TransmitThreshold_t = modm::Configuration; + + enum class + ReceiveThreshold : uint32_t { + Bytes64 = 0b00, + Bytes32 = 0b01, + Bytes96 = 0b10, + Bytes128 = 0b11, + }; + using ReceiveThreshold_t = modm::Configuration; + + enum class + DmaBusMode : uint32_t { + MixedBurst = modm::Bit26, + AddressAlignedBeats = modm::Bit25, + PblModeX4 = modm::Bit24, + UseSeparatePbl = modm::Bit23, + RxDmaPbl5 = modm::Bit22, + RxDmaPbl4 = modm::Bit21, + RxDmaPbl3 = modm::Bit20, + RxDmaPbl2 = modm::Bit19, + RxDmaPbl1 = modm::Bit18, + RxDmaPbl0 = modm::Bit17, + FixedBurst = modm::Bit16, + RxTxPriorityRatio1 = modm::Bit15, + RxTxPriorityRatio0 = modm::Bit14, + Pbl5 = modm::Bit13, + Pbl4 = modm::Bit12, + Pbl3 = modm::Bit11, + Pbl2 = modm::Bit10, + Pbl1 = modm::Bit9, + Pbl0 = modm::Bit8, + EnhancedDescFormat = modm::Bit7, + DescriptorSkipLength4 = modm::Bit6, + DescriptorSkipLength3 = modm::Bit5, + DescriptorSkipLength2 = modm::Bit4, + DescriptorSkipLength1 = modm::Bit3, + DescriptorSkipLength0 = modm::Bit2, + DmaArbitration = modm::Bit1, + SoftwareReset = modm::Bit0, + }; + MODM_FLAGS32(DmaBusMode); + + enum class + BurstLength : uint32_t { + Length1Beat = 0b000001, + Length2Beats = 0b000010, + Length4Beats = 0b000100, + Length8Beats = 0b001000, + Length16Beats = 0b010000, + Length32Beats = 0b100000, + }; + using BurstLength_t = modm::Configuration; + using RxDmaBurstLength_t = modm::Configuration; + + template class... Signals> + struct GpioConfigurator; + template class Signal, template class... Signals> + struct GpioConfigurator + { + static inline void + configure() { + Signal::Gpio::configure(modm::platform::Gpio::OutputType::PushPull, modm::platform::Gpio::OutputSpeed::VeryHigh); + GpioConfigurator::configure(); + } + }; + template + struct GpioConfigurator + { + static inline void + configure() {} + }; + +public: + template class... Signals> + static void + connect() + { + using Configurator = GpioConfigurator; + using Connector = GpioConnector; + + Configurator::configure(); + Connector::connect(); + } + + template + static inline bool + initialize(uint8_t priority=5) + { + using namespace modm::literals; + + Rcc::enable(); + + NVIC_SetPriority(ETH_IRQn, priority); + NVIC_EnableIRQ(ETH_IRQn); + + /* Select MII or RMII Mode*/ + SYSCFG->PMC &= ~(SYSCFG_PMC_MII_RMII_SEL); + SYSCFG->PMC |= uint32_t(Interface); + + /* Ethernet Software reset */ + /* Set the SWR bit: resets all MAC subsystem internal registers and logic */ + /* After reset all the registers holds their respective reset values */ + ETH->DMABMR |= DmaBusMode_t(DmaBusMode::SoftwareReset | DmaBusMode::EnhancedDescFormat).value; + + /* Wait for software reset */ + /* Note: The SWR is not performed if the ETH_RX_CLK or the ETH_TX_CLK are + * not available, please check your external PHY or the IO configuration */ + int timeout = 1'000; // max 1ms + while ((DmaBusMode(ETH->DMABMR) & DmaBusMode_t(DmaBusMode::SoftwareReset)) and (timeout-- > 0)) { + // Wait until the PHY has reset. + modm::delay_us(1); + + // NOTE: If the program hangs here check MII/RMII value. + } + if (not modm_assert_continue_fail_debug(timeout > 0, "eth.init", + "ETH::initialize() timed out on software reset!", 1)) + return false; + + /* Configure SMI clock range */ + uint32_t csr_clock_divider = ETH->MACMIIAR & ETH_MACMIIAR_CR_Msk; + if (SystemCoreClock >= 20_MHz and SystemCoreClock < 35_MHz) + csr_clock_divider |= ETH_MACMIIAR_CR_Div16; + else if (SystemCoreClock >= 35_MHz and SystemCoreClock < 60_MHz) + csr_clock_divider |= ETH_MACMIIAR_CR_Div26; + else if (SystemCoreClock >= 60_MHz and SystemCoreClock < 100_MHz) + csr_clock_divider |= ETH_MACMIIAR_CR_Div42; + else if (SystemCoreClock >= 100_MHz and SystemCoreClock < 150_MHz) + csr_clock_divider |= ETH_MACMIIAR_CR_Div62; + else if (SystemCoreClock >= 150_MHz) + csr_clock_divider |= ETH_MACMIIAR_CR_Div102; + + ETH->MACMIIAR = csr_clock_divider; + + // Initialize PHY + uint32_t phy_register { 0 }; + + /* Reset */ + (void) readPhyRegister(PHY::Register::BCR, phy_register); + phy_register |= PHY::Reset; + if (not writePhyRegister(PHY::Register::BCR, phy_register)) { + configureMac(true); + configureDma(); + return false; + } + + // wait for reset done + modm::delay_us(PHY::ResetDelay); + timeout = 1'000; + + do { + (void) readPhyRegister(PHY::Register::BCR, phy_register); + if ((phy_register & PHY::Reset) == 0) + break; + modm::delay_ms(1); + } while (timeout-- > 0); + if (timeout <= 0) { + linkStatus = LinkStatus::Down; + return false; + } + + // FIXME: Delegate these values to the PHY + // configure auto negotiation + phy_register = 0x100 // 100 FD + | 0x80 // 100 + | 0x40 // 10 FD + | 0x20 // 10 + | 0x01 // 802.3 + ; + (void) writePhyRegister(PHY::Register::AN, phy_register); + + configureMac(true); + configureDma(); + + return true; + } + + static void + configureMac(bool autoNegotiationFailed = false) + { + uint32_t tmp; + + if (autoNegotiationFailed) { + duplexMode = DuplexMode::Full; + speed = Speed::Speed100M; + } + + MacConfiguration_t maccr { + MacConfiguration::Ipv4ChecksumOffLoad | + MacConfiguration::RetryDisable + }; + maccr |= Speed_t(speed); + maccr |= DuplexMode_t(duplexMode); + + tmp = ETH->MACCR & MacCrClearMask; + tmp |= maccr.value; + writeMACCR(tmp); + + MacFrameFilter_t macffr { + PassControlFrame_t(PassControlFrame::BlockAll), + }; + writeMACFFR(macffr.value); + + // Hash table + ETH->MACHTHR = 0x00000000; + ETH->MACHTLR = 0x00000000; + + MacFlowControl_t macfcr { + MacFlowControl::ZeroQuantaPauseDisable + }; + tmp = ETH->MACFCR & MacFcrClearMask; + tmp |= macfcr.value; + writeMACFCR(tmp); + + // no VLAN support for now + writeMACVLANTR(0x00000000); + } + + static void + configureDma() + { + uint32_t tmp; + + DmaOperationMode_t dmaomr { + DmaOperationMode::ReceiveStoreAndForward | + DmaOperationMode::TransmitStoreAndForward | + DmaOperationMode::OperateOnSecondFrame + }; + tmp = ETH->DMAOMR & DmaOmrClearMask; + tmp |= dmaomr.value; + writeDMAOMR(tmp); + + DmaBusMode_t dmabmr { + DmaBusMode::AddressAlignedBeats | + DmaBusMode::FixedBurst | + DmaBusMode::EnhancedDescFormat | + DmaBusMode::UseSeparatePbl + }; + dmabmr |= BurstLength_t(BurstLength::Length32Beats); + dmabmr |= RxDmaBurstLength_t(BurstLength::Length32Beats); + writeDMABMR(dmabmr.value); + + enableInterrupt(Interrupt::NormalIrqSummary | Interrupt::Receive); + + configureMacAddresses(); + } + + static void + setMacAddress(MacAddressIndex index, uint8_t const *macAddress) + { + std::memcpy(macAddresses[uint32_t(index) / 8].data(), macAddress, 6); + } + + static void + configureMacAddresses(); + + static void + start(); + static void + stop(); + + static void + setDmaTxDescriptorTable(uint32_t address) { + ETH->DMATDLAR = address; + } + static void + setDmaRxDescriptorTable(uint32_t address) { + ETH->DMARDLAR = address; + } + + static InterruptFlags + getInterruptFlags() { + return InterruptFlags(ETH->DMASR); + } + static void + acknowledgeInterrupt(InterruptFlags_t irq) { + // set only the bits you want to clear! + // using an |= here would clear other fields as well + ETH->DMASR = irq.value; + } + static void + enableInterrupt(Interrupt_t irq) { + ETH->DMAIER |= irq.value; + } + + // FIXME: Make this more generic by delegating specifics to the PHY + static bool + phyStartAutoNegotiation() + { + uint32_t phy_register { 0 }; + + // enable auto-negotiation + (void) readPhyRegister(PHY::Register::BCR, phy_register); + phy_register |= PHY::RestartAutoNegotiation; + if (not writePhyRegister(PHY::Register::BCR, phy_register)) + return false; + + // wait for auto-negotiation complete (5s) + int timeout = 5'000; + do { + (void) readPhyRegister(PHY::Register::BSR, phy_register); + if ((phy_register & PHY::AutoNegotiationComplete) == PHY::AutoNegotiationComplete) + break; + modm::delay_ms(1); + } while (timeout-- > 0); + if (timeout <= 0) + return false; + + // read auto-negotiation result + if (not readPhyRegister(PHY::Register::SR, phy_register)) + return false; + + if ((phy_register & PHY::DuplexStatus) == PHY::DuplexStatus) + duplexMode = DuplexMode::Full; + else + duplexMode = DuplexMode::Half; + + if ((phy_register & PHY::SpeedStatus) == PHY::SpeedStatus) + speed = Speed::Speed10M; + else + speed = Speed::Speed100M; + + return true; + } + + static LinkStatus + phyReadLinkStatus() + { + uint32_t phy_register { 0 }; + + (void) readPhyRegister(PHY::Register::BSR, phy_register); + if ((phy_register & PHY::LinkedStatus) == PHY::LinkedStatus) + linkStatus = LinkStatus::Up; + else + linkStatus = LinkStatus::Down; + + return linkStatus; + } + static LinkStatus + getLinkStatus() { + return linkStatus; + } + +private: + static void + writeMACCR(uint32_t value) { + ETH->MACCR = value; + (void) ETH->MACCR; + modm::delay_ms(1); + ETH->MACCR = value; + } + static void + writeMACFCR(uint32_t value) { + ETH->MACFCR = value; + (void) ETH->MACFCR; + modm::delay_ms(1); + ETH->MACFCR = value; + } + static void + writeMACFFR(uint32_t value) { + ETH->MACFFR = value; + (void) ETH->MACFFR; + modm::delay_ms(1); + ETH->MACFFR = value; + } + static void + writeMACVLANTR(uint32_t value) { + ETH->MACVLANTR = value; + (void) ETH->MACVLANTR; + modm::delay_ms(1); + ETH->MACVLANTR = value; + } + static void + writeDMABMR(uint32_t value) { + ETH->DMABMR= value; + (void) ETH->DMABMR; + modm::delay_ms(1); + ETH->DMABMR = value; + } + static void + writeDMAOMR(uint32_t value) { + ETH->DMAOMR= value; + (void) ETH->DMAOMR; + modm::delay_ms(1); + ETH->DMAOMR = value; + } + + static bool + writePhyRegister(uint16_t reg, uint32_t value); + static bool + readPhyRegister(uint16_t reg, uint32_t &value); + + static inline DuplexMode duplexMode = eth::DuplexMode::Full; + static inline Speed speed = eth::Speed::Speed100M; + static inline LinkStatus linkStatus = eth::LinkStatus::Down; + + using MacAddress = std::array; + using MacAddresses = std::array; + static inline MacAddresses macAddresses; +}; + +} +} + +#include "eth_impl.hpp" + +#endif // MODM_ETH_HPP diff --git a/src/modm/platform/eth/stm32/eth_impl.hpp b/src/modm/platform/eth/stm32/eth_impl.hpp new file mode 100644 index 0000000000..9ce0b060d8 --- /dev/null +++ b/src/modm/platform/eth/stm32/eth_impl.hpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2020, Mike Wolfram + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_ETH_HPP +# error "Don't include this file directly, use 'eth.hpp' instead!" +#endif + +namespace modm::platform { + + +template +void +Eth::start() +{ + // transmission enable + uint32_t tmp = ETH->MACCR | ETH_MACCR_TE; + writeMACCR(tmp); + + // reception enable + tmp = ETH->MACCR | ETH_MACCR_RE; + writeMACCR(tmp); + + // flush transmission fifo + tmp = ETH->DMAOMR | ETH_DMAOMR_FTF; + writeDMAOMR(tmp); + + // DMA transmission enable + tmp = ETH->DMAOMR | ETH_DMAOMR_ST; + writeDMAOMR(tmp); + + // DMA reception enable + tmp = ETH->DMAOMR | ETH_DMAOMR_SR; + writeDMAOMR(tmp); +} + +template +void +Eth::stop() +{ + // DMA transmission disable + uint32_t tmp = ETH->DMAOMR & ~ETH_DMAOMR_ST; + writeDMAOMR(tmp); + + // DMA reception disable + tmp = ETH->DMAOMR & ~ETH_DMAOMR_SR; + writeDMAOMR(tmp); + + // reception disable + tmp = ETH->MACCR & ~ETH_MACCR_RE; + writeMACCR(tmp); + + // flush transmission fifo + tmp = ETH->DMAOMR | ETH_DMAOMR_FTF; + writeDMAOMR(tmp); + + // transmission disable + tmp = ETH->MACCR & ~ETH_MACCR_TE; + writeMACCR(tmp); +} + +template +void +Eth::configureMacAddresses() +{ + static constexpr uint32_t ETH_MAC_ADDR_HBASE { ETH_MAC_BASE + 0x40 }; /* Ethernet MAC address high offset */ + static constexpr uint32_t ETH_MAC_ADDR_LBASE { ETH_MAC_BASE + 0x44 }; /* Ethernet MAC address low offset */ + + static constexpr MacAddress zeroMac { 0 }; + + for (std::size_t i = 0; i < macAddresses.size(); ++i) { + auto const &macAddress = macAddresses[i]; + if (std::memcmp(zeroMac.data(), macAddress.data(), macAddress.size()) == 0) + continue; + + uint32_t tmp_register = (macAddress[5] << 8) | macAddress[4]; + *reinterpret_cast<__IO uint32_t *>(ETH_MAC_ADDR_HBASE + i * 0x08) = tmp_register; + tmp_register = (macAddress[3] << 24) | (macAddress[2] << 16) | (macAddress[1] << 8) | macAddress[0]; + *reinterpret_cast<__IO uint32_t *>(ETH_MAC_ADDR_LBASE + i * 0x08) = tmp_register; + } +} + +template +bool +Eth::readPhyRegister(uint16_t reg, uint32_t &value) +{ + // get only CR bits from MACMIIAR + uint32_t tmp = ETH->MACMIIAR & ETH_MACMIIAR_CR_Msk; + tmp |= (PHY::Address << 11) & ETH_MACMIIAR_PA; + tmp |= (reg << 6) & ETH_MACMIIAR_MR; + tmp &= ~ETH_MACMIIAR_MW; + tmp |= ETH_MACMIIAR_MB; + + ETH->MACMIIAR = tmp; + + int timeout = PHY::ReadTimeout; + while (timeout-- > 0) { + if ((ETH->MACMIIAR & ETH_MACMIIAR_MB) == 0) { + // busy flag cleared, read data + value = ETH->MACMIIDR; + return true; + } + } + + return false; +} + +template +bool +Eth::writePhyRegister(uint16_t reg, uint32_t value) +{ + // get only CR bits from MACMIIAR + uint32_t tmp = ETH->MACMIIAR & ETH_MACMIIAR_CR_Msk; + tmp |= (PHY::Address << 11) & ETH_MACMIIAR_PA; + tmp |= (reg << 6) & ETH_MACMIIAR_MR; + tmp |= ETH_MACMIIAR_MW; + tmp |= ETH_MACMIIAR_MB; + + ETH->MACMIIDR = value; + ETH->MACMIIAR = tmp; + + int timeout = PHY::WriteTimeout; + while (timeout-- > 0) { + if ((ETH->MACMIIAR & ETH_MACMIIAR_MB) == 0) + return true; + } + + return false; +} + +} diff --git a/src/modm/platform/eth/stm32/module.lb b/src/modm/platform/eth/stm32/module.lb new file mode 100644 index 0000000000..93a9885ea4 --- /dev/null +++ b/src/modm/platform/eth/stm32/module.lb @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Mike Wolfram +# Copyright (c) 2021, Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":platform:eth" + module.description = "Ethernet" + +def prepare(module, options): + device = options[":target"] + + if not device.has_driver("eth:stm32*"): + return False + # FIXME the driver is for F7 only right now + if device.identifier["family"] not in ["f7"]: + return False + + module.depends(":architecture:delay", + ":architecture:interrupt", + ":architecture:register", + ":architecture:assert", + ":platform:gpio", + ":platform:rcc", + ":math:utils") + + return True + +def build(env): + env.substitutions = {"target": env[":target"].identifier} + env.outbasepath = "modm/src/modm/platform/eth" + + env.copy("eth.hpp") + env.copy("eth_impl.hpp") + From ddbd29f59ad554105fbd78513c65d6af6753f863 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Sun, 7 Feb 2021 15:35:43 +0100 Subject: [PATCH 4/5] [freertos] Add STM32 Ethernet port via LAN8720a --- ext/aws/FreeRTOSIPConfig.h.in | 2 +- ext/aws/modm_lan8720a.cpp | 650 ++++++++++++++++++++++++++++++++++ ext/aws/module.lb | 21 +- 3 files changed, 671 insertions(+), 2 deletions(-) create mode 100644 ext/aws/modm_lan8720a.cpp diff --git a/ext/aws/FreeRTOSIPConfig.h.in b/ext/aws/FreeRTOSIPConfig.h.in index 1615f195ec..033f945017 100644 --- a/ext/aws/FreeRTOSIPConfig.h.in +++ b/ext/aws/FreeRTOSIPConfig.h.in @@ -109,7 +109,7 @@ things such as a DHCP transaction number or initial sequence number. Random number generation is performed via this macro to allow applications to use their own random number generation method. For example, it might be possible to generate a random number by sampling noise on an analogue input. */ -extern UBaseType_t uxRand(); +extern UBaseType_t uxRand(void); #define ipconfigRAND32() uxRand() /* If ipconfigUSE_NETWORK_EVENT_HOOK is set to 1 then FreeRTOS+TCP will call the diff --git a/ext/aws/modm_lan8720a.cpp b/ext/aws/modm_lan8720a.cpp new file mode 100644 index 0000000000..9875e70f1a --- /dev/null +++ b/ext/aws/modm_lan8720a.cpp @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2020, Mike Wolfram + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +#include +#include +#include + +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "FreeRTOS_DNS.h" +#include "FreeRTOS_ARP.h" +#include "NetworkBufferManagement.h" + +#include + +using EMAC = modm::platform::Eth; + +namespace modm +{ + +struct ethernet +{ + static constexpr BaseType_t MAX_PACKET_SIZE { 1536 }; + static constexpr BaseType_t RX_BUFFER_SIZE { 1536 }; + static constexpr BaseType_t TX_BUFFER_SIZE { 1536 }; + static constexpr BaseType_t RX_BUFFER_NUMBER { 5 }; + static constexpr BaseType_t TX_BUFFER_NUMBER { 5 }; + + static constexpr configSTACK_DEPTH_TYPE emacTaskStackDepth { configMINIMAL_STACK_SIZE * 2 }; + static constexpr UBaseType_t emacTaskPriority { configMAX_PRIORITIES - 1 }; + + enum class + InitStatus : uint8_t { + Init, + Pass, + Failed + }; + static InitStatus initStatus; + static SemaphoreHandle_t txDescriptorSemaphore; + static TaskHandle_t emacTaskHandle; + + static modm::platform::eth::Event_t isrEvent; + + static TimeOut_t phyLinkStatusTimer; + static constexpr TickType_t PhyLinkStatusHighMs { pdMS_TO_TICKS(2'000) }; + static constexpr TickType_t PhyLinkStatusLowMs { pdMS_TO_TICKS(1'000) }; + static TickType_t phyLinkStatusRemaining; + static modm::platform::eth::LinkStatus lastPhyLinkStatus; + + enum class + TDes0 : uint32_t { + DmaOwned = modm::Bit31, + InterruptOnCompletion = modm::Bit30, + LastSegment = modm::Bit29, + FirstSegment = modm::Bit28, + DisableCrc = modm::Bit27, + DisablePadding = modm::Bit26, + TransmitTimestampEnable = modm::Bit25, + CrcCtrl1 = modm::Bit23, + CrcCtrl0 = modm::Bit22, + TransmitEndOfRing = modm::Bit21, + SecondAddressChained = modm::Bit20, + TransmitTimestampStatus = modm::Bit17, + IpHeaderError = modm::Bit16, + ErrorSummary = modm::Bit15, + JabberTimeoout = modm::Bit14, + FrameFlushed = modm::Bit13, + IpPayloadError = modm::Bit12, + LossOfCarrier = modm::Bit11, + NoCarrier = modm::Bit10, + LateCollision = modm::Bit9, + ExcessiveCollision = modm::Bit8, + VLanFrame = modm::Bit7, + CollisionCount3 = modm::Bit6, + CollisionCount2 = modm::Bit5, + CollisionCount1 = modm::Bit4, + CollisionCount0 = modm::Bit3, + ExcessiveDeferral = modm::Bit2, + UnderflowError = modm::Bit1, + DeferredBit = modm::Bit0, + }; + MODM_FLAGS32(TDes0); + + enum class + CrcControl : uint32_t { + InsertionDisabled = 0b00, + IpHeaderOnly = 0b01, + IpHeaderAndPayload = 0b10, + HardwareCalculated = 0b11 + }; + using CrcControl_t = modm::Configuration; + + enum class + RDes0 : uint32_t { + DmaOwned = modm::Bit31, + DAFilterFail = modm::Bit30, + ErrorSummary = modm::Bit15, + DescriptorError = modm::Bit14, + SAFilterFail = modm::Bit13, + LengthError = modm::Bit12, + OverflowError = modm::Bit11, + VLanFrame = modm::Bit10, + FirstSegment = modm::Bit9, + LastSegment = modm::Bit8, + Ipv4HeaderCrcError = modm::Bit7, + LateCollision = modm::Bit6, + EthernetFrameType = modm::Bit5, + ReceiveWatchdogTimeout = modm::Bit4, + ReceiveError = modm::Bit3, + DribbleBitError = modm::Bit2, + CrcError = modm::Bit1, + PayloadCrcError = modm::Bit0, + }; + MODM_FLAGS32(RDes0); + + enum class + RDes1 : uint32_t { + DisableIrqOnCompletion = modm::Bit31, + ReceiveEndOfRing = modm::Bit15, + SecondAddressChained = modm::Bit14, + }; + MODM_FLAGS32(RDes1); + + static constexpr uint32_t ReceiveDescriptorFrameLengthMask { 0x3fff0000 }; + static constexpr uint32_t ReceiveDescriptorFrameLengthShift { 16 }; + static constexpr uint32_t Buffer1SizeMask { 0x00001fff }; + static constexpr uint32_t Buffer2SizeMask { 0x1fff0000 }; + + struct DmaDescriptor + { + __IO uint32_t Status; /*!< Status */ + uint32_t ControlBufferSize; /*!< Control and Buffer1, Buffer2 lengths */ + uint32_t Buffer1Addr; /*!< Buffer1 address pointer */ + uint32_t Buffer2NextDescAddr; /*!< Buffer2 or next descriptor address pointer */ + + /*!< Enhanced Ethernet DMA PTP Descriptors */ + uint32_t ExtendedStatus; /*!< Extended status for PTP receive descriptor */ + uint32_t Reserved1; /*!< Reserved */ + uint32_t TimeStampLow; /*!< Time Stamp Low value for transmit and receive */ + uint32_t TimeStampHigh; /*!< Time Stamp High value for transmit and receive */ + }; + using DmaDescriptor_t = DmaDescriptor; + + /* Ethernet Rx DMA Descriptor */ + modm_aligned(32) modm_fastdata + static DmaDescriptor_t DmaRxDescriptorTable[RX_BUFFER_NUMBER]; + + /* Ethernet Tx DMA Descriptor */ + modm_aligned(32) modm_fastdata + static DmaDescriptor_t DmaTxDescriptorTable[TX_BUFFER_NUMBER]; + + static DmaDescriptor_t *RxDescriptor; /*!< Rx descriptor to Get */ + static DmaDescriptor_t *TxDescriptor; /*!< Tx descriptor to Set */ + static DmaDescriptor_t *DmaTxDescriptorToClear; + + static void + DMATxDescListInit() + { + static constexpr TDes0_t dmaDescriptorStatus { + TDes0_t(TDes0::SecondAddressChained) | + CrcControl_t(CrcControl::HardwareCalculated) + }; + + DmaDescriptor_t *dmaDescriptor { TxDescriptor }; + + for (BaseType_t index = 0; index < TX_BUFFER_NUMBER ; ++index, ++dmaDescriptor) { + dmaDescriptor->Status = dmaDescriptorStatus.value; + + if (index < TX_BUFFER_NUMBER - 1) + dmaDescriptor->Buffer2NextDescAddr = uint32_t(dmaDescriptor + 1); + else + dmaDescriptor->Buffer2NextDescAddr = uint32_t(TxDescriptor); + } + + EMAC::setDmaTxDescriptorTable(uint32_t(TxDescriptor)); + } + static void + DMARxDescListInit() + { + DmaDescriptor_t *dmaDescriptor { RxDescriptor }; + + for (BaseType_t index = 0; index < RX_BUFFER_NUMBER; ++index, ++dmaDescriptor) { + dmaDescriptor->ControlBufferSize = uint32_t(RDes1::SecondAddressChained) | + uint32_t(RX_BUFFER_SIZE); + + NetworkBufferDescriptor_t *buffer = pxGetNetworkBufferWithDescriptor(RX_BUFFER_SIZE, 100U); + configASSERT(buffer != nullptr); + + if (buffer) { + dmaDescriptor->Buffer1Addr = uint32_t(buffer->pucEthernetBuffer); + dmaDescriptor->Status = uint32_t(RDes0::DmaOwned); + } + + if (index < RX_BUFFER_NUMBER - 1) + dmaDescriptor->Buffer2NextDescAddr = uint32_t(dmaDescriptor + 1); + else + dmaDescriptor->Buffer2NextDescAddr = uint32_t(RxDescriptor); + } + + EMAC::setDmaRxDescriptorTable(uint32_t(RxDescriptor)); + } + + static void + clearTxBuffers() + { + __IO DmaDescriptor_t *txLastDesc { TxDescriptor }; + std::size_t count { TX_BUFFER_NUMBER - uxSemaphoreGetCount(txDescriptorSemaphore) }; + NetworkBufferDescriptor_t *networkBuffer { nullptr }; + uint8_t *payLoad { nullptr }; + + while ((count > 0) and + ((DmaTxDescriptorToClear->Status & uint32_t(TDes0::DmaOwned)) == 0)) { + if (DmaTxDescriptorToClear == txLastDesc and count != TX_BUFFER_NUMBER ) + break; + payLoad = reinterpret_cast(DmaTxDescriptorToClear->Buffer1Addr); + if (payLoad) { + networkBuffer = pxPacketBuffer_to_NetworkBuffer(payLoad); + if (networkBuffer) + vReleaseNetworkBufferAndDescriptor(networkBuffer); + DmaTxDescriptorToClear->Buffer1Addr = 0; + } + + DmaTxDescriptorToClear = reinterpret_cast(DmaTxDescriptorToClear->Buffer2NextDescAddr); + --count; + + xSemaphoreGive(txDescriptorSemaphore); + } + } + + static bool + mayAcceptPacket(uint8_t *buffer) + { + const ProtocolPacket_t *protPaket = reinterpret_cast(buffer); + + switch (protPaket->xTCPPacket.xEthernetHeader.usFrameType) { + case ipARP_FRAME_TYPE: + return true; + case ipIPv4_FRAME_TYPE: + break; + default: + return false; + } + +#if ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 1 + static constexpr uint16_t ipFRAGMENT_OFFSET_BIT_MASK { 0xfff }; + const IPHeader_t *ipHeader { &(protPaket->xTCPPacket.xIPHeader) }; + uint32_t destIpAddress = { 0 }; + + if (ipHeader->usFragmentOffset & FreeRTOS_ntohs(ipFRAGMENT_OFFSET_BIT_MASK) != 0) + return false; + if (ipHeader->false < 0x45 or ipHeader->ucVersionHeaderLength > 0x4f) + return pdFALSE; + + destIpAddress = ipHeader->ulDestinationIPAddress; + if (destIpAddress != *ipLOCAL_IP_ADDRESS_POINTER + and (FreeRTOS_ntohl(destIpAddress) & 0xff) != 0xff + and *ipLOCAL_IP_ADDRESS_POINTER) { + return false; + } + + if (ipHeader->ucProtocol == ipPROTOCOL_UDP) { + uint16_t sourcePort = FreeRTOS_ntohs(protPaket->xUDPPacket.xUDPHeader.usSourcePort); + uint16_t destPort = FreeRTOS_ntohs(protPaket->xUDPPacket.xUDPHeader.usDestinationPort); + + if (not xPortHasUDPSocket(destPort) + and sourcePort != ipDNS_PORT) { + return false; + } + } +#endif + return true; + } + + // check if the packet would be accepted + static void + passMessage(NetworkBufferDescriptor_t *descriptor) + { + IPStackEvent_t rxEvent { + .eEventType = eNetworkRxEvent, + .pvData = reinterpret_cast(descriptor) + }; + + if (xSendEventStructToIPTask(&rxEvent, TickType_t(1000)) != pdPASS) { + do { + NetworkBufferDescriptor_t *next = descriptor->pxNextBuffer; + vReleaseNetworkBufferAndDescriptor(descriptor); + descriptor = next; + } while (descriptor); + + iptraceETHERNET_RX_EVENT_LOST(); + } else { + iptraceNETWORK_INTERFACE_RECEIVE(); + } + } + + static bool + emacInterfaceInput() + { + static constexpr TickType_t descriptorWaitTime { pdMS_TO_TICKS(250) }; + static constexpr RDes0_t receiveStatus {RDes0::CrcError | RDes0::Ipv4HeaderCrcError | RDes0::EthernetFrameType}; + + NetworkBufferDescriptor_t *currentDescriptor { nullptr }; + NetworkBufferDescriptor_t *newDescriptor { nullptr }; + NetworkBufferDescriptor_t *firstDescriptor { nullptr }; + NetworkBufferDescriptor_t *lastDescriptor { nullptr }; + BaseType_t receivedLength { 0 }; + __IO DmaDescriptor_t *dmaRxDescriptor { RxDescriptor }; + uint8_t *buffer { nullptr }; + + while ((dmaRxDescriptor->Status & uint32_t(RDes0::DmaOwned)) == 0x00000000) { + bool accepted = true; + receivedLength = ((dmaRxDescriptor->Status & ReceiveDescriptorFrameLengthMask) >> ReceiveDescriptorFrameLengthShift) - 4; + buffer = reinterpret_cast(dmaRxDescriptor->Buffer1Addr); + RxDescriptor = reinterpret_cast(dmaRxDescriptor->Buffer2NextDescAddr); + + if ((dmaRxDescriptor->Status & receiveStatus.value) != uint32_t(RDes0::EthernetFrameType)) + accepted = false; + else + accepted = mayAcceptPacket(buffer); + + if (accepted) { + newDescriptor = pxGetNetworkBufferWithDescriptor(RX_BUFFER_SIZE, descriptorWaitTime); + if (not newDescriptor) + accepted = false; + } + + currentDescriptor = pxPacketBuffer_to_NetworkBuffer(buffer); + + if (accepted) { + currentDescriptor->xDataLength = receivedLength; + currentDescriptor->pxNextBuffer = 0; + if (not firstDescriptor) + firstDescriptor = currentDescriptor; + else if (lastDescriptor) + lastDescriptor->pxNextBuffer = currentDescriptor; + lastDescriptor = currentDescriptor; + } + + if (newDescriptor) + dmaRxDescriptor->Buffer1Addr = uint32_t(newDescriptor->pucEthernetBuffer); + + dmaRxDescriptor->ControlBufferSize = uint32_t(RDes1::SecondAddressChained) | + uint32_t(RX_BUFFER_SIZE); + dmaRxDescriptor->Status = uint32_t(RDes0::DmaOwned); + + __DSB(); + + if (ETH->DMASR & ETH_DMASR_RBUS) { + ETH->DMASR = ETH_DMASR_RBUS; + ETH->DMARPDR = 0; + } + + dmaRxDescriptor = RxDescriptor; + } + + if (firstDescriptor) + passMessage(firstDescriptor); + + return receivedLength > 0; + } + + static void + updateConfig(bool force) + { + using modm::platform::eth; + + if (force or lastPhyLinkStatus == eth::LinkStatus::Up) { + bool autoNegotiationFailed = not EMAC::phyStartAutoNegotiation(); + EMAC::configureMac(autoNegotiationFailed); + EMAC::start(); + } else { + EMAC::stop(); + } + } + + static bool + phyCheckLinkStatus(bool hasReceived) + { + using modm::platform::eth; + + if (hasReceived) { + vTaskSetTimeOutState(&phyLinkStatusTimer); + phyLinkStatusRemaining = pdMS_TO_TICKS(PhyLinkStatusHighMs); + return false; + } + + bool checkNeeded { false }; + if (xTaskCheckForTimeOut(&phyLinkStatusTimer, &phyLinkStatusRemaining)) { + eth::LinkStatus phyLinkStatus = EMAC::phyReadLinkStatus(); + if (lastPhyLinkStatus != phyLinkStatus) { + lastPhyLinkStatus = phyLinkStatus; + if (phyLinkStatus == eth::LinkStatus::Down) { + IPStackEvent_t xRxEvent = { eNetworkDownEvent, NULL }; + xSendEventStructToIPTask( &xRxEvent, 0 ); + } + checkNeeded = true; + } + + vTaskSetTimeOutState(&phyLinkStatusTimer); + if (phyLinkStatus == eth::LinkStatus::Up) + phyLinkStatusRemaining = pdMS_TO_TICKS(PhyLinkStatusHighMs); + else + phyLinkStatusRemaining = pdMS_TO_TICKS(PhyLinkStatusLowMs); + } + + return checkNeeded; + } + + static void + emacHandlerTask(void *) + { + using modm::platform::eth; + + static constexpr TickType_t maxBlockTime { pdMS_TO_TICKS(100) }; + + UBaseType_t lastMinBufferCount { 0 }; + UBaseType_t currentCount { 0 }; + bool result { false }; + + for (;;) { + result = false; + currentCount = uxGetMinimumFreeNetworkBuffers(); + if (lastMinBufferCount != currentCount) + lastMinBufferCount = currentCount; + + if (txDescriptorSemaphore) { + static UBaseType_t lowestSemCount = TX_BUFFER_NUMBER - 1; + currentCount = uxSemaphoreGetCount(txDescriptorSemaphore); + if (lowestSemCount > currentCount) + lowestSemCount = currentCount; + } + + if (isrEvent == eth::Event(0)) + ulTaskNotifyTake(pdFALSE, maxBlockTime); + else { + if ((isrEvent & eth::Event::Receive) == eth::Event::Receive) { + isrEvent = isrEvent & ~eth::Event::Receive; + result = emacInterfaceInput(); + } + if ((isrEvent & eth::Event::Transmit) == eth::Event::Transmit) { + isrEvent = isrEvent & ~eth::Event::Transmit; + clearTxBuffers(); + } + if ((isrEvent & eth::Event::Error) == eth::Event::Error) { + isrEvent = isrEvent & ~eth::Event::Error; + } + } + + // check link status + if (phyCheckLinkStatus(result)) + updateConfig(false); + } + } +}; + +ethernet::InitStatus ethernet::initStatus = ethernet::InitStatus::Init; +SemaphoreHandle_t ethernet::txDescriptorSemaphore { nullptr }; +TaskHandle_t ethernet::emacTaskHandle { nullptr }; + +modm::platform::eth::Event_t ethernet::isrEvent { modm::platform::eth::Event::None }; + +TimeOut_t ethernet::phyLinkStatusTimer; +modm::platform::eth::LinkStatus ethernet::lastPhyLinkStatus { modm::platform::eth::LinkStatus::Down }; +TickType_t ethernet::phyLinkStatusRemaining { 0 }; + +ethernet::DmaDescriptor_t ethernet::DmaRxDescriptorTable[RX_BUFFER_NUMBER]; +ethernet::DmaDescriptor_t ethernet::DmaTxDescriptorTable[TX_BUFFER_NUMBER]; +ethernet::DmaDescriptor_t *ethernet::RxDescriptor { nullptr }; /*!< Rx descriptor to Get */ +ethernet::DmaDescriptor_t *ethernet::TxDescriptor { nullptr }; /*!< Tx descriptor to Set */ +ethernet::DmaDescriptor_t *ethernet::DmaTxDescriptorToClear { nullptr }; + +} // namespace modm + +extern "C" BaseType_t +xNetworkInterfaceInitialise() +{ + using modm::ethernet; + + if (ethernet::initStatus == ethernet::InitStatus::Init) { + ethernet::txDescriptorSemaphore = xSemaphoreCreateCounting(UBaseType_t(ethernet::TX_BUFFER_NUMBER), + UBaseType_t(ethernet::TX_BUFFER_NUMBER )); + if (ethernet::txDescriptorSemaphore == NULL) { + ethernet::initStatus = ethernet::InitStatus::Failed; + return pdFAIL; + } + + EMAC::setMacAddress(EMAC::MacAddressIndex::Index0, FreeRTOS_GetMACAddress()); +#if (ipconfigUSE_LLMNR != 0) + /* Program the LLMNR address at index 1. */ + EMAC::setMacAddress(EMAC::MacAddressIndex::Index1, + reinterpret_cast(xLLMNR_MACAddress)); +#endif + + (void) EMAC::initialize(); + + ethernet::TxDescriptor = ethernet::DmaTxDescriptorTable; + ethernet::RxDescriptor = ethernet::DmaRxDescriptorTable; + + std::memset(ðernet::DmaTxDescriptorTable, 0, sizeof(ethernet::DmaTxDescriptorTable)); + std::memset(ðernet::DmaRxDescriptorTable, 0, sizeof(ethernet::DmaRxDescriptorTable)); + + ethernet::DmaTxDescriptorToClear = ethernet::DmaTxDescriptorTable; + + ethernet::DMATxDescListInit(); + ethernet::DMARxDescListInit(); + + ethernet::updateConfig(true); + + if (not xTaskCreate(ethernet::emacHandlerTask, "EMAC", ethernet::emacTaskStackDepth, NULL, + ethernet::emacTaskPriority, ðernet::emacTaskHandle)) { + ethernet::initStatus = ethernet::InitStatus::Failed; + return pdFAIL; + } + + ethernet::initStatus = ethernet::InitStatus::Pass; + } + + if (ethernet::initStatus != ethernet::InitStatus::Pass) + return pdFAIL; + + if (EMAC::getLinkStatus() == modm::platform::eth::LinkStatus::Up) { + EMAC::enableInterrupt(EMAC::Interrupt_t( +// EMAC::Interrupt::TimeStampTrigger +// | EMAC::Interrupt::Pmt +// | EMAC::Interrupt::Mmc + EMAC::Interrupt::NormalIrqSummary + | EMAC::Interrupt::EarlyReceive + | EMAC::Interrupt::FatalBusError + | EMAC::Interrupt::ReceiveWatchdog + | EMAC::Interrupt::ReceiveStopped + | EMAC::Interrupt::ReceiveBufferUnavailable + | EMAC::Interrupt::Receive + | EMAC::Interrupt::TransmitUnderflow + | EMAC::Interrupt::ReceiveOverflow + | EMAC::Interrupt::TransmitJabberTimeout + | EMAC::Interrupt::TransmitStopped + | EMAC::Interrupt::Transmit + )); + // link is up + return pdPASS; + } + + return pdFAIL; +} + +extern "C" BaseType_t +xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const descriptor, BaseType_t releaseAfterSend) +{ + using modm::ethernet; + + static constexpr TickType_t blockTimeTicks { pdMS_TO_TICKS(50) }; + static constexpr ethernet::TDes0_t transmitStatus { + ethernet::CrcControl_t(ethernet::CrcControl::HardwareCalculated) | + ethernet::TDes0_t(ethernet::TDes0::InterruptOnCompletion | + ethernet::TDes0::LastSegment | + ethernet::TDes0::FirstSegment + ) + }; + + BaseType_t result { pdFAIL }; + uint32_t transmitSize { 0 }; + __IO ethernet::DmaDescriptor_t *dmaTxDescriptor { nullptr }; + + do { + ProtocolPacket_t *packet = reinterpret_cast(descriptor->pucEthernetBuffer); + if (packet->xICMPPacket.xIPHeader.ucProtocol == ipPROTOCOL_ICMP) + packet->xICMPPacket.xICMPHeader.usChecksum = 0; + + if (EMAC::getLinkStatus() == modm::platform::eth::LinkStatus::Down) + // no link, drop packet + break; + + if (xSemaphoreTake(ethernet::txDescriptorSemaphore, blockTimeTicks) != pdPASS) + break; + + dmaTxDescriptor = ethernet::TxDescriptor; + configASSERT((dmaTxDescriptor->Status & uint32_t(ethernet::TDes0::DmaOwned)) == 0); + + transmitSize = descriptor->xDataLength; + if (transmitSize > ethernet::TX_BUFFER_SIZE) + transmitSize = ethernet::TX_BUFFER_SIZE; + + configASSERT(releaseAfterSend != 0); + + dmaTxDescriptor->Buffer1Addr = uint32_t(descriptor->pucEthernetBuffer); + releaseAfterSend = pdFALSE_SIGNED; + + dmaTxDescriptor->Status |= transmitStatus.value; + + dmaTxDescriptor->ControlBufferSize = transmitSize & modm::ethernet::Buffer1SizeMask; + + dmaTxDescriptor->Status |= uint32_t(ethernet::TDes0::DmaOwned); + ethernet::TxDescriptor = reinterpret_cast(ethernet::TxDescriptor->Buffer2NextDescAddr); + __DSB(); + ETH->DMATPDR = 0; + iptraceNETWORK_INTERFACE_TRANSMIT(); + result = pdPASS; + } while(0); + + if (releaseAfterSend) + vReleaseNetworkBufferAndDescriptor(descriptor); + + return result; +} + +extern "C" +BaseType_t xGetPhyLinkStatus() +{ + return EMAC::getLinkStatus() == modm::platform::eth::LinkStatus::Up ? pdTRUE : pdFALSE; +} + +MODM_ISR(ETH) +{ + using modm::platform::eth; + using modm::ethernet; + + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + EMAC::InterruptFlags_t irq = EMAC::getInterruptFlags(); + EMAC::acknowledgeInterrupt(irq); + + if (irq & (eth::InterruptFlags::Receive | eth::InterruptFlags::ReceiveBufferUnavailable)) { + ethernet::isrEvent |= eth::Event::Receive; + if (ethernet::emacTaskHandle) { + vTaskNotifyGiveFromISR(ethernet::emacTaskHandle, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + } + if (irq & (eth::InterruptFlags::Transmit)) { + ethernet::isrEvent |= eth::Event::Transmit; + if (ethernet::emacTaskHandle) { + vTaskNotifyGiveFromISR(ethernet::emacTaskHandle, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + } + + if (EMAC::getInterruptFlags() & eth::InterruptFlags::AbnormalIrqSummary) { + // not used yet + } +} diff --git a/ext/aws/module.lb b/ext/aws/module.lb index 028613bb7f..2374137b98 100644 --- a/ext/aws/module.lb +++ b/ext/aws/module.lb @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# Copyright (c) 2016-2017, Niklas Hauser +# Copyright (c) 2016-2017, 2021, Niklas Hauser # Copyright (c) 2017, Fabian Greif # # This file is part of the modm project. @@ -11,6 +11,23 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # ----------------------------------------------------------------------------- +class FreeRTOS_TCP_LAN8720A(Module): + def init(self, module): + module.name = "lan8720a" + module.description = """ +# a:FreeRTOS-Plus-TCP Ethernet via LAN8720A + +This module implements TCP over Ethernet via the LAN8720A transceiver. +""" + def prepare(self, module, options): + module.depends(":platform:eth", ":driver:lan8720a") + return options[":target"].has_driver("eth:stm32*") + + def build(self, env): + env.outbasepath = "modm/ext/freertos_plus_tcp" + env.copy("modm_lan8720a.cpp") + + class FreeRTOS_TCP(Module): def init(self, module): module.name = "tcp" @@ -29,6 +46,8 @@ the config macros, then redefine them with your options, for example: """ def prepare(self, module, options): + module.add_submodule(FreeRTOS_TCP_LAN8720A()) + module.depends(":platform:heap") return True def build(self, env): From d8be0a267d010a325770bf24f6703bf723aaabcc Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Sun, 7 Feb 2021 15:43:53 +0100 Subject: [PATCH 5/5] [examples] Add FreeRTOS TCP Ethernet example --- examples/nucleo_f767zi/ethernet/main.cpp | 256 ++++++++++++++++++++ examples/nucleo_f767zi/ethernet/project.xml | 11 + 2 files changed, 267 insertions(+) create mode 100755 examples/nucleo_f767zi/ethernet/main.cpp create mode 100755 examples/nucleo_f767zi/ethernet/project.xml diff --git a/examples/nucleo_f767zi/ethernet/main.cpp b/examples/nucleo_f767zi/ethernet/main.cpp new file mode 100755 index 0000000000..628bd2d34e --- /dev/null +++ b/examples/nucleo_f767zi/ethernet/main.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2020, Mike Wolfram + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +using namespace Board; + +namespace Ethernet +{ + using RMII_Ref_Clk = GpioInputA1; + using RMII_Mdio = GpioA2; + using RMII_Crs_Dv = GpioInputA7; + using RMII_Tx_En = GpioOutputG11; + using RMII_Tx_D0 = GpioOutputG13; + using RMII_Tx_D1 = GpioOutputB13; + using RMII_Mdc = GpioOutputC1; + using RMII_Rx_D0 = GpioInputC4; + using RMII_Rx_D1 = GpioInputC5; + using Port = Eth; +} + +UBaseType_t ulNextRand; + +void vApplicationIPNetworkEventHook(eIPCallbackEvent_t eNetworkEvent); + +class NetworkInitTask : modm::rtos::Thread +{ +public: + NetworkInitTask() + : Thread(5, 2048, "network_init") + {} + + void + run() + { + uint8_t ipAddress[4] { 192, 168, 1, 1 }; + uint8_t netmask[4] { 255, 255, 255, 0 }; + uint8_t gatewayAddress[4] { 0, 0, 0, 0 }; + uint8_t dnsAddress[4] { 0, 0, 0, 0 }; + + // local MAC address + uint8_t macAddress[] { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // A real MAC address can be retrieved from the Microchip 24AA02E48 + // I2C EEPROM, which is locaed at address 0xFA. + + // initialize random numbers + time_t now; + time(&now); + ulNextRand = uint32_t(now); + + FreeRTOS_IPInit(ipAddress, + netmask, + gatewayAddress, + dnsAddress, + &macAddress[0]); + + vTaskDelete(0); + } +}; + +class HttpConnection +{ + static constexpr TickType_t shutdownTimeout { pdMS_TO_TICKS(5000) }; + static constexpr TickType_t receiveTimeout { pdMS_TO_TICKS(5000) }; + static constexpr TickType_t sendTimeout { pdMS_TO_TICKS(5000) }; + +public: + static constexpr char name[] { "HTTPConnection" }; + static constexpr uint8_t httpText[] = { + "HTTP/1.1 200 OK \r\n" + "Content-Type: text/html\r\n" + "Connection: keep-alive\r\n" + "\r\n" + "

Hello from your STM32!

" + }; + + enum class + ResponseStatus : uint16_t { + Ok = 200, + BadRequest = 400, + NotFound = 404, + }; + + static void + run(void *parameter) + { + Socket_t connectedSocket = reinterpret_cast(parameter); + uint8_t *buffer = reinterpret_cast(pvPortMalloc(ipconfigTCP_MSS)); + + if (buffer) { + FreeRTOS_setsockopt(connectedSocket, 0, FREERTOS_SO_RCVTIMEO, &receiveTimeout, + sizeof(receiveTimeout)); + FreeRTOS_setsockopt(connectedSocket, 0, FREERTOS_SO_SNDTIMEO, &sendTimeout, + sizeof(sendTimeout)); + + while (true) { + std::memset(buffer, 0, ipconfigTCP_MSS); + int32_t bytes = FreeRTOS_recv(connectedSocket, buffer, ipconfigTCP_MSS, 0); + if (bytes <= 0) + break; + if (FreeRTOS_send(connectedSocket, httpText, sizeof(httpText) - 1, 0) < 0) + break; + } + } + + FreeRTOS_shutdown(connectedSocket, FREERTOS_SHUT_RDWR); + TickType_t shutdownTime { xTaskGetTickCount() }; + do { + if (FreeRTOS_recv(connectedSocket, buffer, ipconfigTCP_MSS, 0) < 0) + break; + } while ((xTaskGetTickCount() - shutdownTime) < shutdownTimeout); + + vPortFree(buffer); + FreeRTOS_closesocket(connectedSocket); + vTaskDelete(0); + + } +}; + +class HttpServerListener +{ + static constexpr TickType_t receiveTimeout { portMAX_DELAY }; + static constexpr BaseType_t backlog { 20 }; + +public: + static constexpr char name[] { "HTTPListener" }; + + static void + run(void *) + { + Socket_t listeningSocket; + Socket_t connectedSocket; + + listeningSocket = FreeRTOS_socket(FREERTOS_AF_INET, + FREERTOS_SOCK_STREAM, + FREERTOS_IPPROTO_TCP); + FreeRTOS_setsockopt(listeningSocket, 0, FREERTOS_SO_RCVTIMEO, &receiveTimeout, + sizeof(receiveTimeout)); + +#if ipconfigUSE_TCP_WIN == 1 + WinProperties_t winProps { + .lTxBufSize = ipconfigTCP_TX_BUFFER_LENGTH, + .lTxWinSize = 2, + .lRxBufSize = ipconfigTCP_RX_BUFFER_LENGTH, + .lRxWinSize = 2, + }; + FreeRTOS_setsockopt(listeningSocket, 0, FREERTOS_SO_WIN_PROPERTIES, + reinterpret_cast(&winProps), sizeof(winProps)); +#endif + + struct freertos_sockaddr bindAddress { + .sin_port = FreeRTOS_htons(80), + }; + FreeRTOS_bind(listeningSocket, &bindAddress, sizeof(bindAddress)); + FreeRTOS_listen(listeningSocket, backlog); + + struct freertos_sockaddr clientAddress; + + while (true) { + connectedSocket = FreeRTOS_accept(listeningSocket, &clientAddress, 0); + char buffer[16]; + FreeRTOS_inet_ntoa(clientAddress.sin_addr, buffer); + xTaskCreate(HttpConnection::run, HttpConnection::name, configMINIMAL_STACK_SIZE * 5, + reinterpret_cast(connectedSocket), configMAX_PRIORITIES, 0); + } + } +}; + +NetworkInitTask networkInit; + +int +main() +{ + Board::initialize(); + Leds::setOutput(); + MODM_LOG_INFO << "\n\nReboot: Ethernet Example" << modm::endl; + + Ethernet::Port::connect(); + + modm::rtos::Scheduler::schedule(); + + // we should never get here + return 0; +} + +void vApplicationIPNetworkEventHook(eIPCallbackEvent_t eNetworkEvent) +{ + static bool taskCreated = false; + + if (eNetworkEvent != eNetworkUp) + return; + + if (not taskCreated) { + xTaskCreate(HttpServerListener::run, HttpServerListener::name, configMINIMAL_STACK_SIZE * 2, 0, configMAX_PRIORITIES + 1, 0); + taskCreated = true; + } + + uint32_t ipAddress; + uint32_t netmask; + uint32_t gateway; + uint32_t dns; + char buffer[16]; + + FreeRTOS_GetAddressConfiguration(&ipAddress, &netmask, &gateway, &dns); + FreeRTOS_inet_ntoa(ipAddress, buffer); + MODM_LOG_DEBUG << "IP address: " << buffer << modm::endl; + FreeRTOS_inet_ntoa(netmask, buffer); + MODM_LOG_DEBUG << "Netmask : " << buffer << modm::endl; + FreeRTOS_inet_ntoa(gateway, buffer); + MODM_LOG_DEBUG << "Gateway : " << buffer << modm::endl; + FreeRTOS_inet_ntoa(dns, buffer); + MODM_LOG_DEBUG << "DNS : " << buffer << modm::endl; +} + +UBaseType_t uxRand( void ) +{ + static constexpr uint32_t ulMultiplier = 0x015a4e35UL; + static constexpr uint32_t ulIncrement = 1UL; + + /* Utility function to generate a pseudo random number. */ + + ulNextRand = ( ulMultiplier * ulNextRand ) + ulIncrement; + return( ( int ) ( ulNextRand >> 16UL ) & 0x7fffUL ); +} + +BaseType_t xApplicationGetRandomNumber(uint32_t* pulNumber) +{ + *(pulNumber) = uxRand(); + return pdTRUE; +} + +uint32_t ulApplicationGetNextSequenceNumber(uint32_t, uint16_t, uint32_t, uint16_t) +{ + return uxRand(); +} diff --git a/examples/nucleo_f767zi/ethernet/project.xml b/examples/nucleo_f767zi/ethernet/project.xml new file mode 100755 index 0000000000..77967d8985 --- /dev/null +++ b/examples/nucleo_f767zi/ethernet/project.xml @@ -0,0 +1,11 @@ + + modm:nucleo-f767zi + + + + + modm:build:scons + modm:freertos:tcp:lan8720a + modm:processing:rtos + +