Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First implementation of Ethernet for STM32F7 (and later STM32F4). #466

Merged
merged 5 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,20 +153,21 @@ documentation.
<td align="center">NUCLEO-F446ZE</td>
</tr><tr>
<td align="center">NUCLEO-F746ZG</td>
<td align="center">NUCLEO-F767ZI</td>
<td align="center">NUCLEO-G071RB</td>
<td align="center">NUCLEO-G431KB</td>
<td align="center">NUCLEO-G431RB</td>
</tr><tr>
<td align="center">NUCLEO-G431RB</td>
<td align="center">NUCLEO-G474RE</td>
<td align="center">NUCLEO-L152RE</td>
<td align="center">NUCLEO-L432KC</td>
<td align="center">NUCLEO-L476RG</td>
</tr><tr>
<td align="center">NUCLEO-L476RG</td>
<td align="center">OLIMEXINO-STM32</td>
<td align="center">Raspberry Pi</td>
<td align="center">SAMD21-MINI</td>
<td align="center">STM32-F4VE</td>
</tr><tr>
<td align="center">STM32-F4VE</td>
<td align="center">STM32F030F4P6-DEMO</td>
</tr>
</table>
Expand Down Expand Up @@ -220,48 +221,49 @@ can easily configure them for you specific needs.
<td align="center">ITG3200</td>
<td align="center">L3GD20</td>
</tr><tr>
<td align="center">LAN8720A</td>
<td align="center">LAWICEL</td>
<td align="center">LIS302DL</td>
<td align="center">LIS3DSH</td>
<td align="center">LIS3MDL</td>
<td align="center">LM75</td>
<td align="center">LP503X</td>
</tr><tr>
<td align="center">LP503X</td>
<td align="center">LSM303A</td>
<td align="center">LSM6DS33</td>
<td align="center">LTC2984</td>
<td align="center">MAX6966</td>
<td align="center">MAX7219</td>
<td align="center">MCP23X17</td>
</tr><tr>
<td align="center">MCP23X17</td>
<td align="center">MCP2515</td>
<td align="center">MMC5603</td>
<td align="center">NOKIA5110</td>
<td align="center">NRF24</td>
<td align="center">TFT-DISPLAY</td>
<td align="center">PAT9125EL</td>
</tr><tr>
<td align="center">PAT9125EL</td>
<td align="center">PCA8574</td>
<td align="center">PCA9535</td>
<td align="center">PCA9548A</td>
<td align="center">PCA9685</td>
<td align="center">SIEMENS-S65</td>
<td align="center">SIEMENS-S75</td>
</tr><tr>
<td align="center">SIEMENS-S75</td>
<td align="center">SK6812</td>
<td align="center">SK9822</td>
<td align="center">SSD1306</td>
<td align="center">SX1276</td>
<td align="center">TCS3414</td>
<td align="center">TCS3472</td>
</tr><tr>
<td align="center">TCS3472</td>
<td align="center">TLC594X</td>
<td align="center">TMP102</td>
<td align="center">TMP175</td>
<td align="center">VL53L0</td>
<td align="center">VL6180</td>
<td align="center">WS2812</td>
</tr><tr>
<td align="center">WS2812</td>
</tr>
</table>
<!--/drivertable-->
Expand Down
40 changes: 40 additions & 0 deletions examples/nucleo_f767zi/blink/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <modm/board.hpp>

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;
}
9 changes: 9 additions & 0 deletions examples/nucleo_f767zi/blink/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<library>
<extends>modm:nucleo-f767zi</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_f767zi/blink</option>
</options>
<modules>
<module>modm:build:scons</module>
</modules>
</library>
256 changes: 256 additions & 0 deletions examples/nucleo_f767zi/ethernet/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <modm/board.hpp>
#include <modm/driver/ethernet/lan8720a.hpp>
#include <modm/processing/rtos.hpp>
#include <FreeRTOS_IP.h>
#include <FreeRTOS_Sockets.h>

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<modm::Lan8720a>;
}

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);
Comment on lines +59 to +62
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have used real random numbers here...

Suggested change
// initialize random numbers
time_t now;
time(&now);
ulNextRand = uint32_t(now);
// initialize random numbers
modm::platform::RandomNumberGenerator::enable();
while(!modm::platform::RandomNumberGenerator::isReady()) {}
ulNextRand = modm::platform::RandomNumberGenerator::getValue();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and as a fallback you can seed srand with the device's UID.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also time() is not implemented. We could perhaps wire it to modm::Clock, but it's not done yet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and as a fallback you can seed srand with the device's UID.

I am not sure if this is a good idea or a serious security flaw...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, you need to collect entropy somehow. There are a bunch more hardware calibration registers that provide entropy within some bounds. It's a somewhat good idea since the values will be different for every device (assuming a good hash function), however, only if the attacker cannot read them out remotely. However, security is definitely beyond the scope of modm and too much work to implement yourself. We'd have to integrate some embedded TLS library for encryption.


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"
"<html><body><h1>Hello from your STM32!</h1></body></html>"
};

enum class
ResponseStatus : uint16_t {
Ok = 200,
BadRequest = 400,
NotFound = 404,
};

static void
run(void *parameter)
{
Socket_t connectedSocket = reinterpret_cast<Socket_t>(parameter);
uint8_t *buffer = reinterpret_cast<uint8_t *>(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<void *>(&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<void *>(connectedSocket), configMAX_PRIORITIES, 0);
}
}
};

NetworkInitTask networkInit;

int
main()
{
Board::initialize();
Leds::setOutput();
MODM_LOG_INFO << "\n\nReboot: Ethernet Example" << modm::endl;

Ethernet::Port::connect<Ethernet::RMII_Ref_Clk::Refclk,
Ethernet::RMII_Mdc::Mdc,
Ethernet::RMII_Mdio::Mdio,
Ethernet::RMII_Crs_Dv::Rcccrsdv,
Ethernet::RMII_Tx_En::Txen,
Ethernet::RMII_Tx_D0::Txd0,
Ethernet::RMII_Tx_D1::Txd1,
Ethernet::RMII_Rx_D0::Rxd0,
Ethernet::RMII_Rx_D1::Rxd1>();

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();
}
Loading