From 141aa718ded4e624089e69e8c93d48bfbe1bc878 Mon Sep 17 00:00:00 2001 From: luxarf Date: Tue, 14 Apr 2020 10:01:37 +0200 Subject: [PATCH 001/135] [board] add BSP and examples for Nucleo F303RE --- .circleci/config.yml | 2 +- README.md | 7 +- examples/nucleo_f303re/blink/main.cpp | 40 ++++++++ examples/nucleo_f303re/blink/project.xml | 9 ++ examples/nucleo_f303re/itm/main.cpp | 52 ++++++++++ examples/nucleo_f303re/itm/project.xml | 12 +++ examples/nucleo_f303re/rtos/main.cpp | 80 +++++++++++++++ examples/nucleo_f303re/rtos/project.xml | 11 +++ src/modm/board/nucleo_f303re/board.hpp | 119 +++++++++++++++++++++++ src/modm/board/nucleo_f303re/board.xml | 16 +++ src/modm/board/nucleo_f303re/module.lb | 40 ++++++++ 11 files changed, 384 insertions(+), 4 deletions(-) create mode 100644 examples/nucleo_f303re/blink/main.cpp create mode 100644 examples/nucleo_f303re/blink/project.xml create mode 100644 examples/nucleo_f303re/itm/main.cpp create mode 100644 examples/nucleo_f303re/itm/project.xml create mode 100644 examples/nucleo_f303re/rtos/main.cpp create mode 100644 examples/nucleo_f303re/rtos/project.xml create mode 100644 src/modm/board/nucleo_f303re/board.hpp create mode 100644 src/modm/board/nucleo_f303re/board.xml create mode 100644 src/modm/board/nucleo_f303re/module.lb diff --git a/.circleci/config.yml b/.circleci/config.yml index f3a149cce7..b342bcdeed 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -88,7 +88,7 @@ jobs: - run: name: Examples STM32F3 Series command: | - (cd examples && ../tools/scripts/examples_compile.py stm32f3_discovery nucleo_f303k8) + (cd examples && ../tools/scripts/examples_compile.py stm32f3_discovery nucleo_f303k8 nucleo_f303re) - run: name: Examples STM32F7 Series command: | diff --git a/README.md b/README.md index a07c3301e7..f502aa2524 100644 --- a/README.md +++ b/README.md @@ -137,19 +137,20 @@ documentation. NUCLEO-F103RB NUCLEO-F303K8 +NUCLEO-F303RE NUCLEO-F401RE -NUCLEO-F411RE +NUCLEO-F411RE NUCLEO-F429ZI NUCLEO-F446RE NUCLEO-G071RB -NUCLEO-G474RE +NUCLEO-G474RE NUCLEO-L152RE NUCLEO-L432KC NUCLEO-L476RG -OLIMEXINO-STM32 +OLIMEXINO-STM32 STM32F030F4P6-DEMO diff --git a/examples/nucleo_f303re/blink/main.cpp b/examples/nucleo_f303re/blink/main.cpp new file mode 100644 index 0000000000..2c38798a25 --- /dev/null +++ b/examples/nucleo_f303re/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(); + LedD13::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) + { + LedD13::toggle(); + modm::delay_ms(Button::read() ? 100 : 500); + + MODM_LOG_INFO << "loop: " << counter++ << modm::endl; + } + + return 0; +} diff --git a/examples/nucleo_f303re/blink/project.xml b/examples/nucleo_f303re/blink/project.xml new file mode 100644 index 0000000000..0be0b70754 --- /dev/null +++ b/examples/nucleo_f303re/blink/project.xml @@ -0,0 +1,9 @@ + + modm:nucleo-f303re + + + + + modm:build:scons + + diff --git a/examples/nucleo_f303re/itm/main.cpp b/examples/nucleo_f303re/itm/main.cpp new file mode 100644 index 0000000000..5de5f00a6c --- /dev/null +++ b/examples/nucleo_f303re/itm/main.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019, 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 +#include + +using namespace Board; + +modm::IODeviceWrapper itm_device; +modm::IOStream stream(itm_device); + +int +main() +{ + Board::initialize(); + LedD13::setOutput(); + + // Done by OpenOCD: scons itm fcpu=64000000 + // Itm::initialize(); + // Itm::connect(); + + stream << "Hello from the SWO." << modm::endl; + stream << "debug" << modm::endl; + stream << "info" << modm::endl; + stream << "warning" << modm::endl; + stream << "error" << modm::endl; + + + while (true) + { + static modm::PeriodicTimer tmr{500ms}; + if (tmr.execute()) + { + tmr.restart(Button::read() ? 100ms : 500ms); + LedD13::toggle(); + + static uint32_t counter{0}; + stream << "loop: " << counter++ << modm::endl; + } + Itm::update(); + } + + return 0; +} diff --git a/examples/nucleo_f303re/itm/project.xml b/examples/nucleo_f303re/itm/project.xml new file mode 100644 index 0000000000..244fd58fb7 --- /dev/null +++ b/examples/nucleo_f303re/itm/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-f303re + + + + + + modm:build:scons + modm:platform:itm + modm:processing:timer + + diff --git a/examples/nucleo_f303re/rtos/main.cpp b/examples/nucleo_f303re/rtos/main.cpp new file mode 100644 index 0000000000..359c3301a2 --- /dev/null +++ b/examples/nucleo_f303re/rtos/main.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, Georgi Grinshpun + * Copyright (c) 2014, Sascha Schade + * Copyright (c) 2015-2017, 2019 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 +#include + +using namespace modm::platform; +using namespace modm::literals; + +/** + * This example uses four threads to check if task switching works correctly. + * + * What to expect? + * --------------- + * - All our LEDs blinking at different rates, about 3 to 4 Hz + * - A string at 115200 baud + * + * 0aA!1bB"2cC#3dD$4eE%5fF&6gG'7hH(8iI9)jJ0*aA1!bB2"cC + * + * Each thread prints out a sequence + * 0123456789 + * abcdefghij + * ABCDEFGHIJ + * !"#$%&'()* + * respectivly. + */ + +// ---------------------------------------------------------------------------- +template +class P: modm::rtos::Thread +{ + char c; + uint8_t i = 0; + volatile float a = 10.f; +public: + P(char c): Thread(2,1<<10), c(c) {} + + void run() + { + Gpio::setOutput(); + while (true) + { + sleep(SleepTime * MILLISECONDS); + + Gpio::toggle(); + { + static modm::rtos::Mutex lm; + modm::rtos::MutexGuard m(lm); + MODM_LOG_INFO << char(i + c); + } + i = (i+1)%10; + a *= 3.141f; + } + } +}; + +P< Board::LedD13, 260 > p1('0'); +P< Board::LedD13, 260 + 10 > p2('a'); +P< Board::LedD13, 260 + 20 > p3('A'); +P< Board::LedD13, 260 + 30 > p4('!'); + + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + modm::rtos::Scheduler::schedule(); + return 0; +} diff --git a/examples/nucleo_f303re/rtos/project.xml b/examples/nucleo_f303re/rtos/project.xml new file mode 100644 index 0000000000..4045801884 --- /dev/null +++ b/examples/nucleo_f303re/rtos/project.xml @@ -0,0 +1,11 @@ + + modm:nucleo-f303re + + + + + modm:processing:rtos + modm:platform:heap + modm:build:scons + + diff --git a/src/modm/board/nucleo_f303re/board.hpp b/src/modm/board/nucleo_f303re/board.hpp new file mode 100644 index 0000000000..fddbaeffb0 --- /dev/null +++ b/src/modm/board/nucleo_f303re/board.hpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2016-2018, Niklas Hauser + * Copyright (c) 2017, Sascha Schade + * + * 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_F303RE_HPP +#define MODM_STM32_NUCLEO_F303RE_HPP + +#include +#include +#include +/// @ingroup modm_board_nucleo_f303re +#define MODM_BOARD_HAS_LOGGER + +using namespace modm::platform; + +/// @ingroup modm_board_nucleo_f303re +namespace Board +{ + using namespace modm::literals; + +/// STM32F303RE running at 64MHz generated from the internal 8MHz clock +// Dummy clock for devices +struct SystemClock { + static constexpr uint32_t Frequency = 64_MHz; + static constexpr uint32_t Ahb = Frequency; + static constexpr uint32_t Apb1 = Frequency / 2; + static constexpr uint32_t Apb2 = Frequency; + + static constexpr uint32_t Adc1 = Apb2; + static constexpr uint32_t Adc2 = Apb2; + + static constexpr uint32_t Can = Apb1; + + static constexpr uint32_t Spi1 = Apb2; + + // USART1 is on APB2 bus, but default clock is PCLK1 + static constexpr uint32_t Usart1 = Apb1; + static constexpr uint32_t Usart2 = Apb1; + static constexpr uint32_t Usart3 = Apb1; + + // I2C1 clock source is HSI per default + static constexpr uint32_t I2c1 = 8_MHz; + + static constexpr uint32_t Apb1Timer = Apb1 * 2; + static constexpr uint32_t Apb2Timer = Apb2 * 1; + static constexpr uint32_t Timer1 = Apb2Timer; + static constexpr uint32_t Timer2 = Apb1Timer; + static constexpr uint32_t Timer3 = Apb1Timer; + static constexpr uint32_t Timer6 = Apb1Timer; + static constexpr uint32_t Timer7 = Apb1Timer; + static constexpr uint32_t Timer15 = Apb2Timer; + static constexpr uint32_t Timer16 = Apb2Timer; + static constexpr uint32_t Timer17 = Apb2Timer; + + static bool inline + enable() + { + Rcc::enableInternalClock(); // 8MHz + // 8MHz / 2 * 16 = 64MHz + const Rcc::PllFactors pllFactors{ + .pllMul = 16, + .pllPrediv = 2, + }; + Rcc::enablePll(Rcc::PllSource::InternalClock, pllFactors); + // set flash latency for 64MHz + Rcc::setFlashLatency(); + // switch system clock to PLL output + Rcc::enableSystemClock(Rcc::SystemClockSource::Pll); + Rcc::setAhbPrescaler(Rcc::AhbPrescaler::Div1); + // APB1 has max. 36MHz + Rcc::setApb1Prescaler(Rcc::Apb1Prescaler::Div2); + Rcc::setApb2Prescaler(Rcc::Apb2Prescaler::Div1); + // update frequencies for busy-wait delay functions + Rcc::updateCoreFrequency(); + + return true; + } +}; + +// Arduino Footprint +#include "nucleo64_arduino.hpp" + +using Button = GpioInverted; +using LedD13 = D13; + +using Leds = SoftwareGpioPort< LedD13 >; + + +namespace stlink +{ +using Rx = GpioInputA15; +using Tx = GpioOutputA2; +using Uart = Usart2; +} + +using LoggerDevice = modm::IODeviceWrapper< stlink::Uart, modm::IOBuffer::BlockIfFull >; + + +inline void +initialize() +{ + SystemClock::enable(); + SysTickTimer::initialize(); + + stlink::Uart::connect(); + stlink::Uart::initialize(); +} + +} + +#endif // MODM_STM32_NUCLEO_F303RE_HPP diff --git a/src/modm/board/nucleo_f303re/board.xml b/src/modm/board/nucleo_f303re/board.xml new file mode 100644 index 0000000000..cd5f93ed77 --- /dev/null +++ b/src/modm/board/nucleo_f303re/board.xml @@ -0,0 +1,16 @@ + + + + ../../../../repo.lb + + + + + + + + + + modm:board:nucleo-f303re + + diff --git a/src/modm/board/nucleo_f303re/module.lb b/src/modm/board/nucleo_f303re/module.lb new file mode 100644 index 0000000000..28ed996567 --- /dev/null +++ b/src/modm/board/nucleo_f303re/module.lb @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# Copyright (c) 2020, Philipp Graf +# +# 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-f303re" + module.description = """\ +# NUCLEO-F303RE + +[Nucleo kit for STM32F303RE](https://www.st.com/en/evaluation-tools/nucleo-f303re.html) +""" + +def prepare(module, options): + if not options[":target"].partname.startswith("stm32f303ret"): + return False + + module.depends(":platform:core", ":platform:gpio", ":platform:clock", ":platform:uart:2", + ":debug", ":architecture:clock") + 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("../nucleo64_arduino.hpp", "nucleo64_arduino.hpp") + env.collect(":build:openocd.source", "board/st_nucleo_f3.cfg"); From 3dad446fd906b6dfe395285d421987b3c0429090 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 25 Apr 2020 18:39:20 +0200 Subject: [PATCH 002/135] [build] Add default.avrdude.options collector --- tools/build_script_generator/common.py | 2 ++ tools/build_script_generator/module.lb | 3 +++ 2 files changed, 5 insertions(+) diff --git a/tools/build_script_generator/common.py b/tools/build_script_generator/common.py index d6e810c131..c4ab1d113f 100644 --- a/tools/build_script_generator/common.py +++ b/tools/build_script_generator/common.py @@ -152,6 +152,8 @@ def common_avrdude_options(env): option_port = env.collector_values(":build:default.avrdude.port", option_port)[0] if not option_baudrate: option_baudrate = env.collector_values(":build:default.avrdude.baudrate", option_baudrate)[0] + if not option_options: + option_options = env.collector_values(":build:default.avrdude.options", option_options)[0] options = { "avrdude_programmer": option_programmer, "avrdude_port": option_port, diff --git a/tools/build_script_generator/module.lb b/tools/build_script_generator/module.lb index 946a921eec..c722247900 100644 --- a/tools/build_script_generator/module.lb +++ b/tools/build_script_generator/module.lb @@ -127,6 +127,9 @@ def prepare(module, options): module.add_collector( StringCollector(name="default.avrdude.port", description="Default AvrDude port")) + module.add_collector( + StringCollector(name="default.avrdude.options", + description="Default AvrDude options")) # Compile flag collectors def flag_validate(flag): From fca14fd01dda1ae535e83d821ced93275fb2bb4c Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 25 Apr 2020 18:38:50 +0200 Subject: [PATCH 003/135] [board] Add Mega 2560 PRO BSP and example --- README.md | 9 +- examples/avr/mega_pro/main.cpp | 44 +++++++ examples/avr/mega_pro/project.xml | 9 ++ src/modm/board/mega_2560_pro/board.hpp | 123 ++++++++++++++++++ src/modm/board/mega_2560_pro/board.xml | 15 +++ src/modm/board/mega_2560_pro/board_serial.cpp | 22 ++++ src/modm/board/mega_2560_pro/module.lb | 45 +++++++ 7 files changed, 263 insertions(+), 4 deletions(-) create mode 100644 examples/avr/mega_pro/main.cpp create mode 100644 examples/avr/mega_pro/project.xml create mode 100644 src/modm/board/mega_2560_pro/board.hpp create mode 100644 src/modm/board/mega_2560_pro/board.xml create mode 100644 src/modm/board/mega_2560_pro/board_serial.cpp create mode 100644 src/modm/board/mega_2560_pro/module.lb diff --git a/README.md b/README.md index f502aa2524..29fbf3f8b6 100644 --- a/README.md +++ b/README.md @@ -130,26 +130,27 @@ documentation. DISCO-L152RC DISCO-L476VG +MEGA-2560-PRO MINI-F401 MINI-F411 NUCLEO-F031K6 -NUCLEO-F042K6 +NUCLEO-F042K6 NUCLEO-F103RB NUCLEO-F303K8 NUCLEO-F303RE -NUCLEO-F401RE +NUCLEO-F401RE NUCLEO-F411RE NUCLEO-F429ZI NUCLEO-F446RE -NUCLEO-G071RB +NUCLEO-G071RB NUCLEO-G474RE NUCLEO-L152RE NUCLEO-L432KC -NUCLEO-L476RG +NUCLEO-L476RG OLIMEXINO-STM32 STM32F030F4P6-DEMO diff --git a/examples/avr/mega_pro/main.cpp b/examples/avr/mega_pro/main.cpp new file mode 100644 index 0000000000..8892d07065 --- /dev/null +++ b/examples/avr/mega_pro/main.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011, Fabian Greif + * Copyright (c) 2013-2014, 2017-2018, Niklas Hauser + * Copyright (c) 2014, Sascha Schade + * + * 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 std::chrono_literals; +#include + +// Set the log level +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::DEBUG + +int +main() +{ + Board::initialize(); + Board::LedD13::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; + + uint8_t counter{0}; + while (true) + { + MODM_LOG_INFO << "Loop " << counter++ << modm::endl; + Board::LedD13::toggle(); + + MODM_LOG_INFO << "sin " << (int)sinf(counter) << modm::endl; + modm::delay(1s); + } +} diff --git a/examples/avr/mega_pro/project.xml b/examples/avr/mega_pro/project.xml new file mode 100644 index 0000000000..4e1c2afb34 --- /dev/null +++ b/examples/avr/mega_pro/project.xml @@ -0,0 +1,9 @@ + + modm:mega-2560-pro + + + + + modm:build:scons + + diff --git a/src/modm/board/mega_2560_pro/board.hpp b/src/modm/board/mega_2560_pro/board.hpp new file mode 100644 index 0000000000..73236ad301 --- /dev/null +++ b/src/modm/board/mega_2560_pro/board.hpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2016-2018, Niklas Hauser + * Copyright (c) 2017, Sascha Schade + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +/// @ingroup modm_board_mega_2560_pro +#define MODM_BOARD_HAS_LOGGER + +using namespace modm::platform; + +/// @ingroup modm_board_mega_2560_pro +namespace Board +{ +using namespace modm::literals; +using SystemClock = modm::platform::SystemClock; + +using D0 = GpioE0; +using D1 = GpioE1; +using D2 = GpioE4; +using D3 = GpioE5; +using D4 = GpioG5; +using D5 = GpioE3; +using D6 = GpioH3; +using D7 = GpioH4; +using D8 = GpioH5; +using D9 = GpioH6; +using D10 = GpioB4; +using D11 = GpioB5; +using D12 = GpioB6; +using D13 = GpioB7; +using D14 = GpioJ1; +using D15 = GpioJ0; +using D16 = GpioH1; +using D17 = GpioH0; +using D18 = GpioD3; +using D19 = GpioD2; +using D20 = GpioD1; +using D21 = GpioD0; +using D22 = GpioA0; +using D23 = GpioA1; +using D24 = GpioA2; +using D25 = GpioA3; +using D26 = GpioA4; +using D27 = GpioA5; +using D28 = GpioA6; +using D29 = GpioA7; +using D30 = GpioC7; +using D31 = GpioC6; + +using A0 = GpioF0; +using A1 = GpioF1; +using A2 = GpioF2; +using A3 = GpioF3; +using A4 = GpioF4; +using A5 = GpioF5; +using A6 = GpioF6; +using A7 = GpioF7; +using A8 = GpioK0; +using A9 = GpioK1; +using A10 = GpioK2; +using A11 = GpioK3; +using A12 = GpioK4; +using A13 = GpioK5; +using A14 = GpioK6; +using A15 = GpioK7; + +using D32 = GpioC5; +using D33 = GpioC4; +using D34 = GpioC3; +using D35 = GpioC2; +using D36 = GpioC1; +using D37 = GpioC0; +using D38 = GpioD7; +using D39 = GpioG2; +using D40 = GpioG1; +using D41 = GpioG0; +using D42 = GpioL7; +using D43 = GpioL6; +using D44 = GpioL5; +using D45 = GpioL4; +using D46 = GpioL3; +using D47 = GpioL2; +using D48 = GpioL1; +using D49 = GpioL0; +using D50 = GpioB3; +using D51 = GpioB2; +using D52 = GpioB1; +using D53 = GpioB0; + +using Button = GpioUnused; +using LedD13 = D13; + +using Leds = SoftwareGpioPort< LedD13 >; + +using LoggerDevice = modm::IODeviceWrapper< Uart0, modm::IOBuffer::BlockIfFull >; + +inline void +initialize() +{ + SystemClock::enable(); + + // Uart0 is connected to onboard USB bridge + Uart0::connect(); + Uart0::initialize(); + + enableInterrupts(); +} + +} + +using namespace Board; +extern modm::IOStream serialStream; diff --git a/src/modm/board/mega_2560_pro/board.xml b/src/modm/board/mega_2560_pro/board.xml new file mode 100644 index 0000000000..9add93edc2 --- /dev/null +++ b/src/modm/board/mega_2560_pro/board.xml @@ -0,0 +1,15 @@ + + + + ../../../../repo.lb + + + + + + + + + modm:board:mega-2560-pro + + diff --git a/src/modm/board/mega_2560_pro/board_serial.cpp b/src/modm/board/mega_2560_pro/board_serial.cpp new file mode 100644 index 0000000000..ef7c3b9e45 --- /dev/null +++ b/src/modm/board/mega_2560_pro/board_serial.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016-2018, 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 "board.hpp" + +// Create an IODeviceWrapper around the Uart Peripheral we want to use +Board::LoggerDevice loggerDevice; + +// Set all four logger streams to use the UART +modm::IOStream serialStream(loggerDevice); +modm::log::Logger modm::log::debug(loggerDevice); +modm::log::Logger modm::log::info(loggerDevice); +modm::log::Logger modm::log::warning(loggerDevice); +modm::log::Logger modm::log::error(loggerDevice); diff --git a/src/modm/board/mega_2560_pro/module.lb b/src/modm/board/mega_2560_pro/module.lb new file mode 100644 index 0000000000..b01371e4fa --- /dev/null +++ b/src/modm/board/mega_2560_pro/module.lb @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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:mega-2560-pro" + module.description = """ +# Mega 2560 PRO (Embed) CH340G + +A compact breakout board similiar to the Arduino Mega 2560 board. +You can order this for little money from well known Chinese online stores. + +See: https://robotdyn.com/mega-2560-pro-embed-ch340g-atmega2560-16au.html +""" + +def prepare(module, options): + if not options[":target"].partname.startswith("atmega2560"): + return False + + module.depends( + ":architecture:clock", + ":architecture:interrupt", + ":debug", + ":platform:clock", + ":platform:core", + ":platform:gpio", + ":platform:uart:0") + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + env.copy('.') + env.collect(":build:default.avrdude.programmer", "stk500v2"); + env.collect(":build:default.avrdude.baudrate", "115200"); + # The STK500v2 bootloader of the atmega is unwilling to chip erase + env.collect(":build:default.avrdude.options", "-D"); From 37bc53d29312c1c17c17df16898a141de5315fb6 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 25 Apr 2020 18:40:47 +0200 Subject: [PATCH 004/135] [ext] Update libstdc++ submodule to fix --- ext/gcc/libstdc++ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/gcc/libstdc++ b/ext/gcc/libstdc++ index 2030cc27f3..2f8a64275e 160000 --- a/ext/gcc/libstdc++ +++ b/ext/gcc/libstdc++ @@ -1 +1 @@ -Subproject commit 2030cc27f3238419f792ac4558877f9bf571af2a +Subproject commit 2f8a64275ef779fc4c0dbb26de6826d83d36367d From 80b62fbaf0bf569571cfc9c142dc7d7c81678e38 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 25 Apr 2020 18:39:48 +0200 Subject: [PATCH 005/135] [test] Compile stdc++ tests for all platforms --- test/stdc++/module.lb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/stdc++/module.lb b/test/stdc++/module.lb index 4fc1ab316d..21df65ec91 100644 --- a/test/stdc++/module.lb +++ b/test/stdc++/module.lb @@ -16,10 +16,6 @@ def init(module): def prepare(module, options): - target = options[":target"].identifier - if target["platform"] != "avr": - return False - module.depends(":stdc++") return True From 11ffe92cabe9e58a5dae9ac673f426f2c963890c Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 25 Apr 2020 18:40:22 +0200 Subject: [PATCH 006/135] [test] Add large AVR unittests for Mega 2560 board --- .circleci/config.yml | 1 + test/Makefile | 17 ++++++++++------ test/config/mega-2560-pro.xml | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 test/config/mega-2560-pro.xml diff --git a/.circleci/config.yml b/.circleci/config.yml index b342bcdeed..a49a6694b3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,6 +42,7 @@ jobs: (cd test && make compile-al-avreb-can) (cd test && make compile-arduino-uno) (cd test && make compile-arduino-nano) + (cd test && make compile-mega-2560-pro) - run: name: Linux Examples command: | diff --git a/test/Makefile b/test/Makefile index 7f38c7562c..d6284a0d6e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -60,18 +60,23 @@ run-nucleo-f103: compile-al-avreb-can: - $(call compile-test,al-avreb-can) + $(call compile-test,al-avreb-can,size) run-al-avreb-can: - $(call run-test,al-avreb-can) + $(call run-test,al-avreb-can,size) compile-arduino-uno: - $(call compile-test,arduino-uno) + $(call compile-test,arduino-uno,size) run-arduino-uno: - $(call run-test,arduino-uno) + $(call run-test,arduino-uno,size) compile-arduino-nano: - $(call compile-test,arduino-nano) + $(call compile-test,arduino-nano,size) run-arduino-nano: - $(call run-test,arduino-nano) + $(call run-test,arduino-nano,size) + +compile-mega-2560-pro: + $(call compile-test,mega-2560-pro,size) +run-mega-2560-pro: + $(call run-test,mega-2560-pro,size) diff --git a/test/config/mega-2560-pro.xml b/test/config/mega-2560-pro.xml new file mode 100644 index 0000000000..fced95122f --- /dev/null +++ b/test/config/mega-2560-pro.xml @@ -0,0 +1,38 @@ + + + modm:mega-2560-pro + + + + + + + + + + + modm-test:test:architecture + modm-test:test:communication:sab + + + + + + modm-test:test:container + + + modm-test:test:driver + modm-test:test:stdc++ + + + modm-test:test:io + modm-test:test:platform:** + + + modm-test:test:processing + + + modm-test:test:ui + modm-test:test:math + + From ab447399ddedfc67a753c10865363db65ca17cf1 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 27 Apr 2020 01:42:31 +0200 Subject: [PATCH 007/135] [architecture] Reduce interface dependencies Particularly prevent inclusion of module `:io` only for formatting functions! --- .../architecture/driver/atomic/container.hpp | 5 +- src/modm/architecture/driver/atomic/queue.hpp | 5 +- .../architecture/driver/atomic/queue_impl.hpp | 16 +++---- ...cessor_flash.hpp => accessor_flash.hpp.in} | 10 ++-- .../{can_message.cpp => can_message.cpp.in} | 48 ++++++++----------- .../{can_message.hpp => can_message.hpp.in} | 16 +++---- src/modm/architecture/interface/i2c.cpp | 7 +-- .../interface/{i2c.hpp => i2c.hpp.in} | 9 ++-- .../{register.hpp => register.hpp.in} | 4 ++ src/modm/architecture/module.lb | 29 ++++++----- src/modm/platform/can/stm32/can.cpp.in | 1 + 11 files changed, 71 insertions(+), 79 deletions(-) rename src/modm/architecture/interface/{accessor_flash.hpp => accessor_flash.hpp.in} (95%) rename src/modm/architecture/interface/{can_message.cpp => can_message.cpp.in} (87%) rename src/modm/architecture/interface/{can_message.hpp => can_message.hpp.in} (96%) rename src/modm/architecture/interface/{i2c.hpp => i2c.hpp.in} (99%) rename src/modm/architecture/interface/{register.hpp => register.hpp.in} (99%) diff --git a/src/modm/architecture/driver/atomic/container.hpp b/src/modm/architecture/driver/atomic/container.hpp index f0354c163a..270fe95780 100644 --- a/src/modm/architecture/driver/atomic/container.hpp +++ b/src/modm/architecture/driver/atomic/container.hpp @@ -15,7 +15,6 @@ #define MODM_ATOMIC_CONTAINER_HPP #include -#include namespace modm { @@ -90,7 +89,7 @@ namespace modm get() { Lock lock; - return accessor::asVolatile(this->object); + return (volatile T&)this->object; } /** @@ -123,7 +122,7 @@ namespace modm { Lock lock; - T oldValue = accessor::asVolatile(this->object); + T oldValue = (volatile T&)this->object; this->object = value; return oldValue; diff --git a/src/modm/architecture/driver/atomic/queue.hpp b/src/modm/architecture/driver/atomic/queue.hpp index 1b80f3af8b..1c3a77407b 100644 --- a/src/modm/architecture/driver/atomic/queue.hpp +++ b/src/modm/architecture/driver/atomic/queue.hpp @@ -20,7 +20,6 @@ #include #include #include -#include namespace modm { @@ -93,8 +92,8 @@ namespace modm pop(); private: - Index head; - Index tail; + volatile Index head; + volatile Index tail; T buffer[N+1]; }; diff --git a/src/modm/architecture/driver/atomic/queue_impl.hpp b/src/modm/architecture/driver/atomic/queue_impl.hpp index 485d843e7a..a1188b2f32 100644 --- a/src/modm/architecture/driver/atomic/queue_impl.hpp +++ b/src/modm/architecture/driver/atomic/queue_impl.hpp @@ -29,12 +29,12 @@ template modm_always_inline bool modm::atomic::Queue::isFull() const { - Index tmphead = modm::accessor::asVolatile(this->head) + 1; + Index tmphead = this->head + 1; if (tmphead >= (N+1)) { tmphead = 0; } - return (tmphead == modm::accessor::asVolatile(this->tail)); + return (tmphead == this->tail); } template @@ -43,8 +43,8 @@ modm::atomic::Queue::isNearlyFull() const { static_assert(N > 3, "Not possible the check for 'nearly full' of such a small queue."); - Index tmphead = modm::accessor::asVolatile(this->head); - Index tmptail = modm::accessor::asVolatile(this->tail); + Index tmphead = this->head; + Index tmptail = this->tail; Index free; if (tmphead >= tmptail) { @@ -61,7 +61,7 @@ template modm_always_inline bool modm::atomic::Queue::isEmpty() const { - return (modm::accessor::asVolatile(this->head) == modm::accessor::asVolatile(this->tail)); + return (this->head == this->tail); } template @@ -70,8 +70,8 @@ modm::atomic::Queue::isNearlyEmpty() const { static_assert(N > 3, "Not possible the check for 'nearly empty' of such a small queue. "); - Index tmphead = modm::accessor::asVolatile(this->head); - Index tmptail = modm::accessor::asVolatile(this->tail); + Index tmphead = this->head; + Index tmptail = this->tail; Index stored; if (tmphead >= tmptail) { @@ -107,7 +107,7 @@ modm::atomic::Queue::push(const T& value) if (tmphead >= (N+1)) { tmphead = 0; } - if (tmphead == modm::accessor::asVolatile(this->tail)) { + if (tmphead == this->tail) { return false; } else { diff --git a/src/modm/architecture/interface/accessor_flash.hpp b/src/modm/architecture/interface/accessor_flash.hpp.in similarity index 95% rename from src/modm/architecture/interface/accessor_flash.hpp rename to src/modm/architecture/interface/accessor_flash.hpp.in index 44b74be281..fce2903713 100644 --- a/src/modm/architecture/interface/accessor_flash.hpp +++ b/src/modm/architecture/interface/accessor_flash.hpp.in @@ -15,7 +15,9 @@ #define MODM_INTERFACE_ACCESSOR_FLASH_HPP #include +%% if with_io #include +%% endif #ifdef __DOXYGEN__ @@ -162,11 +164,12 @@ class Flash private: const T* address; - +%% if with_io private: template friend IOStream& operator << ( IOStream&, const Flash&); +%% endif }; /// Convert a normal pointer to a accessor::Flash @@ -182,9 +185,7 @@ asFlash(const T* ptr) } // namespace modm -// ----------------------------------------------------------------------------- -// IMPLEMENTATION -// ----------------------------------------------------------------------------- +%% if with_io template modm::IOStream& operator << (modm::IOStream& os, modm::accessor::Flash ptr); @@ -202,5 +203,6 @@ operator << (modm::IOStream& os, modm::accessor::Flash ptr) } return os; } +%% endif #endif // MODM_INTERFACE_ACCESSOR_FLASH_HPP diff --git a/src/modm/architecture/interface/can_message.cpp b/src/modm/architecture/interface/can_message.cpp.in similarity index 87% rename from src/modm/architecture/interface/can_message.cpp rename to src/modm/architecture/interface/can_message.cpp.in index c76fee2458..1e1b57a3a1 100644 --- a/src/modm/architecture/interface/can_message.cpp +++ b/src/modm/architecture/interface/can_message.cpp.in @@ -9,32 +9,7 @@ */ // ---------------------------------------------------------------------------- -#include -#include - -#include -#include -#include - -namespace modm -{ - -namespace can -{ - -modm::IOStream& -operator << (modm::IOStream& s, const modm::can::Message m) -{ - s.printf("id = %04" PRIx32 ", len = ", m.identifier); - s << m.length; - s.printf(", flags = %c%c, data = ", - m.flags.rtr ? 'R' : 'r', - m.flags.extended ? 'E' : 'e'); - for (uint_fast8_t ii = 0; ii < m.length; ++ii) { - s.printf("%02x ", m.data[ii]); - } - return s; -} +#include "can_message.hpp" bool modm::can::Message::operator == (const modm::can::Message& rhs) const @@ -55,5 +30,22 @@ modm::can::Message::operator == (const modm::can::Message& rhs) const } return false; } -} // can namespace -} // modm namespace + +%% if with_io +#include +#include + +modm::IOStream& +operator << (modm::IOStream& s, const modm::can::Message m) +{ + s.printf("id = %04" PRIx32 ", len = ", m.identifier); + s << m.length; + s.printf(", flags = %c%c, data = ", + m.flags.rtr ? 'R' : 'r', + m.flags.extended ? 'E' : 'e'); + for (uint_fast8_t ii = 0; ii < m.length; ++ii) { + s.printf("%02x ", m.data[ii]); + } + return s; +} +%% endif diff --git a/src/modm/architecture/interface/can_message.hpp b/src/modm/architecture/interface/can_message.hpp.in similarity index 96% rename from src/modm/architecture/interface/can_message.hpp rename to src/modm/architecture/interface/can_message.hpp.in index de8dfc3a11..c7f5958159 100644 --- a/src/modm/architecture/interface/can_message.hpp +++ b/src/modm/architecture/interface/can_message.hpp.in @@ -14,13 +14,9 @@ #define MODM_CAN_MESSAGE_HPP #include -#include #include -namespace modm -{ - -namespace can +namespace modm::can { /// Representation of a CAN message @@ -100,11 +96,13 @@ struct Message operator == (const modm::can::Message& rhs) const; }; -modm::IOStream& -operator << (modm::IOStream& s, const modm::can::Message m); +} // namespace modm::can -} // namespace can +%% if with_io +#include -} // namespace modm +modm::IOStream& +operator << (modm::IOStream& s, const modm::can::Message m); +%% endif #endif // MODM_CAN_MESSAGE_HPP diff --git a/src/modm/architecture/interface/i2c.cpp b/src/modm/architecture/interface/i2c.cpp index 3e52c9cf0b..c577b5054c 100644 --- a/src/modm/architecture/interface/i2c.cpp +++ b/src/modm/architecture/interface/i2c.cpp @@ -9,11 +9,7 @@ */ // ---------------------------------------------------------------------------- -#include -#include - -namespace modm -{ +#include "i2c.hpp" modm::IOStream& operator << (modm::IOStream& s, const modm::I2c::Operation op) @@ -49,4 +45,3 @@ operator << (modm::IOStream& s, const modm::I2c::OperationAfterWrite op) return s; } -} diff --git a/src/modm/architecture/interface/i2c.hpp b/src/modm/architecture/interface/i2c.hpp.in similarity index 99% rename from src/modm/architecture/interface/i2c.hpp rename to src/modm/architecture/interface/i2c.hpp.in index 6af7490210..8872fd67e0 100644 --- a/src/modm/architecture/interface/i2c.hpp +++ b/src/modm/architecture/interface/i2c.hpp.in @@ -17,7 +17,6 @@ #include #include -#include namespace modm { @@ -129,6 +128,11 @@ struct I2c }; +} // namespace modm + +%% if with_io +#include + modm::IOStream& operator << (modm::IOStream& s, const modm::I2c::Operation op); @@ -140,7 +144,6 @@ operator << (modm::IOStream& s, const modm::I2c::OperationAfterRead op); modm::IOStream& operator << (modm::IOStream& s, const modm::I2c::OperationAfterWrite op); - -} // namespace modm +%% endif #endif // MODM_INTERFACE_I2C_HPP diff --git a/src/modm/architecture/interface/register.hpp b/src/modm/architecture/interface/register.hpp.in similarity index 99% rename from src/modm/architecture/interface/register.hpp rename to src/modm/architecture/interface/register.hpp.in index efe0e7a817..47244d38b2 100644 --- a/src/modm/architecture/interface/register.hpp +++ b/src/modm/architecture/interface/register.hpp.in @@ -17,7 +17,9 @@ #include #include #include +%% if with_io #include +%% endif namespace modm { @@ -82,10 +84,12 @@ struct Register { return not bool(value); } /// @} +%% if with_io /// Printing a register will output its numeric value. friend IOStream& operator << (IOStream& s, const Register& m) { return (s << m.value); } +%% endif protected: /// This class is meant to be subclassed diff --git a/src/modm/architecture/module.lb b/src/modm/architecture/module.lb index 17eea11103..37a7652c00 100644 --- a/src/modm/architecture/module.lb +++ b/src/modm/architecture/module.lb @@ -17,14 +17,13 @@ class Accessor(Module): module.description = "Memory Accessors" def prepare(self, module, options): - module.depends(":io") - # module.depends(":platform:core") return True def build(self, env): + env.substitutions = {"with_io": env.has_module(":io")} env.outbasepath = "modm/src/modm/architecture" env.copy("interface/accessor.hpp") - env.copy("interface/accessor_flash.hpp") + env.template("interface/accessor_flash.hpp.in") env.copy("interface/accessor_ram.hpp") # ----------------------------------------------------------------------------- @@ -79,7 +78,6 @@ class Atomic(Module): module.description = "Atomic Operations and Containers" def prepare(self, module, options): - module.depends(":utils", ":architecture:accessor") return True def build(self, env): @@ -122,15 +120,15 @@ class Can(Module): module.description = FileReader("interface/can.md") def prepare(self, module, options): - module.depends(":io") return True def build(self, env): + env.substitutions = {"with_io": env.has_module(":io")} env.outbasepath = "modm/src/modm/architecture" env.copy("interface/can.hpp") env.copy("interface/can.cpp") - env.copy("interface/can_message.hpp") - env.copy("interface/can_message.cpp") + env.template("interface/can_message.hpp.in") + env.template("interface/can_message.cpp.in") # ----------------------------------------------------------------------------- class Clock(Module): @@ -154,7 +152,6 @@ class Delay(Module): module.description = FileReader("interface/delay.md") def prepare(self, module, options): - # module.depends(":platform:core") return True def build(self, env): @@ -196,13 +193,15 @@ class I2c(Module): module.description = "Inter-Integrated Circuit (I²C)" def prepare(self, module, options): - module.depends(":architecture:gpio", ":architecture:delay", ":io") + module.depends(":architecture:gpio", ":architecture:delay") return True def build(self, env): + env.substitutions = {"with_io": env.has_module(":io")} env.outbasepath = "modm/src/modm/architecture" - env.copy("interface/i2c.hpp") - env.copy("interface/i2c.cpp") + env.template("interface/i2c.hpp.in") + if env.has_module(":io"): + env.copy("interface/i2c.cpp") env.copy("interface/i2c_master.hpp") env.copy("interface/i2c_transaction.hpp") # ----------------------------------------------------------------------------- @@ -297,12 +296,13 @@ class Register(Module): module.description = FileReader("interface/register.md") def prepare(self, module, options): - module.depends(":io", ":math:utils") + module.depends(":math:utils") return True def build(self, env): + env.substitutions = {"with_io": env.has_module(":io")} env.outbasepath = "modm/src/modm/architecture" - env.copy("interface/register.hpp") + env.template("interface/register.hpp.in") # ----------------------------------------------------------------------------- class Spi(Module): @@ -353,7 +353,6 @@ class Unaligned(Module): module.description = "Unaligned Memory Accessor" def prepare(self, module, options): - # module.depends(":platform:core") return True def build(self, env): @@ -365,7 +364,7 @@ import re def init(module): module.name = ":architecture" - module.description = """\ + module.description = """ # Architecture Interfaces All hardware peripherals with common interfaces. diff --git a/src/modm/platform/can/stm32/can.cpp.in b/src/modm/platform/can/stm32/can.cpp.in index b1a8451c7a..71c3ff0611 100644 --- a/src/modm/platform/can/stm32/can.cpp.in +++ b/src/modm/platform/can/stm32/can.cpp.in @@ -20,6 +20,7 @@ #include #include #include +#include %% if id == "" #include "can.hpp" From cd053302db88585fb62a907b79ca8ce84b648cfa Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 27 Apr 2020 00:03:45 +0200 Subject: [PATCH 008/135] [gpio] Remove :platform:gpio.common module --- .../platform/gpio/at90_tiny_mega/module.lb | 6 +++- .../gpio/common/connector_detail.hpp.in | 4 +-- src/modm/platform/gpio/common/module.lb | 33 ------------------- src/modm/platform/gpio/hosted/module.lb | 3 +- src/modm/platform/gpio/stm32/module.lb | 5 ++- 5 files changed, 13 insertions(+), 38 deletions(-) delete mode 100644 src/modm/platform/gpio/common/module.lb diff --git a/src/modm/platform/gpio/at90_tiny_mega/module.lb b/src/modm/platform/gpio/at90_tiny_mega/module.lb index f267b5012a..3eae2a69e9 100644 --- a/src/modm/platform/gpio/at90_tiny_mega/module.lb +++ b/src/modm/platform/gpio/at90_tiny_mega/module.lb @@ -54,7 +54,7 @@ def prepare(module, options): return False # port.hpp depends on modm::bitReverse()! - module.depends(":architecture:gpio", ":platform:gpio.common", ":math:utils") + module.depends(":architecture:gpio", ":math:utils") return True bprops = {} @@ -154,3 +154,7 @@ def build(env): env.template("software_port.hpp.in") env.template("set.hpp.in") env.template("unused.hpp.in") + + env.copy("../common/inverted.hpp", "inverted.hpp") + env.copy("../common/connector.hpp", "connector.hpp") + env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") diff --git a/src/modm/platform/gpio/common/connector_detail.hpp.in b/src/modm/platform/gpio/common/connector_detail.hpp.in index c6b99b312f..aa6330b702 100644 --- a/src/modm/platform/gpio/common/connector_detail.hpp.in +++ b/src/modm/platform/gpio/common/connector_detail.hpp.in @@ -65,7 +65,7 @@ struct GpioSignalConnect; template< Peripheral peripheral, template class SignalT, template class... Signals > struct GpioSignalConnect { -%% if target["platform"] == "stm32" and target["family"] in ["f1", "l1"] +%% if target.family in ["f1", "l1"] static constexpr uint32_t id = GpioSignalConnect::id & SignalT::Groups; %% endif static inline void connect() @@ -82,7 +82,7 @@ struct GpioSignalConnect template< Peripheral peripheral > struct GpioSignalConnect { -%% if target["platform"] == "stm32" and target["family"] in ["f1", "l1"] +%% if target.family in ["f1", "l1"] static constexpr uint32_t id = uint32_t(-1); %% endif static inline void connect() {} diff --git a/src/modm/platform/gpio/common/module.lb b/src/modm/platform/gpio/common/module.lb deleted file mode 100644 index 7c2b8c8029..0000000000 --- a/src/modm/platform/gpio/common/module.lb +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# Copyright (c) 2016-2018, 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:gpio.common" - module.description = "GPIO Common" - -def prepare(module, options): - if not options[":target"].has_driver("gpio"): - return False - - module.depends( - ":architecture:gpio", - ":utils", - ) - return True - -def build(env): - env.substitutions = {"target": env[":target"].identifier} - env.outbasepath = "modm/src/modm/platform/gpio" - - env.template("connector_detail.hpp.in") - env.copy("connector.hpp") - env.copy("inverted.hpp") diff --git a/src/modm/platform/gpio/hosted/module.lb b/src/modm/platform/gpio/hosted/module.lb index ef08d15996..d4641c325c 100644 --- a/src/modm/platform/gpio/hosted/module.lb +++ b/src/modm/platform/gpio/hosted/module.lb @@ -21,7 +21,7 @@ def prepare(module, options): if not options[":target"].has_driver("gpio:hosted"): return False - module.depends(":architecture:gpio", ":platform:gpio.common") + module.depends(":architecture:gpio") return True @@ -31,3 +31,4 @@ def build(env): env.template("base.hpp.in") env.template("unused.hpp.in") + env.copy("../common/inverted.hpp", "inverted.hpp") diff --git a/src/modm/platform/gpio/stm32/module.lb b/src/modm/platform/gpio/stm32/module.lb index 9fd2d1b2e3..4f37814201 100644 --- a/src/modm/platform/gpio/stm32/module.lb +++ b/src/modm/platform/gpio/stm32/module.lb @@ -149,7 +149,6 @@ def prepare(module, options): ":architecture:gpio", ":cmsis:device", ":math:utils", - ":platform:gpio.common", ":platform:rcc") return True @@ -272,6 +271,10 @@ def build(env): if len(env["enable_ports"]): env.template("enable.cpp.in") + env.copy("../common/inverted.hpp", "inverted.hpp") + env.copy("../common/connector.hpp", "connector.hpp") + env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") + # FIXME: Move to modm:platform:core! env.outbasepath = "modm/src/modm/platform/core" env.template("peripherals.hpp.in") From 79e04904e89858b98ef202a3965007553cf9e487 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 27 Apr 2020 19:44:25 +0200 Subject: [PATCH 009/135] [ext] Update submodules --- README.md | 2 +- .../cmsis_dsp/linear_interp/main.cpp | 4 ---- ext/arm/cmsis | 2 +- ext/arm/core.lb | 5 ++++- ext/arm/dsp.lb | 20 +++++++++++++++++-- ext/modm-devices | 2 +- ext/st/stm32 | 2 +- src/modm/platform/adc/stm32f3/module.lb | 4 ++-- 8 files changed, 28 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 29fbf3f8b6..d0696dbeaf 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ git clone --recurse-submodules https://github.com/modm-io/modm.git ## Targets -modm can generate code for 530 AVR and 1857 +modm can generate code for 530 AVR and 1959 STM32 devices, however, there are different levels of support and testing.
diff --git a/examples/nucleo_f429zi/cmsis_dsp/linear_interp/main.cpp b/examples/nucleo_f429zi/cmsis_dsp/linear_interp/main.cpp index 0a01c24aa3..ce22a0931e 100644 --- a/examples/nucleo_f429zi/cmsis_dsp/linear_interp/main.cpp +++ b/examples/nucleo_f429zi/cmsis_dsp/linear_interp/main.cpp @@ -17,10 +17,6 @@ #define main arm_cmsis_dsp_example #include "../../../../ext/arm/cmsis/CMSIS/DSP/Examples/ARM/arm_linear_interp_example/math_helper.h" #include "../../../../ext/arm/cmsis/CMSIS/DSP/Examples/ARM/arm_linear_interp_example/math_helper.c" -/* `float arm_linear_interep_table[188495]` is 736kB large and MUST be placed in Flash - * memory NOT SRAM otherwise it won't fit on the device. Since we don't want (=cannot) - * to change the example, we need to take drastic measures... */ -#define float const float #include "../../../../ext/arm/cmsis/CMSIS/DSP/Examples/ARM/arm_linear_interp_example/arm_linear_interp_data.c" #define while return status; void #include "../../../../ext/arm/cmsis/CMSIS/DSP/Examples/ARM/arm_linear_interp_example/arm_linear_interp_example_f32.c" diff --git a/ext/arm/cmsis b/ext/arm/cmsis index 024649ebfe..6815863161 160000 --- a/ext/arm/cmsis +++ b/ext/arm/cmsis @@ -1 +1 @@ -Subproject commit 024649ebfeef05f4e0ea814f2c4a453504d13f8d +Subproject commit 681586316107feb5c9a24d5337c4da230dae33ec diff --git a/ext/arm/core.lb b/ext/arm/core.lb index 945c613c3c..2fe37579f7 100644 --- a/ext/arm/core.lb +++ b/ext/arm/core.lb @@ -27,7 +27,10 @@ def build(env): env.copy("cmsis/CMSIS/Core/Include/cmsis_version.h", "cmsis_version.h") env.copy("cmsis/CMSIS/Core/Include/cmsis_compiler.h", "cmsis_compiler.h") env.copy("cmsis/CMSIS/Core/Include/cmsis_gcc.h", "cmsis_gcc.h") - if "m0" not in core: + if core != "0": # 0+ has MPU support though! env.copy("cmsis/CMSIS/Core/Include/mpu_armv7.h", "mpu_armv7.h") + print(core) + if core == "7": + env.copy("cmsis/CMSIS/Core/Include/cachel1_armv7.h", "cachel1_armv7.h") env.collect(":build:path.include", "modm/ext/cmsis/core") diff --git a/ext/arm/dsp.lb b/ext/arm/dsp.lb index 8b4f5aee14..70b629b99d 100644 --- a/ext/arm/dsp.lb +++ b/ext/arm/dsp.lb @@ -66,13 +66,19 @@ class CmsisDspModule(Module): if not env.get(":cmsis:dsp:unaligned_data", False): metadata[":build:cppdefines"].append("UNALIGNED_SUPPORT_DISABLE") + if env.get(":cmsis:dsp:loop_unroll", False): + metadata[":build:cppdefines"].append("ARM_MATH_LOOPUNROLL") + env.outbasepath = "modm/ext/cmsis/dsp" + if "tables" in self.name: env.copy("cmsis/CMSIS/DSP/Include/arm_common_tables.h", "arm_common_tables.h") env.copy("cmsis/CMSIS/DSP/Include/arm_const_structs.h", "arm_const_structs.h") + if "support" in self.name: + env.copy("cmsis/CMSIS/DSP/PrivateInclude/arm_sorting.h", "arm_sorting.h") + operations = env.copy("cmsis/CMSIS/DSP/Source/{}".format(self.path), "{}".format(self.path), - ignore=env.ignore_files("arm_bitreversal2.c", "CMakeLists.txt", - "*Functions.c", "*Tables.c")) + ignore=env.ignore_files("arm_bitreversal2.c")) # For all sources add these compile flags for key, values in metadata.items(): @@ -105,6 +111,11 @@ def prepare(module, options): name="round_float_inputs", description=descr_round_floats, default=True)) + module.add_option( + BooleanOption( + name="loop_unroll", + description=descr_loop_unroll, + default=True)) for path in Path(localpath("cmsis/CMSIS/DSP/Source")).iterdir(): if path.is_dir(): @@ -139,3 +150,8 @@ descr_round_floats = """# Round float inputs Rounds float inputs properly during all conversions. """ + +descr_loop_unroll = """# Loop Unroll + +Enables manual loop unrolling in DSP functions. +""" diff --git a/ext/modm-devices b/ext/modm-devices index b5071a72d7..fdb70e01e8 160000 --- a/ext/modm-devices +++ b/ext/modm-devices @@ -1 +1 @@ -Subproject commit b5071a72d75244b1714676bf64044b0374c556dc +Subproject commit fdb70e01e8a5cd71a6293e5cd22900a89bb541dc diff --git a/ext/st/stm32 b/ext/st/stm32 index 0ced8ee40a..d417430e80 160000 --- a/ext/st/stm32 +++ b/ext/st/stm32 @@ -1 +1 @@ -Subproject commit 0ced8ee40a1489247ac87432a0cb42a5f0145978 +Subproject commit d417430e80a0d65f4048fd52df98f309ddfef25b diff --git a/src/modm/platform/adc/stm32f3/module.lb b/src/modm/platform/adc/stm32f3/module.lb index 8831bbd88b..c2843e47fa 100644 --- a/src/modm/platform/adc/stm32f3/module.lb +++ b/src/modm/platform/adc/stm32f3/module.lb @@ -99,10 +99,10 @@ class Instance(Module): properties["clock_mux"] = False elif target["family"] == "l4": properties["adc_ccr"] = "ADC_CCR" - if len(driver["instance"]) == 1: + if len(driver["instance"]) == 1 and "q5a" not in target.string: properties["id_common"] = "1" properties["id_common_u"] = "1_COMMON" - elif len(driver["instance"]) == 2: + elif len(driver["instance"]) == 2 or "q5a" in target.string: properties["id_common"] = "12" properties["id_common_u"] = "12_COMMON" else: From 5cadbf7c89c9a00b60511c962cbb00cfc5d0990a Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Tue, 28 Apr 2020 09:29:41 +0200 Subject: [PATCH 010/135] [can] Do not print data in Remote Transmission Request (RTR) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As I understood the spec (and as Wikipedia states), “in the Remote Frame there is no Data Field. The DLC field indicates the data length of the requested message (not the transmitted one)”. --- .../architecture/interface/can_message.cpp.in | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/modm/architecture/interface/can_message.cpp.in b/src/modm/architecture/interface/can_message.cpp.in index 1e1b57a3a1..dd0b0b89f2 100644 --- a/src/modm/architecture/interface/can_message.cpp.in +++ b/src/modm/architecture/interface/can_message.cpp.in @@ -38,14 +38,16 @@ modm::can::Message::operator == (const modm::can::Message& rhs) const modm::IOStream& operator << (modm::IOStream& s, const modm::can::Message m) { - s.printf("id = %04" PRIx32 ", len = ", m.identifier); - s << m.length; - s.printf(", flags = %c%c, data = ", - m.flags.rtr ? 'R' : 'r', - m.flags.extended ? 'E' : 'e'); - for (uint_fast8_t ii = 0; ii < m.length; ++ii) { - s.printf("%02x ", m.data[ii]); - } - return s; + s.printf("id = %04" PRIx32 ", len = ", m.identifier); + s << m.length; + s.printf(", flags = %c%c, data = ", + m.flags.rtr ? 'R' : 'r', + m.flags.extended ? 'E' : 'e'); + if (not m.isRemoteTransmitRequest()) { + for (uint_fast8_t ii = 0; ii < m.length; ++ii) { + s.printf("%02x ", m.data[ii]); + } + } + return s; } %% endif From 69a531b8a843904769c6cea29f284dc91f3383e0 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Tue, 28 Apr 2020 09:30:26 +0200 Subject: [PATCH 011/135] [example] Use integrated CAN message print method --- examples/stm32f103c8t6_blue_pill/can/main.cpp | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/examples/stm32f103c8t6_blue_pill/can/main.cpp b/examples/stm32f103c8t6_blue_pill/can/main.cpp index ec3482914f..88fd6b18ca 100644 --- a/examples/stm32f103c8t6_blue_pill/can/main.cpp +++ b/examples/stm32f103c8t6_blue_pill/can/main.cpp @@ -24,29 +24,9 @@ displayMessage(const modm::can::Message& message, const uint8_t& filter_id) { static uint32_t receiveCounter = 0; receiveCounter++; - MODM_LOG_INFO<< "filter_id =" << filter_id; - MODM_LOG_INFO<< "id =" << message.getIdentifier(); - if (message.isExtended()) { - MODM_LOG_INFO<< " extended"; - } - else { - MODM_LOG_INFO<< " standard"; - } - if (message.isRemoteTransmitRequest()) { - MODM_LOG_INFO<< ", rtr"; - } - MODM_LOG_INFO<< modm::endl; - - MODM_LOG_INFO<< "dlc =" << message.getLength() << modm::endl; - if (!message.isRemoteTransmitRequest()) - { - MODM_LOG_INFO << "data="; - for (uint32_t i = 0; i < message.getLength(); ++i) { - MODM_LOG_INFO<< modm::hex << message.data[i] << modm::ascii << ' '; - } - MODM_LOG_INFO<< modm::endl; - } - MODM_LOG_INFO<< "# received=" << receiveCounter << modm::endl; + MODM_LOG_INFO << "filter_id = " << filter_id << ", "; + MODM_LOG_INFO << message << modm::endl; + MODM_LOG_INFO << "# received=" << receiveCounter << modm::endl; } // ---------------------------------------------------------------------------- From 8546bcb491edce4746e4c64ccefc4f0dcd1adc79 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Tue, 28 Apr 2020 14:50:18 +0200 Subject: [PATCH 012/135] Remove zeromq as it is not maintained anymore and broken --- .circleci/config.yml | 2 +- docs/src/guide/installation.md | 4 +- .../basic/component_receiver/receiver.cpp | 53 ------ .../basic/component_receiver/receiver.hpp | 45 ------ .../xpcc/basic/component_sender/sender.cpp | 63 -------- .../xpcc/basic/component_sender/sender.hpp | 44 ----- examples/linux/xpcc/basic/main.cpp | 71 -------- examples/linux/xpcc/basic/project.xml | 15 -- .../1_stm32/component_odometry/odometry.cpp | 56 ------- .../1_stm32/component_odometry/odometry.hpp | 40 ----- examples/zmq/1_stm32/main.cpp | 119 -------------- examples/zmq/1_stm32/project.xml | 17 -- examples/zmq/2_zmq_gateway/main.cpp | 114 ------------- examples/zmq/2_zmq_gateway/project.xml | 15 -- examples/zmq/3_zmq_app/component_gui/gui.cpp | 57 ------- examples/zmq/3_zmq_app/component_gui/gui.hpp | 36 ----- examples/zmq/3_zmq_app/main.cpp | 58 ------- examples/zmq/3_zmq_app/project.xml | 15 -- examples/zmq/3_zmq_app/sub.py | 27 ---- examples/zmq/4_zmq_backtoback/main.cpp | 76 --------- examples/zmq/4_zmq_backtoback/project.xml | 14 -- examples/zmq/README.md | 71 -------- .../communication/xpcc/backend/zeromq.hpp | 15 -- .../xpcc/backend/zeromq/connector.cpp | 146 ----------------- .../xpcc/backend/zeromq/connector.hpp | 85 ---------- .../xpcc/backend/zeromq/reader.cpp | 151 ------------------ .../xpcc/backend/zeromq/reader.hpp | 100 ------------ src/modm/communication/xpcc/module.lb | 9 -- 28 files changed, 3 insertions(+), 1515 deletions(-) delete mode 100644 examples/linux/xpcc/basic/component_receiver/receiver.cpp delete mode 100644 examples/linux/xpcc/basic/component_receiver/receiver.hpp delete mode 100644 examples/linux/xpcc/basic/component_sender/sender.cpp delete mode 100644 examples/linux/xpcc/basic/component_sender/sender.hpp delete mode 100644 examples/linux/xpcc/basic/main.cpp delete mode 100644 examples/linux/xpcc/basic/project.xml delete mode 100644 examples/zmq/1_stm32/component_odometry/odometry.cpp delete mode 100644 examples/zmq/1_stm32/component_odometry/odometry.hpp delete mode 100644 examples/zmq/1_stm32/main.cpp delete mode 100644 examples/zmq/1_stm32/project.xml delete mode 100644 examples/zmq/2_zmq_gateway/main.cpp delete mode 100644 examples/zmq/2_zmq_gateway/project.xml delete mode 100644 examples/zmq/3_zmq_app/component_gui/gui.cpp delete mode 100644 examples/zmq/3_zmq_app/component_gui/gui.hpp delete mode 100644 examples/zmq/3_zmq_app/main.cpp delete mode 100644 examples/zmq/3_zmq_app/project.xml delete mode 100755 examples/zmq/3_zmq_app/sub.py delete mode 100644 examples/zmq/4_zmq_backtoback/main.cpp delete mode 100644 examples/zmq/4_zmq_backtoback/project.xml delete mode 100644 examples/zmq/README.md delete mode 100644 src/modm/communication/xpcc/backend/zeromq.hpp delete mode 100644 src/modm/communication/xpcc/backend/zeromq/connector.cpp delete mode 100644 src/modm/communication/xpcc/backend/zeromq/connector.hpp delete mode 100644 src/modm/communication/xpcc/backend/zeromq/reader.cpp delete mode 100644 src/modm/communication/xpcc/backend/zeromq/reader.hpp diff --git a/.circleci/config.yml b/.circleci/config.yml index a49a6694b3..83121040d4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,7 +46,7 @@ jobs: - run: name: Linux Examples command: | - (cd examples && ../tools/scripts/examples_compile.py linux zmq) + (cd examples && ../tools/scripts/examples_compile.py linux) - run: name: Generic Examples command: | diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md index 19ef9f0985..713859b0f4 100644 --- a/docs/src/guide/installation.md +++ b/docs/src/guide/installation.md @@ -64,7 +64,7 @@ For a less intrusive way to run all scons scripts with Python 3 add this to your To compile modm *for macOS* (and not the embedded target) you need to install some of these libraries as well, depending on what modm modules you use: - brew install boost zmqpp gcc + brew install boost gcc ## Linux @@ -102,7 +102,7 @@ We recommend the use of a graphical frontend for GDB called [gdbgui][]: To compile modm *for Linux* (and not the embedded target) you need to install some of these libraries as well, depending on what modm modules you use: - sudo apt-get install gcc build-essential libboost-all-dev libzmqpp-dev + sudo apt-get install gcc build-essential libboost-all-dev ## Windows diff --git a/examples/linux/xpcc/basic/component_receiver/receiver.cpp b/examples/linux/xpcc/basic/component_receiver/receiver.cpp deleted file mode 100644 index f487ffd0bd..0000000000 --- a/examples/linux/xpcc/basic/component_receiver/receiver.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2012, 2016, Sascha Schade - * Copyright (c) 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 - -// set new log level -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::DEBUG - -#include "receiver.hpp" - -// ---------------------------------------------------------------------------- -component::Receiver::Receiver(uint8_t id, xpcc::Dispatcher &communication) : - xpcc::AbstractComponent(id, communication) -{ -} - -// ---------------------------------------------------------------------------- -void -component::Receiver::actionSetPosition(const xpcc::ResponseHandle&, - const robot::packet::Position *parameter) -{ - MODM_LOG_INFO << MODM_FILE_INFO - << "action set position: x=" << parameter->x - << ", y=" << parameter->y << modm::endl; - - position = *parameter; -} - -// ---------------------------------------------------------------------------- -void -component::Receiver::actionGetPosition(const xpcc::ResponseHandle& handle) -{ - MODM_LOG_INFO << MODM_FILE_INFO << "action get position" << modm::endl; - - this->sendResponse(handle, position); -} - -// ---------------------------------------------------------------------------- -void -component::Receiver::update() -{ -} diff --git a/examples/linux/xpcc/basic/component_receiver/receiver.hpp b/examples/linux/xpcc/basic/component_receiver/receiver.hpp deleted file mode 100644 index 824574b1c2..0000000000 --- a/examples/linux/xpcc/basic/component_receiver/receiver.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2009, 2011, Georgi Grinshpun - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012-2013, 2016, Sascha Schade - * Copyright (c) 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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef COMPONENT_RECEIVER_HPP -#define COMPONENT_RECEIVER_HPP - -#include - -#include - -namespace component -{ - class Receiver : public xpcc::AbstractComponent - { - public: - Receiver(uint8_t id, xpcc::Dispatcher &communication); - - void - actionSetPosition(const xpcc::ResponseHandle& handle, - const robot::packet::Position *parameter); - - void - actionGetPosition(const xpcc::ResponseHandle& handle); - - void - update(); - - private: - robot::packet::Position position; - }; -} - -#endif // COMPONENT_RECEIVER_HPP diff --git a/examples/linux/xpcc/basic/component_sender/sender.cpp b/examples/linux/xpcc/basic/component_sender/sender.cpp deleted file mode 100644 index 9300be9f8a..0000000000 --- a/examples/linux/xpcc/basic/component_sender/sender.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2012, 2016, Sascha Schade - * Copyright (c) 2015, 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 - -// set new log level -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::DEBUG - -#include - -#include "sender.hpp" -using namespace std::chrono_literals; - -// ---------------------------------------------------------------------------- -component::Sender::Sender(uint8_t id, xpcc::Dispatcher &communication) : - xpcc::AbstractComponent(id, communication), - positionCallback(this, &Sender::getPositionCallback), - timer(2s) -{ -} - -// ---------------------------------------------------------------------------- -void -component::Sender::getPositionCallback(const xpcc::Header&, - const robot::packet::Position *parameter) -{ - MODM_LOG_INFO << MODM_FILE_INFO - << "get position callback: x=" << parameter->x - << ", y=" << parameter->y << modm::endl; -} - -// ---------------------------------------------------------------------------- -void -component::Sender::update() -{ - if (timer.execute()) - { - MODM_LOG_INFO << MODM_FILE_INFO << "sender update" << modm::endl; - - robot::packet::Position position(10, 20); - - this->callAction( - robot::component::RECEIVER, - robot::action::SET_POSITION, - position); - - this->callAction( - robot::component::RECEIVER, - robot::action::GET_POSITION, - positionCallback); - } -} diff --git a/examples/linux/xpcc/basic/component_sender/sender.hpp b/examples/linux/xpcc/basic/component_sender/sender.hpp deleted file mode 100644 index 74db449ff5..0000000000 --- a/examples/linux/xpcc/basic/component_sender/sender.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2009, 2011, Georgi Grinshpun - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012-2013, 2016, Sascha Schade - * Copyright (c) 2012, 2015, 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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef COMPONENT_SENDER_HPP -#define COMPONENT_SENDER_HPP - -#include -#include - -#include - -namespace component -{ - class Sender : public xpcc::AbstractComponent - { - public: - Sender(uint8_t id, xpcc::Dispatcher &communication); - - void - update(); - - private: - void - getPositionCallback(const xpcc::Header& header, - const robot::packet::Position *parameter); - - xpcc::ResponseCallback positionCallback; - modm::ShortPeriodicTimer timer; - }; -} - -#endif // COMPONENT_SENDER_HPP diff --git a/examples/linux/xpcc/basic/main.cpp b/examples/linux/xpcc/basic/main.cpp deleted file mode 100644 index 562007436b..0000000000 --- a/examples/linux/xpcc/basic/main.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2011, Fabian Greif - * Copyright (c) 2013-2014, 2016, Sascha Schade - * Copyright (c) 2014, 2016-2018, 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 std::chrono_literals; - -#include -// #include -#include - -#include - -// set new log level -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::DEBUG - -#include "component_receiver/receiver.hpp" -#include "component_sender/sender.hpp" - -#include -#include - -// Use TIPC on Linux only -// xpcc::TipcConnector connector; - -// Use ZeroMQ on Linux and Darwin -const std::string endpointIn = "tcp://127.0.0.1:8211"; -const std::string endpointOut = "tcp://127.0.0.1:8212"; -static xpcc::ZeroMQConnector connector(endpointIn, endpointOut, xpcc::ZeroMQConnector::Mode::SubPush); - -// create an instance of the generated postman -Postman postman; - -xpcc::Dispatcher dispatcher(&connector, &postman); - -namespace component -{ - Sender sender(robot::component::SENDER, dispatcher); - Receiver receiver(robot::component::RECEIVER, dispatcher); -} - -int -main() -{ - // Required for TIPC on Linux only - // connector.addReceiverId(robot::component::SENDER); - // connector.addReceiverId(robot::component::RECEIVER); - - MODM_LOG_INFO << "Welcome to the communication test!" << modm::endl; - - while (true) - { - // deliver received messages - dispatcher.update(); - - component::receiver.update(); - component::sender.update(); - - modm::delay(100us); - } -} diff --git a/examples/linux/xpcc/basic/project.xml b/examples/linux/xpcc/basic/project.xml deleted file mode 100644 index fe61e03c84..0000000000 --- a/examples/linux/xpcc/basic/project.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - modm:architecture:delay - modm:platform:core - modm:communication:xpcc:generator - modm:debug - modm:build:scons - - diff --git a/examples/zmq/1_stm32/component_odometry/odometry.cpp b/examples/zmq/1_stm32/component_odometry/odometry.cpp deleted file mode 100644 index 90b6bf78d7..0000000000 --- a/examples/zmq/1_stm32/component_odometry/odometry.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2016, Sascha Schade - * Copyright (c) 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 - -// set new log level -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::DISABLED - -#include -#include -#include - -#include "odometry.hpp" - -#include - -// ---------------------------------------------------------------------------- -component::Odometry::Odometry(uint8_t id, xpcc::Dispatcher &communication) : - xpcc::AbstractComponent(id, communication), - timer(50ms) -{ -} - - -// ---------------------------------------------------------------------------- -void -component::Odometry::update() -{ - if (timer.execute()) - { - MODM_LOG_INFO << MODM_FILE_INFO << "Odometry update" << modm::endl; - - robot::packet::Location location(Timer1::getValue(), 11, 0.5); - - robot::EventPublisher::robotLocation(getCommunicator(), location); - } -} - -void -component::Odometry::actionSetLedRed( - const xpcc::ResponseHandle& responseHandle, - const robot::packet::Bool *payload) -{ - GpioOutputB14::set(*payload); - sendResponse(responseHandle); -} diff --git a/examples/zmq/1_stm32/component_odometry/odometry.hpp b/examples/zmq/1_stm32/component_odometry/odometry.hpp deleted file mode 100644 index e316211783..0000000000 --- a/examples/zmq/1_stm32/component_odometry/odometry.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2016, Sascha Schade - * Copyright (c) 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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef COMPONENT_ODOMETRY_HPP -#define COMPONENT_ODOMETRY_HPP - -#include -#include - -#include - -namespace component -{ - class Odometry : public xpcc::AbstractComponent - { - public: - Odometry(uint8_t id, xpcc::Dispatcher &communication); - - void - update(); - - public: - // Callback for modm - void actionSetLedRed(const xpcc::ResponseHandle& responseHandle, const robot::packet::Bool *payload); - - private: - modm::ShortPeriodicTimer timer; - }; -} - -#endif // COMPONENT_ODOMETRY_HPP diff --git a/examples/zmq/1_stm32/main.cpp b/examples/zmq/1_stm32/main.cpp deleted file mode 100644 index 7f1478fb22..0000000000 --- a/examples/zmq/1_stm32/main.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2016, Sascha Schade - * Copyright (c) 2017-2018, 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 - -#include - -#include -#include - -#include "component_odometry/odometry.hpp" - -#include -#include - -using namespace Board; - -namespace Led -{ - using B = LedBlue; - using G = LedGreen; - using R = LedRed; -} - -/* - * Periodically publish an MODM message on the CAN bus. - * If it is longer than eight bytes it will fragmented and creates more than one CAN frame. - * - * Used in the ZeroMQ Example to generate MODM Odometry messages on the CAN bus. - * - * When rotating the encoder the x coordinate of the odometry message changes. - * - * Connect an Encoder: - * * GND to GND - * * A to E9 - * * B to E11 - * - * Connect a CAN transceiver - * * CTX to B9 - * * CRX to B8 - * - * CAN baud rate is 125 kBits - */ - -static Can1 device; - -// CanConnector does the fragmentation and defragmentation of xpcc messages to and from CAN frames. -static xpcc::CanConnector< Can1 > connector(&device); - -// create an instance of the generated postman -Postman postman; - -xpcc::Dispatcher dispatcher(&connector, &postman); - -namespace component -{ - Odometry odometry(robot::component::ODOMETRY, dispatcher); -} - -modm::ShortPeriodicTimer tmr(50ms); - -int -main() -{ - Board::initialize(); - - // Led::R::setOutput(); - // Led::G::setOutput(); - // Led::B::setOutput(); - - using Timer = Timer1; - using ChannelA = GpioInputE9; - using ChannelB = GpioInputE11; - auto ChannelAInputType = Gpio::InputType::PullUp; - auto ChannelBInputType = Gpio::InputType::PullUp; - - // Initialze the encoder - Timer::enable(); - Timer::setMode(Timer::Mode::UpCounter, Timer::SlaveMode::Encoder3); - // Overflow must be 16bit because else a lot of our motor control code will break! - Timer::setOverflow(0xffff); - ChannelA::setInput(ChannelAInputType); - ChannelB::setInput(ChannelBInputType); - Timer::connect(); - Timer::start(); - - // Initialize Can1 - Can1::connect(Gpio::InputType::PullUp); - Can1::initialize(10); - CanFilter::setFilter(0, CanFilter::FIFO0, - CanFilter::ExtendedIdentifier(0), - CanFilter::ExtendedFilterMask(0)); - - - LedGreen::set(); - - while (true) - { - dispatcher.update(); - component::odometry.update(); - - if (tmr.execute()) { - LedOrange::toggle(); - } - - modm::delay(100us); - } - - return 0; -} diff --git a/examples/zmq/1_stm32/project.xml b/examples/zmq/1_stm32/project.xml deleted file mode 100644 index f5c43a0add..0000000000 --- a/examples/zmq/1_stm32/project.xml +++ /dev/null @@ -1,17 +0,0 @@ - - modm:disco-f407vg - - - - - - - modm:communication:xpcc:generator - modm:platform:can:1 - modm:platform:gpio - modm:platform:heap - modm:platform:timer:1 - modm:processing:timer - modm:build:scons - - diff --git a/examples/zmq/2_zmq_gateway/main.cpp b/examples/zmq/2_zmq_gateway/main.cpp deleted file mode 100644 index 0dd9610bad..0000000000 --- a/examples/zmq/2_zmq_gateway/main.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2016-2017, Sascha Schade - * Copyright (c) 2017-2018, 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 -#include - -#include -#include -// #include - -#include - -#include -#include -using namespace modm::literals; -using namespace std::chrono_literals; - -/** - * Listens to a CAN bus connected by a CAN2USB and publishes modm messages with zeromq. - * - * How to use: - * - Connect a CAN2USB to a CAN bus with traffic. - * - Connect this CAN2USB by USB to your computer. - * - Make sure the user can access the /dev/ttyUSB0 device. - * Brute force method is: #sudo chmod 777 /dev/ttyUSB0 - * A wiser method is to add the user the appropriate group - * - Adjust the baud rate of the CAN bus in this example. - * - Do - * scons run - * - All modm messages will be published on port 8211 by zeromq - */ - -// Default CAN bitrate -static constexpr modm::bitrate_t canBusBitRate = 125_kbps; - -/* Either use an USB CAN2USB adaptor with modm Lawicel interpreter - or use a CAN controller supported by Linux' SocketCAN. - - With SocketCAN the baudrate must be set with the operating system. - $ ip link set can0 type can bitrate -*/ -modm::platform::SerialInterface port("/dev/ttyUSB0", 115200); -using CanUsb = modm::platform::CanUsb; -CanUsb canUsb(port); -// static SocketCan canSocket; - -static xpcc::CanConnector< CanUsb > canConnector(&canUsb); -// static xpcc::CanConnector< SocketCan > canConnector(&canSocket); - -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::DEBUG - -int -main() -{ - MODM_LOG_DEBUG << "ZeroMQ SocketCAN MODM bridge" << modm::endl; - - if (not canUsb.open(canBusBitRate)) { - // if (not canSocket.open("can0" /*, canBusBitRate */)) { - MODM_LOG_ERROR << "Could not open port" << modm::endl; - exit(EXIT_FAILURE); - } - - const std::string endpointOut = "tcp://*:8211"; - const std::string endpointIn = "tcp://*:8212"; - - xpcc::ZeroMQConnector zmqConnector(endpointIn, endpointOut, xpcc::ZeroMQConnector::Mode::PubPull); - - MODM_LOG_DEBUG << "Entering main loop" << modm::endl; - - while(true) - { - canConnector.update(); - zmqConnector.update(); - - while (canConnector.isPacketAvailable()) - { - xpcc::Header header = canConnector.getPacketHeader(); - modm::SmartPointer payload = canConnector.getPacketPayload(); - - MODM_LOG_DEBUG << "C->Z " << header << " " << payload.getSize() << " " << payload << modm::endl; - - zmqConnector.sendPacket(header, payload); - - canConnector.dropPacket(); - } - - while (zmqConnector.isPacketAvailable()) - { - xpcc::Header header = zmqConnector.getPacketHeader(); - modm::SmartPointer payload = zmqConnector.getPacketPayload(); - - MODM_LOG_DEBUG << "Z->C " << header << " " << payload.getSize() << " " << payload << modm::endl; - - canConnector.sendPacket(header, payload); - - zmqConnector.dropPacket(); - } - - modm::delay(10ms); - } - - canUsb.close(); - // canSocket.close(); -} diff --git a/examples/zmq/2_zmq_gateway/project.xml b/examples/zmq/2_zmq_gateway/project.xml deleted file mode 100644 index e6a90fd1e9..0000000000 --- a/examples/zmq/2_zmq_gateway/project.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - modm:architecture:delay - modm:communication:xpcc - modm:debug - modm:platform:canusb - modm:platform:core - modm:platform:uart - modm:build:scons - - diff --git a/examples/zmq/3_zmq_app/component_gui/gui.cpp b/examples/zmq/3_zmq_app/component_gui/gui.cpp deleted file mode 100644 index fb5e0fc77a..0000000000 --- a/examples/zmq/3_zmq_app/component_gui/gui.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2016, Sascha Schade - * Copyright (c) 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 - -#include -#include -#include - -#include "gui.hpp" - -// set new log level -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::DEBUG - -// ---------------------------------------------------------------------------- -component::Gui::Gui(uint8_t id, xpcc::Dispatcher &communication) : - xpcc::AbstractComponent(id, communication) -{ -} - - -// ---------------------------------------------------------------------------- -void -component::Gui::update() -{ -} - -// ---------------------------------------------------------------------------- -void -component::Gui::eventRobotLocation(xpcc::Header /* header */, const robot::packet::Location * const location) -{ - MODM_LOG_DEBUG << MODM_FILE_INFO << *location << modm::endl; - // MODM_LOG_DEBUG.printf("Payload: x = %5d, y = %d, phi = %4.2f\n", location->x, location->y, location->phi); - - static bool positive = false; - - bool pos_new = (location->x > 0); - if (positive xor pos_new) - { - MODM_LOG_DEBUG << "State change to " << pos_new << modm::endl; - - positive = pos_new; - robot::packet::Bool b(positive); - - robot::Odometry::setLedRed(getCommunicator(), b); - } -} diff --git a/examples/zmq/3_zmq_app/component_gui/gui.hpp b/examples/zmq/3_zmq_app/component_gui/gui.hpp deleted file mode 100644 index 23a4a17ade..0000000000 --- a/examples/zmq/3_zmq_app/component_gui/gui.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2016, Sascha Schade - * Copyright (c) 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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef COMPONENT_GUI_HPP -#define COMPONENT_GUI_HPP - -#include -#include - -#include - -namespace component -{ - class Gui : public xpcc::AbstractComponent - { - public: - Gui(uint8_t id, xpcc::Dispatcher &communication); - - void - update(); - - void - eventRobotLocation(xpcc::Header header, const robot::packet::Location * const payload); - }; -} - -#endif // COMPONENT_GUI_HPP diff --git a/examples/zmq/3_zmq_app/main.cpp b/examples/zmq/3_zmq_app/main.cpp deleted file mode 100644 index edc6a91e0d..0000000000 --- a/examples/zmq/3_zmq_app/main.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2016, Sascha Schade - * Copyright (c) 2017-2018, 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 -#include - -#include - -#include "component_gui/gui.hpp" - -#include -#include -using namespace std::chrono_literals; - -/** - * Simple subscriber with zeromq. - * - * The MODM Header and Payload is reconstructed from the zeromq message. - */ - -// set new log level -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::DEBUG - -const std::string endpointIn = "tcp://127.0.0.1:8211"; -const std::string endpointOut = "tcp://127.0.0.1:8212"; -static xpcc::ZeroMQConnector connector(endpointIn, endpointOut, xpcc::ZeroMQConnector::Mode::SubPush); - -// create an instance of the generated postman -Postman postman; - -xpcc::Dispatcher dispatcher(&connector, &postman); - -namespace component -{ - Gui gui(robot::component::GUI, dispatcher); -} - -int -main() -{ - while(true) - { - dispatcher.update(); - component::gui.update(); - - modm::delay(25ms); - } -} diff --git a/examples/zmq/3_zmq_app/project.xml b/examples/zmq/3_zmq_app/project.xml deleted file mode 100644 index e203917995..0000000000 --- a/examples/zmq/3_zmq_app/project.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - modm:architecture:delay - modm:communication:xpcc:generator - modm:debug - modm:platform:core - modm:build:scons - - diff --git a/examples/zmq/3_zmq_app/sub.py b/examples/zmq/3_zmq_app/sub.py deleted file mode 100755 index 34095c8268..0000000000 --- a/examples/zmq/3_zmq_app/sub.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# Copyright (c) 2016, Sascha Schade -# -# 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/. -# ----------------------------------------------------------------------------- - -import time -import zmq - -def subscriber(): - ctx = zmq.Context() - zmq_socket = ctx.socket(zmq.SUB) - zmq_socket.connect('tcp://127.0.0.1:8211') - zmq_socket.setsockopt(zmq.SUBSCRIBE, '') - - while True: - string = zmq_socket.recv() - print('Subscriber received: >>%s<<' % string.encode("hex")) - -if __name__ == "__main__": - subscriber() diff --git a/examples/zmq/4_zmq_backtoback/main.cpp b/examples/zmq/4_zmq_backtoback/main.cpp deleted file mode 100644 index 251e9cb568..0000000000 --- a/examples/zmq/4_zmq_backtoback/main.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2016, Sascha Schade - * Copyright (c) 2017-2018, 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 -#include -#include - -#include -using namespace std::chrono_literals; - -modm::PeriodicTimer pt(2s); - -int -main() -{ - const std::string endpointOut = "tcp://127.0.0.1:8211"; - const std::string endpointIn = "tcp://127.0.0.1:8212"; - - xpcc::ZeroMQConnector zmqConnectorServer(endpointIn, endpointOut, xpcc::ZeroMQConnector::Mode::PubPull); - xpcc::ZeroMQConnector zmqConnectorClient(endpointOut, endpointIn, xpcc::ZeroMQConnector::Mode::SubPush); - - while(true) - { - zmqConnectorServer.update(); - zmqConnectorClient.update(); - - if (zmqConnectorServer.isPacketAvailable()) - { - xpcc::Header header = zmqConnectorServer.getPacketHeader(); - modm::SmartPointer payload = zmqConnectorServer.getPacketPayload(); - - MODM_LOG_DEBUG << "Server Received Header is: " << header << modm::endl; - MODM_LOG_DEBUG << "Server Received Payload size is: " << payload.getSize() << modm::endl; - MODM_LOG_DEBUG << "Server Received Payload is: " << payload << modm::endl; - - // zmqConnector.sendPacket(header, payload); - - zmqConnectorServer.dropPacket(); - } - - if (zmqConnectorClient.isPacketAvailable()) - { - xpcc::Header header = zmqConnectorClient.getPacketHeader(); - modm::SmartPointer payload = zmqConnectorClient.getPacketPayload(); - - MODM_LOG_DEBUG << "Client Received Header is: " << header << modm::endl; - MODM_LOG_DEBUG << "Client Received Payload size is: " << payload.getSize() << modm::endl; - MODM_LOG_DEBUG << "Client Received Payload is: " << payload << modm::endl; - - // zmqConnector.sendPacket(header, payload); - - zmqConnectorClient.dropPacket(); - } - - if (pt.execute()) - { - xpcc::Header header; - - uint8_t buf[] = { 0xde, 0xad, 0xbe, 0xef}; - modm::SmartPointer payload(&buf); - - zmqConnectorServer.sendPacket(header, payload); - } - - modm::delay(100ms); - } -} diff --git a/examples/zmq/4_zmq_backtoback/project.xml b/examples/zmq/4_zmq_backtoback/project.xml deleted file mode 100644 index 2cb35df771..0000000000 --- a/examples/zmq/4_zmq_backtoback/project.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - modm:architecture:delay - modm:communication:xpcc - modm:debug - modm:platform:core - modm:processing:timer - modm:build:scons - - diff --git a/examples/zmq/README.md b/examples/zmq/README.md deleted file mode 100644 index 4ee551a2a4..0000000000 --- a/examples/zmq/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Simple ZeroMQ example with MODM - -## Setup - - * STM32F4 Discovery Board - * CAN transceiver breakout board with SN65HVD230 - * CAN2USB - -Connect the CAN2USB to your computer and to the CAN transceiver. - -## STM32 MODM publisher - -Flash `1_stm32` on the STM32F4 Discovery Board - - cd 1_stm32 - scons program - -The board will periodically send MODM message over the CAN bus to the CAN2USB. The RX LED of CAN2USB should toggle every 1.25 seconds. - -## ZeroMQ Gateway on PC - -Start ZeroMQ Gateway on PC - - cd 2_zmq_gateway - scons - -This will connect to the CAN2USB and receives the CAN frames, reassembles them with MODM CAN Connector and creates MODM Header and MODM Payload. This data is printed on the console. You may need to update the hardcoded USB device (`/dev/tty.usbserial-14112`). - - Debug: Payload is: 0xDEADBEEF112233445566778802 - Debug: Header is: (t=0,a=false,d=05,s=07,i=0B) - Debug: Payload size is: 13 - -The MODM Header and MODM Payload is then manually serialised into a ZeroMQ message and published at port 8211 on localhost. - -The application is polling the USB which needs a lot of CPU. - -Now run as many ZeroMQ subscribers as you like - -## Simple Python Subscriber - -There is a simple Python subscriber - - cd 3_zmq_app - python sub.py - -The subscriber will just display the raw ZeroMQ message: - - Subscriber received: >>0005070bdeadbeef112233445566778811<< - -## C++ MODM subscriber - -There is a C++ MODM subscriber - - cd 3_zmq_app - scons - -It will parse the ZeroMQ message into MODM Header and MODM Payload. - - Debug: 0MQ size is 17 - Debug: Header is: (t=0,a=false,d=05,s=07,i=0B) - Debug: Payload size is: 13 - Debug: Payload is: 0xDEADBEEF112233445566778819 - -## ToDo - -* Add a back channel with `PUSH/PULL` ports in ZeroMQ - * With `PUSH/PULL` the Gateway will open a TCP socket. That avoids potential problems with firewalls. - * Any client then can send MODM messages to the CAN bus. The MODM messages will be fragmented into CAN frames on the Gatway. -* Use MODM communication packets from XML definitions. -* Use the MODM Postman and Dispatcher -* Add a sensor (rotary switch / odometry) to the STM32 to simulate odometry data. diff --git a/src/modm/communication/xpcc/backend/zeromq.hpp b/src/modm/communication/xpcc/backend/zeromq.hpp deleted file mode 100644 index 671d043d5f..0000000000 --- a/src/modm/communication/xpcc/backend/zeromq.hpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2009-2010, 2014, Martin Rosekeit - * Copyright (c) 2012, 2014, Niklas Hauser - * Copyright (c) 2016, Sascha Schade - * - * 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 "zeromq/connector.hpp" diff --git a/src/modm/communication/xpcc/backend/zeromq/connector.cpp b/src/modm/communication/xpcc/backend/zeromq/connector.cpp deleted file mode 100644 index 1c34b5c8d6..0000000000 --- a/src/modm/communication/xpcc/backend/zeromq/connector.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// coding: utf-8 -/* - * Copyright (c) 2017, Christopher Durand - * Copyright (c) 2017, Niklas Hauser - * Copyright (c) 2018, Fabian Greif - * - * 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 "connector.hpp" - -namespace xpcc -{ - -ZeroMQConnector::ZeroMQConnector(std::string endpointIn, std::string endpointOut, Mode mode) : - socketIn (context, (mode == Mode::SubPush ? zmqpp::socket_type::sub : zmqpp::socket_type::pull)), - socketOut(context, (mode == Mode::SubPush ? zmqpp::socket_type::push : zmqpp::socket_type::pub)), - reader(socketIn) -{ - switch(mode) - { - case - Mode::SubPush: - this->socketIn.connect(endpointIn); - - if(this->socketIn.type() == zmqpp::socket_type::sub) { - this->socketIn.subscribe(""); - } - - this->socketOut.connect(endpointOut); - break; - - case - Mode::PubPull: - this->socketIn.bind(endpointIn); - this->socketOut.bind(endpointOut); - break; - } - - this->reader.start(); -} - -// ---------------------------------------------------------------------------- -ZeroMQConnector::~ZeroMQConnector() -{ - this->reader.stop(); - - if(this->socketIn.type() == zmqpp::socket_type::sub) { - this->socketIn.unsubscribe(""); - } - - this->socketIn.close(); - this->socketOut.close(); -} - -// ---------------------------------------------------------------------------- -void -ZeroMQConnector::sendPacket(const Header &header, modm::SmartPointer payload) -{ - // Publish the reassembled message with zeromq - zmqpp::message message; - - // Manual serialisation of MODM Header and Payload into a byte buffer - // The mapping of type, ack, dest, src and id into a uint32_t from - // CanConnectorBase is used. - - constexpr uint16_t headerSize = 5; - - // Maximum valid size of modm::SmartPointer - constexpr uint16_t maxPayloadSize = 65529; - - if (payload.getSize() > maxPayloadSize) - { - MODM_LOG_ERROR << MODM_FILE_INFO; - MODM_LOG_ERROR << "Trying to send message with invalid size: "; - MODM_LOG_ERROR << payload.getSize() << modm::endl; - - return; - } - - const std::size_t buf_size = headerSize + payload.getSize(); - uint8_t buf[buf_size]; - - buf[0] = static_cast(header.type); - buf[1] = header.isAcknowledge; - buf[2] = header.destination; - buf[3] = header.source; - buf[4] = header.packetIdentifier; - - memcpy(buf + headerSize, payload.getPointer(), payload.getSize()); - -# if ZMQPP_VERSION_MAJOR >= 4 - /** - * Breaking change in 4.1.1: - * Removed message::add(pointer, size_t) as there were situations it conflicts with the new easier - * to use templated add. This has been replaced with a message::add_raw(pointer, size_t) method. - * https://github.com/zeromq/zmqpp/blob/develop/CHANGES.md - */ - message.add_raw(buf, buf_size); -# else - message.add(buf, buf_size); -# endif - - socketOut.send(message, /* dont_block = */ true); -} - -// ---------------------------------------------------------------------------- -bool -ZeroMQConnector::isPacketAvailable() const -{ - return this->reader.isPacketAvailable(); -} - -// ---------------------------------------------------------------------------- -const Header& -ZeroMQConnector::getPacketHeader() const -{ - return this->reader.getPacket().header; -}; - -// ---------------------------------------------------------------------------- -const modm::SmartPointer -ZeroMQConnector::getPacketPayload() const -{ - return this->reader.getPacket().payload; -}; - -// ---------------------------------------------------------------------------- -void -ZeroMQConnector::dropPacket() -{ - this->reader.dropPacket(); -} - -// ---------------------------------------------------------------------------- -void -ZeroMQConnector::update() -{ -} - -} // modm namespace diff --git a/src/modm/communication/xpcc/backend/zeromq/connector.hpp b/src/modm/communication/xpcc/backend/zeromq/connector.hpp deleted file mode 100644 index 1473ed040d..0000000000 --- a/src/modm/communication/xpcc/backend/zeromq/connector.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2016, Sascha Schade - * Copyright (c) 2017, Christopher Durand - * Copyright (c) 2017, Niklas Hauser - * Copyright (c) 2018, Fabian Greif - * - * 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 XPCC_ZEROMQ_CONNECTOR_HPP -#define XPCC_ZEROMQ_CONNECTOR_HPP - -#include // for std::memcpy - -#include - -#include -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::ERROR - -#include "../backend_interface.hpp" -#include "reader.hpp" - -namespace xpcc -{ -class ZeroMQConnectorBase -{ -public: - enum class Mode - { - SubPush, /// In this mode the backend connects to a remote machine. - PubPull, /// Server mode in which the backend binds to two ports. The ports must be accessible. - }; -}; - -/** - * @brief ZeroMQ communication backend for hosted - * - * @ingroup modm_communication_xpcc_backend - * - * @author strongly-typed - * @author Christopher Durand - */ -class ZeroMQConnector : public ZeroMQConnectorBase, public BackendInterface -{ -public: - ZeroMQConnector(std::string endpointIn, std::string endpointOut, - Mode mode = Mode::SubPush); - - ~ZeroMQConnector() override; - - virtual void - sendPacket(const Header &header, modm::SmartPointer payload) override; - - virtual bool - isPacketAvailable() const override; - - virtual const Header& - getPacketHeader() const override; - - virtual const modm::SmartPointer - getPacketPayload() const override; - - virtual void - dropPacket() override; - - virtual void - update() override; - -protected: - zmqpp::context context; - zmqpp::socket socketIn; - zmqpp::socket socketOut; - - ZeroMQReader reader; -}; - -} // xpcc namespace - -#endif // XPCC_ZEROMQ_CONNECTOR_HPP diff --git a/src/modm/communication/xpcc/backend/zeromq/reader.cpp b/src/modm/communication/xpcc/backend/zeromq/reader.cpp deleted file mode 100644 index 600dea4b58..0000000000 --- a/src/modm/communication/xpcc/backend/zeromq/reader.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// coding: utf-8 -/* - * Copyright (c) 2017, Christopher Durand - * Copyright (c) 2017, Niklas Hauser - * Copyright (c) 2018, Fabian Greif - * - * 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 "reader.hpp" - -#include - -namespace xpcc -{ - -// ---------------------------------------------------------------------------- -ZeroMQReader::ZeroMQReader(zmqpp::socket& socketIn_, std::size_t maxQueueSize_) : - socketIn(socketIn_), maxQueueSize(maxQueueSize_), stopThread(false) -{ -} - -// ---------------------------------------------------------------------------- -ZeroMQReader::~ZeroMQReader() -{ - stop(); -} - -// ---------------------------------------------------------------------------- -void -ZeroMQReader::start() -{ - stop(); - - this->stopThread = false; - this->thread = std::thread([this]() { - receiveThread(); - }); -} - -// ---------------------------------------------------------------------------- -void -ZeroMQReader::stop() -{ - if(this->thread.joinable()) { - this->stopThread = true; - this->thread.join(); - } -} - -// ---------------------------------------------------------------------------- -bool -ZeroMQReader::isPacketAvailable() const -{ - std::lock_guard lock(this->queueMutex); - return not this->queue.empty(); -} - -// ---------------------------------------------------------------------------- -const ZeroMQReader::Packet& -ZeroMQReader::getPacket() const -{ - std::lock_guard lock(this->queueMutex); - return this->queue.front(); -} - -// ---------------------------------------------------------------------------- -void -ZeroMQReader::dropPacket() -{ - std::lock_guard lock(this->queueMutex); - - if(not this->queue.empty()) { - this->queue.pop_front(); - } -} - -// ---------------------------------------------------------------------------- -void -ZeroMQReader::receiveThread() -{ - zmqpp::poller poller; - zmqpp::message message; - - poller.add(this->socketIn, zmqpp::poller::poll_in); - - while(not this->stopThread) { - while(this->socketIn.receive(message, /* no_block = */ true)) { - readPacket(message); - -# if ZMQPP_VERSION_MAJOR < 4 - // swap and discard old message as in zmqpp 3 - // "receiving can only be done to empty messages" - zmqpp::message emptyMessage; - std::swap(emptyMessage, message); -# endif - } - poller.poll(PollTimeoutMs); - } -} - -// ---------------------------------------------------------------------------- -void -ZeroMQReader::readPacket(const zmqpp::message& message) -{ - constexpr uint16_t headerSize = 5; - - // Maximum payload size of modm::SmartPointer - constexpr uint16_t maxPayloadSize = 65529; - - const auto size = message.size(0); - - if(size >= headerSize && size <= (headerSize + maxPayloadSize)) { - const uint8_t* const data = static_cast(message.raw_data()); - const auto payloadSize = size - headerSize; - - xpcc::Header header = xpcc::Header( - /* type = */ xpcc::Header::Type(data[0]), - /* ack = */ data[1], - /* dest = */ data[2], - /* src = */ data[3], - /* id = */ data[4]); - - { - std::lock_guard lock(this->queueMutex); - - if (this->queue.size() >= this->maxQueueSize) { - this->queue.pop_front(); - - MODM_LOG_ERROR << MODM_FILE_INFO; - MODM_LOG_ERROR << "Receive queue is full, dropping packets" << modm::endl; - } - - this->queue.emplace_back(payloadSize, header); - - // Copy received payload to packet - uint8_t* const payloadBuffer = this->queue.back().payload.getPointer(); - std::copy_n(data + headerSize, payloadSize, payloadBuffer); - } - } else { - MODM_LOG_ERROR << MODM_FILE_INFO; - MODM_LOG_ERROR << "Invalid message length: " << size << modm::endl; - } -} - -} // modm namespace diff --git a/src/modm/communication/xpcc/backend/zeromq/reader.hpp b/src/modm/communication/xpcc/backend/zeromq/reader.hpp deleted file mode 100644 index b89b5ea380..0000000000 --- a/src/modm/communication/xpcc/backend/zeromq/reader.hpp +++ /dev/null @@ -1,100 +0,0 @@ -// coding: utf-8 -/* - * Copyright (c) 2017, Christopher Durand - * Copyright (c) 2017, Niklas Hauser - * Copyright (c) 2018, Fabian Greif - * - * 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 XPCC_ZEROMQ_READER_HPP -#define XPCC_ZEROMQ_READER_HPP - -#include -#include -#include -#include - -#include - -#include "../header.hpp" - -#include -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::ERROR - -namespace xpcc -{ - -/** - * @brief Reads packets from a zmqpp socket in a background thread - * - * @ingroup modm_communication_xpcc_backend - * - * @author Christopher Durand - * @author strongly-typed - */ -class ZeroMQReader -{ -public: - static constexpr int PollTimeoutMs = 100; - - struct Packet - { - Packet(uint16_t size, const Header& inHeader) : - header(inHeader), payload(size) - { - } - - xpcc::Header header; - modm::SmartPointer payload; - }; - - ZeroMQReader(zmqpp::socket& socketIn_, std::size_t maxQueueSize_ = 1000); - - ~ZeroMQReader(); - - ZeroMQReader(const ZeroMQReader&) = delete; - ZeroMQReader& operator=(const ZeroMQReader&) = delete; - - void - start(); - - void - stop(); - - bool - isPacketAvailable() const; - - const Packet& - getPacket() const; - - void - dropPacket(); - -private: - void - receiveThread(); - - void - readPacket(const zmqpp::message& message); - -private: - zmqpp::socket& socketIn; - - std::deque queue; - mutable std::mutex queueMutex; - const std::size_t maxQueueSize; - - std::thread thread; - std::atomic stopThread; -}; - -} // modm namespace - -#endif // XPCC_ZEROMQ_READER_HPP diff --git a/src/modm/communication/xpcc/module.lb b/src/modm/communication/xpcc/module.lb index 90701e8247..e1f98ab2a5 100644 --- a/src/modm/communication/xpcc/module.lb +++ b/src/modm/communication/xpcc/module.lb @@ -48,15 +48,6 @@ def build(env): if env[":target"].identifier["platform"] in ["avr"]: ignore.append("*postman/dynamic*") - if env[":target"].identifier["platform"] in ["hosted"]: - env.collect(":build:library", "zmqpp") - env.collect(":build:library", "zmq") - - if env[":target"].identifier["family"] in ["linux"]: - env.collect(":build:library", "pthread") - else: - ignore.append("*backend/zeromq*") - env.copy(".", ignore=env.ignore_paths(*ignore)) env.copy("../xpcc.hpp") env.template("dispatcher.hpp.in") From 4d5cb7b20fc3b2bc08644d2974866c9f8073bf18 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Tue, 28 Apr 2020 21:02:28 +0200 Subject: [PATCH 013/135] [timer] Fix time_point cast for short versions --- src/modm/processing/timer/periodic_timer.hpp | 4 ++-- src/modm/processing/timer/timeout.hpp | 3 +++ src/modm/processing/timer/timeout_impl.hpp | 13 ++++++++++--- test/modm/processing/timer/timeout_test.cpp | 5 +++++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/modm/processing/timer/periodic_timer.hpp b/src/modm/processing/timer/periodic_timer.hpp index 2f841ead13..4b33d422e1 100644 --- a/src/modm/processing/timer/periodic_timer.hpp +++ b/src/modm/processing/timer/periodic_timer.hpp @@ -54,7 +54,7 @@ class GenericPeriodicTimer : public GenericTimeout size_t count{0}; if (this->_interval.count()) { - const typename Clock::time_point now = Clock::now(); + const auto now = this->now(); while(1) { this->_start += this->_interval; @@ -64,7 +64,7 @@ class GenericPeriodicTimer : public GenericTimeout } } else { - this->_start = Clock::now(); + this->_start = this->now(); count = 1; } this->_state = this->ARMED; diff --git a/src/modm/processing/timer/timeout.hpp b/src/modm/processing/timer/timeout.hpp index 0d959e98c0..ecd683d8d1 100644 --- a/src/modm/processing/timer/timeout.hpp +++ b/src/modm/processing/timer/timeout.hpp @@ -123,6 +123,9 @@ class GenericTimeout bool checkExpiration() const; + time_point + now() const; + enum InternalState : uint8_t { diff --git a/src/modm/processing/timer/timeout_impl.hpp b/src/modm/processing/timer/timeout_impl.hpp index b1758cec7e..7468871b70 100644 --- a/src/modm/processing/timer/timeout_impl.hpp +++ b/src/modm/processing/timer/timeout_impl.hpp @@ -38,7 +38,7 @@ modm::GenericTimeout::restart(std::chrono::duration(Clock::now()); + _start = now(); _interval = cast_interval; _state = ARMED; } @@ -83,7 +83,7 @@ modm::GenericTimeout::remaining() const return wide_signed_duration{0}; return std::chrono::duration_cast(_interval) + std::chrono::time_point_cast(_start) - - std::chrono::time_point_cast(Clock::now()); + std::chrono::time_point_cast(now()); } // ---------------------------------------------------------------------------- @@ -124,5 +124,12 @@ template< class Clock, class Duration > bool modm::GenericTimeout::checkExpiration() const { - return (_state & ARMED) and (Clock::now() - _start) >= _interval; + return (_state & ARMED) and (now() - _start) >= _interval; +} + +template< class Clock, class Duration > +typename modm::GenericTimeout::time_point +modm::GenericTimeout::now() const +{ + return std::chrono::time_point_cast(Clock::now()); } diff --git a/test/modm/processing/timer/timeout_test.cpp b/test/modm/processing/timer/timeout_test.cpp index f9b939c7c6..9a521f1929 100644 --- a/test/modm/processing/timer/timeout_test.cpp +++ b/test/modm/processing/timer/timeout_test.cpp @@ -236,6 +236,11 @@ TimeoutTest::testTimeOverflow() timeoutShort.restart(time+1ms); TEST_ASSERT_EQUALS(timeoutShort.remaining(), 0ms); TEST_ASSERT_TRUE(timeoutShort.execute()); + + test_clock::setTime(-1000); + timeoutShort.restart(500ms); + TEST_ASSERT_EQUALS(timeoutShort.remaining(), 500ms); + TEST_ASSERT_FALSE(timeoutShort.execute()); } void From f6052ecd8b1ef501c91899882b5b5218433b4bac Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Tue, 28 Apr 2020 19:48:52 +0200 Subject: [PATCH 014/135] [platform] CANUSB: add missing mutex usage I was not able to receive messages from a CAN2USB. The mutex was already added but newer used. --- src/modm/platform/can/canusb/canusb.hpp | 3 ++- src/modm/platform/can/canusb/canusb_impl.hpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modm/platform/can/canusb/canusb.hpp b/src/modm/platform/can/canusb/canusb.hpp index 6335b9565c..80942d63a3 100644 --- a/src/modm/platform/can/canusb/canusb.hpp +++ b/src/modm/platform/can/canusb/canusb.hpp @@ -53,7 +53,8 @@ class CanUsb : public ::modm::Can inline bool isMessageAvailable() { - return (!this->readBuffer.empty()); + MutexGuard stateGuard(readBufferLock); + return (not this->readBuffer.empty()); } bool diff --git a/src/modm/platform/can/canusb/canusb_impl.hpp b/src/modm/platform/can/canusb/canusb_impl.hpp index 11b22b6569..84fdc23434 100644 --- a/src/modm/platform/can/canusb/canusb_impl.hpp +++ b/src/modm/platform/can/canusb/canusb_impl.hpp @@ -180,6 +180,7 @@ template bool modm::platform::CanUsb::getMessage(can::Message& message) { + MutexGuard stateGuard(readBufferLock); if (not this->readBuffer.empty()) { message = this->readBuffer.front(); @@ -229,6 +230,7 @@ modm::platform::CanUsb::update() if (modm::CanLawicelFormatter::convertToCanMessage( this->tmpRead.c_str(), message)) { + MutexGuard stateGuard(readBufferLock); this->readBuffer.push(message); } } From 6190dc5fe48d6d387ed6a1e00416794136a53b85 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Fri, 26 Jul 2019 20:27:19 +0200 Subject: [PATCH 015/135] [cmake] Do not suffix all tools --- .../cmake_scripts/configure-gcc.cmake.in | 34 +++++++------------ .../cmake/resources/CMakeLists.txt.in | 2 ++ 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in b/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in index 8163c745e3..564ff4a78a 100644 --- a/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in +++ b/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in @@ -40,15 +40,9 @@ else() set(TOOL_EXECUTABLE_SUFFIX "") endif() -if(${CMAKE_VERSION} VERSION_LESS 3.6.0) - include(CMakeForceCompiler) - cmake_force_c_compiler("${TOOL_EXECUTABLE_PREFIX}gcc${TOOL_EXECUTABLE_SUFFIX}" GNU) - cmake_force_cxx_compiler("${TOOL_EXECUTABLE_PREFIX}g++${TOOL_EXECUTABLE_SUFFIX}" GNU) -else() - set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - set(CMAKE_C_COMPILER "${TOOL_EXECUTABLE_PREFIX}gcc${TOOL_EXECUTABLE_SUFFIX}") - set(CMAKE_CXX_COMPILER "${TOOL_EXECUTABLE_PREFIX}g++${TOOL_EXECUTABLE_SUFFIX}") -endif() +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(CMAKE_C_COMPILER "${TOOL_EXECUTABLE_PREFIX}gcc${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "c") +set(CMAKE_CXX_COMPILER "${TOOL_EXECUTABLE_PREFIX}g++${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "c++") set(CMAKE_ASM_COMPILER "${CMAKE_C_COMPILER}" CACHE INTERNAL "asm") set(CMAKE_AR "${TOOL_EXECUTABLE_PREFIX}ar" CACHE INTERNAL "ar") @@ -58,20 +52,18 @@ if(TOOL_EXECUTABLE_SUFFIX STREQUAL "") set(CMAKE_NM "${TOOL_EXECUTABLE_PREFIX}nm" CACHE INTERNAL "nm") else() set(CMAKE_AS "${CMAKE_CXX_COMPILER}" CACHE INTERNAL "as") - set(CMAKE_NM "${TOOL_EXECUTABLE_PREFIX}gcc-nm${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "nm") - if(NOT APPLE) - set(CMAKE_AR "${TOOL_EXECUTABLE_PREFIX}gcc-ar${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "ar") - set(CMAKE_RANLIB "${TOOL_EXECUTABLE_PREFIX}gcc-ranlib${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "ranlib") - endif() + set(CMAKE_NM "${TOOL_EXECUTABLE_PREFIX}gcc-nm${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "nm") + set(CMAKE_AR "${TOOL_EXECUTABLE_PREFIX}gcc-ar${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "ar") + set(CMAKE_RANLIB "${TOOL_EXECUTABLE_PREFIX}gcc-ranlib${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "ranlib") endif() -set(CMAKE_OBJCOPY "${TOOL_EXECUTABLE_PREFIX}objcopy${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "objcopy") -set(CMAKE_OBJDUMP "${TOOL_EXECUTABLE_PREFIX}objdump${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "objdump") -set(CMAKE_SIZE "${TOOL_EXECUTABLE_PREFIX}size${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "size") -set(CMAKE_STRIP "${TOOL_EXECUTABLE_PREFIX}strip${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "strip") -set(CMAKE_LINKER "${CMAKE_CXX_COMPILER}" CACHE INTERNAL "linker") -set(CMAKE_DEBUGGER "${TOOL_EXECUTABLE_PREFIX}gdb${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "debugger") -set(CMAKE_CPPFILT "${TOOL_EXECUTABLE_PREFIX}c++filt${TOOL_EXECUTABLE_SUFFIX}" CACHE INTERNAL "C++filt") +set(CMAKE_OBJCOPY "${TOOL_EXECUTABLE_PREFIX}objcopy" CACHE INTERNAL "objcopy") +set(CMAKE_OBJDUMP "${TOOL_EXECUTABLE_PREFIX}objdump" CACHE INTERNAL "objdump") +set(CMAKE_SIZE "${TOOL_EXECUTABLE_PREFIX}size" CACHE INTERNAL "size") +set(CMAKE_STRIP "${TOOL_EXECUTABLE_PREFIX}strip" CACHE INTERNAL "strip") +set(CMAKE_LINKER "${CMAKE_CXX_COMPILER}" CACHE INTERNAL "linker") +set(CMAKE_DEBUGGER "${TOOL_EXECUTABLE_PREFIX}gdb" CACHE INTERNAL "debugger") +set(CMAKE_CPPFILT "${TOOL_EXECUTABLE_PREFIX}c++filt" CACHE INTERNAL "C++filt") set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") diff --git a/tools/build_script_generator/cmake/resources/CMakeLists.txt.in b/tools/build_script_generator/cmake/resources/CMakeLists.txt.in index bf5c56edae..c10f28af69 100644 --- a/tools/build_script_generator/cmake/resources/CMakeLists.txt.in +++ b/tools/build_script_generator/cmake/resources/CMakeLists.txt.in @@ -24,9 +24,11 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_executable(${CMAKE_PROJECT_NAME} ${SOURCE_FILES}) set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES SUFFIX ".elf") +%% if platform != "hosted" add_custom_target(${CMAKE_PROJECT_NAME}.bin ALL DEPENDS ${CMAKE_PROJECT_NAME} COMMAND ${CMAKE_OBJCOPY} -Obinary ${CMAKE_PROJECT_NAME}.elf ${CMAKE_PROJECT_NAME}.bin) add_custom_target(${CMAKE_PROJECT_NAME}.hex ALL DEPENDS ${CMAKE_PROJECT_NAME} COMMAND ${CMAKE_OBJCOPY} -Oihex ${CMAKE_PROJECT_NAME}.elf ${CMAKE_PROJECT_NAME}.hex) add_custom_target(${CMAKE_PROJECT_NAME}.lss ALL DEPENDS ${CMAKE_PROJECT_NAME} COMMAND ${CMAKE_OBJDUMP} -x -s -S -l -w ${CMAKE_PROJECT_NAME}.elf > ${CMAKE_PROJECT_NAME}.lss) +%% endif %% if core.startswith("avr") add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_SIZE} ARGS -C -d --mcu={{ partname }} ${CMAKE_PROJECT_NAME}.elf) From 4e02180276255258ac285582d77a43c56f0f6c19 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 29 Apr 2020 16:39:03 +0200 Subject: [PATCH 016/135] [ci] Enable macOS build on TravisCI --- .travis.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..85a5c9244b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +os: osx +osx_image: xcode11.3 + +before_install: + - brew update + - brew tap osx-cross/avr + - brew tap osx-cross/arm + - brew install scons git doxygen boost gcc avr-gcc arm-gcc-bin cmake || true + - brew upgrade boost gcc git || true + - export PATH=/Users/travis/Library/Python/3.7/bin:$PATH + - pip3 install --user modm + +script: + - (cd test && make run-hosted-darwin) + - (cd examples && ../tools/scripts/examples_compile.py linux) + - (cd examples && ../tools/scripts/examples_compile.py stm32f1_discovery nucleo_f103rb olimexino_stm32 stm32f103c8t6_blue_pill stm32f103c8t6_black_pill) + - (cd test && make compile-arduino-nano) From 59ce752eb3e4b5b51307c8e0878e9685c38a69f9 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Tue, 5 May 2020 22:59:38 +0200 Subject: [PATCH 017/135] Explicitly set the stack pointer in the reset handler. It turned out that running from debugger the stack pointer might be wrong (initialized to the value of the system flash). --- src/modm/platform/core/cortex/reset_handler.sx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modm/platform/core/cortex/reset_handler.sx b/src/modm/platform/core/cortex/reset_handler.sx index 25fd6e7774..c92c88d8de 100644 --- a/src/modm/platform/core/cortex/reset_handler.sx +++ b/src/modm/platform/core/cortex/reset_handler.sx @@ -16,6 +16,8 @@ .type Reset_Handler, %function .func Reset_Handler Reset_Handler: + ldr r0,=__main_stack_top + mov sp,r0 bl __modm_initialize_platform b __modm_startup .endfunc From 0217a1953aabead5116e3e0923bfc9462117b286 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Wed, 6 May 2020 10:17:04 +0200 Subject: [PATCH 018/135] [dma] New DMA implementation, starting with some STM32L4 MCU. --- src/modm/platform/dma/stm32/dma.cpp.in | 27 ++ src/modm/platform/dma/stm32/dma.hpp.in | 436 ++++++++++++++---- src/modm/platform/dma/stm32/dma_base.hpp.in | 90 +++- src/modm/platform/dma/stm32/dma_hal.hpp.in | 263 +++++++++++ .../platform/dma/stm32/dma_hal_impl.hpp.in | 58 +++ src/modm/platform/dma/stm32/dma_impl.hpp.in | 251 ---------- src/modm/platform/dma/stm32/module.lb | 61 +-- 7 files changed, 801 insertions(+), 385 deletions(-) create mode 100644 src/modm/platform/dma/stm32/dma.cpp.in create mode 100644 src/modm/platform/dma/stm32/dma_hal.hpp.in create mode 100644 src/modm/platform/dma/stm32/dma_hal_impl.hpp.in delete mode 100644 src/modm/platform/dma/stm32/dma_impl.hpp.in diff --git a/src/modm/platform/dma/stm32/dma.cpp.in b/src/modm/platform/dma/stm32/dma.cpp.in new file mode 100644 index 0000000000..66a53b2b19 --- /dev/null +++ b/src/modm/platform/dma/stm32/dma.cpp.in @@ -0,0 +1,27 @@ +/* + * 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 "dma.hpp" + +%% for channels in dma["channels"] +/* + * IRQ handler for DMA{{ channels.instance }} + */ +%% for channel in channels.channel + +MODM_ISR(DMA{{ channels.instance }}_Channel{{ channel.position }}) +{ + using namespace modm::platform; + Dma{{ channels.instance }}::Channel::interruptHandler(); +} + +%% endfor +%% endfor diff --git a/src/modm/platform/dma/stm32/dma.hpp.in b/src/modm/platform/dma/stm32/dma.hpp.in index 39068a1b9b..a953718ecd 100644 --- a/src/modm/platform/dma/stm32/dma.hpp.in +++ b/src/modm/platform/dma/stm32/dma.hpp.in @@ -1,6 +1,7 @@ /* * Copyright (c) 2014, Kevin Läufer * Copyright (c) 2014-2017, Niklas Hauser + * Copyright (c) 2020, Mike Wolfram * * This file is part of the modm project. * @@ -10,22 +11,12 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32_DMA{{ id }}_HPP -#define MODM_STM32_DMA{{ id }}_HPP +#ifndef MODM_STM32_DMA_HPP +#define MODM_STM32_DMA_HPP #include #include "../device.hpp" -#include "dma_base.hpp" - -%% if target["family"] == "f4" - %% set streams = range(0,8) -%% elif target["family"] == "f3" - %% if id == 1 - %% set streams = range(1,7) - %% elif id == 2 - %% set streams = range(1,5) - %% endif -%% endif +#include "dma_hal.hpp" namespace modm { @@ -34,107 +25,358 @@ namespace platform { /** - * DMA + * DMA controller * * Does not support - among other things - double buffering or FIFO usage * - * @author Kevin Laeufer - * @ingroup modm_platform_dma modm_platform_dma_{{id}} + * @author Mike Wolfram + * @ingroup modm_platform_dma */ -class Dma{{ id }} +template +class DmaController : public DmaBase { + static_assert(ID >= 1 and ID <= {{ dma.instance | length }}, "invalid DMA controller ID"); + public: - static inline void - enable(); + /** + * Enable the DMA controller in the RCC + */ + static void + enable() + { + if constexpr (ID == 1) + Rcc::enable(); + else + Rcc::enable(); + } + /** + * Disable the DMA controller in the RCC + */ + static void + disable() + { + if constexpr (ID == 1) + Rcc::disable(); + else + Rcc::disable(); + } - static inline void - disable(); + /** + * Class representing a DMA channel/stream + */ + template + class Channel + { + static_assert( +%% for controller in dmaController +%% if controller["instance"] > 1 + or +%% endif + (ID == {{ controller["instance"] }} and + ChannelID >= DmaBase::Channel::Channel1 and + ChannelID <= DmaBase::Channel::Channel{{ controller["channels"] }}) +%% endfor + , "invalid Channel ID for that DMA controller" + ); + using ControlHal = DmaHal; + + static constexpr uint32_t CHANNEL_BASE { ControlHal::CHANNEL_BASE + + uint32_t(ChannelID) * ControlHal::CHANNEL_2_CHANNEL }; + + using ChannelHal = DmaChannelHal; -public: -%% for stream in streams - class Stream{{ stream }} : public DmaBase - { public: - %% set pointer_types = [8, 16, 32] - %% for type in pointer_types - /// will disable the stream - static inline void - setMemorySource(uint{{type}}_t* address, - MemoryIncrementMode inc = MemoryIncrementMode::Increment - %% if target["family"] == "f4" - , MemoryBurstTransfer transfer = MemoryBurstTransfer::Single); - %% elif target["family"] == "f3" - ); - %% endif - - /// will disable the stream - static inline void - setPeripheralSource(uint{{type}}_t* address, - PeripheralIncrementMode inc = PeripheralIncrementMode::Fixed - %% if target["family"] == "f4" - , PeripheralBurstTransfer transfer = PeripheralBurstTransfer::Single); - %% elif target["family"] == "f3" - ); - %% endif - %% endfor + /** + * Configure the DMA channel + * + * Stops the DMA channel and writes the new values to its control register. + * + * @param[in] direction Direction of the DMA channel + * @param[in] memoryDataSize Size of data in memory (byte, halfword, word) + * @param[in] peripheralDataSize Size of data in peripheral (byte, halfword, word) + * @param[in] memoryIncrement Defines whether the memory address is incremented + * after a transfer completed + * @param[in] peripheralIncrement Defines whether the peripheral address is + * incremented after a transfer completed + * @param[in] priority Priority of the DMA channel + * @param[in] circularMode Transfer data in circular mode? + */ + static void + configure(DataTransferDirection direction, MemoryDataSize memoryDataSize, + PeripheralDataSize peripheralDataSize, + MemoryIncrementMode memoryIncrement, + PeripheralIncrementMode peripheralIncrement, + Priority priority = Priority::Medium, + CircularMode circularMode = CircularMode::Disabled) + { + ChannelHal::configure(direction, memoryDataSize, peripheralDataSize, + memoryIncrement, peripheralIncrement, priority, circularMode); + } - %% for type in pointer_types - /// will disable the stream - static inline void - setMemoryDestination(uint{{type}}_t* address, - MemoryIncrementMode inc = MemoryIncrementMode::Increment - %% if target["family"] == "f4" - , MemoryBurstTransfer transfer = MemoryBurstTransfer::Single); - %% elif target["family"] == "f3" - ); - %% endif - - /// will disable the stream - static inline void - setPeripheralDestination(uint{{type}}_t* address, - PeripheralIncrementMode inc = PeripheralIncrementMode::Fixed - %% if target["family"] == "f4" - , PeripheralBurstTransfer transfer = PeripheralBurstTransfer::Single); - %% elif target["family"] == "f3" - ); - %% endif - %% endfor + /** + * Start the transfer of the DMA channel + */ + static void + start() + { + ControlHal::clearInterruptFlags(uint32_t(Interrupt::Global) << (uint32_t(ChannelID) * 4)); + ChannelHal::start(); + } + /** + * Stop a DMA channel transfer + */ + static void + stop() + { + ChannelHal::stop(); + } + + /** + * Get the direction of the data transfer + */ + static DataTransferDirection + getDataTransferDirection() + { + return ChannelHal::getDataTransferDirection(); + } + + /** + * Set the memory address of the DMA channel + * + * @note In Mem2Mem mode use this method to set the memory source address. + * + * @param[in] address Source address + */ + static void + setMemoryAddress(uintptr_t address) + { + ChannelHal::setMemoryAddress(address); + } + /** + * Set the peripheral address of the DMA channel + * + * @note In Mem2Mem mode use this method to set the memory destination address. + * + * @param[in] address Destination address + */ + static void + setPeripheralAddress(uintptr_t address) + { + ChannelHal::setPeripheralAddress(address); + } + + /** + * Enable/disable memory increment + * + * When enabled, the memory address is incremented by the size of the data + * (e.g. 1 for byte transfers, 4 for word transfers) after the transfer + * completed. + * + * @param[in] increment Enable/disable + */ + static void + setMemoryIncrementMode(bool increment) + { + ChannelHal::setMemoryIncrementMode(increment); + } + /** + * Enable/disable peripheral increment + * + * When enabled, the peripheral address is incremented by the size of the data + * (e.g. 1 for byte transfers, 4 for word transfers) after the transfer + * completed. + * + * @param[in] increment Enable/disable + */ + static void + setPeripheralIncrementMode(bool increment) + { + ChannelHal::setPeripheralIncrementMode(increment); + } + + /** + * Set the length of data to be transfered + */ + static void + setDataLength(std::size_t length) + { + ChannelHal::setDataLength(length); + } + + /** + * Set the IRQ handler for transfer errors + * + * The handler will be called from the channels IRQ handler function + * when the IRQ status indicates an error occured. + */ + static void + setTransferErrorIrqHandler(IrqHandler irqHandler) + { + transferError = irqHandler; + } + /** + * Set the IRQ handler for transfer complete + * + * Called by the channels IRQ handler when the transfer is complete. + */ + static void + setTransferCompleteIrqHandler(IrqHandler irqHandler) + { + transferComplete = irqHandler; + } + /** + * Set the peripheral that operates the channel + */ + template + static void + setPeripheralRequest() + { + DMA_Request_TypeDef *DMA_REQ = reinterpret_cast(ControlHal::DMA_CSEL); + DMA_REQ->CSELR &= ~(0x0f << (uint32_t(ChannelID) * 4)); + DMA_REQ->CSELR |= uint32_t(dmaRequest) << (uint32_t(ChannelID) * 4); + } + + /** + * IRQ handler of the DMA channel + * + * Reads the IRQ status and checks for error or transfer complete. In case + * of error the DMA channel will be disabled. + */ + static void + interruptHandler() + { + static const uint32_t TC_Flag { + uint32_t(Interrupt::TransferComplete) << (uint32_t(ChannelID) * 4) + }; + static const uint32_t TE_Flag { + uint32_t(Interrupt::Error) << (uint32_t(ChannelID) * 4) + }; + + auto isr { ControlHal::getInterruptFlags() }; + if (isr & TE_Flag) { + disable(); + if (transferError) + transferError(); + } + if ((isr & TC_Flag) and transferComplete) + transferComplete(); + + ControlHal::clearInterruptFlags(uint32_t(Interrupt::Global) << (uint32_t(ChannelID) * 4)); + } + + /** + * Enable the IRQ vector of the channel + * + * @param[in] priority Priority of the IRQ + */ + static void + enableInterruptVector(uint32_t priority = 1) + { + NVIC_SetPriority(DmaBase::Nvic::DmaIrqs[uint32_t(ChannelID)], priority); + NVIC_EnableIRQ(DmaBase::Nvic::DmaIrqs[uint32_t(ChannelID)]); + } + /** + * Disable the IRQ vector of the channel + */ + static void + disableInterruptVector() + { + NVIC_DisableIRQ(DmaBase::Nvic::DmaIrqs[uint32_t(ChannelID)]); + } - static inline void - stop(); - - /// will disable the stream - static inline void - configure( - %% if target["family"] == "f4" - Channel channel, - %% endif - uint16_t sample_length, - Priority priority = Priority::Medium, - CircularMode circular = CircularMode::Disabled - %% if target["family"] == "f4" - , FlowControl flow = FlowControl::Dma); - %% elif target["family"] == "f3" - ); - %% endif - - static inline void - start(); - - static inline bool - isFinished(); - - static inline DataTransferDirection - getDataTransferDirection(); + /** + * Enable the specified interrupt of the channel + */ + static void + enableInterrupt(Interrupt_t irq) + { + ChannelHal::enableInterrupt(irq); + } + /** + * Disable the specified interrupt of the channel + */ + static void + disableInterrupt(Interrupt_t irq) + { + ChannelHal::disableInterrupt(irq); + } + + /** + * Helper to verify that the selected channel supports the selected + * hardware and provides the Request to be set in setPeripheralRequest(). + */ + template + struct RequestMapping { + }; + + private: + static inline DmaBase::IrqHandler transferError { nullptr }; + static inline DmaBase::IrqHandler transferComplete { nullptr }; }; +}; + +/* + * Derive DMA controller classes for convenience. Every derived class defines + * the channels available on that controller. + */ +%% for channels in dma["channels"] +class Dma{{ channels.instance }}: public DmaController<{{ channels.instance }}> +{ +public: + %% for channel in channels.channel + using Channel{{ channel.position }} = DmaController<{{ channels.instance }}>::Channel; + %% endfor +}; %% endfor + + +/* + * Specialization of the RequestMapping. For all hardware supported by DMA the + * RequestMapping structure defines the channel and the Request. It can be used + * by hardware classes to verify that the provided channel is valid and to + * get the value to set in setPeripheralRequest(). + * + * Example: + * template + * class SpiMaster1_Dma : public SpiMaster1 + * { + * using RxChannel = typename DmaRx::template RequestMapping::Channel; + * using TxChannel = typename DmaTx::template RequestMapping::Channel; + * static constexpr DmaBase::Request RxRequest = DmaRx::template RequestMapping::Request; + * static constexpr DmaBase::Request TxRequest = DmaTx::template RequestMapping::Request; + * + * ... + * }; + */ +%% for channels in dma["channels"] + %% for channel in channels.channel + %% for request in channel.request + %% for signal in request.signal + %% set peripheral = signal.driver.capitalize() + %% if signal.instance is defined + %% set peripheral = peripheral ~ signal.instance + %% else + %% if peripheral not in ["Quadspi", "Aes", "Dcmi"] + %% set peripheral = peripheral ~ 1 + %% endif + %% endif +template <> +template <> +template <> +struct DmaController<{{ channels.instance }}>::Channel::RequestMapping +{ + using Channel = DmaController<{{ channels.instance }}>::Channel; + static constexpr DmaBase::Request Request = DmaBase::Request::Request{{ request.position }}; }; + %% endfor + %% endfor + %% endfor +%% endfor + } // namespace platform } // namespace modm -#include "dma_{{ id }}_impl.hpp" - -#endif // MODM_STM32_DMA{{ id }}_HPP +#endif // MODM_STM32_DMA_HPP diff --git a/src/modm/platform/dma/stm32/dma_base.hpp.in b/src/modm/platform/dma/stm32/dma_base.hpp.in index 0a89008f73..913ddc70ba 100644 --- a/src/modm/platform/dma/stm32/dma_base.hpp.in +++ b/src/modm/platform/dma/stm32/dma_base.hpp.in @@ -1,6 +1,7 @@ /* * Copyright (c) 2014, Kevin Läufer * Copyright (c) 2014-2017, Niklas Hauser + * Copyright (c) 2020, Mike Wolfram * * This file is part of the modm project. * @@ -10,15 +11,19 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32F3_DMA_BASE_HPP -#define MODM_STM32F3_DMA_BASE_HPP +#ifndef MODM_STM32_DMA_BASE_HPP +#define MODM_STM32_DMA_BASE_HPP #include #include "../device.hpp" +#include +#include +#include + %% if target["family"] == "f4" %% set reg_prefix = "DMA_SxCR" -%% elif target["family"] == "f3" +%% elif target["family"] in ["f3", "l4"] %% set reg_prefix = "DMA_CCR" %% endif @@ -27,11 +32,11 @@ namespace modm namespace platform { - /** * DMA * * @author Kevin Laeufer + * @author Mike Wolfram * @ingroup modm_platform_dma */ class DmaBase @@ -76,6 +81,40 @@ public: Dma = 0, Peripheral = DMA_SxCR_PFCTRL, ///< the peripheral is the flow controller }; +%% elif target["family"] in ["f3", "l4"] + %% set channel_count = namespace(max_channels = 0) + %% for controller in dmaController + %% if channel_count.max_channels < controller.channels + %% set channel_count.max_channels = controller.channels + %% endif + %% endfor + enum class + Channel + { + %% for channel in range(1, channel_count.max_channels + 1) + Channel{{ channel }}{% if channel == 1 %} = 0{% endif %}, + %% endfor + }; + + %% if target["family"] == "l4" + %% set request_count = namespace(max_requests = 0) + %% for channels in dma["channels"] + %% for channel in channels.channel + %% for request in channel.request + %% if request_count.max_requests < request.position | int + %% set request_count.max_requests = request.position | int + %% endif + %% endfor + %% endfor + %% endfor + enum class + Request + { + %% for request in range(0, request_count.max_requests + 1) + Request{{ request }}{% if request == 0 %} = 0{% endif %}, + %% endfor + }; + %% endif %% endif enum class @@ -142,7 +181,7 @@ public: MemoryToPeripheral = DMA_SxCR_DIR_0, /// Source: DMA_SxPAR; Sink: DMA_SxM0AR MemoryToMemory = DMA_SxCR_DIR_1, -%% elif target["family"] == "f3" +%% elif target["family"] in ["f3", "l4"] /// Source: DMA_CPARx; Sink: DMA_CMARx PeripheralToMemory = 0, /// Source: DMA_CMARx; Sink: DMA_CPARx @@ -152,6 +191,17 @@ public: %% endif }; + /** + * Peripheral signals that can be used in DMA channels + */ + enum class + Signal : uint8_t { + NoSignal, +%% for signal in dmaSignals + {{ signal }}, +%% endfor + }; + protected: %% if target["family"] == "f4" static constexpr uint32_t memoryMask = @@ -169,7 +219,7 @@ protected: DMA_SxCR_PL_1 | DMA_SxCR_PL_0 | // Priority DMA_SxCR_CIRC | // CircularMode DMA_SxCR_PFCTRL; // FlowControl -%% elif target["family"] == "f3" +%% elif target["family"] in ["f3", "l4"] static constexpr uint32_t memoryMask = DMA_CCR_MSIZE_0 | DMA_CCR_MSIZE_1 | // MemoryDataSize DMA_CCR_MINC | // MemoryIncrementMode @@ -182,10 +232,36 @@ protected: DMA_CCR_CIRC | // CircularMode DMA_CCR_PL_1 | DMA_CCR_PL_0; // Priority %% endif + + enum class Interrupt { + Global = 0x01, + TransferComplete = 0x02, + HalfTransferComplete = 0x04, + Error = 0x08, + All = 0x0f, + }; + MODM_FLAGS32(Interrupt); + + using IrqHandler = void (*)(void); + + template + struct Nvic; +}; + +%% for channels in dma["channels"] +template <> +struct DmaBase::Nvic<{{ channels.instance }}> +{ + static constexpr IRQn_Type DmaIrqs[] { + %% for channel in channels.channel + DMA{{ channels.instance }}_Channel{{ channel.position }}_IRQn, + %% endfor + }; }; +%% endfor } // namespace platform } // namespace modm -#endif // MODM_STM32F3_DMA_BASE_HPP +#endif // MODM_STM32_DMA_BASE_HPP diff --git a/src/modm/platform/dma/stm32/dma_hal.hpp.in b/src/modm/platform/dma/stm32/dma_hal.hpp.in new file mode 100644 index 0000000000..f4aa5dd63f --- /dev/null +++ b/src/modm/platform/dma/stm32/dma_hal.hpp.in @@ -0,0 +1,263 @@ +/* + * 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_DMA_HAL_HPP +#define MODM_STM32_DMA_HAL_HPP + +#include + +#include "dma_base.hpp" + +namespace modm +{ + +namespace platform +{ + +/** + * Hardware abstraction of DMA controller + * + * @tparam ID The number of the DMA controller + * + * @author Mike Wolfram + * @ingroup modm_platform_dma + */ +template +class DmaHal : public DmaBase +{ + static_assert(ID >= 1 and ID <= {{ dma.instance | length }}, + "invalid DMA controller ID"); + + /** + * Get the base address of the DMA controller registers + * + * @tparam id The number of the DMA controller + */ + template + static constexpr uint32_t getBaseAddress() { + if (id == 1) + return DMA1_BASE; + else + return DMA2_BASE; + } + /** + * Get the base address of the DMA channel reigsters + * + * @tparam id The number of the DMA channel + */ + template + static constexpr uint32_t getChannelBaseAddress() { + if (id == 1) + return DMA1_Channel1_BASE; + else + return DMA2_Channel1_BASE; + } + /** + * Get the address of the channel selection register + * + * @tparam id The number of the DMA controller + */ + template + static constexpr uint32_t getCselAddress() { + if (id == 1) + return DMA1_CSELR_BASE; + else + return DMA2_CSELR_BASE; + } + +public: + /// DMA base register address + static constexpr uint32_t DMA_BASE { getBaseAddress() }; + /// DMA channel selection register address + static constexpr uint32_t DMA_CSEL { getCselAddress() }; + /// DMA channel base register address + static constexpr uint32_t CHANNEL_BASE { getChannelBaseAddress() }; + /// Register offset from channel to channel + static constexpr uint32_t CHANNEL_2_CHANNEL { 0x14 }; + + static void + clearInterruptFlags(uint32_t flags) + { + DMA_TypeDef *DMA = reinterpret_cast(DMA_BASE); + DMA->IFCR |= flags; + } + + static uint32_t + getInterruptFlags() + { + DMA_TypeDef *DMA = reinterpret_cast(DMA_BASE); + return DMA->ISR; + } +}; + +/** + * Hardware abstraction layer of a DMA channel + * + * @tparam ChannelID the ID of the channel + * @tparam CHANNEL_BASE base address of the channel registers + * + * @author Mike Wolfram + * @ingroup modm_platform_dma + */ +template +class DmaChannelHal : public DmaBase +{ +public: + /** + * Configure the DMA channel (HAL) + * + * Stops the DMA channel and writes the new values to its control register. + * + * @param[in] direction Direction of the DMA channel + * @param[in] memoryDataSize Size of data in memory (byte, halfword, word) + * @param[in] peripheralDataSize Size of data in peripheral (byte, halfword, word) + * @param[in] memoryIncrement Defines whether the memory address is incremented + * after a transfer completed + * @param[in] peripheralIncrement Defines whether the peripheral address is + * incremented after a transfer completed + * @param[in] priority Priority of the DMA channel + * @param[in] circularMode Transfer data in circular mode? + */ + static void + configure(DataTransferDirection direction, MemoryDataSize memoryDataSize, + PeripheralDataSize peripheralDataSize, + MemoryIncrementMode memoryIncrement, + PeripheralIncrementMode peripheralIncrement, + Priority priority = Priority::Medium, + CircularMode circularMode = CircularMode::Disabled) + { + stop(); + + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CCR = uint32_t(direction) | uint32_t(memoryDataSize) | + uint32_t(peripheralDataSize) | uint32_t(memoryIncrement) | + uint32_t(peripheralIncrement) | uint32_t(priority) | + uint32_t(circularMode); + } + + /** + * Start the transfer of the DMA channel + */ + static void + start(); + /** + * Stop a DMA channel transfer + */ + static void + stop(); + + /** + * Get the direction of the data transfer + */ + static DataTransferDirection + getDataTransferDirection(); + + /** + * Set the memory address of the DMA channel + * + * @note In Mem2Mem mode use this method to set the memory source address. + * + * @param[in] address Source address + */ + static void + setMemoryAddress(uintptr_t address) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CMAR = address; + } + /** + * Set the peripheral address of the DMA channel + * + * @note In Mem2Mem mode use this method to set the memory destination address. + * + * @param[in] address Destination address + */ + static void + setPeripheralAddress(uintptr_t address) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CPAR = address; + } + + /** + * Enable/disable memory increment + * + * When enabled, the memory address is incremented by the size of the data + * (e.g. 1 for byte transfers, 4 for word transfers) after the transfer + * completed. + * + * @param[in] increment Enable/disable + */ + static void + setMemoryIncrementMode(bool increment) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + if (increment) + Base->CCR |= uint32_t(MemoryIncrementMode::Increment); + else + Base->CCR &= ~uint32_t(MemoryIncrementMode::Increment); + } + + /** + * Enable/disable peripheral increment + * + * When enabled, the peripheral address is incremented by the size of the data + * (e.g. 1 for byte transfers, 4 for word transfers) after the transfer + * completed. + * + * @param[in] increment Enable/disable + */ + static void + setPeripheralIncrementMode(bool increment) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + if (increment) + Base->CCR |= uint32_t(PeripheralIncrementMode::Increment); + else + Base->CCR &= ~uint32_t(PeripheralIncrementMode::Increment); + } + + /** + * Set length of data to transfer + */ + static void + setDataLength(std::size_t length) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CNDTR = length; + } + + /** + * Enable IRQ of this DMA channel (e.g. transfer complete or error) + */ + static void + enableInterrupt(Interrupt_t irq) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CCR |= irq.value; + } + /** + * Disable IRQ of this DMA channel (e.g. transfer complete or error) + */ + static void + disableInterrupt(Interrupt_t irq) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CCR &= ~(irq.value); + } +}; + +} // namespace platform +} // namespace modm + +#include "dma_hal_impl.hpp" + +#endif // MODM_STM32_DMA_HAL_HPP diff --git a/src/modm/platform/dma/stm32/dma_hal_impl.hpp.in b/src/modm/platform/dma/stm32/dma_hal_impl.hpp.in new file mode 100644 index 0000000000..74368fd20a --- /dev/null +++ b/src/modm/platform/dma/stm32/dma_hal_impl.hpp.in @@ -0,0 +1,58 @@ +/* + * 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_DMA_HAL_HPP +# error "Don't include this file directly, use 'dma.hpp' instead!" +#endif +#include + +template +void +modm::platform::DmaChannelHal::start() +{ + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + +%% if target["family"] == "f4" + Base->CR |= DMA_SxCR_EN; +%% else + Base->CCR |= DMA_CCR_EN; +%% endif +} + +template +void +modm::platform::DmaChannelHal::stop() +{ + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + +%% if target["family"] == "f4" + Base->CR &= ~DMA_SxCR_EN; + while (Base->SCR & DMA_SxCR_EN); // wait for stream to be stopped +%% else + Base->CCR &= ~DMA_CCR_EN; + while (Base->CCR & DMA_CCR_EN); // wait for stream to be stopped +%% endif +} + +template +modm::platform::DmaBase::DataTransferDirection +modm::platform::DmaChannelHal::getDataTransferDirection() +{ + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + + return static_cast( +%% if target["family"] == "f4" + Base->CR & (DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1)); +%% else + Base->CCR & (DMA_CCR_MEM2MEM | DMA_CCR_DIR)); +%% endif +} diff --git a/src/modm/platform/dma/stm32/dma_impl.hpp.in b/src/modm/platform/dma/stm32/dma_impl.hpp.in deleted file mode 100644 index 322e0f5ebf..0000000000 --- a/src/modm/platform/dma/stm32/dma_impl.hpp.in +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2014, Kevin Läufer - * Copyright (c) 2014, 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/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_STM32_DMA{{ id }}_HPP -# error "Don't include this file directly, use 'dma_{{ id }}.hpp' instead!" -#endif -#include - -%% if target["family"] == "f4" - %% set streams = range(0,8) -%% elif target["family"] == "f3" - %% if id == 1 - %% set streams = range(1,7) - %% elif id == 2 - %% set streams = range(1,5) - %% endif -%% endif - -void -modm::platform::Dma{{ id }}::enable() -{ - Rcc::enable(); -} - -void -modm::platform::Dma{{ id }}::disable() -{ - Rcc::disable(); -} - - -%% for stream_id in streams - -%% if target["family"] == "f4" - %% set stream = "DMA" ~ id ~ "_Stream" ~ stream_id - %% set reg = stream ~ "->CR" - %% set mem = stream ~ "->M0AR" - %% set per = stream ~ "->PAR" - %% set length = stream ~ "->NDTR" - %% set prefix = "DMA_SxCR" -%% elif target["family"] == "f3" - %% set channel = "DMA" ~ id ~ "_Channel" ~ stream_id - %% set reg = channel ~ "->CCR" - %% set mem = channel ~ "->CMAR" - %% set per = channel ~ "->CPAR" - %% set length = channel ~ "->CNDTR" - %% set prefix = "DMA_CCR" -%% endif - -%% set pointer_types = [8, 16, 32] -%% for type in pointer_types -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::setMemorySource(uint{{type}}_t* address, - MemoryIncrementMode inc -%% if target["family"] == "f4" -, MemoryBurstTransfer transfer) -%% else -) -%% endif -{ - //stop(); - static constexpr MemoryDataSize size = MemoryDataSize::Bit{{type}}; - // a memory source could mean that either a memory-to-peripheral or a - // memory-to-memory transfer is intended - // we'll use the current transfer mode to determine what to do - DataTransferDirection dir; - if(getDataTransferDirection() == DataTransferDirection::MemoryToPeripheral) { - dir = DataTransferDirection::MemoryToPeripheral; - {{ mem }} = reinterpret_cast(address); - - } else { - dir = DataTransferDirection::MemoryToMemory; - {{ per }} = reinterpret_cast(address); - } - {{ reg }} = ({{ reg }} & ~memoryMask) | static_cast(size) - | static_cast(inc) | static_cast(dir) -%% if target["family"] == "f4" - | static_cast(transfer); -%% else - ; -%% endif -} - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::setPeripheralSource(uint{{type}}_t* address, - PeripheralIncrementMode inc -%% if target["family"] == "f4" -, PeripheralBurstTransfer transfer) -%% else -) -%% endif -{ - //stop(); - static constexpr PeripheralDataSize size = PeripheralDataSize::Bit{{type}}; - static constexpr DataTransferDirection dir = DataTransferDirection::PeripheralToMemory; - {{ per }} = reinterpret_cast(address); - {{ reg }} = ({{ reg }} & ~peripheralMask) | static_cast(size) - | static_cast(inc) | static_cast(dir) -%% if target["family"] == "f4" - | static_cast(transfer); -%% else - ; -%% endif -} -%% endfor - -%% for type in pointer_types - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::setMemoryDestination(uint{{type}}_t* address, - MemoryIncrementMode inc -%% if target["family"] == "f4" -, MemoryBurstTransfer transfer) -%% else -) -%% endif -{ - //stop(); - static constexpr MemoryDataSize size = MemoryDataSize::Bit{{type}}; - // a memory source could mean that either a memory-to-peripheral or a - // memory-to-memory transfer is intended - // we'll use the current transfer mode to determine what to do - DataTransferDirection dir; - if(getDataTransferDirection() == DataTransferDirection::PeripheralToMemory) { - dir = DataTransferDirection::PeripheralToMemory; - {{ mem }} = reinterpret_cast(address); - } else { - dir = DataTransferDirection::MemoryToMemory; - {{ mem }} = reinterpret_cast(address); - } - {{ reg }} = ({{ reg }} & ~memoryMask) | static_cast(size) - | static_cast(inc) | static_cast(dir) -%% if target["family"] == "f4" - | static_cast(transfer); -%% else - ; -%% endif -} - - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::setPeripheralDestination(uint{{type}}_t* address, - PeripheralIncrementMode inc -%% if target["family"] == "f4" -, PeripheralBurstTransfer transfer) -%% else -) -%% endif -{ - //stop(); - static constexpr PeripheralDataSize size = PeripheralDataSize::Bit{{type}}; - // if memory-to-memory mode was selected, the memory source - // will have been saved in the Peripheral Register - // in memory-to-peripheral mode, the memory source will - // need to be saved in the Memory Register - if(getDataTransferDirection() == DataTransferDirection::MemoryToMemory) { - {{ mem }} = {{ per }}; - } - static constexpr DataTransferDirection dir = DataTransferDirection::MemoryToPeripheral; - {{ per }} = reinterpret_cast(address); - {{ reg }} = ({{ reg }} & ~peripheralMask) | static_cast(size) - | static_cast(inc) | static_cast(dir) -%% if target["family"] == "f4" - | static_cast(transfer); -%% else - ; -%% endif -} -%% endfor - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::stop() -{ -%% if target["family"] == "f4" - {{ reg }} &= ~DMA_SxCR_EN; - while({{ reg }} & DMA_SxCR_EN); // wait for stream to be stopped -%% else - {{ reg }} &= ~DMA_CCR_EN; - while({{ reg }} & DMA_CCR_EN); // wait for stream to be stopped -%% endif -} - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::configure( -%% if target["family"] == "f4" - Channel channel, -%% endif - uint16_t sample_length, Priority priority, CircularMode circular -%% if target["family"] == "f4" - , FlowControl flow) -%% elif target["family"] == "f3" - ) -%% endif -{ - //stop(); - {{ reg }} = ({{ reg }} & ~configmask) | static_cast(priority) - | static_cast(circular) -%% if target["family"] == "f4" - | static_cast(channel) | static_cast(flow); -%% else - ; -%% endif - {{ length }} = sample_length; -} - -void -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::start() -{ -%% if target["family"] == "f4" - {{ reg }} |= DMA_SxCR_EN; -%% else - // clear interrupt flags - DMA{{ id }}->ISR |= DMA_ISR_TEIF{{ stream_id }} | - DMA_ISR_TCIF{{ stream_id }} | - DMA_ISR_HTIF{{ stream_id }} | - DMA_ISR_GIF{{ stream_id }}; - {{ reg }} |= DMA_CCR_EN; -%% endif -} - -bool -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::isFinished() -{ -%% if target["family"] == "f4" - return !({{ reg }} & DMA_SxCR_EN); -%% else - return (DMA{{ id }}->ISR & DMA_ISR_TCIF{{ stream_id }}); -%% endif -} - -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::DataTransferDirection -modm::platform::Dma{{ id }}::Stream{{ stream_id }}::getDataTransferDirection() -{ - return static_cast( -%% if target["family"] == "f4" - {{ reg }} & (DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1)); -%% else - {{ reg }} & (DMA_CCR_MEM2MEM | DMA_CCR_DIR)); -%% endif -} - -%% endfor diff --git a/src/modm/platform/dma/stm32/module.lb b/src/modm/platform/dma/stm32/module.lb index 8be23f694d..20d50a2d39 100644 --- a/src/modm/platform/dma/stm32/module.lb +++ b/src/modm/platform/dma/stm32/module.lb @@ -3,6 +3,7 @@ # # Copyright (c) 2016-2018, Niklas Hauser # Copyright (c) 2017, Fabian Greif +# Copyright (c) 2020, Mike Wolfram # # This file is part of the modm project. # @@ -11,31 +12,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # ----------------------------------------------------------------------------- -class Instance(Module): - def __init__(self, instance): - self.instance = instance - - def init(self, module): - module.name = str(self.instance) - module.description = "Instance {}".format(self.instance) - - def prepare(self, module, options): - module.depends(":platform:dma") - return True - - def build(self, env): - device = env[":target"] - - properties = device.properties - properties["target"] = device.identifier - properties["id"] = self.instance - - env.substitutions = properties - env.outbasepath = "modm/src/modm/platform/dma" - - env.template("dma.hpp.in", "dma_{}.hpp".format(self.instance)) - env.template("dma_impl.hpp.in", "dma_{}_impl.hpp".format(self.instance)) - def init(module): module.name = ":platform:dma" module.description = "Direct Memory Access (DMA)" @@ -45,15 +21,14 @@ def prepare(module, options): if not device.has_driver("dma:stm32*"): return False - # FIXME the driver is referenced in STM32F0 device files, but appears only to support STM32F3/4 - if device.identifier["family"] not in ["f3", "f4"]: + # FIXME the driver is for L4 only + if device.identifier["family"] not in ["l4"]: + return False + if device.identifier["name"] not in ["12", "22", "31", "32", "33", "42", "43", "51", "52", "62", "75", "76", "86", "96", "A6"]: return False module.depends(":cmsis:device", ":platform:rcc") - for instance in device.get_driver("dma")["instance"]: - module.add_submodule(Instance(int(instance))) - return True def build(env): @@ -61,8 +36,34 @@ def build(env): properties = device.properties properties["target"] = device.identifier + dma = device.get_driver("dma") + properties["dma"] = dma + + # Get the peripheral supported by DMA from device info and create a list of signals + # (also determines the maximum number of channels per controller) + signal_names = {} + controller = [] + for channels in dma["channels"]: + max_channels = 0 + for channel in channels["channel"]: + max_channels = channel["position"] + for request in channel["request"]: + for signal in request["signal"]: + if "name" in signal: + signal_name = signal["name"].capitalize() + signal_names[signal_name] = 1 + controller.append({"instance": int(channels["instance"]), "channels": int(max_channels)}) + + signal_names = sorted(list(set(signal_names))) + properties["dmaSignals"] = signal_names + properties["dmaController"] = controller env.substitutions = properties env.outbasepath = "modm/src/modm/platform/dma" env.template("dma_base.hpp.in") + env.template("dma_hal.hpp.in") + env.template("dma_hal_impl.hpp.in") + env.template("dma.hpp.in") + env.template("dma.cpp.in") + From f14ab9c221cd46808b36ff574fdd56b29f7abfc3 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 7 May 2020 06:02:44 +0200 Subject: [PATCH 019/135] [ext] Remove stray debug print() in :cmsis:core --- ext/arm/core.lb | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/arm/core.lb b/ext/arm/core.lb index 2fe37579f7..addea029f1 100644 --- a/ext/arm/core.lb +++ b/ext/arm/core.lb @@ -29,7 +29,6 @@ def build(env): env.copy("cmsis/CMSIS/Core/Include/cmsis_gcc.h", "cmsis_gcc.h") if core != "0": # 0+ has MPU support though! env.copy("cmsis/CMSIS/Core/Include/mpu_armv7.h", "mpu_armv7.h") - print(core) if core == "7": env.copy("cmsis/CMSIS/Core/Include/cachel1_armv7.h", "cachel1_armv7.h") From f7ef4a9a7ef6fefda7f9cd88927e16d01cce8ca6 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 7 May 2020 06:01:50 +0200 Subject: [PATCH 020/135] [scons] Make SConscript/site_tools more path independent SCons also adds the directory of every SConscript to the Python path, so there is no need to explicitly add it again. --- tools/build_script_generator/scons/resources/SConscript.in | 5 ----- .../scons/resources/build_target.py.in | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/tools/build_script_generator/scons/resources/SConscript.in b/tools/build_script_generator/scons/resources/SConscript.in index 1d32dc0637..43634abf74 100644 --- a/tools/build_script_generator/scons/resources/SConscript.in +++ b/tools/build_script_generator/scons/resources/SConscript.in @@ -11,11 +11,6 @@ from os.path import join, abspath Import("env") -%% if is_modm -import sys -sys.path.append(join(env.Dir("#").abspath, "modm")) -%% endif - profile = env["CONFIG_PROFILE"] %% if is_modm env["BUILDPATH"] = join(env["CONFIG_BUILD_BASE"], profile) diff --git a/tools/build_script_generator/scons/resources/build_target.py.in b/tools/build_script_generator/scons/resources/build_target.py.in index 90be860b13..30d673b10d 100644 --- a/tools/build_script_generator/scons/resources/build_target.py.in +++ b/tools/build_script_generator/scons/resources/build_target.py.in @@ -33,7 +33,7 @@ def build_target(env, sources): env.Alias("all", ["build", "run"]) %% else # The executable depends on the linkerscript - env.Depends(target=program, dependency=abspath("modm/link/linkerscript.ld")) + env.Depends(target=program, dependency="$BASEPATH/link/linkerscript.ld") env.Alias("size", env.Size(program)) %% if core.startswith("cortex-m") env.Alias("log-itm", env.LogItmOpenOcd()) From 82e95c4bbf25f33011a70f396bbabef90ea136f9 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 7 May 2020 06:07:00 +0200 Subject: [PATCH 021/135] [core] Fix Jinja2 issue in AVR delay template --- src/modm/platform/core/avr/delay_impl.hpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modm/platform/core/avr/delay_impl.hpp.in b/src/modm/platform/core/avr/delay_impl.hpp.in index 866c934eae..754935fe88 100644 --- a/src/modm/platform/core/avr/delay_impl.hpp.in +++ b/src/modm/platform/core/avr/delay_impl.hpp.in @@ -44,7 +44,7 @@ delay_us(uint32_t us) const uint32_t cycles = uint32_t(ceil(fabs((F_CPU * double(us)) / 1e6))); __builtin_avr_delay_cycles(cycles); }) : ({ -%% if f_cpu <= 6e6 +%% if f_cpu <= 6000000 constexpr int32_t cycles = (F_CPU / 1e6); for (int16_t _us = us; _us > 0; _us -= cycles) __builtin_avr_delay_cycles(1); From aaae1d127c62769b4f9c16897f84555e38d6b63c Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 7 May 2020 12:42:08 +0200 Subject: [PATCH 022/135] [gpio] Fix missing GPIO connector files for Hosted --- src/modm/platform/gpio/hosted/module.lb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modm/platform/gpio/hosted/module.lb b/src/modm/platform/gpio/hosted/module.lb index d4641c325c..99f48eb6ec 100644 --- a/src/modm/platform/gpio/hosted/module.lb +++ b/src/modm/platform/gpio/hosted/module.lb @@ -32,3 +32,5 @@ def build(env): env.template("base.hpp.in") env.template("unused.hpp.in") env.copy("../common/inverted.hpp", "inverted.hpp") + env.copy("../common/connector.hpp", "connector.hpp") + env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") From 80a9c666af1bbe8eb978abed48334e9310cceb0f Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Thu, 7 May 2020 14:24:57 +0200 Subject: [PATCH 023/135] [spi] Enhance STM32 SPI with DMA support --- examples/nucleo_l432kc/spi_dma/main.cpp | 44 +++++ examples/nucleo_l432kc/spi_dma/project.xml | 12 ++ src/modm/board/nucleo_l432kc/board.hpp | 3 + src/modm/platform/spi/stm32/module.lb | 3 + src/modm/platform/spi/stm32/spi_base.hpp.in | 11 ++ src/modm/platform/spi/stm32/spi_hal.hpp.in | 6 + .../platform/spi/stm32/spi_hal_impl.hpp.in | 10 + .../platform/spi/stm32/spi_master_dma.hpp.in | 99 ++++++++++ .../spi/stm32/spi_master_dma_impl.hpp.in | 185 ++++++++++++++++++ 9 files changed, 373 insertions(+) create mode 100644 examples/nucleo_l432kc/spi_dma/main.cpp create mode 100644 examples/nucleo_l432kc/spi_dma/project.xml create mode 100644 src/modm/platform/spi/stm32/spi_master_dma.hpp.in create mode 100644 src/modm/platform/spi/stm32/spi_master_dma_impl.hpp.in diff --git a/examples/nucleo_l432kc/spi_dma/main.cpp b/examples/nucleo_l432kc/spi_dma/main.cpp new file mode 100644 index 0000000000..3c5b45d0f1 --- /dev/null +++ b/examples/nucleo_l432kc/spi_dma/main.cpp @@ -0,0 +1,44 @@ +/* + * 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 + +using Mosi = GpioOutputB5; +using Miso = GpioInputB4; +using Sck = GpioOutputB3; +using DmaRx = Dma1::Channel2; +using DmaTx = Dma1::Channel3; +using Spi = SpiMaster1_Dma; + +int main() +{ + Board::initialize(); + + // Enable DMA1 controller + Dma1::enable(); + // Enable SPI1 + Spi::connect(); + Spi::initialize(); + + while (true) + { + uint8_t sendBuffer[13] { "data to send" }; + uint8_t receiveBuffer[13]; + + // send out 12 bytes, don't care about response + Spi::transferBlocking(sendBuffer, nullptr, 12); + + // send out 12 bytes, read in 12 bytes + Spi::transferBlocking(sendBuffer, receiveBuffer, 12); + } + + return 0; +} diff --git a/examples/nucleo_l432kc/spi_dma/project.xml b/examples/nucleo_l432kc/spi_dma/project.xml new file mode 100644 index 0000000000..a7c7453465 --- /dev/null +++ b/examples/nucleo_l432kc/spi_dma/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-l432kc + + + + + modm:platform:gpio + modm:platform:dma + modm:platform:spi:1 + modm:build:scons + + diff --git a/src/modm/board/nucleo_l432kc/board.hpp b/src/modm/board/nucleo_l432kc/board.hpp index 5513d5fc0a..f1d4ed8165 100644 --- a/src/modm/board/nucleo_l432kc/board.hpp +++ b/src/modm/board/nucleo_l432kc/board.hpp @@ -40,6 +40,9 @@ struct SystemClock { static constexpr uint32_t Usart4 = Apb1; static constexpr uint32_t Usart5 = Apb1; + static constexpr uint32_t Spi1 = Apb2; + static constexpr uint32_t Spi2 = Apb2; + static bool inline enable() { diff --git a/src/modm/platform/spi/stm32/module.lb b/src/modm/platform/spi/stm32/module.lb index ae6ab449d3..783b3eb8a3 100644 --- a/src/modm/platform/spi/stm32/module.lb +++ b/src/modm/platform/spi/stm32/module.lb @@ -43,6 +43,9 @@ class Instance(Module): env.template("spi_hal_impl.hpp.in", "spi_hal_{}_impl.hpp".format(self.instance)) env.template("spi_master.hpp.in", "spi_master_{}.hpp".format(self.instance)) env.template("spi_master.cpp.in", "spi_master_{}.cpp".format(self.instance)) + if env.has_module(":platform:dma"): + env.template("spi_master_dma.hpp.in", "spi_master_{}_dma.hpp".format(self.instance)) + env.template("spi_master_dma_impl.hpp.in", "spi_master_{}_dma_impl.hpp".format(self.instance)) def init(module): diff --git a/src/modm/platform/spi/stm32/spi_base.hpp.in b/src/modm/platform/spi/stm32/spi_base.hpp.in index d8fad02aab..875fb3ac03 100644 --- a/src/modm/platform/spi/stm32/spi_base.hpp.in +++ b/src/modm/platform/spi/stm32/spi_base.hpp.in @@ -2,6 +2,7 @@ * Copyright (c) 2013, Kevin Läufer * Copyright (c) 2013-2017, Niklas Hauser * Copyright (c) 2014, Daniel Krebs + * Copyright (c) 2020, Mike Wolfram * * This file is part of the modm project. * @@ -41,6 +42,8 @@ public: RxBufferNotEmpty = SPI_CR2_RXNEIE, TxBufferEmpty = SPI_CR2_TXEIE, Error = SPI_CR2_ERRIE, + RxDmaEnable = SPI_CR2_RXDMAEN, + TxDmaEnable = SPI_CR2_TXDMAEN, }; MODM_FLAGS32(Interrupt); @@ -128,6 +131,14 @@ public: Div256 = SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0, }; +%% if "fifo" in features + enum class + RxFifoThreshold : uint32_t + { + HalfFull = 0, + QuarterFull = SPI_CR2_FRXTH, + }; +%% endif }; } // namespace platform diff --git a/src/modm/platform/spi/stm32/spi_hal.hpp.in b/src/modm/platform/spi/stm32/spi_hal.hpp.in index 2342300a39..cac0022289 100644 --- a/src/modm/platform/spi/stm32/spi_hal.hpp.in +++ b/src/modm/platform/spi/stm32/spi_hal.hpp.in @@ -2,6 +2,7 @@ * Copyright (c) 2013, Kevin Läufer * Copyright (c) 2013-2018, Niklas Hauser * Copyright (c) 2014, Daniel Krebs + * Copyright (c) 2020, Mike Wolfram * * This file is part of the modm project. * @@ -67,6 +68,11 @@ public: static void setMasterSelection(MasterSelection masterSelection); +%% if "fifo" in features + static void + setRxFifoThreshold(RxFifoThreshold threshold); +%% endif + /// Returns true if data has been received static bool isReceiveRegisterNotEmpty(); diff --git a/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in b/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in index f63c4658fd..75d5170a8b 100644 --- a/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in +++ b/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in @@ -2,6 +2,7 @@ * Copyright (c) 2013, Kevin Läufer * Copyright (c) 2013-2017, Niklas Hauser * Copyright (c) 2014, Daniel Krebs + * Copyright (c) 2020, Mike Wolfram * * This file is part of the modm project. * @@ -97,6 +98,15 @@ modm::platform::SpiHal{{ id }}::setMasterSelection(MasterSelection masterSelecti | static_cast(masterSelection); } +%% if "fifo" in features +inline void +modm::platform::SpiHal{{ id }}::setRxFifoThreshold(RxFifoThreshold threshold) +{ + SPI{{ id }}->CR2 = (SPI{{ id }}->CR2 & ~static_cast(RxFifoThreshold::QuarterFull)) + | static_cast(threshold); +} +%% endif + inline bool modm::platform::SpiHal{{ id }}::isReceiveRegisterNotEmpty() { diff --git a/src/modm/platform/spi/stm32/spi_master_dma.hpp.in b/src/modm/platform/spi/stm32/spi_master_dma.hpp.in new file mode 100644 index 0000000000..24878b1680 --- /dev/null +++ b/src/modm/platform/spi/stm32/spi_master_dma.hpp.in @@ -0,0 +1,99 @@ +/* + * 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_SPI_MASTER{{ id }}_DMA_HPP +#define MODM_STM32_SPI_MASTER{{ id }}_DMA_HPP + +#include +#include "spi_master_{{ id }}.hpp" + +namespace modm +{ + +namespace platform +{ + +/** + * Serial peripheral interface (SPI{{ id }}) with DMA support. + * + * This class uses the DMA controller for sending and receiving data, which + * greatly reduces the CPU load. Beside passing the DMA channels as template + * parameters the class can be used in the same way like the SpiMaster{{ id }}. + * + * @tparam DmaChannelRX DMA channel for receiving + * @tparam DmaChannelTX DMA channel for sending + * + * @author Mike Wolfram + * @ingroup modm_platform_spi modm_platform_spi_{{id}} + */ +template +class SpiMaster{{ id }}_Dma : public SpiMaster{{ id }} +{ + struct Dma { + using RxChannel = typename DmaChannelRx::template RequestMapping< + Peripheral::Spi{{ id }}, DmaBase::Signal::Rx>::Channel; + using TxChannel = typename DmaChannelTx::template RequestMapping< + Peripheral::Spi{{ id }}, DmaBase::Signal::Tx>::Channel; + static constexpr DmaBase::Request RxRequest = DmaChannelRx::template RequestMapping< + Peripheral::Spi{{ id }}, DmaBase::Signal::Rx>::Request; + static constexpr DmaBase::Request TxRequest = DmaChannelTx::template RequestMapping< + Peripheral::Spi{{ id }}, DmaBase::Signal::Tx>::Request; + }; + +public: + /** + * Initialize DMA and SPI{{ id }} + */ + template + static void + initialize(); + + static uint8_t + transferBlocking(uint8_t data) + { + return RF_CALL_BLOCKING(transfer(data)); + } + + static void + transferBlocking(uint8_t *tx, uint8_t *rx, std::size_t length) + { + RF_CALL_BLOCKING(transfer(tx, rx, length)); + } + + static modm::ResumableResult + transfer(uint8_t data); + + static modm::ResumableResult + transfer(uint8_t *tx, uint8_t *rx, std::size_t length); + +private: + static void + handleDmaTransferError(); + static void + handleDmaReceiveComplete(); + static void + handleDmaTransmitComplete(); + + static inline bool dmaError { false }; + static inline bool dmaTransmitComplete { false }; + static inline bool dmaReceiveComplete { false }; + + // needed for transfers where no RX or TX buffers are given + static inline uint8_t dmaDummy { 0 }; +}; + +} // namespace platform + +} // namespace modm + +#include "spi_master_{{ id }}_dma_impl.hpp" + +#endif // MODM_STM32_SPI_MASTER{{ id }}_DMA_HPP diff --git a/src/modm/platform/spi/stm32/spi_master_dma_impl.hpp.in b/src/modm/platform/spi/stm32/spi_master_dma_impl.hpp.in new file mode 100644 index 0000000000..5ca44002db --- /dev/null +++ b/src/modm/platform/spi/stm32/spi_master_dma_impl.hpp.in @@ -0,0 +1,185 @@ +/* + * 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_SPI_MASTER{{ id }}_DMA_HPP +# error "Don't include this file directly, use 'spi_master_{{ id }}_dma.hpp' instead!" +#endif + +template +template +void +modm::platform::SpiMaster{{ id }}_Dma::initialize() +{ + // Configure the DMA channels, then calls SpiMaster{{ id }}::initialzie(). + Dma::RxChannel::configure(DmaBase::DataTransferDirection::PeripheralToMemory, + DmaBase::MemoryDataSize::Byte, DmaBase::PeripheralDataSize::Byte, + DmaBase::MemoryIncrementMode::Increment, DmaBase::PeripheralIncrementMode::Fixed, + DmaBase::Priority::High); + Dma::RxChannel::setPeripheralAddress(SPI{{ id }}_BASE + 0x0c); + Dma::RxChannel::setTransferErrorIrqHandler(handleDmaTransferError); + Dma::RxChannel::setTransferCompleteIrqHandler(handleDmaReceiveComplete); + Dma::RxChannel::enableInterruptVector(); + Dma::RxChannel::enableInterrupt(DmaBase::Interrupt::Error | + DmaBase::Interrupt::TransferComplete); + Dma::RxChannel::template setPeripheralRequest(); + + Dma::TxChannel::configure(DmaBase::DataTransferDirection::MemoryToPeripheral, + DmaBase::MemoryDataSize::Byte, DmaBase::PeripheralDataSize::Byte, + DmaBase::MemoryIncrementMode::Increment, DmaBase::PeripheralIncrementMode::Fixed, + DmaBase::Priority::High); + Dma::TxChannel::setPeripheralAddress(SPI{{ id }}_BASE + 0x0c); + Dma::TxChannel::setTransferErrorIrqHandler(handleDmaTransferError); + Dma::TxChannel::setTransferCompleteIrqHandler(handleDmaTransmitComplete); + Dma::TxChannel::enableInterruptVector(); + Dma::TxChannel::enableInterrupt(DmaBase::Interrupt::Error | + DmaBase::Interrupt::TransferComplete); + Dma::TxChannel::template setPeripheralRequest(); + + SpiMaster{{ id }}::initialize(); + +%% if "fifo" in features + SpiHal{{ id }}::setRxFifoThreshold(SpiHal{{ id }}::RxFifoThreshold::QuarterFull); +%% endif +} + +template +modm::ResumableResult +modm::platform::SpiMaster{{ id }}_Dma::transfer(uint8_t data) +{ + // this is a manually implemented "fast resumable function" + // there is no context or nesting protection, since we don't need it. + // there are only two states encoded into 1 bit (LSB of state): + // 1. waiting to start, and + // 2. waiting to finish. + // LSB != Bit0 ? + if ( !(state & Bit0) ) + { + // disable DMA for single byte transfer + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxDmaEnable | + SpiBase::Interrupt::RxDmaEnable); + + // wait for previous transfer to finish + if (!SpiHal{{ id }}::isTransmitRegisterEmpty()) + return {modm::rf::Running}; + + // start transfer by copying data into register + SpiHal{{ id }}::write(data); + + // set LSB = Bit0 + state |= Bit0; + } + + if (!SpiHal{{ id }}::isReceiveRegisterNotEmpty()) + return {modm::rf::Running}; + + SpiHal{{ id }}::read(data); + + // transfer finished + state &= ~Bit0; + return {modm::rf::Stop, data}; +} + +template +modm::ResumableResult +modm::platform::SpiMaster{{ id }}_Dma::transfer(uint8_t *tx, + uint8_t *rx, std::size_t length) +{ + // this is a manually implemented "fast resumable function" + // there is no context or nesting protection, since we don't need it. + // there are only two states encoded into 1 bit (Bit1 of state): + // 1. initialize index, and + // 2. wait for 1-byte transfer to finish. + + // we are only interested in Bit1 + switch(state & Bit1) + { + case 0: + // we will only visit this state once + state |= Bit1; + dmaError = false; + SpiHal{{ id }}::enableInterrupt(SpiBase::Interrupt::TxDmaEnable | + SpiBase::Interrupt::RxDmaEnable); + + if (tx) { + Dma::TxChannel::setMemoryAddress(uint32_t(tx)); + Dma::TxChannel::setMemoryIncrementMode(true); + } else { + Dma::TxChannel::setMemoryAddress(uint32_t(&dmaDummy)); + Dma::TxChannel::setMemoryIncrementMode(false); + } + if (rx) { + Dma::RxChannel::setMemoryAddress(uint32_t(rx)); + Dma::RxChannel::setMemoryIncrementMode(true); + } else { + Dma::RxChannel::setMemoryAddress(uint32_t(&dmaDummy)); + Dma::RxChannel::setMemoryIncrementMode(false); + } + + Dma::RxChannel::setDataLength(length); + dmaReceiveComplete = false; + Dma::RxChannel::start(); + + Dma::TxChannel::setDataLength(length); + dmaTransmitComplete = false; + Dma::TxChannel::start(); + + [[fallthrough]]; + + default: + while (true) { + if (dmaError) + break; + if (not dmaTransmitComplete and not dmaReceiveComplete) + return { modm::rf::Running }; + if (SpiHal{{ id }}::getInterruptFlags() & SpiBase::InterruptFlag::FifoTxLevel) + return { modm::rf::Running }; + if (SpiHal{{ id }}::getInterruptFlags() & SpiBase::InterruptFlag::Busy) + return { modm::rf::Running }; + if (SpiHal{{ id }}::getInterruptFlags() & SpiBase::InterruptFlag::FifoRxLevel) + return { modm::rf::Running }; + + break; + } + + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxDmaEnable | + SpiBase::Interrupt::RxDmaEnable); + // clear the state + state &= ~Bit1; + return {modm::rf::Stop}; + } +} + +template +void +modm::platform::SpiMaster{{ id }}_Dma::handleDmaTransferError() +{ + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxDmaEnable | + SpiBase::Interrupt::RxDmaEnable); + Dma::RxChannel::stop(); + Dma::TxChannel::stop(); + dmaError = true; +} + +template +void +modm::platform::SpiMaster{{ id }}_Dma::handleDmaReceiveComplete() +{ + Dma::RxChannel::stop(); + dmaReceiveComplete = true; +} + +template +void +modm::platform::SpiMaster{{ id }}_Dma::handleDmaTransmitComplete() +{ + Dma::TxChannel::stop(); + dmaTransmitComplete = true; +} From cd065e113df76feb66a23566561ee61f99b295b8 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Fri, 8 May 2020 23:21:31 +0200 Subject: [PATCH 024/135] [doc] How to create a SystemClock struct --- src/modm/board/module.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/modm/board/module.md b/src/modm/board/module.md index 0738c893d2..c7960b06de 100644 --- a/src/modm/board/module.md +++ b/src/modm/board/module.md @@ -80,3 +80,42 @@ own BSP: (check for `module.depends(...)` in the BSPs `module.lb`). 4. You may need to manually add the pre-defined collector values to your project configuration (check for `env.collect(...)` in the BSPs `module.lb`). + + +## Create the SystemClock struct + +The easiest way using ST's CubeMX tool. + +### 1. CubeMX Clock Graph + +First we create a project in CubeMX with the desired microcontroller using the +largest (pin-count, flash) variant. CubeMX displays something like this in the +"Clock configuration" tab: + +![ST CubeMX Clock Configuration example](https://user-images.githubusercontent.com/2820734/81305132-43715000-907e-11ea-8b8e-b444dc7560cf.png) + +Then configure all clocks, muxes, multipliers and dividers to the highest +allowed clock speeds (*). + +(*) exceptions: E.g. USB usually requires exactly 48 MHz. + +This settings are reflected in the constants `static constexpr uint32_t Frequency`, +`Apb1` and `Apb2` as well as in `const Rcc::PllFactors pllFactors{...}` and the +following lines. The `PllFactors` struct should be fairly self-explanatory. + + +### 2. Peripheral Mapping + +As we can see in the graphic above, there are different clock ranges. +Each peripheral is connected to a clock domain. +Some peripherals have an upstream clock mux, this is currently ignored in +modm and the default setting for the clock mux is assumed. + +The figure shows the block diagram of the controller, which can be found at +the beginning of the data sheet (not in the reference manual): + +![STM32 Block Diagram example](https://user-images.githubusercontent.com/2820734/81448035-fffe0b00-917d-11ea-8a1f-428548851e63.png) + +For each peripheral we create a `static constexpr uint32_t` member in +the `struct SystemClock` and assign the value of the clock domain to which +the peripheral is connected. From a2ca836b853121043881751ab18500d02a843011 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Mon, 23 Mar 2020 01:52:07 +0100 Subject: [PATCH 025/135] gitignore: codium/vscode editor --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9e1833ef6a..3f3d717da9 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ CMakeLists.txt #Clion **/.idea/ +# Codium/Code +**/.vscode/ + # ignore generated code in the example folder examples/**/modm examples/**/compile_commands.json From a010cbb1e1c9e6ce03a1448a6d61c81864dbaab2 Mon Sep 17 00:00:00 2001 From: Felix Petriconi Date: Mon, 11 May 2020 00:30:37 +0200 Subject: [PATCH 026/135] [board] Add NUCLEO-F746ZG support --- README.md | 4 +- docs/src/guide/installation.md | 13 ++- src/modm/board/nucleo_f746zg/board.hpp | 152 +++++++++++++++++++++++++ src/modm/board/nucleo_f746zg/board.xml | 16 +++ src/modm/board/nucleo_f746zg/module.lb | 45 ++++++++ 5 files changed, 227 insertions(+), 3 deletions(-) create mode 100755 src/modm/board/nucleo_f746zg/board.hpp create mode 100755 src/modm/board/nucleo_f746zg/board.xml create mode 100755 src/modm/board/nucleo_f746zg/module.lb diff --git a/README.md b/README.md index d0696dbeaf..2902fd020f 100644 --- a/README.md +++ b/README.md @@ -145,14 +145,16 @@ documentation. NUCLEO-F429ZI NUCLEO-F446RE +NUCLEO-F746ZG NUCLEO-G071RB NUCLEO-G474RE NUCLEO-L152RE -NUCLEO-L432KC +NUCLEO-L432KC NUCLEO-L476RG OLIMEXINO-STM32 STM32F030F4P6-DEMO + diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md index 713859b0f4..b66c58be6f 100644 --- a/docs/src/guide/installation.md +++ b/docs/src/guide/installation.md @@ -7,7 +7,7 @@ with modm: - [Software Construct][scons] or [CMake][] - [Library Builder][lbuild] - AVR toolchain: [avr-gcc][] and [avrdude][] -- ARM toolchain: [gcc-arm-toolchain][] and [OpenOCD][]. +- ARM toolchain: [gcc-arm-toolchain][] and [OpenOCD][] (HEAD version!). - Optional: [Doxygen][] or [Doxypress][] - Optional: [gdbgui][] for IDE-independent debugging @@ -71,7 +71,7 @@ some of these libraries as well, depending on what modm modules you use: For Ubuntu 18.04LTS, these commands install the basic build system: - sudo apt-get install python3 python3-pip scons git openocd + sudo apt-get install python3 python3-pip scons git sudo apt-get --no-install-recommends install doxygen pip3 install modm @@ -95,6 +95,14 @@ for 64-bit Linux and adding its `/bin` directory to your path. **Even though your distribution may ship their own ARM toolchain, we very strongly recommend using the official toolchain, since all of modm is tested with it.** +Install OpenOCD via your package manager: + + sudo apt-get install openocd + +The latest OpenOCD release v0.10.0 (as of May 2020) is too old for some targets +(STM32G0, STM32G4, STM32F7). To program these targets you need to compile the +[HEAD version of OpenOCD from source][openocd-source]. + We recommend the use of a graphical frontend for GDB called [gdbgui][]: pip3 install gdbgui @@ -141,6 +149,7 @@ You'll need to add both `/bin` paths to your `PATH` variable manually. [examples]: https://github.com/modm-io/modm/tree/develop/examples [gcc-arm-toolchain]: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm [openocd]: http://openocd.org +[openocd-source]: https://github.com/ntfreak/openocd [avr-gcc]: https://www.nongnu.org/avr-libc [avrdude]: https://www.nongnu.org/avrdude [lbuild]: https://github.com/modm-io/lbuild diff --git a/src/modm/board/nucleo_f746zg/board.hpp b/src/modm/board/nucleo_f746zg/board.hpp new file mode 100755 index 0000000000..54d9451a3b --- /dev/null +++ b/src/modm/board/nucleo_f746zg/board.hpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016-2017, Sascha Schade + * Copyright (c) 2016-2018, Niklas Hauser + * Cpoyright (c) 2020, Felix Petriconi + * + * 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_F746ZG_HPP +#define MODM_STM32_NUCLEO_F746ZG_HPP + +#include +#include +#include +/// @ingroup modm_board_nucleo_f746zg +#define MODM_BOARD_HAS_LOGGER + +using namespace modm::platform; + +/// @ingroup modm_board_nucleo_f746zg +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); + PWR->CR1 |= PWR_CR1_ODEN; // Enable overdrive mode + while (not (PWR->CSR1 & PWR_CSR1_ODRDY)) ; + 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_F746ZG_HPP diff --git a/src/modm/board/nucleo_f746zg/board.xml b/src/modm/board/nucleo_f746zg/board.xml new file mode 100755 index 0000000000..d4b054b195 --- /dev/null +++ b/src/modm/board/nucleo_f746zg/board.xml @@ -0,0 +1,16 @@ + + + + ../../../../repo.lb + + + + + + + + + + modm:board:nucleo-f746zg + + diff --git a/src/modm/board/nucleo_f746zg/module.lb b/src/modm/board/nucleo_f746zg/module.lb new file mode 100755 index 0000000000..70e01ac5c7 --- /dev/null +++ b/src/modm/board/nucleo_f746zg/module.lb @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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-f746zg" + module.description = """\ +# NUCLEO-F746ZG + +[Nucleo kit for STM32F746ZG](https://www.st.com/en/evaluation-tools/nucleo-f746zg.html) +""" + +def prepare(module, options): + if not options[":target"].partname.startswith("stm32f746zg"): + 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 c38550a4b6f391feb96f7aa3b411a95046f75bfa Mon Sep 17 00:00:00 2001 From: Felix Petriconi Date: Mon, 11 May 2020 00:31:00 +0200 Subject: [PATCH 027/135] [examples] Add NUCLEO-F746ZG example --- .circleci/config.yml | 2 +- examples/nucleo_f746zg/blink/main.cpp | 40 ++++++++++++++++++++++++ examples/nucleo_f746zg/blink/project.xml | 9 ++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100755 examples/nucleo_f746zg/blink/main.cpp create mode 100755 examples/nucleo_f746zg/blink/project.xml diff --git a/.circleci/config.yml b/.circleci/config.yml index 83121040d4..25330c979a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -93,7 +93,7 @@ jobs: - run: name: Examples STM32F7 Series command: | - (cd examples && ../tools/scripts/examples_compile.py stm32f746g_discovery stm32f769i_discovery) + (cd examples && ../tools/scripts/examples_compile.py stm32f746g_discovery stm32f769i_discovery nucleo_f746zg) - run: name: Examples STM32G0 Series command: | diff --git a/examples/nucleo_f746zg/blink/main.cpp b/examples/nucleo_f746zg/blink/main.cpp new file mode 100755 index 0000000000..205bc44743 --- /dev/null +++ b/examples/nucleo_f746zg/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_f746zg/blink/project.xml b/examples/nucleo_f746zg/blink/project.xml new file mode 100755 index 0000000000..f35dffd1f0 --- /dev/null +++ b/examples/nucleo_f746zg/blink/project.xml @@ -0,0 +1,9 @@ + + modm:nucleo-f746zg + + + + + modm:build:scons + + From 30c95fdf225eaf17a6ae71a6a33a242a5f0c483f Mon Sep 17 00:00:00 2001 From: Christopher Durand Date: Mon, 4 Nov 2019 00:14:00 +0100 Subject: [PATCH 028/135] [driver] Add driver for LP503x pwm led driver --- README.md | 9 +- src/modm/driver/pwm/lp503x.hpp.in | 245 +++++++++++++++++++++++++ src/modm/driver/pwm/lp503x.lb | 37 ++++ src/modm/driver/pwm/lp503x_impl.hpp.in | 184 +++++++++++++++++++ 4 files changed, 471 insertions(+), 4 deletions(-) create mode 100644 src/modm/driver/pwm/lp503x.hpp.in create mode 100644 src/modm/driver/pwm/lp503x.lb create mode 100644 src/modm/driver/pwm/lp503x_impl.hpp.in diff --git a/README.md b/README.md index 2902fd020f..af0fdde0d6 100644 --- a/README.md +++ b/README.md @@ -207,34 +207,35 @@ can easily configure them for you specific needs. LIS3DSH LM75 +LP503X LSM303A LTC2984 MAX6966 MAX7219 MCP23X17 -MCP2515 +MCP2515 NOKIA5110 NRF24 TFT-DISPLAY PAT9125EL PCA8574 -PCA9535 +PCA9535 PCA9548A PCA9685 SIEMENS-S65 SIEMENS-S75 SK6812 -SK9822 +SK9822 SSD1306 TCS3414 TCS3472 TLC594X TMP102 -TMP175 +TMP175 VL53L0 VL6180 WS2812 diff --git a/src/modm/driver/pwm/lp503x.hpp.in b/src/modm/driver/pwm/lp503x.hpp.in new file mode 100644 index 0000000000..b973d81166 --- /dev/null +++ b/src/modm/driver/pwm/lp503x.hpp.in @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2019, Christopher Durand + * + * 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_LP503x_HPP +#define MODM_LP503x_HPP + +#include + +namespace modm +{ + +/// @ingroup modm_driver_lp503x +struct lp503x +{ + enum class Register : uint8_t + { + DeviceConfig0 = 0x00, + DeviceConfig1 = 0x01, + LedConfig0 = 0x02, + LedConfig1 = 0x03, + BankBrightness = 0x04, + BankAColor = 0x05, + BankBColor = 0x06, + BankCColor = 0x07, + Led0Brightness = 0x08, + // Led1...11Brightness = 0x09...0x13 + Out0Color = 0x14, + // Out1...35Color = 0x15...0x37 + + Reset = 0x38 + }; + + enum class DeviceConfig0 : uint8_t + { + ChipEnable = (1 << 6) + }; + MODM_FLAGS8(DeviceConfig0); + + enum class DeviceConfig1 : uint8_t + { + LedGlobalOff = (1 << 0), + HighCurrent = (1 << 1), + PwmDithering = (1 << 2), + AutoIncrement = (1 << 3), + PowerSave = (1 << 4), + LogScale = (1 << 5) + }; + MODM_FLAGS8(DeviceConfig1); + + enum class LedBankMode : uint16_t + { + Led0 = (1 << 0), + Led1 = (1 << 1), + Led2 = (1 << 2), + Led3 = (1 << 3), + Led4 = (1 << 4), + Led5 = (1 << 5), + Led6 = (1 << 6), + Led7 = (1 << 7), + Led8 = (1 << 8), + Led9 = (1 << 9), + Led10 = (1 << 10), + Led11 = (1 << 11) + }; + MODM_FLAGS16(LedBankMode); + + enum class Reset : uint8_t + { + ResetDevice = 0xff + }; + MODM_FLAGS8(Reset); + + using RegisterValue_t = FlagsGroup; +}; // struct lp503x + +/** + * LP5030/LP5036 30/36-channel, 12-bit PWM LED controller + * + * @tparam I2cMaster I2C interface + * + * @author Christopher Durand + * @ingroup modm_driver_lp503x + */ +template +class Lp503x : public lp503x, public modm::I2cDevice +{ +public: + static_assert(Channels == 30 || Channels == 36); + + DeviceConfig1_t DefaultConfiguration = + DeviceConfig1::HighCurrent | + DeviceConfig1::PwmDithering | + DeviceConfig1::PowerSave | + DeviceConfig1::LogScale; + + Lp503x(uint8_t i2cAddress); + + /// Initialize device, call before using any other function + modm::ResumableResult + initialize(); + + /// Enable device + modm::ResumableResult + enable(); + + /// Disable device + modm::ResumableResult + disable(); + + /// Set device configuration + /// AutoIncrement is always set + modm::ResumableResult + setConfiguration(DeviceConfig1_t configuration); + + /** + * Set brightness value for individual output channel. + * + * This writes to the OutXColor registers. + * + * If the LogScale flag is set these values are scaled + * logithmically to 12 bit pwm duty-cycles (default). + * + * @param channel output channel index + * @param value 8-bit brightness value + */ + modm::ResumableResult + setChannelBrightness(uint8_t channel, uint8_t value); + +%% if options.multichannel_write + /** + * Set brightness values for multiple output channels + * in one transaction. + * + * This writes the OutXColor registers. + * + * @param channel output channel start index + * @param numChannels number of values to write + * @param value 8-bit brightness value + */ + modm::ResumableResult + setChannelBrightnessValues(uint8_t startChannel, + const uint8_t* values, uint8_t numChannels); +%% endif + + /** + * Set brightness multiplicator for output channel + * group (3n, 3n+1, 3n+2). Intended for dimming RGB Leds. + * + * When this feature is not required, this function can be ignored. + * The default of 0xFF corresponds to full on. Then the resulting LED + * brightness only depends on the individual channel settings. + * + * This setting has no effect when the LED group is configured + * in bank mode. Then the value from the BankBrightness register + * is used. + * + * Default value is 0xFF. + * + * @param value 8-bit brightness value + */ + modm::ResumableResult + setLedGroupBrightness(uint8_t index, uint8_t value); + + /** + * Enable bank mode for a led group. This allows to + * simultaniously dim multiple RGB leds by accessing + * the global bank registers. + * + * If bank mode is enabled for a led group + * (3n, 3n+1, 3n+2) its brightnesses are controlled by + * the global BankAColor, BankBColor, BankCColor registers + * and the BankBrightness register. + * The inidividual channel settings are ignored then. + */ + modm::ResumableResult + setBankModeEnabled(LedBankMode_t bankMode); + + /** + * Set bank brightness multiplicator. This controls + * all channels in bank mode. + */ + modm::ResumableResult + setBankBrightness(uint8_t value); + + /** + * Set brightness for all channels 3n + * configured in bank mode. + */ + modm::ResumableResult + setBankABrightness(uint8_t value); + + /** + * Set brightness for all channels 3n+1 + * configured in bank mode. + */ + modm::ResumableResult + setBankBBrightness(uint8_t value); + + /** + * Set brightness for all channels 3n+2 + * configured in bank mode. + */ + modm::ResumableResult + setBankCBrightness(uint8_t value); + +private: + modm::ResumableResult + writeRegister(Register reg, uint8_t value); + + modm::ResumableResult + writeRegister(Register reg, RegisterValue_t value); + +%% if options.multichannel_write + modm::ResumableResult + writeRegisters(Register startRegister, const uint8_t* values, + uint8_t numValues); +%% endif + + bool success = false; +%% if options.multichannel_write + uint8_t buffer[Channels + 1]; +%% else + uint8_t buffer[3]; +%% endif +}; + +template +using Lp5030 = Lp503x; + +template +using Lp5036 = Lp503x; + +} // namespace modm + +#include "lp503x_impl.hpp" + +#endif // MODM_LP503x_HPP diff --git a/src/modm/driver/pwm/lp503x.lb b/src/modm/driver/pwm/lp503x.lb new file mode 100644 index 0000000000..7aa70193ef --- /dev/null +++ b/src/modm/driver/pwm/lp503x.lb @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Christopher Durand +# +# 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:lp503x" + module.description = """\ +# LP503x 30/36 channel LED PWM driver + +LP5030/LP5036 30/36-channel, 12-bit PWM LED driver with I2C interface. +""" + +def prepare(module, options): + module.depends( + ":architecture:i2c.device") + module.add_option( + BooleanOption( + name="multichannel_write", + description="Allow setting multiple channels in one I2C transaction. " + "This feature requires additional 34 bytes of static storage. " + "It can be disabled for very small devices", + default=True)) + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/pwm" + env.template("lp503x.hpp.in", "lp503x.hpp") + env.template("lp503x_impl.hpp.in", "lp503x_impl.hpp") diff --git a/src/modm/driver/pwm/lp503x_impl.hpp.in b/src/modm/driver/pwm/lp503x_impl.hpp.in new file mode 100644 index 0000000000..c393bdf249 --- /dev/null +++ b/src/modm/driver/pwm/lp503x_impl.hpp.in @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2020, Christopher Durand + * + * 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_LP503x_HPP +# error "Don't include this file directly, use 'lp503x.hpp' instead!" +#endif + +#include + +namespace modm +{ + +template +Lp503x::Lp503x(uint8_t i2cAddress) + : I2cDevice(i2cAddress) +{ +} + +template +ResumableResult +Lp503x::initialize() +{ + RF_BEGIN(); + success = RF_CALL(writeRegister(Register::Reset, Reset::ResetDevice)); + if(!success) { + RF_RETURN(false); + } + + RF_END_RETURN_CALL(setConfiguration(DefaultConfiguration)); +} + +template +ResumableResult +Lp503x::enable() +{ + return writeRegister(Register::DeviceConfig0, DeviceConfig0::ChipEnable); +} + +template +ResumableResult +Lp503x::disable() +{ + return writeRegister(Register::DeviceConfig0, DeviceConfig0_t{}); +} + +template +ResumableResult +Lp503x::setConfiguration(DeviceConfig1_t configuration) +{ + return writeRegister(Register::DeviceConfig1, configuration | DeviceConfig1::AutoIncrement); +} + +template +ResumableResult +Lp503x::setChannelBrightness(uint8_t channel, uint8_t value) +{ + const Register reg = static_cast(uint8_t(Register::Out0Color) + channel); + + RF_BEGIN(); + if(channel >= Channels) { + RF_RETURN(false); + } + + RF_END_RETURN_CALL(writeRegister(reg, value)); +} + +%% if options.multichannel_write +template +ResumableResult +Lp503x::setChannelBrightnessValues(uint8_t startChannel, + const uint8_t* values, + uint8_t numChannels) +{ + RF_BEGIN(); + if(numChannels == 0 || (startChannel + numChannels) > Channels) { + RF_RETURN(false); + } + + RF_END_RETURN_CALL(writeRegisters( + static_cast(uint8_t(Register::Out0Color) + startChannel), + values, numChannels)); +} +%% endif + +template +ResumableResult +Lp503x::setLedGroupBrightness(uint8_t index, uint8_t value) +{ + const Register reg = static_cast(uint8_t(Register::Led0Brightness) + index); + + RF_BEGIN(); + if(index >= (Channels / 3)) { + RF_RETURN(false); + } + + RF_END_RETURN_CALL(writeRegister(reg, value)); +} + +template +ResumableResult +Lp503x::setBankModeEnabled(LedBankMode_t bankMode) +{ + RF_BEGIN(); + buffer[0] = static_cast(Register::LedConfig0); + buffer[1] = bankMode.value & 0xff; + buffer[2] = (bankMode.value & 0xf00) >> 8; + this->transaction.configureWrite(buffer, 3); + RF_END_RETURN_CALL(this->runTransaction()); +} + +template +ResumableResult +Lp503x::setBankABrightness(uint8_t value) +{ + return writeRegister(Register::BankAColor, value); +} + +template +ResumableResult +Lp503x::setBankBBrightness(uint8_t value) +{ + return writeRegister(Register::BankBColor, value); +} + +template +ResumableResult +Lp503x::setBankCBrightness(uint8_t value) +{ + return writeRegister(Register::BankCColor, value); +} + +template +ResumableResult +Lp503x::setBankBrightness(uint8_t value) +{ + return writeRegister(Register::BankBrightness, value); +} + +template +ResumableResult +Lp503x::writeRegister(Register reg, RegisterValue_t value) +{ + return writeRegister(reg, value.value); +} + +template +ResumableResult +Lp503x::writeRegister(Register reg, uint8_t value) +{ + RF_BEGIN(); + buffer[0] = static_cast(reg); + buffer[1] = value; + this->transaction.configureWrite(buffer, 2); + RF_END_RETURN_CALL(this->runTransaction()); +} + +%% if options.multichannel_write +template +ResumableResult +Lp503x::writeRegisters(Register startRegister, + const uint8_t* values, uint8_t numValues) +{ + RF_BEGIN(); + + if((numValues + 1u) > sizeof(buffer)) { + RF_RETURN(false); + } + + buffer[0] = static_cast(startRegister); + std::copy_n(values, numValues, &buffer[0] + 1); + this->transaction.configureWrite(buffer, numValues + 1u); + RF_END_RETURN_CALL(this->runTransaction()); +} +%% endif + +} From 6e5ebf4b63932ed4b137d9697576546ea41d3a75 Mon Sep 17 00:00:00 2001 From: Christopher Durand Date: Sun, 22 Mar 2020 01:06:55 +0100 Subject: [PATCH 029/135] [examples] Add example for LP503x led driver for Nucleo F042 --- examples/nucleo_f042k6/lp503x/main.cpp | 73 +++++++++++++++++++++++ examples/nucleo_f042k6/lp503x/project.xml | 11 ++++ 2 files changed, 84 insertions(+) create mode 100644 examples/nucleo_f042k6/lp503x/main.cpp create mode 100644 examples/nucleo_f042k6/lp503x/project.xml diff --git a/examples/nucleo_f042k6/lp503x/main.cpp b/examples/nucleo_f042k6/lp503x/main.cpp new file mode 100644 index 0000000000..8cc9e41828 --- /dev/null +++ b/examples/nucleo_f042k6/lp503x/main.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020, Christopher Durand + * + * 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 + +using namespace Board; + +/* + * Example to demonstrate LP503x driver + * It assumes an LP5036 is connected to the following pins: + * A10 SDA + * A11 SCL + * A7 Enable + */ + +using I2cSda = GpioA10; +using I2cScl = GpioA11; +using LedEnable = GpioA7; + +constexpr uint8_t i2cAddress = 0b011'0000; + +int +main() +{ + Board::initialize(); + LedEnable::setOutput(); + + MODM_LOG_INFO << "LP5036 demo" << modm::endl; + + I2cMaster1::connect(); + I2cMaster1::initialize(); + + modm::Lp5036 leds{i2cAddress}; + LedEnable::set(); + + // Initialize and enable chip + RF_CALL_BLOCKING(leds.initialize()); + RF_CALL_BLOCKING(leds.enable()); + + // Turn on all leds with increasing brightness + for(uint8_t channel = 0; channel < 36; ++channel) { + const uint8_t brightness = (channel + 1) * 7; + RF_CALL_BLOCKING(leds.setChannelBrightness(channel, brightness)); + } + + modm::delayMilliseconds(1000); + + // Configure outputs 0-5 (rgb led 0-1) in bank mode + using LedBankMode = modm::lp503x::LedBankMode; + const auto bankLeds = LedBankMode::Led0 | LedBankMode::Led1; + RF_CALL_BLOCKING(leds.setBankModeEnabled(bankLeds)); + + // Set bank leds to full brightness + RF_CALL_BLOCKING(leds.setBankABrightness(255)); + RF_CALL_BLOCKING(leds.setBankBBrightness(255)); + RF_CALL_BLOCKING(leds.setBankCBrightness(255)); + + // Blink leds in bank mode + while(true) { + RF_CALL_BLOCKING(leds.setBankBrightness(255)); + modm::delayMilliseconds(500); + RF_CALL_BLOCKING(leds.setBankBrightness(0)); + modm::delayMilliseconds(500); + } +} diff --git a/examples/nucleo_f042k6/lp503x/project.xml b/examples/nucleo_f042k6/lp503x/project.xml new file mode 100644 index 0000000000..c0e3195964 --- /dev/null +++ b/examples/nucleo_f042k6/lp503x/project.xml @@ -0,0 +1,11 @@ + + modm:nucleo-f042k6 + + + + + modm:build:scons + modm:driver:lp503x + modm:platform:i2c:1 + + From 4eb8adfa63cea1f703e543eebd645d03fb3c7453 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Tue, 12 May 2020 16:44:09 +0200 Subject: [PATCH 030/135] Fixes __wrap__free_r() to ignore freeing null pointers. --- src/modm/platform/heap/cortex/heap_tlsf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modm/platform/heap/cortex/heap_tlsf.cpp b/src/modm/platform/heap/cortex/heap_tlsf.cpp index 00bdc5d8fe..303fc10130 100644 --- a/src/modm/platform/heap/cortex/heap_tlsf.cpp +++ b/src/modm/platform/heap/cortex/heap_tlsf.cpp @@ -148,6 +148,8 @@ void *__wrap__realloc_r(struct _reent *, void *p, size_t size) void __wrap__free_r(struct _reent *, void *p) { + // do nothing if NULL pointer + if (!p) return; tlsf_t pool = get_tlsf_for_ptr(p); // if pointer belongs to no pool, exit. if (!pool) return; From f101d0b8d0ec6bdda23cecaf523271bc645c0e40 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Fri, 10 Aug 2018 22:43:08 +0200 Subject: [PATCH 031/135] [scons] add command-line arguments to manipulate CCFLAGS,CFLAGS,etc --- .../scons/resources/SConscript.in | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/build_script_generator/scons/resources/SConscript.in b/tools/build_script_generator/scons/resources/SConscript.in index 43634abf74..77d3000de9 100644 --- a/tools/build_script_generator/scons/resources/SConscript.in +++ b/tools/build_script_generator/scons/resources/SConscript.in @@ -169,4 +169,20 @@ env.AppendUnique(LIBPATH=[ env.ParseConfig("pkg-config --cflags --libs {{ packages | sort | join(" ") }}") %% endif +for flags in ["CCFLAGS", "CFLAGS", "CXXFLAGS", "ASFLAGS", "ARCHFLAGS", "LINKFLAGS"]: + flags_str = ARGUMENTS.get(flags) + if flags_str is not None: + flags_list = flags_str.split(",") + for flag in flags_list: + if len(flag) > 1 and flag[0] == "~": + try: + env[flags].remove(flag[1:]) + except ValueError: + print("'" + flag[1:] + "' does not exist in " + flags + + " and therefore can not be removed.") + print("Info: " + flags + ": " + ", ".join(env[flags])) + exit(1) + else: + env[flags].append(flag) + Return("library") From 2c29b6780d9d5d56811e148c64e8727b79008ecf Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Fri, 22 May 2020 12:06:35 +0200 Subject: [PATCH 032/135] [spi] Fix writing SPI data size into correct register and add missing method. --- src/modm/platform/spi/stm32/spi_hal_impl.hpp.in | 2 +- src/modm/platform/spi/stm32/spi_master.hpp.in | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in b/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in index 75d5170a8b..c75a7667c7 100644 --- a/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in +++ b/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in @@ -80,8 +80,8 @@ modm::platform::SpiHal{{ id }}::setDataOrder(DataOrder dataOrder) void inline modm::platform::SpiHal{{ id }}::setDataSize(DataSize dataSize) { -%% if "data-size" in features // TODO: implement as set/reset bit +%% if "data-size" not in features SPI{{ id }}->CR1 = (SPI{{ id }}->CR1 & ~static_cast(DataSize::All)) | static_cast(dataSize); %% else diff --git a/src/modm/platform/spi/stm32/spi_master.hpp.in b/src/modm/platform/spi/stm32/spi_master.hpp.in index 0e6445899d..1c7ab48cc9 100644 --- a/src/modm/platform/spi/stm32/spi_master.hpp.in +++ b/src/modm/platform/spi/stm32/spi_master.hpp.in @@ -64,6 +64,8 @@ public: LsbFirst = SPI_CR1_LSBFIRST }; + using DataSize = Hal::DataSize; + public: template< template class... Signals > static void @@ -108,6 +110,11 @@ public: { SpiHal{{ id }}::setDataOrder(static_cast(order)); } + static modm_always_inline void + setDataSize(DataSize size) + { + SpiHal{{ id }}::setDataSize(static_cast(size)); + } static uint8_t From f12252beda697f181632c541d1789fbeec0dd611 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Fri, 22 May 2020 12:22:59 +0200 Subject: [PATCH 033/135] Derive iterators from std::iterator to make the containers work with algorithms from STL. --- src/modm/container/deque.hpp | 3 ++- src/modm/container/doubly_linked_list.hpp | 5 +++-- src/modm/container/dynamic_array.hpp | 5 +++-- src/modm/container/linked_list.hpp | 5 +++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/modm/container/deque.hpp b/src/modm/container/deque.hpp index ae7ffe5997..a4470946fb 100644 --- a/src/modm/container/deque.hpp +++ b/src/modm/container/deque.hpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace modm { @@ -189,7 +190,7 @@ namespace modm * * \todo check if a simpler implementation is possible */ - class const_iterator + class const_iterator : public std::iterator { friend class BoundedDeque; diff --git a/src/modm/container/doubly_linked_list.hpp b/src/modm/container/doubly_linked_list.hpp index 9b4a1a692a..9ff38d58ee 100644 --- a/src/modm/container/doubly_linked_list.hpp +++ b/src/modm/container/doubly_linked_list.hpp @@ -18,6 +18,7 @@ #include #include +#include namespace modm { @@ -103,7 +104,7 @@ namespace modm * * \todo decrement operator doesn't work correctly */ - class iterator + class iterator : public std::iterator { friend class DoublyLinkedList; friend class const_iterator; @@ -132,7 +133,7 @@ namespace modm * * \todo decrement operator doesn't work correctly */ - class const_iterator + class const_iterator : public std::iterator { friend class DoublyLinkedList; diff --git a/src/modm/container/dynamic_array.hpp b/src/modm/container/dynamic_array.hpp index 7ae3af39d8..a2e765a58b 100644 --- a/src/modm/container/dynamic_array.hpp +++ b/src/modm/container/dynamic_array.hpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace modm { @@ -258,7 +259,7 @@ namespace modm /** * \brief Forward iterator */ - class iterator + class iterator : public std::iterator { friend class DynamicArray; friend class const_iterator; @@ -288,7 +289,7 @@ namespace modm /** * \brief forward const iterator */ - class const_iterator + class const_iterator : public std::iterator { friend class DynamicArray; diff --git a/src/modm/container/linked_list.hpp b/src/modm/container/linked_list.hpp index 9748599d7b..2ace7dbe33 100644 --- a/src/modm/container/linked_list.hpp +++ b/src/modm/container/linked_list.hpp @@ -17,6 +17,7 @@ #define MODM_LINKED_LIST_HPP #include +#include #include namespace modm @@ -116,7 +117,7 @@ namespace modm /** * \brief Forward iterator */ - class iterator + class iterator : public std::iterator { friend class LinkedList; friend class const_iterator; @@ -142,7 +143,7 @@ namespace modm /** * \brief forward const iterator */ - class const_iterator + class const_iterator : public std::iterator { friend class LinkedList; From cfc320d3183cdb0dc2f0ae4849a492494d13e600 Mon Sep 17 00:00:00 2001 From: Vivien Henry Date: Mon, 18 May 2020 11:22:33 +0200 Subject: [PATCH 034/135] [timer] Move BDTR to GeneralPurpose on STM32 --- .../platform/timer/stm32/advanced_base.hpp.in | 18 ---- .../timer/stm32/general_purpose.hpp.in | 90 +++++++++++++++++++ .../timer/stm32/general_purpose_base.hpp.in | 16 ++++ 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/src/modm/platform/timer/stm32/advanced_base.hpp.in b/src/modm/platform/timer/stm32/advanced_base.hpp.in index 48ff52bc3b..3b15a49e26 100644 --- a/src/modm/platform/timer/stm32/advanced_base.hpp.in +++ b/src/modm/platform/timer/stm32/advanced_base.hpp.in @@ -120,18 +120,6 @@ public: ExternalClock = TIM_SMCR_SMS_2 | TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0, }; - enum class OffStateForRunMode : uint32_t - { - Disable = 0, - Enable = TIM_BDTR_OSSR, - }; - - enum class OffStateForIdleMode : uint32_t - { - Disable = 0, - Enable = TIM_BDTR_OSSI, - }; - enum class OutputIdleState : uint32_t { Reset = 0, @@ -152,12 +140,6 @@ public: CaptureCompare1 = TIM_EGR_CC1G, Update = TIM_EGR_UG, }; - - /** - * Enable output pins - */ - static void - enableOutput(); }; } // namespace platform diff --git a/src/modm/platform/timer/stm32/general_purpose.hpp.in b/src/modm/platform/timer/stm32/general_purpose.hpp.in index 055fc68b7e..cd8dffb9db 100644 --- a/src/modm/platform/timer/stm32/general_purpose.hpp.in +++ b/src/modm/platform/timer/stm32/general_purpose.hpp.in @@ -212,6 +212,96 @@ public: TIM{{ id }}->CNT = value; } + +%% if target.family not in ["l1"] and id not in [14] + static inline void + enableOutput() + { + TIM{{ id }}->BDTR |= TIM_BDTR_MOE; + } + + static inline void + disableOutput() + { + TIM{{ id }}->BDTR &= ~(TIM_BDTR_MOE); + } + + /* + * Enable/Disable automatic set of MOE bit at the next update event + */ + static inline void + setAutomaticUpdate(bool enable) + { + if(enable) + TIM{{ id }}->BDTR |= TIM_BDTR_AOE; + else + TIM{{ id }}->BDTR &= ~TIM_BDTR_AOE; + } + + static inline void + setOffState(OffStateForRunMode runMode, OffStateForIdleMode idleMode) + { + uint32_t flags = TIM{{ id }}->BDTR; + flags &= ~(TIM_BDTR_OSSR | TIM_BDTR_OSSI); + flags |= static_cast(runMode); + flags |= static_cast(idleMode); + TIM{{ id }}->BDTR = flags; + } + + /* + * Set Dead Time Value + * + * Different Resolution Depending on DeadTime[7:5]: + * 0xx => DeadTime[6:0] * T(DTS) + * 10x => (DeadTime[5:0] + 32) * 2 * T(DTS) + * 110 => (DeadTime[4:0] + 4) * 8 * T(DTS) + * 111 => (DeadTime[4:0] + 2) * 16 * T(DTS) + */ + static inline void + setDeadTime(uint8_t deadTime) + { + uint32_t flags = TIM{{ id }}->BDTR; + flags &= ~TIM_BDTR_DTG; + flags |= deadTime; + TIM{{ id }}->BDTR = flags; + } + + /* + * Set Dead Time Value + * + * Different Resolution Depending on DeadTime[7:5]: + * 0xx => DeadTime[6:0] * T(DTS) + * 10x => (DeadTime[5:0] + 32) * 2 * T(DTS) + * 110 => (DeadTime[4:0] + 4) * 8 * T(DTS) + * 111 => (DeadTime[4:0] + 2) * 16 * T(DTS) + */ + static inline void + setDeadTime(DeadTimeResolution resolution, uint8_t deadTime) + { + uint8_t bitmask; + switch(resolution){ + case DeadTimeResolution::From0With125nsStep: + bitmask = 0b01111111; + break; + case DeadTimeResolution::From16usWith250nsStep: + bitmask = 0b00111111; + break; + case DeadTimeResolution::From32usWith1usStep: + case DeadTimeResolution::From64usWith2usStep: + bitmask = 0b00011111; + break; + default: + bitmask = 0x00; + break; + } + uint32_t flags = TIM{{ id }}->BDTR; + flags &= ~TIM_BDTR_DTG; + flags |= (deadTime & bitmask) | static_cast(resolution); + TIM{{ id }}->BDTR = flags; + } +%% endif + + public: static void configureInputChannel(uint32_t channel, InputCaptureMapping input, diff --git a/src/modm/platform/timer/stm32/general_purpose_base.hpp.in b/src/modm/platform/timer/stm32/general_purpose_base.hpp.in index e59ad02f79..e65e2edf46 100644 --- a/src/modm/platform/timer/stm32/general_purpose_base.hpp.in +++ b/src/modm/platform/timer/stm32/general_purpose_base.hpp.in @@ -201,6 +201,20 @@ public: }; %% endif +%% if target.family not in ["l1"] + enum class OffStateForRunMode : uint32_t + { + Disable = 0, + Enable = TIM_BDTR_OSSR, + }; + + enum class OffStateForIdleMode : uint32_t + { + Disable = 0, + Enable = TIM_BDTR_OSSI, + }; +%% endif + public: /** * Set operation mode of the timer @@ -212,6 +226,8 @@ public: setMode(Mode mode, SlaveMode slaveMode = SlaveMode::Disabled, SlaveModeTrigger slaveModeTrigger = (SlaveModeTrigger) 0); + + public: /** * TODO Description From 4f5bed1ba34c38e0cc55d428b971c80fa39c9f25 Mon Sep 17 00:00:00 2001 From: Benjamin Carrick Date: Sun, 7 Jun 2020 16:32:54 +0200 Subject: [PATCH 035/135] Changed the Clock config of the Nucleo F042K6 --- src/modm/board/nucleo_f042k6/board.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modm/board/nucleo_f042k6/board.hpp b/src/modm/board/nucleo_f042k6/board.hpp index 75a70b4318..cd37501c6a 100644 --- a/src/modm/board/nucleo_f042k6/board.hpp +++ b/src/modm/board/nucleo_f042k6/board.hpp @@ -55,10 +55,10 @@ struct SystemClock { enable() { Rcc::enableInternalClock(); // 8MHz - // (internal clock / 1) * 6 = 48MHz + // (internal clock / 2) * 12 = 48MHz const Rcc::PllFactors pllFactors{ - .pllMul = 6, - .pllPrediv = 1 + .pllMul = 12, + .pllPrediv = 2 }; Rcc::enablePll(Rcc::PllSource::InternalClock, pllFactors); // set flash latency for 48MHz From 8d3ca7d5361c7fe028918ad6d8b9ad9ef70c6ac1 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Mon, 8 Jun 2020 12:46:07 +0200 Subject: [PATCH 036/135] [stm32] RCC: Fix wrong pll hsi source selection --- src/modm/platform/clock/stm32/rcc.hpp.in | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/modm/platform/clock/stm32/rcc.hpp.in b/src/modm/platform/clock/stm32/rcc.hpp.in index 4f199db78f..d5a7e86f33 100644 --- a/src/modm/platform/clock/stm32/rcc.hpp.in +++ b/src/modm/platform/clock/stm32/rcc.hpp.in @@ -43,20 +43,26 @@ public: %% if target["family"] in ["f2", "f4", "f7", "l4", "g0", "g4"] /// High speed internal clock (16 MHz) Hsi = RCC_PLLCFGR_PLLSRC_HSI, + InternalClock = Hsi, /// High speed external clock (see HseConfig) Hse = RCC_PLLCFGR_PLLSRC_HSE, %% elif target["family"] in ["f0", "f3"] /// High speed internal clock (8 MHz) - %% if target["family"] == "f3" and target["name"] in ["02", "03", "98"] and target["size"] in ["d", "e"] - Hsi = 0, - %% else - Hsi = RCC_CFGR_PLLSRC_HSI_DIV2, + // On STM32F0 and STM32F3 some devices have a fixed /2 pre-divider, some have a configurable + // pre-divider and some have both, selectable via pll source mux + %% if (target["family"] == "f0" and (target["name"] in ["42", "48", "70", "71", "72", "78", "91", "98"] or (target["name"] in ["30"] and target["size"] in ["c"]))) or (target["family"] == "f3" and target["name"] in ["02", "03", "98"] and target["size"] in ["d", "e"]) + Hsi = RCC_CFGR_PLLSRC_HSI_PREDIV, + InternalClock = Hsi, + %% endif + %% if (target["family"] == "f0") or (target["family"] == "f3" and target["size"] in ["8", "c"]) + HsiDiv2 = RCC_CFGR_PLLSRC_HSI_DIV2, %% endif /// High speed external clock (see HseConfig) Hse = RCC_CFGR_PLLSRC_HSE_PREDIV, %% elif target["family"] in ["f1", "l1"] /// High speed internal clock (8 MHz) Hsi = 0, + InternalClock = Hsi, /// High speed external clock (see HseConfig) Hse = RCC_CFGR_PLLSRC, %% endif @@ -69,8 +75,6 @@ public: Msi = RCC_PLLCFGR_PLLSRC_MSI, MultiSpeedInternalClock = Msi, %% endif - - InternalClock = Hsi, ExternalClock = Hse, ExternalCrystal = Hse, }; From 01d439cc936f4aabc20f97ae9aa9cb8655d75b58 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Mon, 8 Jun 2020 17:00:40 +0200 Subject: [PATCH 037/135] [board] Fix pll source for disci_f051, nucleo_f031 and nucleo_f303 --- src/modm/board/disco_f051r8/board.hpp | 6 +++--- src/modm/board/nucleo_f031k6/board.hpp | 4 ++-- src/modm/board/nucleo_f303k8/board.hpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modm/board/disco_f051r8/board.hpp b/src/modm/board/disco_f051r8/board.hpp index 2a67343ce3..6b9ca3a7a8 100644 --- a/src/modm/board/disco_f051r8/board.hpp +++ b/src/modm/board/disco_f051r8/board.hpp @@ -37,12 +37,12 @@ struct SystemClock { // enable internal 8 MHz HSI RC clock Rcc::enableInternalClock(); - // (internal clock / 2) / 1 * 12 = 48MHz + // (internal clock / 2) * 12 = 48MHz const Rcc::PllFactors pllFactors{ .pllMul = 12, - .pllPrediv = 1 + .pllPrediv = 1 // only used with Hse }; - Rcc::enablePll(Rcc::PllSource::InternalClock, pllFactors); + Rcc::enablePll(Rcc::PllSource::HsiDiv2, pllFactors); // set flash latency for 48MHz Rcc::setFlashLatency(); // switch system clock to PLL output diff --git a/src/modm/board/nucleo_f031k6/board.hpp b/src/modm/board/nucleo_f031k6/board.hpp index 53a2023093..92ecfc2eda 100644 --- a/src/modm/board/nucleo_f031k6/board.hpp +++ b/src/modm/board/nucleo_f031k6/board.hpp @@ -55,9 +55,9 @@ struct SystemClock { // (internal clock / 2) * 12 = 48MHz const Rcc::PllFactors pllFactors{ .pllMul = 12, - .pllPrediv = 1 + .pllPrediv = 1 // only used with Hse }; - Rcc::enablePll(Rcc::PllSource::InternalClock, pllFactors); + Rcc::enablePll(Rcc::PllSource::HsiDiv2, pllFactors); // set flash latency for 48MHz Rcc::setFlashLatency(); // switch system clock to PLL output diff --git a/src/modm/board/nucleo_f303k8/board.hpp b/src/modm/board/nucleo_f303k8/board.hpp index 259e3cc40e..c1079d8c13 100644 --- a/src/modm/board/nucleo_f303k8/board.hpp +++ b/src/modm/board/nucleo_f303k8/board.hpp @@ -67,9 +67,9 @@ struct SystemClock { // 8MHz / 2 * 16 = 64MHz const Rcc::PllFactors pllFactors{ .pllMul = 16, - .pllPrediv = 2, + .pllPrediv = 2, // only used with Hse }; - Rcc::enablePll(Rcc::PllSource::InternalClock, pllFactors); + Rcc::enablePll(Rcc::PllSource::HsiDiv2, pllFactors); // set flash latency for 64MHz Rcc::setFlashLatency(); // switch system clock to PLL output From 91a590e5f1642c27e1e5ff72ba17f6a9b92b0629 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Jun 2020 17:51:15 +0200 Subject: [PATCH 038/135] [docs] Fix mkdocs-material social icons --- docs/mkdocs.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 3f6497105d..66d3d21010 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -9,7 +9,7 @@ repo_url: 'https://github.com/modm-io/modm' edit_uri: "" # dev_addr: 192.168.1.2:8000 -copyright: 'Copyright © 2009-2019 Niklas Hauser & modm authors' +copyright: 'Copyright © 2009-2020 Niklas Hauser & modm authors' # Turn around for GitHub Hosting in /docs docs_dir: 'src' @@ -33,10 +33,10 @@ theme: extra: social: - - type: github-alt + - icon: fontawesome/brands/github-alt link: https://github.com/modm-io - - type: twitter - link: https://twitter.com/RCA_eV + - icon: fontawesome/brands/twitter + link: https://twitter.com/modm_embedded markdown_extensions: - markdown.extensions.admonition From 7f553665297105850d0bcc2ce4f9ea9195abe045 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Tue, 12 May 2020 01:46:02 +0200 Subject: [PATCH 039/135] [tools] Improve sync docs script --- tools/scripts/synchronize_docs.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tools/scripts/synchronize_docs.py b/tools/scripts/synchronize_docs.py index acb5586a8f..7106ec1044 100755 --- a/tools/scripts/synchronize_docs.py +++ b/tools/scripts/synchronize_docs.py @@ -40,7 +40,7 @@ def run(where, command, stdin=None): def name(raw_name): result = [] - raw_name = raw_name.replace("_", " ").replace(".", " ").replace(":", " ") + raw_name = str(raw_name).replace("_", " ").replace(".", " ").replace(":", " ") for part in raw_name.split(" "): part = part.upper() result.append(part) @@ -61,7 +61,7 @@ def name(raw_name): .replace("BLOCK-", "")\ .replace("-SPI", "") if result in ["DEVICE", "LIS3-TRANSPORT", "MEMORY-BUS", "TERMINAL", "ALLOCATOR", - "MIRROR", "ADC-SAMPLER", "FAT", "HEAP", "--PYCACHE--"]: + "MIRROR", "ADC-SAMPLER", "FAT", "HEAP", "--PYCACHE--", "FILE"]: return None return result @@ -80,9 +80,12 @@ def format_table(items, width, align=None): if align: subs["align"] = align; return Environment().from_string(TABLE_TEMPLATE).render(subs) -def get_lbuild_in(path): - builder = lbuild.api.Builder(cwd=path, config=join(path, "project.xml")) - builder.load() +def get_lbuild(root, target=None): + if target is None: target = "stm32"; + target = {"avr": "atmega2560-16au", "stm32": "stm32f469nih6", + "hosted": "hosted-linux"}[target] + builder = lbuild.api.Builder(cwd=root, options=[":target={}".format(target)]) + builder.load(root / "repo.lb") return builder.parser @@ -104,8 +107,8 @@ def get_lbuild_in(path): modules_path = root / "docs/src/reference/modules.md" # We cannot use lbuild to enumerate the boards since they only make themselves available for certain devices -# boards = [{"name": name(x.name), "url": url(x.relative_to(root))} for x in board_path.iterdir() if x.is_dir() and name(x.name)] -boards = [{"name": name(x.name), "url": None} for x in board_path.iterdir() if x.is_dir() and name(x.name)] +boards = (b.relative_to(board_path) for b in board_path.glob('**/board.xml')) +boards = [{"name": name(b.parent.name), "url": None} for b in boards] boards.sort(key=lambda b: b["name"]) bsp_table = format_table(boards, 4) @@ -116,8 +119,7 @@ def get_lbuild_in(path): example_table = format_table(examples, 2, "left") # Get all supported targets -lbuild_parser = get_lbuild_in("{}/examples/stm32f4_discovery/blink/".format(root)) -targets = set(lbuild_parser.find_option("modm:target").values) +targets = set(get_lbuild(root).find_option("modm:target").values) ignored_devices = set(d for d in ignored_path.read_text().strip().splitlines() if "#" not in d) targets -= ignored_devices avr_count = len([t for t in targets if t.startswith("at")]) @@ -129,7 +131,9 @@ def get_lbuild_in(path): # Get all the modules that are available for the STM32 # Get all drivers, we assume they are available for all devices -drivers = sorted([m.replace("modm:driver:", "") for m in lbuild_parser.modules if m.startswith("modm:driver:")]) +drivers = (get_lbuild(root, t).modules for t in {"avr", "stm32", "hosted"}) +drivers = {m for mg in drivers for m in mg if m.startswith("modm:driver:")} +drivers = sorted(m.replace("modm:driver:", "") for m in drivers) drivers = [{"name": name(d), "url": None} for d in drivers if name(d)] driver_table = format_table(drivers, 6) From 5c24e029009ee666b3880318dbf7dd92015a464f Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Jun 2020 02:40:01 +0200 Subject: [PATCH 040/135] [match] Add CRC8,16,32 implementations --- src/modm/math/utils/crc.hpp | 101 ++++++++++++++++++++++++++++++++++ src/modm/math/utils/crc32.hpp | 43 --------------- 2 files changed, 101 insertions(+), 43 deletions(-) create mode 100644 src/modm/math/utils/crc.hpp delete mode 100644 src/modm/math/utils/crc32.hpp diff --git a/src/modm/math/utils/crc.hpp b/src/modm/math/utils/crc.hpp new file mode 100644 index 0000000000..86af35950e --- /dev/null +++ b/src/modm/math/utils/crc.hpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019-2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#ifdef __AVR__ +#include +#endif + +namespace modm::math +{ + +/// @ingroup modm_math_utils +inline uint8_t +crc8_ccitt_update(uint8_t crc, uint8_t data) +{ +#ifdef __AVR__ + return _crc8_ccitt_update(crc, data); +#else + data ^= crc; + for (uint8_t ii = 0; ii < 8; ii++) + { + data <<= 1; + if (data & 0x80) data ^= 0x07; + } + return data; +#endif +} + +/// @ingroup modm_math_utils +inline uint16_t +crc16_ccitt_update(uint16_t crc, uint8_t data) +{ +#ifdef __AVR__ + return _crc_ccitt_update(crc, data); +#else + data ^= uint8_t(crc); data ^= data << 4; + return (((uint16_t(data) << 8) | uint8_t(crc >> 8)) ^ + uint8_t(data >> 4) ^ (uint16_t(data) << 3)); +#endif +} + +/// @see https://github.com/stbrumme/crc32 +/// @ingroup modm_math_utils +inline uint32_t +crc32_update(uint32_t crc, uint8_t data) +{ + static constexpr uint32_t polynomial{0xEDB88320}; + crc ^= data; + for (uint_fast8_t ii = 0; ii < 8; ii++) + crc = (crc >> 1) ^ (-int32_t(crc & 1) & polynomial); + return crc; +} + +/// @ingroup modm_math_utils +static constexpr uint8_t crc8_ccitt_init{0xFFu}; +/// @ingroup modm_math_utils +static constexpr uint16_t crc16_ccitt_init{0xFFFFu}; +/// @ingroup modm_math_utils +static constexpr uint32_t crc32_init{0xFFFFFFFFul}; + +/// @ingroup modm_math_utils +inline uint8_t +crc8_ccitt(const uint8_t *data, size_t length) +{ + uint8_t crc{crc8_ccitt_init}; + while (length--) crc = crc8_ccitt_update(crc, *data++); + return crc; +} + +/// @ingroup modm_math_utils +inline uint16_t +crc16_ccitt(const uint8_t *data, size_t length) +{ + uint16_t crc{crc16_ccitt_init}; + while (length--) crc = crc16_ccitt_update(crc, *data++); + return crc; +} + +/// Slow, but table-less computation of CRC32. +/// @ingroup modm_math_utils +inline uint32_t +crc32(const uint8_t *data, size_t length) +{ + uint32_t crc{crc32_init}; + while (length--) crc = crc32_update(crc, *data++); + return ~crc; +} + +} // namespace modm::math + diff --git a/src/modm/math/utils/crc32.hpp b/src/modm/math/utils/crc32.hpp deleted file mode 100644 index caf65bcc8e..0000000000 --- a/src/modm/math/utils/crc32.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2019, 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/. - */ -// ---------------------------------------------------------------------------- - -#pragma once - -#include - -namespace modm::math -{ - -/** - * Slow, but table-less computation of CRC32. - * - * @see http://www.hackersdelight.org/hdcodetxt/crc.c.txt - * @ingroup modm_math_utils - */ -inline uint32_t -crc32(const uint8_t *data, size_t length) -{ - uint32_t mask{0}; - uint32_t crc{0xFFFFFFFF}; - while (length--) - { - crc = crc ^ *data++; - for (uint_fast8_t ii = 0; ii < 8; ii++) - { - mask = -(crc & 1); - crc = (crc >> 1) ^ (0xEDB88320 & mask); - } - } - return ~crc; -} - -} // namespace modm::math - From 9335bcf5ae421800da35498f7cb2c6ef9351eeb2 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Jun 2020 02:40:23 +0200 Subject: [PATCH 041/135] [cortex-m] Add .ramcode section to linkerscript --- src/modm/architecture/utils.hpp | 4 +++- src/modm/platform/core/cortex/linker.macros | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modm/architecture/utils.hpp b/src/modm/architecture/utils.hpp index 6929ff7b56..2412ec68e8 100644 --- a/src/modm/architecture/utils.hpp +++ b/src/modm/architecture/utils.hpp @@ -133,11 +133,13 @@ # define modm_weak __attribute__((weak)) #endif - #ifdef MODM_OS_HOSTED + #if defined(MODM_OS_HOSTED) || defined(MODM_CPU_AVR) # define modm_fastcode + # define modm_ramcode # define modm_fastdata #else # define modm_fastcode modm_section(".fastcode") + # define modm_ramcode modm_section(".ramcode") # define modm_fastdata modm_section(".fastdata") #endif diff --git a/src/modm/platform/core/cortex/linker.macros b/src/modm/platform/core/cortex/linker.macros index 7b0152e560..df16e8a711 100644 --- a/src/modm/platform/core/cortex/linker.macros +++ b/src/modm/platform/core/cortex/linker.macros @@ -287,7 +287,12 @@ TOTAL_STACK_SIZE = MAIN_STACK_SIZE + PROCESS_STACK_SIZE; __data_load = LOADADDR(.data); __data_start = .; *(.data .data.* .gnu.linkonce.d.*) - . = ALIGN(4); + } >{{memory}} AT >{{rom}} + + /* code routines in RAM */ + .ramcode : ALIGN(4) + { + *(.ramcode .ramcode*) __data_end = .; } >{{memory}} AT >{{rom}} From ed4e6d0124457f3dcd02c1888f10971c49581ef5 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Jun 2020 02:40:46 +0200 Subject: [PATCH 042/135] [heap] Fix block heap implementation --- src/modm/platform/heap/cortex/heap_block.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modm/platform/heap/cortex/heap_block.cpp b/src/modm/platform/heap/cortex/heap_block.cpp index cf7043f3c4..a76b0eac00 100644 --- a/src/modm/platform/heap/cortex/heap_block.cpp +++ b/src/modm/platform/heap/cortex/heap_block.cpp @@ -45,7 +45,7 @@ void __modm_initialize_memory(void) bool success = modm::platform::HeapTable::find_largest(&heap_start, &heap_end); modm_assert(success, "heap.init", "Could not find main heap memory!"); // clamp the heap size to the maximum - if ((heap_end - heap_start) > max_heap_size) { + if (size_t(heap_end - heap_start) > max_heap_size) { heap_end = heap_start + max_heap_size; } // initialize the heap @@ -67,7 +67,7 @@ void* __wrap__calloc_r(struct _reent *r, size_t size) return ptr; } -void* __wrap__realloc_r(struct _reent *, void *, size_t) +void* __wrap__realloc_r(struct _reent *, void *, size_t size) { // NOT IMPLEMENTED! modm_assert(0, "realloc", "Realloc is not implemented for Block heap!", size); From 64edfcd3fcfc0ca4ad2770e74ae8f1a7a0f26149 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Jun 2020 02:41:22 +0200 Subject: [PATCH 043/135] [timer] Add missing timer.interval() method --- src/modm/processing/timer/timeout_impl.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modm/processing/timer/timeout_impl.hpp b/src/modm/processing/timer/timeout_impl.hpp index 7468871b70..da48cbee7b 100644 --- a/src/modm/processing/timer/timeout_impl.hpp +++ b/src/modm/processing/timer/timeout_impl.hpp @@ -86,6 +86,13 @@ modm::GenericTimeout::remaining() const std::chrono::time_point_cast(now()); } +template< class Clock, class Duration > +typename modm::GenericTimeout::duration +modm::GenericTimeout::interval() const +{ + return _interval; +} + // ---------------------------------------------------------------------------- template< class Clock, class Duration > modm::TimerState From b69574446c5e5efaa43d94912992b9b8531e6265 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Jun 2020 02:42:46 +0200 Subject: [PATCH 044/135] [resumable] Fix RF_CALL for template functions The CPP interprets the comma separating multiple template arguments as a comma separating macro arguments. --- src/modm/processing/resumable/macros.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/modm/processing/resumable/macros.hpp b/src/modm/processing/resumable/macros.hpp index bbe9453eb9..0a6572bbaf 100644 --- a/src/modm/processing/resumable/macros.hpp +++ b/src/modm/processing/resumable/macros.hpp @@ -72,8 +72,8 @@ * @warning Use at end of the `resumable()` implementation only! * @hideinitializer */ -#define RF_END_RETURN_CALL(resumable) \ - RF_RETURN_CALL(resumable); \ +#define RF_END_RETURN_CALL(...) \ + RF_RETURN_CALL(__VA_ARGS__); \ modm_fallthrough; \ default: \ this->popRf(); \ @@ -108,10 +108,10 @@ /// Calls a resumable function and returns its result. /// @hideinitializer -#define RF_CALL(resumable) \ +#define RF_CALL(...) \ ({ \ RF_INTERNAL_SET_CASE(__COUNTER__); \ - auto rfResult = resumable; \ + auto rfResult = (__VA_ARGS__); \ if (rfResult.getState() > modm::rf::NestingError) { \ this->popRf(); \ return {modm::rf::Running}; \ @@ -125,21 +125,21 @@ * @warning Use this with extreme caution, this can cause deadlocks! * @hideinitializer */ -#define RF_CALL_BLOCKING(resumable) \ +#define RF_CALL_BLOCKING(...) \ ({ \ - auto rfResult = resumable; \ - while (rfResult.getState() > modm::rf::NestingError) \ - { rfResult = resumable; } \ + decltype(__VA_ARGS__) rfResult{0}; \ + do { rfResult = (__VA_ARGS__); } \ + while (rfResult.getState() > modm::rf::NestingError); \ rfResult.getResult(); \ }) /// Exits a resumable function and returns another resumable function's result. /// @hideinitializer -#define RF_RETURN_CALL(resumable) \ +#define RF_RETURN_CALL(...) \ do { \ RF_INTERNAL_SET_CASE(__COUNTER__); \ { \ - auto rfResult = resumable; \ + auto rfResult = (__VA_ARGS__); \ if (rfResult.getState() > modm::rf::NestingError) { \ this->popRf(); \ return {modm::rf::Running}; \ From 746aceabe3c7b1d04b592f8f1a5fec643dca7df6 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Jun 2020 02:44:09 +0200 Subject: [PATCH 045/135] [atomic] Expose approximate size of Queue --- src/modm/architecture/driver/atomic/queue.hpp | 3 ++ .../architecture/driver/atomic/queue_impl.hpp | 38 ++++++++----------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/modm/architecture/driver/atomic/queue.hpp b/src/modm/architecture/driver/atomic/queue.hpp index 1c3a77407b..ad1162bed6 100644 --- a/src/modm/architecture/driver/atomic/queue.hpp +++ b/src/modm/architecture/driver/atomic/queue.hpp @@ -82,6 +82,9 @@ namespace modm modm_always_inline Size getMaxSize() const; + Size + getSize() const; + const T& get() const; diff --git a/src/modm/architecture/driver/atomic/queue_impl.hpp b/src/modm/architecture/driver/atomic/queue_impl.hpp index a1188b2f32..8e8101f718 100644 --- a/src/modm/architecture/driver/atomic/queue_impl.hpp +++ b/src/modm/architecture/driver/atomic/queue_impl.hpp @@ -42,19 +42,7 @@ bool modm::atomic::Queue::isNearlyFull() const { static_assert(N > 3, "Not possible the check for 'nearly full' of such a small queue."); - - Index tmphead = this->head; - Index tmptail = this->tail; - - Index free; - if (tmphead >= tmptail) { - free = (N + 1) - tmphead + tmptail; - } - else { - free = tmptail - tmphead; - } - - return (free < 3); + return (getSize() > (N - 3)); } template @@ -69,7 +57,21 @@ bool modm::atomic::Queue::isNearlyEmpty() const { static_assert(N > 3, "Not possible the check for 'nearly empty' of such a small queue. "); + return (getSize() < 3); +} + + +template +modm_always_inline typename modm::atomic::Queue::Size +modm::atomic::Queue::getMaxSize() const +{ + return N; +} +template +typename modm::atomic::Queue::Size +modm::atomic::Queue::getSize() const +{ Index tmphead = this->head; Index tmptail = this->tail; @@ -81,15 +83,7 @@ modm::atomic::Queue::isNearlyEmpty() const stored = (N + 1) - tmptail + tmphead; } - return (stored < 3); -} - - -template -modm_always_inline typename modm::atomic::Queue::Size -modm::atomic::Queue::getMaxSize() const -{ - return N; + return stored; } template From 965ba5038fc70000b61fd34de83f7e51a44c2d80 Mon Sep 17 00:00:00 2001 From: Jeff McBride Date: Tue, 9 Jun 2020 17:29:53 -0700 Subject: [PATCH 046/135] [cortex-m] Add option for project-provided linker script --- src/modm/platform/core/avr/module.lb | 2 +- src/modm/platform/core/cortex/module.lb | 18 +++++++++++++++--- tools/build_script_generator/cmake/module.lb | 2 +- tools/build_script_generator/scons/module.lb | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/modm/platform/core/avr/module.lb b/src/modm/platform/core/avr/module.lb index 0bce179f2b..bbd92ddd75 100644 --- a/src/modm/platform/core/avr/module.lb +++ b/src/modm/platform/core/avr/module.lb @@ -35,7 +35,7 @@ def build(env): env.outbasepath = "modm/link" env.copy("linkerscript/linkerscript.ld", "linkerscript.ld") - env.collect(":build:linkflags", "-L{linkdir}", "-Tlinkerscript.ld") + env.collect(":build:linkflags", "-L{project_source_dir}", "-Tmodm/link/linkerscript.ld") env.substitutions = { "target": target.identifier, diff --git a/src/modm/platform/core/cortex/module.lb b/src/modm/platform/core/cortex/module.lb index 12df97a3b3..8139b55583 100644 --- a/src/modm/platform/core/cortex/module.lb +++ b/src/modm/platform/core/cortex/module.lb @@ -190,6 +190,15 @@ def prepare(module, options): maximum=hex(flash_size), default=0)) + module.add_option( + PathOption( + name="linkerscript.override", + description="Path to project provided linkerscript", + default="", + empty_ok=True, + absolute=False, + )) + module.add_collector( StringCollector( name="linkerscript.memory", @@ -315,9 +324,13 @@ def build(env): if env.has_module(":architecture:unaligned"): env.template("unaligned_impl.hpp.in") + env.collect(":build:linkflags", "-Wl,--no-wchar-size-warning", "-L{project_source_dir}") # Linkerscript - env.collect(":build:linkflags", "-L{linkdir}", "-Tlinkerscript.ld", - "-Wl,--no-wchar-size-warning") + linkerscript_override_option = env["linkerscript.override"] + if linkerscript_override_option: + env.collect(":build:linkflags", "-T{}".format(linkerscript_override_option)) + else: + env.collect(":build:linkflags", "-Tmodm/link/linkerscript.ld") # Compilation for FPU core = env[":target"].get_driver("core")["type"] @@ -337,4 +350,3 @@ def build(env): if single_precision: env.collect(":build:ccflags", "-fsingle-precision-constant", "-Wdouble-promotion") - diff --git a/tools/build_script_generator/cmake/module.lb b/tools/build_script_generator/cmake/module.lb index 11579c5dbe..fd1bc67f78 100644 --- a/tools/build_script_generator/cmake/module.lb +++ b/tools/build_script_generator/cmake/module.lb @@ -45,7 +45,7 @@ def build(env): def flag_format(flag): subs = { "target_base": "${CMAKE_PROJECT_NAME}", - "linkdir": "${CMAKE_CURRENT_LIST_DIR}/link", + "project_source_dir": "${CMAKE_CURRENT_SOURCE_DIR}", } if "{" in flag: return flag.format(**subs) diff --git a/tools/build_script_generator/scons/module.lb b/tools/build_script_generator/scons/module.lb index 62c5b8c438..c0486ea4a5 100644 --- a/tools/build_script_generator/scons/module.lb +++ b/tools/build_script_generator/scons/module.lb @@ -66,7 +66,7 @@ def build(env): def flag_format(flag): subs = { "target_base": "\"${TARGET.base}\"", - "linkdir": "abspath(\"link\")" + "linkdir": "abspath(\"..\")" } flag = '"{}"'.format(flag) vals = ["{}={}".format(t, r) for t, r in subs.items() if "{{{}}}".format(t) in flag] From 47d9f4f366eb9777ebb7619d49b6cc131774578f Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 11 Jun 2020 00:01:27 +0200 Subject: [PATCH 047/135] [flash] Add simple STM32 Flash implementation --- src/modm/platform/flash/stm32/flash.cpp.in | 109 ++++++++++++++++++++ src/modm/platform/flash/stm32/flash.hpp.in | 111 +++++++++++++++++++++ src/modm/platform/flash/stm32/module.lb | 50 ++++++++++ 3 files changed, 270 insertions(+) create mode 100644 src/modm/platform/flash/stm32/flash.cpp.in create mode 100644 src/modm/platform/flash/stm32/flash.hpp.in create mode 100644 src/modm/platform/flash/stm32/module.lb diff --git a/src/modm/platform/flash/stm32/flash.cpp.in b/src/modm/platform/flash/stm32/flash.cpp.in new file mode 100644 index 0000000000..70667f4ab6 --- /dev/null +++ b/src/modm/platform/flash/stm32/flash.cpp.in @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020, 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 "flash.hpp" + +static constexpr uint32_t FLASH_SR_ERR = 0xfffe; + +namespace modm::platform +{ + +bool +Flash::unlock() +{ + Flash::enable(); + if (isLocked()) + { + FLASH->KEYR = 0x45670123; + FLASH->KEYR = 0xCDEF89AB; + } + return not isLocked(); +} + +uint8_t +Flash::getPage(uintptr_t offset) +{ + const uint8_t index = (offset >> {{ shift }}); +%% if has_sectors + uint8_t small_index{0}; + // 128kB Block 0 and 8 are subdivided into 4x16kB + 64kB + if (index == 0 or index == 8) + { + if (index == 8) small_index += 4; + // Check upper 64kB first + if (offset & 0x1'0000ul) small_index += 4; + // Otherwise check lower 16kB + else small_index += ((offset & 0xC000) >> 14); + } + else small_index += 4; + // 128kB Blocks + return index + small_index; +%% else + return index; +%% endif +} + +modm_ramcode uint32_t +%% if has_sectors +Flash::erase(uint8_t index, WordSize size) +%% else +Flash::erase(uint8_t index) +%% endif +{ + FLASH->SR = FLASH_SR_ERR; +%% if has_sectors + FLASH->CR = FLASH_CR_STRT | FLASH_CR_SER | uint32_t(size) | + ((index << FLASH_CR_SNB_Pos) & FLASH_CR_SNB_Msk); +%% else + FLASH->CR = FLASH_CR_STRT | FLASH_CR_PER | + ((index << FLASH_CR_PNB_Pos) & FLASH_CR_PNB_Msk); +%% endif + + while(isBusy()) ; + FLASH->CR = 0; + + return FLASH->SR & FLASH_SR_ERR; +} + +modm_ramcode uint32_t +%% if has_sectors +Flash::program(uintptr_t addr, MaxWordType data, WordSize size) +%% else +Flash::program(uintptr_t addr, MaxWordType data) +%% endif +{ + FLASH->SR = FLASH_SR_ERR; +%% if has_sectors + FLASH->CR = FLASH_CR_PG | uint32_t(size); + switch(size) + { + case WordSize::B8: + *(uint8_t *) addr = data; + break; + case WordSize::B16: + *(uint16_t *) addr = data; + break; + default: + *(uint32_t *) addr = data; + break; + } +%% else + FLASH->CR = FLASH_CR_PG; + *(uint64_t*) addr = data; +%% endif + + while(isBusy()) ; + FLASH->CR = 0; + + return FLASH->SR & FLASH_SR_ERR; +} + +} // namespace modm::platform diff --git a/src/modm/platform/flash/stm32/flash.hpp.in b/src/modm/platform/flash/stm32/flash.hpp.in new file mode 100644 index 0000000000..be66a6f7c3 --- /dev/null +++ b/src/modm/platform/flash/stm32/flash.hpp.in @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once +#include "../device.hpp" +#include +#include + +namespace modm::platform +{ + +/// @ingroup modm_platform_flash +class Flash +{ +public: + static constexpr uintptr_t OriginAddr{ 0x{{ "%0x" | format(start) }} }; + static constexpr size_t Size{ 0x{{ "%0x" | format(size) }} }; + static inline uint8_t *const Origin{(uint8_t*)OriginAddr}; + using MaxWordType = uint{{ 32 if has_sectors else 64 }}_t; + +%% if has_sectors + enum class + WordSize : uint32_t + { + B8 = 0, + B16 = FLASH_CR_PSIZE_0, + B32 = FLASH_CR_PSIZE_1, + }; +%% endif + +public: + inline static void + enable() + { +%% if not has_sectors + Rcc::enable(); +%% endif + } + + inline static void + disable() + { +%% if not has_sectors + Rcc::disable(); +%% endif + + } + + static bool + isLocked() + { return FLASH->CR & FLASH_CR_LOCK; } + + static inline bool + isBusy() +%% if has_sectors + { return FLASH->SR & FLASH_SR_BSY; } +%% else + { return FLASH->SR & FLASH_SR_BSY1; } +%% endif + + static bool + unlock(); + + static inline uint8_t + getSector(uint8_t *ptr) + { return getPage(ptr - Flash::Origin); } + + static uint8_t + getSector(uintptr_t offset) + { return getPage(offset); } + + static inline uint8_t + getPage(uint8_t *ptr) + { return getPage(ptr - Flash::Origin); } + + static uint8_t + getPage(uintptr_t offset); + + static uint32_t +%% if has_sectors + erase(uint8_t {{ type }}, WordSize size = WordSize::B32); +%% else + erase(uint8_t {{ type }}); +%% endif + +%% if has_sectors + static inline uint32_t + program(uintptr_t addr, uint8_t data) + { return program(addr, data, WordSize::B8); } + + static inline uint32_t + program(uintptr_t addr, uint16_t data) + { return program(addr, data, WordSize::B16); } + + static uint32_t + program(uintptr_t addr, MaxWordType data, WordSize size = WordSize::B32); +%% else + static uint32_t + program(uintptr_t addr, MaxWordType data); +%% endif +}; + +} // namespace modm::platform diff --git a/src/modm/platform/flash/stm32/module.lb b/src/modm/platform/flash/stm32/module.lb new file mode 100644 index 0000000000..f7c266d61c --- /dev/null +++ b/src/modm/platform/flash/stm32/module.lb @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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:flash" + module.description = "Flash Memory" + +def prepare(module, options): + device = options[":target"] + if device.identifier.family not in ["g0", "f4"]: + return False + if not device.has_driver("flash:stm32*"): + return False + + module.depends(":cmsis:device", ":platform:rcc", + ":architecture:register") + return True + +def build(env): + target = env[":target"].identifier + memories = listify(env[":target"].get_driver("core")["memory"]) + flash = next(filter(lambda m: m["name"] == "flash", memories)) + + if target.family in ["f4"]: + block_shift = 17 + ftype = "sector" + elif target.family in ["g0"]: + block_shift = 11 + ftype = "page" + + env.substitutions = { + "start": int(flash["start"], 16), + "size": int(flash["size"]), + "type": ftype, + "shift": block_shift, + "has_sectors": ftype == "sector", + } + env.outbasepath = "modm/src/modm/platform/flash" + env.template("flash.hpp.in") + env.template("flash.cpp.in") From 9e285dbfa3cd66f73877c401cff037c58e2e2f40 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 11 Jun 2020 00:01:45 +0200 Subject: [PATCH 048/135] [examples] Add Flash programming for STM32 --- examples/nucleo_f042k6/lp503x/main.cpp | 7 ++- examples/nucleo_f446re/flash/main.cpp | 76 ++++++++++++++++++++++++ examples/nucleo_f446re/flash/project.xml | 12 ++++ examples/nucleo_g071rb/flash/main.cpp | 63 ++++++++++++++++++++ examples/nucleo_g071rb/flash/project.xml | 12 ++++ 5 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 examples/nucleo_f446re/flash/main.cpp create mode 100644 examples/nucleo_f446re/flash/project.xml create mode 100644 examples/nucleo_g071rb/flash/main.cpp create mode 100644 examples/nucleo_g071rb/flash/project.xml diff --git a/examples/nucleo_f042k6/lp503x/main.cpp b/examples/nucleo_f042k6/lp503x/main.cpp index 8cc9e41828..3245db712a 100644 --- a/examples/nucleo_f042k6/lp503x/main.cpp +++ b/examples/nucleo_f042k6/lp503x/main.cpp @@ -12,6 +12,7 @@ #include using namespace Board; +using namespace std::chrono_literals; /* * Example to demonstrate LP503x driver @@ -51,7 +52,7 @@ main() RF_CALL_BLOCKING(leds.setChannelBrightness(channel, brightness)); } - modm::delayMilliseconds(1000); + modm::delay(1s); // Configure outputs 0-5 (rgb led 0-1) in bank mode using LedBankMode = modm::lp503x::LedBankMode; @@ -66,8 +67,8 @@ main() // Blink leds in bank mode while(true) { RF_CALL_BLOCKING(leds.setBankBrightness(255)); - modm::delayMilliseconds(500); + modm::delay(0.5s); RF_CALL_BLOCKING(leds.setBankBrightness(0)); - modm::delayMilliseconds(500); + modm::delay(0.5s); } } diff --git a/examples/nucleo_f446re/flash/main.cpp b/examples/nucleo_f446re/flash/main.cpp new file mode 100644 index 0000000000..4cefe0267c --- /dev/null +++ b/examples/nucleo_f446re/flash/main.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, 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 +#include + +using namespace std::chrono_literals; + +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::INFO + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + + MODM_LOG_INFO << "\n\nReboot\n"; + if (not Flash::unlock()) { + MODM_LOG_INFO << "Flash unlock failed!" << modm::endl; + } + + for (uintptr_t offset{0}, sector{255}; offset < Flash::Size; offset += 1) + { + const uint8_t nsector = Flash::getSector(offset); + if (sector != nsector) { + MODM_LOG_INFO << "Sector " << nsector << " found at boundary " << + (Flash::Origin + offset) << modm::endl; + sector = nsector; + } + } + + { + uint32_t err{0}; + const uint8_t sector_start = Flash::getSector(Flash::Size/2); + const uint8_t sector_end = Flash::getSector(Flash::Size); + MODM_LOG_INFO << "Erasing sectors [" << sector_start << ", " << sector_end << ")" << modm::endl; + MODM_LOG_INFO.flush(); + modm::delay(1s); + + const modm::PreciseTimestamp start = modm::PreciseClock::now(); + + for (uint8_t sector{sector_start}; sector < sector_end; sector++) + err |= Flash::erase(sector); + + const auto diff = (modm::PreciseClock::now() - start); + MODM_LOG_INFO << "Erasing done in " << diff << " with errors: " << err << modm::endl; + MODM_LOG_INFO << "Erasing with " << (Flash::Size/2 / (diff.count() >> 10) ) << "kiB/s" << modm::endl; + MODM_LOG_INFO.flush(); + } + + { + uint32_t err{0}; + const modm::PreciseTimestamp start = modm::PreciseClock::now(); + for (uint32_t dst_addr{Flash::OriginAddr + Flash::Size/2}, src_addr{Flash::OriginAddr}; + src_addr < (Flash::OriginAddr + Flash::Size/2); + src_addr += sizeof(Flash::MaxWordType), dst_addr += sizeof(Flash::MaxWordType)) + { + err |= Flash::program(dst_addr, *(Flash::MaxWordType*)src_addr); + } + + const auto diff = (modm::PreciseClock::now() - start); + MODM_LOG_INFO << "Programming done in " << diff << " with errors: " << err << modm::endl; + MODM_LOG_INFO << "Programming with " << (Flash::Size/2 / (diff.count() >> 10) ) << "kiB/s" << modm::endl; + } + + while(1) ; + return 0; +} diff --git a/examples/nucleo_f446re/flash/project.xml b/examples/nucleo_f446re/flash/project.xml new file mode 100644 index 0000000000..69f785966e --- /dev/null +++ b/examples/nucleo_f446re/flash/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-f446re + + + + + modm:platform:gpio + modm:platform:flash + modm:processing:timer + modm:build:scons + + diff --git a/examples/nucleo_g071rb/flash/main.cpp b/examples/nucleo_g071rb/flash/main.cpp new file mode 100644 index 0000000000..f1b685b5a5 --- /dev/null +++ b/examples/nucleo_g071rb/flash/main.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020, 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 +#include + +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::INFO + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + + MODM_LOG_INFO << "\n\nReboot\n"; + if (not Flash::unlock()) { + MODM_LOG_INFO << "Flash unlock failed!" << modm::endl; + } + + { + uint32_t err{0}; + MODM_LOG_INFO << "Erasing sectors [32, 64)" << modm::endl; + MODM_LOG_INFO.flush(); + modm::delay(1s); + + const modm::PreciseTimestamp start = modm::PreciseClock::now(); + + for (uint8_t page{32}; page < 64u; page++) + err |= Flash::erase(page); + + const auto diff = (modm::PreciseClock::now() - start); + MODM_LOG_INFO << "Erasing done in " << diff << " with errors: " << err << modm::endl; + MODM_LOG_INFO << "Erasing with " << (Flash::Size/2 / (diff.count() >> 10) ) << "kiB/s" << modm::endl; + MODM_LOG_INFO.flush(); + } + + + { + uint32_t err{0}; + const modm::PreciseTimestamp start = modm::PreciseClock::now(); + for (uint32_t dst_addr{Flash::OriginAddr + Flash::Size/2}, src_addr{Flash::OriginAddr}; + src_addr < (Flash::OriginAddr + Flash::Size/2); + src_addr += sizeof(Flash::MaxWordType), dst_addr += sizeof(Flash::MaxWordType)) + { + err |= Flash::program(dst_addr, *(Flash::MaxWordType*)src_addr); + } + + const auto diff = (modm::PreciseClock::now() - start); + MODM_LOG_INFO << "Programming done in " << diff << " with errors: " << err << modm::endl; + MODM_LOG_INFO << "Programming with " << (Flash::Size/2 / (diff.count() >> 10) ) << "kiB/s" << modm::endl; + } + + while(1) ; + return 0; +} diff --git a/examples/nucleo_g071rb/flash/project.xml b/examples/nucleo_g071rb/flash/project.xml new file mode 100644 index 0000000000..d1d5f56b79 --- /dev/null +++ b/examples/nucleo_g071rb/flash/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-g071rb + + + + + modm:platform:gpio + modm:platform:flash + modm:processing:timer + modm:build:scons + + From 5c381ab18569c9dad350374fb53e5dc0b979c89f Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sat, 13 Jun 2020 19:45:50 +0200 Subject: [PATCH 049/135] [driver] Add Sx1276 LoRa Modem --- README.md | 3 +- src/modm/driver/radio/sx1276.hpp | 180 +++++++++ src/modm/driver/radio/sx1276.lb | 33 ++ src/modm/driver/radio/sx1276_definitions.hpp | 202 ++++++++++ src/modm/driver/radio/sx1276_impl.hpp | 366 +++++++++++++++++++ 5 files changed, 783 insertions(+), 1 deletion(-) create mode 100644 src/modm/driver/radio/sx1276.hpp create mode 100644 src/modm/driver/radio/sx1276.lb create mode 100644 src/modm/driver/radio/sx1276_definitions.hpp create mode 100644 src/modm/driver/radio/sx1276_impl.hpp diff --git a/README.md b/README.md index af0fdde0d6..f4ba2d5b0f 100644 --- a/README.md +++ b/README.md @@ -230,11 +230,12 @@ can easily configure them for you specific needs. SK9822 SSD1306 +SX1276 TCS3414 TCS3472 TLC594X -TMP102 +TMP102 TMP175 VL53L0 VL6180 diff --git a/src/modm/driver/radio/sx1276.hpp b/src/modm/driver/radio/sx1276.hpp new file mode 100644 index 0000000000..f5187b9e41 --- /dev/null +++ b/src/modm/driver/radio/sx1276.hpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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_SX1276_HPP +#define MODM_SX1276_HPP + +#include + +#include "sx1276_definitions.hpp" + +namespace modm +{ + +/** + * @tparam SpiMaster SpiMaster interface + * @tparam Cs Chip-select pin + * + * @author Benjamin Carrick + * @ingroup modm_driver_sx1276 + */ +template +class Sx1276 : public sx1276, public modm::SpiDevice, protected modm::NestedResumable<6> +{ +public: + /** + * \brief Construct a new Sx1276 Driver + */ + Sx1276(); + + /** + * \brief Initialize the modem into Lora mode + */ + modm::ResumableResult + initialize(); + + /** + * \brief Setup the modem parameters + * + * \param bw The Bandwidth of the LoRa Transmission + * \param sf The Spreading factor for each symbol + * \param cr The Coding rate for the symbols + * \param implicitHeader Run the modem in implicit header mode + * \param payloadCrc Append CRC checksums to validate the packages + */ + modm::ResumableResult + setModemParams( Bandwidth bw, + SpreadingFactor sf, + CodingRate cr, + bool implicitHeader, + bool payloadCrc); + + /** + * \brief Set the carrier frequency of the Modem + * + * \param freq The Carrier frequency the modem will be tuned to + */ + modm::ResumableResult + setCarrierFrequency(frequency_t freq); + + /** + * \brief Set the sync word for the LoRa packages + * + * \param syncWord The new sync word of upcoming lora packages + */ + modm::ResumableResult + setSyncWord(uint8_t syncWord); + + /** + * \brief Transmit a single package + * + * \param data A pointer to the payload data of the Package + * \param length The length of the payload data in bytes + */ + modm::ResumableResult + transmit(uint8_t* data, uint8_t length); + + /** + * \brief Enable the Rx mode and start listening for packages + */ + modm::ResumableResult + enableListening(); + + /** + * \brief Disable Rx mode + */ + modm::ResumableResult + disableListening(); + + /** + * \brief Reads a package from the modem if one is available. + * The return value indicates how many bytes have been read from the last received package. + * If the return value is 0 no package has been received, is it larger than the max length + * parameter, the package was bigger than the buffer provided and got discarded + * + * \param data A pointer to a buffer where received data will be stored + * \param maxLength The maximum amount of bytes that can be read + * \return The amount of bytes in the last package + */ + modm::ResumableResult + readPacket(uint8_t* data, uint8_t maxLength); + + /** + * \brief Get the signal to noise ratio for the last received packet + * + * \return The raw signal to noise ratio in dB (has to be divided by 4 for the actual value) + */ + modm::ResumableResult + getPacketSnr(); + + /** + * \brief Get the recieved signal strength indicator (RSSI) of the last received packet + * + * \return The RSSI value of the last received packet + */ + modm::ResumableResult + getPacketRssi(); + + /** + * \brief Get the current recieved signal strength indicator (RSSI) + * + * \return The RSSI value of the last received packet + */ + modm::ResumableResult + getCurrentRssi(); + +private: + + /// The frequency of the connected oscillator + static constexpr frequency_t osc_freq = 32_MHz; + + /// The internal buffer for spi transfers + uint8_t buffer[4]; + + // Shadow registers of the chip registers + + OpModeRegister_t opModeShadow; + ModemConfig1_t modemConf1Shadow; + ModemConfig2_t modemConf2Shadow; + ModemConfig3_t modemConf3Shadow; + int32_t frfShadow; + Interrupts_t irqMaskShadow; + + /// The latest irq flags + Interrupts_t irqFlags; + /// The payload size of the last packet + uint8_t lastPacketSize; + /// Temporary storage for RSSI values + int16_t tempRssi; + + /// Reads a single register + modm::ResumableResult + readRegister(Sx1276Register reg); + + /// Writes a single register with a given value + modm::ResumableResult + writeRegister(Sx1276Register reg, uint8_t value); + + /// Transfers the buffer to the SPI bus + modm::ResumableResult + transferBuffer(uint8_t length); + + /// change the operation mode of the modem + modm::ResumableResult + changeMode(ModemMode mode); + +}; + +} + +#include "sx1276_impl.hpp" + +#endif diff --git a/src/modm/driver/radio/sx1276.lb b/src/modm/driver/radio/sx1276.lb new file mode 100644 index 0000000000..156f21f588 --- /dev/null +++ b/src/modm/driver/radio/sx1276.lb @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, 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 = ":driver:sx1276" + module.description = "Semtech SX1276 Driver" + +def prepare(module, options): + module.depends( + ":architecture:delay", + ":architecture:register", + ":architecture:spi", + ":architecture:spi.device", + ":architecture:clock", + ":debug", + ":processing:timer") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/radio" + env.copy("sx1276.hpp") + env.copy("sx1276_impl.hpp") + env.copy("sx1276_definitions.hpp") diff --git a/src/modm/driver/radio/sx1276_definitions.hpp b/src/modm/driver/radio/sx1276_definitions.hpp new file mode 100644 index 0000000000..5720961480 --- /dev/null +++ b/src/modm/driver/radio/sx1276_definitions.hpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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_SX1276_DEFINITIONS_HPP +#define MODM_SX1276_DEFINITIONS_HPP + +#include +#include +#include + +namespace modm +{ + +/// @ingroup modm_driver_sx1276 +struct sx1276 +{ +public: + + ///Definitions of all the Registers of the modem in LoRa mode + enum class + Sx1276Register : uint8_t + { + Fifo = 0x00, ///< Fifo I/O for the transceiver + OpMode = 0x01, ///< Operation Mode Register + FrMsb = 0x06, ///< MSB of the Carrier Frequency Register + FrMid = 0x07, ///< Middle Byte of the Carrier Frequency Register + FrLsb = 0x08, ///< LSB of the Carrier Frequency Register + PaConfig = 0x09, ///< Power output control + PaRamp = 0x0A, ///< Power ramp steepness + Ocp = 0x0B, ///< Overload current protection + Lna = 0x0C, ///< Low Noise Amplifier Control + FifoAddrPtr = 0x0D, ///< Address Pointer in Fifo Buffer + FifoTxBaseAddr = 0x0E, ///< Base Write Address for Tx Fifo + FifoRxBaseAddr = 0x0F, ///< Base Read Address for Rx Fifo + FifoRxCurrentAddr = 0x10, ///< Fifo Address of the last received packet + IrqFlagsMask = 0x11, ///< Interrupt Masks + IrqFlags = 0x12, ///< Interrupt Flags + RxNbBytes = 0x13, ///< Amount of payload bytes in last received message + RxHeaderCntValueMsb = 0x14, ///< MSB of valid header counter since last transition to rx mode + RxHeaderCntValueLsb = 0x15, ///< LSB of valid header counter since last transition to rx mode + RxPacketCntValueMsb = 0x16, ///< MSB of valid packet counter since last transition to rx mode + RxPacketCntValueLsb = 0x17, ///< LSB of valid packet counter since last transition to rx mode + ModemStat = 0x18, ///< Modem status + PktSnrValue = 0x19, ///< Estimation of the signal-to-noise ration of the last packet received + PktRssiValue = 0x1A, ///< RSSI value of the last packet received + RssiValue = 0x1B, ///< Current RSSI value + HopChannel = 0x1C, ///< Configuration of the frequency hopping + ModemConfig1 = 0x1D, ///< Lora PHY configuration register 1 + ModemConfig2 = 0x1E, ///< Lora PHY configuration register 2 + SymbTimeoutLsb = 0x1F, ///< LSB symbol timeout (MSB is in ModemConfig 2 Register) + PreambleMsb = 0x20, ///< MSB of the preamble length + PreambleLsb = 0x21, ///< LSB of the preamble length + PayloadLength = 0x22, ///< Payload length + MaxPayloadLength = 0x23, ///< Maximum payload length + HopPeriod = 0x24, ///< Frequency Hopping Period + FifoRxByteAddr = 0x25, ///< Current Rx Fifo Pointer + ModemConfig3 = 0x26, ///< Lora PHY configuration register 3 + PpmCorrection = 0x27, ///< Data rate offset control + FeiMsb = 0x28, ///< MSB of the estimated Frequency Error + FeiMid = 0x29, ///< Middle byte of the estimated Frequency error + FeiLsb = 0x2A, ///< LSB of the estimated frequency error + RssiWideband = 0x2C, ///< Wideband RSSI measurement + DetectOptimize = 0x31, ///< Lora Detection Optimize + InvertIQ = 0x33, ///< Invert I and Q + HighBWOptimize1 = 0x36, ///< High bandwidth optimization register 1 + DetectionThreshold = 0x37, ///< Lora detection threshold + SyncWord = 0x39, ///< Lora syncword (0x34 for LoraWAN) + HighBWOptimize2 = 0x3A, ///< High bandwidth optimization 2 + InvertIQ2 = 0x3B ///< Set to 0x19 for inverted IQ + }; + + /// Operation mode register + enum class + OpModeRegister : uint8_t + { + LoraMode = Bit7, ///< Enables the Lora Mode - if zero the FSK mode is active + AccessSharedReg = Bit6, ///< Enables shared access to FSK register in Lora mode + // Bit 5 : reserved + // Bit 4 : reserved + LowFrequencyMode = Bit3 /// ModemMode_t; + + //Available interrupts with their positions in the flag and mask register + enum class + Interrupts : uint8_t + { + CAD_DETECED = 1, + FHSS_CHANGE_CHANNEL = 2, + CAD_DONE = 4, + TX_DONE = 8, + VALID_HEADER = 16, + PAYLOAD_CRC_ERROR = 32, + RX_DONE = 64, + RX_TIMEOUT = 128 + }; + MODM_FLAGS8(Interrupts); + + //Modem Config + + /// Operation mode register + enum class + ModemConfig1 : uint8_t + { + //Bit 7 - 4: Bandwidth + //Bit 3 - 1: Error Coding Rate + ImplicitHeaderModeOn = Bit0 ///< Headers will not transmitted + }; + MODM_FLAGS8(ModemConfig1); + + /// LoRa Bandwidth Modes + enum class + Bandwidth : uint8_t + { + BW_0 = 0x00, ///< 7.8 kHz + BW_1 = 0x10, ///< 10.4 kHZ + BW_2 = 0x20, ///< 15.6 kHz + BW_3 = 0x30, ///< 20.8 kHz + BW_4 = 0x40, ///< 31.25 kHz + BW_5 = 0x50, ///< 41.7 kHz + BW_6 = 0x60, ///< 62.5 kHz + BW_7 = 0x70, ///< 125 kHz + BW_8 = 0x80, ///< 250 kHz + BW_9 = 0x90, ///< 500 kHz + }; + typedef modm::Configuration Bandwidth_t; + + ///LoRa Coding Rate + enum class + CodingRate : uint8_t + { + CR_4_5 = 0b0010, ///< Error Coding Rate 4/5 + CR_4_6 = 0b0100, ///< Error Coding Rate 4/6 + CR_4_7 = 0b0110, ///< Error Coding Rate 4/7 + CR_4_8 = 0b1000, ///< Error Coding Rate 4/8 + }; + typedef modm::ConfigurationCodingRate_t; + + // Flag definitions for configuration registers + enum class + ModemConfig2 : uint8_t + { + //Bit 7 - 4: Spreading Factor + TxContinousMode = Bit3, ///< Continously Transmit Data + RxPayloadCrc = Bit2 ///< Messages will be transmitted along with a CRC + }; + MODM_FLAGS8(ModemConfig2); + + enum class + ModemConfig3 : uint8_t + { + //Bit 7 - 4: Unused + LowDataRateOptimize = Bit3, ///< Optimize the modem for symbol times larger than 16ms + AgcAutoOn = Bit2 ///< Turn on the Automatic Gain Control + }; + MODM_FLAGS8(ModemConfig3); + + ///Available Spreading Factors + enum class + SpreadingFactor : uint8_t + { + SF_6 = 0x60, ///< 64 chips/symbol + SF_7 = 0x70, ///< 128 chips/symbol + SF_8 = 0x80, ///< 256 chips/symbol + SF_9 = 0x90, ///< 512 chips/symbol + SF_10 = 0xA0, ///< 1024 chips/symbol + SF_11 = 0xB0, ///< 2048 chips/symbol + SF_12 = 0xC0, ///< 4096 chips/symbol + + }; + typedef modm::ConfigurationSpreadingFactor_t; + + static constexpr int16_t rssiOffsetLF = -164; + +}; // struct sx1276 + +} +#endif diff --git a/src/modm/driver/radio/sx1276_impl.hpp b/src/modm/driver/radio/sx1276_impl.hpp new file mode 100644 index 0000000000..45c09d06bf --- /dev/null +++ b/src/modm/driver/radio/sx1276_impl.hpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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_SX1276_HPP +# error "Don't include this file directly, use 'sx1276.hpp' instead!" +#endif + +#include "sx1276.hpp" +#include +#include + +namespace modm +{ + +// ---------------------------------------------------------------------------- + +template +Sx1276::Sx1276() +{ + //init the config shadow registers with the default values + opModeShadow.value = 0x09; + modemConf1Shadow.value = 0x72; + modemConf2Shadow.value = 0x70; + modemConf3Shadow.value = 0x00; + irqMaskShadow.value = 0x00; +} + +// ----------------------------------------------------------------------------- + +template +ResumableResult +Sx1276::initialize() +{ + RF_BEGIN(); + + // set the mode to sleep + RF_CALL(changeMode(ModemMode::SLEEP)); + + // change to the Lora mode + opModeShadow.set(OpModeRegister::LoraMode); + opModeShadow.reset(OpModeRegister::AccessSharedReg); + opModeShadow.set(OpModeRegister::LowFrequencyMode); + + RF_CALL(writeRegister(Sx1276Register::OpMode,opModeShadow.value)); + + //mask out all interrupts + irqMaskShadow.value = 0xFF; + RF_CALL(writeRegister(Sx1276Register::IrqFlagsMask,irqMaskShadow.value)); + + // set the mode back to standby + RF_CALL(changeMode(ModemMode::STDBY)); + + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::setCarrierFrequency(frequency_t freq) +{ + RF_BEGIN(); + + frfShadow = static_cast(freq * (static_cast(1<<19) / static_cast(osc_freq))); + + // do a burst write to the three frequency registers instead of seperate register write calls + buffer[0] = 0x80 | static_cast(Sx1276Register::FrMsb); + buffer[1] = static_cast((frfShadow >> 16) & 0xFF); + buffer[2] = static_cast((frfShadow >> 8) & 0xFF); + buffer[3] = static_cast(frfShadow & 0xFF); + + RF_CALL(transferBuffer(4)); + + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::setModemParams( Bandwidth bw, + SpreadingFactor sf, + CodingRate cr, + bool implicitHeader, + bool payloadCrc) +{ + RF_BEGIN(); + //configure the modem config shadow registers + Bandwidth_t::set(modemConf1Shadow,bw); + CodingRate_t::set(modemConf1Shadow,cr); + modemConf1Shadow.update(ModemConfig1::ImplicitHeaderModeOn,implicitHeader); + + SpreadingFactor_t::set(modemConf2Shadow,sf); + modemConf2Shadow.reset(ModemConfig2::TxContinousMode); + modemConf2Shadow.update(ModemConfig2::RxPayloadCrc,payloadCrc); + + //determine if the low bandwith optimization flag needs to be set + if( (sf == SpreadingFactor::SF_7 && bw < Bandwidth::BW_1) || + (sf == SpreadingFactor::SF_8 && bw < Bandwidth::BW_3) || + (sf == SpreadingFactor::SF_9 && bw < Bandwidth::BW_5) || + (sf == SpreadingFactor::SF_10 && bw < Bandwidth::BW_7) || + (sf == SpreadingFactor::SF_11 && bw < Bandwidth::BW_8) || + (sf == SpreadingFactor::SF_12 && bw < Bandwidth::BW_9) + ) + { + modemConf3Shadow.set(ModemConfig3::LowDataRateOptimize); + } + else + { + modemConf3Shadow.reset(ModemConfig3::LowDataRateOptimize); + } + + // Write out the config registers + RF_CALL(writeRegister(Sx1276Register::ModemConfig1,modemConf1Shadow.value)); + RF_CALL(writeRegister(Sx1276Register::ModemConfig2,modemConf2Shadow.value)); + RF_CALL(writeRegister(Sx1276Register::ModemConfig3,modemConf3Shadow.value)); + + //Configure the optimizations + if(sf == SpreadingFactor::SF_6) + { + RF_CALL(writeRegister(Sx1276Register::DetectOptimize,0x05)); + RF_CALL(writeRegister(Sx1276Register::DetectionThreshold,0x0C)); + } + else + { + RF_CALL(writeRegister(Sx1276Register::DetectOptimize,0x03)); + RF_CALL(writeRegister(Sx1276Register::DetectionThreshold,0x0A)); + } + + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::setSyncWord(uint8_t syncWord) +{ + RF_BEGIN(); + RF_CALL(writeRegister(Sx1276Register::SyncWord,syncWord)); + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::transmit(uint8_t* data, uint8_t length) +{ + RF_BEGIN(); + + RF_CALL(changeMode(ModemMode::STDBY)); + + RF_CALL(writeRegister(Sx1276Register::PayloadLength,length)); + RF_CALL(writeRegister(Sx1276Register::FifoTxBaseAddr,0x00)); + RF_CALL(writeRegister(Sx1276Register::FifoAddrPtr,0x00)); + + RF_WAIT_UNTIL(this->acquireMaster()); + + buffer[0] = 0x80 | static_cast(Sx1276Register::Fifo); + + Cs::reset(); + + RF_CALL(SpiMaster::transfer(buffer,nullptr,1)); + RF_CALL(SpiMaster::transfer(data,nullptr,length)); + + if(this->releaseMaster()) + { + Cs::set(); + } + + RF_CALL(changeMode(ModemMode::TX)); + + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::enableListening() +{ + RF_BEGIN(); + //enable the rx interrupts + irqMaskShadow.reset(Interrupts::RX_DONE); + + RF_CALL(writeRegister(Sx1276Register::FifoAddrPtr,0x00)); + RF_CALL(writeRegister(Sx1276Register::FifoRxBaseAddr,0x00)); + + RF_CALL(writeRegister(Sx1276Register::IrqFlagsMask,irqMaskShadow.value)); + RF_CALL(writeRegister(Sx1276Register::IrqFlags, 0xFF)); + + RF_CALL(changeMode(ModemMode::RXCONTINOUS)); + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::disableListening() +{ + RF_BEGIN(); + + irqMaskShadow.set(Interrupts::RX_DONE); + RF_CALL(writeRegister(Sx1276Register::IrqFlagsMask,irqMaskShadow.value)); + + RF_CALL(changeMode(ModemMode::SLEEP)); + + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::readPacket(uint8_t* data, uint8_t maxLength) +{ + RF_BEGIN(); + + irqFlags.value = RF_CALL(readRegister(Sx1276Register::IrqFlags)); + RF_CALL(writeRegister(Sx1276Register::IrqFlags, 0xFF)); + + lastPacketSize = 0; + + if(irqFlags & Interrupts::RX_DONE) + { + lastPacketSize = RF_CALL(readRegister(Sx1276Register::RxNbBytes)); + RF_WAIT_UNTIL(this->acquireMaster()); + + buffer[0] = static_cast(Sx1276Register::Fifo); + + Cs::reset(); + + RF_CALL(SpiMaster::transfer(buffer,nullptr,1)); + if(lastPacketSize > maxLength) + { + MODM_LOG_ERROR<<"Read buffer is too small, packet discarded!"<releaseMaster()) + { + Cs::set(); + } + + } + + RF_END_RETURN(lastPacketSize); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::getPacketSnr() +{ + RF_BEGIN(); + RF_CALL(readRegister(Sx1276Register::PktSnrValue)); + RF_END_RETURN(static_cast(buffer[1])); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::getPacketRssi() +{ + RF_BEGIN(); + tempRssi = static_cast(RF_CALL(readRegister(Sx1276Register::PktRssiValue))); + tempRssi += rssiOffsetLF; + RF_END_RETURN(tempRssi); +} +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::getCurrentRssi() +{ + RF_BEGIN(); + tempRssi = static_cast(RF_CALL(readRegister(Sx1276Register::RssiValue))); + tempRssi += rssiOffsetLF; + RF_END_RETURN(tempRssi); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::transferBuffer(uint8_t length) +{ + RF_BEGIN(); + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + RF_CALL(SpiMaster::transfer(buffer,buffer,length)); + + if(this->releaseMaster()) + { + Cs::set(); + } + + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::writeRegister(Sx1276Register reg, uint8_t value) +{ + RF_BEGIN(); + + buffer[0] = 0x80 | static_cast(reg); + buffer[1] = value; + + RF_CALL(transferBuffer(2)); + + RF_END(); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::readRegister(Sx1276Register reg) +{ + RF_BEGIN(); + + buffer[0] = static_cast(reg); + buffer[1] = 0x00; + + RF_CALL(transferBuffer(2)); + + RF_END_RETURN(buffer[1]); +} + +// ----------------------------------------------------------------------------- + +template +modm::ResumableResult +Sx1276::changeMode(ModemMode mode) +{ + RF_BEGIN(); + + ModemMode_t::set(opModeShadow,mode); + RF_CALL(writeRegister(Sx1276Register::OpMode,opModeShadow.value)); + + RF_END(); +} + +} \ No newline at end of file From 7d1f7cc3201ccf23bed973b5109e4380bbfd1922 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sat, 13 Jun 2020 19:45:56 +0200 Subject: [PATCH 050/135] [examples] Added Sx1276 LoRa example for Nucleo64 --- examples/nucleo_f411re/sx1276_rx/main.cpp | 90 ++++++++++++++++++++ examples/nucleo_f411re/sx1276_rx/project.xml | 11 +++ examples/nucleo_f411re/sx1276_tx/main.cpp | 69 +++++++++++++++ examples/nucleo_f411re/sx1276_tx/project.xml | 11 +++ 4 files changed, 181 insertions(+) create mode 100644 examples/nucleo_f411re/sx1276_rx/main.cpp create mode 100644 examples/nucleo_f411re/sx1276_rx/project.xml create mode 100644 examples/nucleo_f411re/sx1276_tx/main.cpp create mode 100644 examples/nucleo_f411re/sx1276_tx/project.xml diff --git a/examples/nucleo_f411re/sx1276_rx/main.cpp b/examples/nucleo_f411re/sx1276_rx/main.cpp new file mode 100644 index 0000000000..d09f857412 --- /dev/null +++ b/examples/nucleo_f411re/sx1276_rx/main.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020 Benjamin Carrick + * + * 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 + +using namespace Board; +using namespace modm; + +int +main() +{ + Board::initialize(); + + //RxTx selection on the mbed shield + using RxTx = Board::A4; + + using SpiMosi = Board::D11; + using SpiMiso = Board::D12; + using SpiSck = Board::D13; + using SpiCs = Board::D10; + + //Set the RF switch to Receive + RxTx::setOutput(); + RxTx::reset(); + + //Initialize the SPI + SpiCs::setOutput(); + SpiCs::set(); + + SpiMaster1::connect(); + SpiMaster1::initialize(); + + MODM_LOG_DEBUG << "Starting the modem" << modm::endl; + + // Create an instance of the driver + Sx1276 loraModem; + + // Initialize the modem + RF_CALL_BLOCKING(loraModem.initialize()); + + // Set Modulation Parameters + RF_CALL_BLOCKING(loraModem.setModemParams(sx1276::Bandwidth::BW_7, + sx1276::SpreadingFactor::SF_8, + sx1276::CodingRate::CR_4_8, + false, + false)); + // Set Carrier Frequency + RF_CALL_BLOCKING(loraModem.setCarrierFrequency(433.920_MHz)); + + // Enable the continous listening mode + RF_CALL_BLOCKING(loraModem.enableListening()); + + //the variable to hold the counter transmitted by the tx example + uint32_t rxCounter(0); + + while (true) + { + //check the modem if new data is available and read it + uint8_t bytesReceived = RF_CALL_BLOCKING(loraModem.readPacket(reinterpret_cast(&rxCounter),4)); + if(bytesReceived > 0) + { + int8_t snr = RF_CALL_BLOCKING(loraModem.getPacketSnr()); + + //dirty fixed point numbers + uint8_t snr_dec = (snr & 0x03) * 25; + snr = snr >> 2; + + int16_t rssi = RF_CALL_BLOCKING(loraModem.getPacketRssi()); + + MODM_LOG_DEBUG << "Received Message" << modm::endl; + MODM_LOG_DEBUG << "Counter Value: " << rxCounter << modm::endl; + MODM_LOG_DEBUG << "SNR: "<< snr <<"."<< snr_dec << " dB" << modm::endl; + MODM_LOG_DEBUG << "RSSI: "<< rssi << " dBm" << modm::endl; + MODM_LOG_DEBUG << modm::endl; + } + //check for new data every 100ms + modm::delay(100ms); + } + + return 0; +} diff --git a/examples/nucleo_f411re/sx1276_rx/project.xml b/examples/nucleo_f411re/sx1276_rx/project.xml new file mode 100644 index 0000000000..7fc65465b5 --- /dev/null +++ b/examples/nucleo_f411re/sx1276_rx/project.xml @@ -0,0 +1,11 @@ + + modm:nucleo-f411re + + + + + modm:build:scons + modm:platform:spi:1 + modm:driver:sx1276 + + diff --git a/examples/nucleo_f411re/sx1276_tx/main.cpp b/examples/nucleo_f411re/sx1276_tx/main.cpp new file mode 100644 index 0000000000..51141f1f55 --- /dev/null +++ b/examples/nucleo_f411re/sx1276_tx/main.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 Benjamin Carrick + * + * 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 + +using namespace Board; +using namespace modm; + +int +main() +{ + Board::initialize(); + + //RxTx selection on the mbed shield + using RxTx = Board::A4; + + using SpiMosi = Board::D11; + using SpiMiso = Board::D12; + using SpiSck = Board::D13; + using SpiCs = Board::D10; + + //Set the RF switch to Transmit + RxTx::setOutput(); + RxTx::set(); + + //Initialize the SPI + SpiCs::setOutput(); + SpiCs::set(); + + SpiMaster1::connect(); + SpiMaster1::initialize(); + + // Create an instance of the driver + Sx1276 loraModem; + + // Initialize the modem + RF_CALL_BLOCKING(loraModem.initialize()); + + // Set Modulation Parameters + RF_CALL_BLOCKING(loraModem.setModemParams(sx1276::Bandwidth::BW_7, + sx1276::SpreadingFactor::SF_8, + sx1276::CodingRate::CR_4_8, + false, + false)); + // Set Carrier Frequency + RF_CALL_BLOCKING(loraModem.setCarrierFrequency(433.920_MHz)); + + //The message to be sent + uint32_t counter(0); + + while (true) + { + MODM_LOG_INFO << "Trasmitting Message " << counter << modm::endl; + RF_CALL_BLOCKING(loraModem.transmit(reinterpret_cast(&counter),4)); + counter++; + modm::delay(1000ms); + } + + return 0; +} diff --git a/examples/nucleo_f411re/sx1276_tx/project.xml b/examples/nucleo_f411re/sx1276_tx/project.xml new file mode 100644 index 0000000000..571712fbc8 --- /dev/null +++ b/examples/nucleo_f411re/sx1276_tx/project.xml @@ -0,0 +1,11 @@ + + modm:nucleo-f411re + + + + + modm:build:scons + modm:platform:spi:1 + modm:driver:sx1276 + + From 7755151071140b75cadff41fac6b5563ab3c9bab Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Sun, 24 Feb 2019 20:18:59 +0100 Subject: [PATCH 051/135] [driver] Driver for AB (incremental) encoder output --- README.md | 16 ++-- src/modm/driver/encoder/encoder_output.hpp | 86 +++++++++++++++++ src/modm/driver/encoder/encoder_output.lb | 33 +++++++ .../driver/encoder/encoder_output_impl.hpp | 92 +++++++++++++++++++ 4 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 src/modm/driver/encoder/encoder_output.hpp create mode 100644 src/modm/driver/encoder/encoder_output.lb create mode 100644 src/modm/driver/encoder/encoder_output_impl.hpp diff --git a/README.md b/README.md index f4ba2d5b0f..473001fb18 100644 --- a/README.md +++ b/README.md @@ -190,56 +190,58 @@ can easily configure them for you specific needs. DS1631 DS18B20 EA-DOG +ENCODER-OUTPUT FT245 -FT6X06 +FT6X06 HCLAx HD44780 HMC58x HMC6343 HX711 -I2C-EEPROM +I2C-EEPROM ITG3200 L3GD20 LAWICEL LIS302DL LIS3DSH -LM75 +LM75 LP503X LSM303A LTC2984 MAX6966 MAX7219 -MCP23X17 +MCP23X17 MCP2515 NOKIA5110 NRF24 TFT-DISPLAY PAT9125EL -PCA8574 +PCA8574 PCA9535 PCA9548A PCA9685 SIEMENS-S65 SIEMENS-S75 -SK6812 +SK6812 SK9822 SSD1306 SX1276 TCS3414 TCS3472 -TLC594X +TLC594X TMP102 TMP175 VL53L0 VL6180 WS2812 + diff --git a/src/modm/driver/encoder/encoder_output.hpp b/src/modm/driver/encoder/encoder_output.hpp new file mode 100644 index 0000000000..896d1be3e3 --- /dev/null +++ b/src/modm/driver/encoder/encoder_output.hpp @@ -0,0 +1,86 @@ +// coding: utf-8 +/* + * Copyright (c) 2019, Raphael Lehmann + * + * 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_ENCODER_OUTPUT_HPP +#define MODM_ENCODER_OUTPUT_HPP + +#include + +namespace modm +{ + +/** + * @ingroup modm_driver_encoder_output + * @author Raphael Lehmann + * + * @brief This driver generates a AB (incremental) encoder signal to + * emulate a hardware encoder with a microcontroller. + * + * @tparam PinA First modm::platform::Gpio pin to output the encoder signal. + * @tparam PinB Second modm::platform::Gpio pin to output the encoder signal. + * @tparam PositionType Data type (integer) of the position value, default `int32_t`. + * @tparam PeriodicTimer Defaults to `modm::PeriodicTimer`, must be replaced for + * encoder frequencies above 1kHz by a class that offers + * time steps less than 1ms, e.g. `modm::PrecisePeriodicTimer`. + * @tparam period Timebase for the output signal. This limits the maximal + * frequency of the encoder signal. Defaults to `1`. + */ +template < + class PinA, + class PinB, + typename PositionType = int32_t, + class PeriodicTimer = modm::PeriodicTimer, + uint32_t period = 1 + > +class EncoderOutput { +public: + /** + * @brief Initializes the Encoder. + * + * Sets pins (PinA and PinB) as output pins. + * + * @param initialValue for the encoder. Useful with unsigned PositionType + * data types. Defaults to 0. + */ + EncoderOutput(PositionType initialValue); + + /** + * @brief Update method. Generates the encoder signal. + * + * Call this function in your main loop. + */ + void update(); + + /** + * @brief Set the position for the encoder. + * @param position The position setpoint for the encoder. + */ + void setPosition(PositionType position) { setpoint = position; }; + +private: + PositionType setpoint; + PositionType actualValue; + PeriodicTimer timer; + enum class State : uint8_t { + State0, + State1, + State2, + State3, + }; + State state; +}; + +} + +#include "encoder_output_impl.hpp" + +#endif diff --git a/src/modm/driver/encoder/encoder_output.lb b/src/modm/driver/encoder/encoder_output.lb new file mode 100644 index 0000000000..e5068bb063 --- /dev/null +++ b/src/modm/driver/encoder/encoder_output.lb @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Raphael Lehmann +# +# 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.parent = "driver" + module.name = "encoder.output" + module.description = """\ +# Encoder Output + +This driver generates a AB (incremental) encoder signal to +emulate a hardware encoder with a microcontroller. +""" + +def prepare(module, options): + module.depends( + ":architecture:gpio", + ":processing:timer") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/encoder" + env.copy("encoder_output.hpp") + env.copy("encoder_output_impl.hpp") diff --git a/src/modm/driver/encoder/encoder_output_impl.hpp b/src/modm/driver/encoder/encoder_output_impl.hpp new file mode 100644 index 0000000000..e215f599fb --- /dev/null +++ b/src/modm/driver/encoder/encoder_output_impl.hpp @@ -0,0 +1,92 @@ +// coding: utf-8 +/* + * Copyright (c) 2019, Raphael Lehmann + * + * 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_ENCODER_OUTPUT_HPP +# error "Don't include this file directly, use 'encoder_output.hpp' instead!" +#endif +#include "encoder_output.hpp" + +template < + class PinA, + class PinB, + typename PositionType, + class PeriodicTimer, + uint32_t period + > +modm::EncoderOutput::EncoderOutput(PositionType initialValue) : + setpoint(initialValue), actualValue(initialValue), timer(period), state(State::State0) +{ + PinA::setOutput(); + PinB::setOutput(); + PinA::set(); + PinB::set(); + setpoint = initialValue; + actualValue = initialValue; +} + +template < + class PinA, + class PinB, + typename PositionType, + class PeriodicTimer, + uint32_t period + > +void +modm::EncoderOutput::update() +{ + if(timer.execute()){ + if(setpoint > actualValue) { + // generate forward tick + switch(state) { + case State::State0: + PinA::reset(); + state = State::State1; + break; + case State::State1: + PinB::reset(); + state = State::State2; + break; + case State::State2: + PinA::set(); + state = State::State3; + break; + case State::State3: + PinB::set(); + state = State::State0; + break; + } + actualValue++; + } + else if(setpoint < actualValue) { + // generate backward tick + switch(state) { + case State::State0: + PinB::reset(); + state = State::State3; + break; + case State::State1: + PinA::reset(); + state = State::State0; + break; + case State::State2: + PinB::set(); + state = State::State1; + break; + case State::State3: + PinA::set(); + state = State::State2; + break; + } + actualValue--; + } + } +} From 21af57b793f37db71de3e417e6f712b54b0d1f70 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Sun, 24 Feb 2019 20:19:38 +0100 Subject: [PATCH 052/135] [examples] Encoder output examle for STM32F4_Discovery --- .../stm32f4_discovery/encoder_output/main.cpp | 39 +++++++++++++++++++ .../encoder_output/project.xml | 10 +++++ 2 files changed, 49 insertions(+) create mode 100644 examples/stm32f4_discovery/encoder_output/main.cpp create mode 100644 examples/stm32f4_discovery/encoder_output/project.xml diff --git a/examples/stm32f4_discovery/encoder_output/main.cpp b/examples/stm32f4_discovery/encoder_output/main.cpp new file mode 100644 index 0000000000..7ab00dd744 --- /dev/null +++ b/examples/stm32f4_discovery/encoder_output/main.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, Raphael Lehmann + * + * 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 + +using namespace Board; + +// ---------------------------------------------------------------------------- +int +main() +{ + + initialize(); + + LedBlue::set(); + LedGreen::set(); + + // period=100000 // 100000us => 10Hz (visible) + using TestEncoder = modm::EncoderOutput; + TestEncoder testEncoder{0}; + + testEncoder.setPosition(1 << 31); + while (1) + { + testEncoder.update(); + LedRed::toggle(); + } + + return 0; +} diff --git a/examples/stm32f4_discovery/encoder_output/project.xml b/examples/stm32f4_discovery/encoder_output/project.xml new file mode 100644 index 0000000000..b07ef45845 --- /dev/null +++ b/examples/stm32f4_discovery/encoder_output/project.xml @@ -0,0 +1,10 @@ + + modm:disco-f407vg + + + + + modm:driver:encoder.output + modm:build:scons + + From 78d18f6c74a9f918c99ae14d24e2fb0f3513e63d Mon Sep 17 00:00:00 2001 From: Jeff McBride Date: Mon, 18 May 2020 13:39:04 -0700 Subject: [PATCH 053/135] [dac] Add basic STM32F4 driver --- src/modm/platform/dac/stm32/dac.hpp | 123 +++++++++++++++++++++++ src/modm/platform/dac/stm32/dac_impl.hpp | 68 +++++++++++++ src/modm/platform/dac/stm32/module.lb | 24 +++++ src/modm/platform/gpio/stm32/pin.hpp.in | 9 ++ 4 files changed, 224 insertions(+) create mode 100644 src/modm/platform/dac/stm32/dac.hpp create mode 100644 src/modm/platform/dac/stm32/dac_impl.hpp create mode 100644 src/modm/platform/dac/stm32/module.lb diff --git a/src/modm/platform/dac/stm32/dac.hpp b/src/modm/platform/dac/stm32/dac.hpp new file mode 100644 index 0000000000..68249d6f86 --- /dev/null +++ b/src/modm/platform/dac/stm32/dac.hpp @@ -0,0 +1,123 @@ +#ifndef MODM_STM32_DAC_HPP +#define MODM_STM32_DAC_HPP + +#include +#include "../device.hpp" +#include + +namespace modm { + +namespace platform { + +/** + * Digital-to-Analog Converter (DAC) + * + * Supports simple synchronous access to the 2-channel DAC found on most STM32 + * microcontrollers. + * + * @author Jeff McBride + * @ingroup modm_platform_dac + */ +class Dac { +public: + /** + * The DAC has two output channels, Channel1 and Channel2 + */ + enum class Channel : uint8_t { + Channel1 = 0, + Channel2 = 1 + }; + + template< template class... Signals > + static void + connect() + { + using Connector = GpioConnector; + Connector::connect(); + } + + /** + * Initialize the D/A converter. + * + * Enables the RCC clock output for the DAC + */ + static inline void initialize(); + + /** Enable DAC output for the given channel + * + * @param chan The channel to be enabled + * + * @pre The DAC clock must be enabled with initialize() + */ + static inline void enableChannel(Channel chan); + + /** + * Disable DAC output for the given channel + * + * @param chan The channel to be disabled + * + * @pre The DAC clock must be enabled with initialize() + */ + static inline void disableChannel(Channel chan); + + /** + * Control the output buffer setting + * + * @param chan The channel to enable/disable the output buffer on + * @param enable true to turn on the output buffer, false to turn it off + * + * @pre The DAC clock must be enabled with initialize() + */ + static inline void enableOutputBuffer(Channel chan, bool enable); + + /** + * Set the output voltage for a DAC channel + * + * @param chan The channel to set + * @param value The 12-bit DAC output value, range 0-4095. + * + * @pre The DAC clock must be enabled with initialize() + */ + static inline void setOutput(Channel chan, uint16_t value); + + /** + * Set output value for Channel1 + * + * Equivalent to setOutput(modm::platform::Dac::Channel1, value) + * + * @param value The 12-bit DAC output value, range 0-4095 + * + * @pre The DAC clock must be enabled with initialize() + */ + static inline void setOutput1(uint16_t value); + + /** + * Set output value for Channel2 + * + * Equivalent to setOutput(modm::platform::Dac::Channel2, value) + * + * @param value The 12-bit DAC output value, range 0-4095 + * + * @pre The DAC clock must be enabled with initialize() + */ + static inline void setOutput2(uint16_t value); + + /// Get the channel for a Pin + template< class Gpio > + static inline constexpr Channel + getPinChannel() + { + constexpr int8_t channel{Gpio::template DacChannel}; + static_assert(channel >= 0, "Dac does not have a channel for this pin!"); + return Channel(channel); + } + +}; + +} // namespace platform + +} // namespace modm + +#include "dac_impl.hpp" + +#endif // MODM_STM32_DAC_HPP \ No newline at end of file diff --git a/src/modm/platform/dac/stm32/dac_impl.hpp b/src/modm/platform/dac/stm32/dac_impl.hpp new file mode 100644 index 0000000000..5dd1e9b775 --- /dev/null +++ b/src/modm/platform/dac/stm32/dac_impl.hpp @@ -0,0 +1,68 @@ +#ifndef MODM_STM32_DAC_HPP +# error "Don't include this file directly, use 'dac.hpp' instead!" +#endif + +#include + +void modm::platform::Dac::initialize() { + Rcc::enable(); +} + +void modm::platform::Dac::enableChannel(Channel chan) { + switch(chan) { + case Channel::Channel1: + DAC1->CR |= DAC_CR_EN1_Msk; + break; + case Channel::Channel2: + DAC1->CR |= DAC_CR_EN2_Msk; + break; + } +} + +void modm::platform::Dac::disableChannel(Channel chan) { + switch(chan) { + case Channel::Channel1: + DAC1->CR &= ~DAC_CR_EN1_Msk; + break; + case Channel::Channel2: + DAC1->CR &= ~DAC_CR_EN2_Msk; + break; + } +} + +void modm::platform::Dac::enableOutputBuffer(Channel chan, bool enable) { + uint32_t chanShift = 0; + switch(chan) { + case Channel::Channel1: + chanShift = 0; + break; + case Channel::Channel2: + chanShift = 16; + break; + }; + + if(enable) { + DAC1->CR &= ~(DAC_CR_BOFF1_Msk << chanShift); + } else { + DAC1->CR |= DAC_CR_BOFF1_Msk << chanShift; + } +} + +void modm::platform::Dac::setOutput(Channel chan, uint16_t value) { + switch(chan) { + case Channel::Channel1: + DAC1->DHR12R1 = value & DAC_DHR12R1_DACC1DHR_Msk; + break; + case Channel::Channel2: + DAC1->DHR12R2 = value & DAC_DHR12R2_DACC2DHR_Msk; + break; + } +} + +void modm::platform::Dac::setOutput1(uint16_t value) { + setOutput(Channel::Channel1, value); +} + +void modm::platform::Dac::setOutput2(uint16_t value) { + setOutput(Channel::Channel2, value); +} \ No newline at end of file diff --git a/src/modm/platform/dac/stm32/module.lb b/src/modm/platform/dac/stm32/module.lb new file mode 100644 index 0000000000..dac90cb6c3 --- /dev/null +++ b/src/modm/platform/dac/stm32/module.lb @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os + +def init(module): + module.name = ":platform:dac" + module.description = "Digital-to-Analog Converter (DAC)" + +def prepare(module, options): + if not options[":target"].has_driver("dac:stm32"): + return False + + if not options[":target"].identifier.family == 'f4': + return False + + module.depends(":cmsis:device") + + return True + +def build(env): + env.outbasepath = "modm/src/modm/platform/dac" + env.copy("dac.hpp") + env.copy("dac_impl.hpp") \ No newline at end of file diff --git a/src/modm/platform/gpio/stm32/pin.hpp.in b/src/modm/platform/gpio/stm32/pin.hpp.in index 956d889eb9..b6c58e2ed9 100644 --- a/src/modm/platform/gpio/stm32/pin.hpp.in +++ b/src/modm/platform/gpio/stm32/pin.hpp.in @@ -209,6 +209,8 @@ public: private: template< Peripheral peripheral > static constexpr int8_t AdcChannel = -1; + template< Peripheral peripheral > + static constexpr int8_t DacChannel = -1; }; /// @cond @@ -245,6 +247,13 @@ template<> constexpr int8_t Gpio{{ port ~ pin }}::AdcChannel = {{ signal.name | to_adc_channel }}; %% endif + + %% if signal.driver.startswith("Dac") and signal.name.startswith("Out") +template<> +constexpr int8_t +Gpio{{ port ~ pin }}::DacChannel = {{ signal.name | to_adc_channel }}; + %% endif + %% endfor %% endfor From ad56ca256c5eded997ff5bac84cabbf5ed3e4183 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sat, 20 Jun 2020 23:08:15 +0200 Subject: [PATCH 054/135] Added checks for tx and rx calls --- src/modm/driver/radio/sx1276.hpp | 6 +- src/modm/driver/radio/sx1276_definitions.hpp | 2 +- src/modm/driver/radio/sx1276_impl.hpp | 94 ++++++++++++-------- 3 files changed, 62 insertions(+), 40 deletions(-) diff --git a/src/modm/driver/radio/sx1276.hpp b/src/modm/driver/radio/sx1276.hpp index f5187b9e41..2854d2fb5a 100644 --- a/src/modm/driver/radio/sx1276.hpp +++ b/src/modm/driver/radio/sx1276.hpp @@ -13,6 +13,7 @@ #define MODM_SX1276_HPP #include +#include #include "sx1276_definitions.hpp" @@ -78,8 +79,9 @@ class Sx1276 : public sx1276, public modm::SpiDevice, protected modm: * * \param data A pointer to the payload data of the Package * \param length The length of the payload data in bytes + * \return Returns true if transmission has started. False if the buffer was already sending */ - modm::ResumableResult + modm::ResumableResult transmit(uint8_t* data, uint8_t length); /** @@ -150,6 +152,8 @@ class Sx1276 : public sx1276, public modm::SpiDevice, protected modm: /// The latest irq flags Interrupts_t irqFlags; + /// Result of the last transmission attempt + bool lastTransmitResult; /// The payload size of the last packet uint8_t lastPacketSize; /// Temporary storage for RSSI values diff --git a/src/modm/driver/radio/sx1276_definitions.hpp b/src/modm/driver/radio/sx1276_definitions.hpp index 5720961480..51ac4c984c 100644 --- a/src/modm/driver/radio/sx1276_definitions.hpp +++ b/src/modm/driver/radio/sx1276_definitions.hpp @@ -105,7 +105,7 @@ struct sx1276 }; typedef modm::Configuration ModemMode_t; - //Available interrupts with their positions in the flag and mask register + /// Available interrupts with their positions in the flag and mask register enum class Interrupts : uint8_t { diff --git a/src/modm/driver/radio/sx1276_impl.hpp b/src/modm/driver/radio/sx1276_impl.hpp index 45c09d06bf..ef41310150 100644 --- a/src/modm/driver/radio/sx1276_impl.hpp +++ b/src/modm/driver/radio/sx1276_impl.hpp @@ -152,34 +152,43 @@ Sx1276::setSyncWord(uint8_t syncWord) // ----------------------------------------------------------------------------- template -modm::ResumableResult +modm::ResumableResult Sx1276::transmit(uint8_t* data, uint8_t length) { RF_BEGIN(); - RF_CALL(changeMode(ModemMode::STDBY)); + lastTransmitResult = false; - RF_CALL(writeRegister(Sx1276Register::PayloadLength,length)); - RF_CALL(writeRegister(Sx1276Register::FifoTxBaseAddr,0x00)); - RF_CALL(writeRegister(Sx1276Register::FifoAddrPtr,0x00)); + //read the mode register and check if we're in the RX Mode + opModeShadow.value = RF_CALL(readRegister(Sx1276Register::OpMode)); + if(ModemMode_t::get(opModeShadow) == ModemMode::STDBY) + { + RF_CALL(writeRegister(Sx1276Register::PayloadLength,length)); + RF_CALL(writeRegister(Sx1276Register::FifoTxBaseAddr,0x00)); + RF_CALL(writeRegister(Sx1276Register::FifoAddrPtr,0x00)); - RF_WAIT_UNTIL(this->acquireMaster()); + RF_WAIT_UNTIL(this->acquireMaster()); - buffer[0] = 0x80 | static_cast(Sx1276Register::Fifo); + buffer[0] = 0x80 | static_cast(Sx1276Register::Fifo); - Cs::reset(); + Cs::reset(); - RF_CALL(SpiMaster::transfer(buffer,nullptr,1)); - RF_CALL(SpiMaster::transfer(data,nullptr,length)); + RF_CALL(SpiMaster::transfer(buffer,nullptr,1)); + RF_CALL(SpiMaster::transfer(data,nullptr,length)); - if(this->releaseMaster()) + if(this->releaseMaster()) + { + Cs::set(); + } + + RF_CALL(changeMode(ModemMode::TX)); + } + else { - Cs::set(); + MODM_LOG_ERROR<<"SX1276: Transmission failed as the modem is busy"<::readPacket(uint8_t* data, uint8_t maxLength) { RF_BEGIN(); - irqFlags.value = RF_CALL(readRegister(Sx1276Register::IrqFlags)); - RF_CALL(writeRegister(Sx1276Register::IrqFlags, 0xFF)); - lastPacketSize = 0; - if(irqFlags & Interrupts::RX_DONE) + //read the mode register and check if we're in the RX Mode + opModeShadow.value = RF_CALL(readRegister(Sx1276Register::OpMode)); + if(ModemMode_t::get(opModeShadow) == ModemMode::RXCONTINOUS) { - lastPacketSize = RF_CALL(readRegister(Sx1276Register::RxNbBytes)); - RF_WAIT_UNTIL(this->acquireMaster()); + irqFlags.value = RF_CALL(readRegister(Sx1276Register::IrqFlags)); + if(irqFlags & Interrupts::RX_DONE) + { + lastPacketSize = RF_CALL(readRegister(Sx1276Register::RxNbBytes)); + RF_WAIT_UNTIL(this->acquireMaster()); - buffer[0] = static_cast(Sx1276Register::Fifo); + buffer[0] = static_cast(Sx1276Register::Fifo); - Cs::reset(); + Cs::reset(); - RF_CALL(SpiMaster::transfer(buffer,nullptr,1)); - if(lastPacketSize > maxLength) - { - MODM_LOG_ERROR<<"Read buffer is too small, packet discarded!"< maxLength) + { + MODM_LOG_ERROR<<"SX1276: Read buffer is too small, packet discarded!"<releaseMaster()) - { - Cs::set(); - } + if(this->releaseMaster()) + { + Cs::set(); + } + + RF_CALL(writeRegister(Sx1276Register::IrqFlags, static_cast(Interrupts::RX_DONE))); + } + } + else + { + MODM_LOG_ERROR<<"SX1276: ReadPacket can only called when enabling listening first!"< Date: Thu, 25 Jun 2020 08:55:20 +0300 Subject: [PATCH 055/135] Patches for arm32v7 Signed-off-by: delphi --- src/modm/io/iostream.hpp.in | 7 +++++++ src/modm/platform/core/cortex/assert.cpp.in | 3 ++- tools/build_script_generator/common.py | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/modm/io/iostream.hpp.in b/src/modm/io/iostream.hpp.in index 66b84ad837..926e47fa15 100644 --- a/src/modm/io/iostream.hpp.in +++ b/src/modm/io/iostream.hpp.in @@ -150,6 +150,13 @@ public: { writeIntegerMode(v); return *this; } %% if options["with_long_long"] +#ifdef __arm__ +#ifdef __linux__ + // This overload is used on arm32v7. See issue with OSX below + inline IOStream& operator << (const long unsigned int& v) + { writeIntegerMode(static_cast(v)); return *this; } +#endif +#endif inline IOStream& operator << (const int64_t& v) { writeIntegerMode(v); return *this; } inline IOStream& operator << (const uint64_t& v) diff --git a/src/modm/platform/core/cortex/assert.cpp.in b/src/modm/platform/core/cortex/assert.cpp.in index 54f2920312..ed25699158 100644 --- a/src/modm/platform/core/cortex/assert.cpp.in +++ b/src/modm/platform/core/cortex/assert.cpp.in @@ -12,6 +12,7 @@ %% if target.platform in ["hosted"] #include +#include #include %% elif core.startswith("avr") #include @@ -84,7 +85,7 @@ void modm_abandon(const modm::AssertionInfo &info) { %% if target.platform in ["hosted"] MODM_LOG_ERROR.printf("Assertion '%s'", info.name); - if (info.context != uintptr_t(-1)) { MODM_LOG_ERROR.printf(" @ %p (%lu)", (void *)info.context, info.context); } + if (info.context != uintptr_t(-1)) { MODM_LOG_ERROR.printf(" @ %p (%" PRIuPTR ")", (void *)info.context, info.context); } %% if options.get(":architecture:assert:with_description", False) MODM_LOG_ERROR.printf(" failed!\n %s\nAbandoning...\n", info.description) << modm::flush; %% else diff --git a/tools/build_script_generator/common.py b/tools/build_script_generator/common.py index c4ab1d113f..1f00acc1af 100644 --- a/tools/build_script_generator/common.py +++ b/tools/build_script_generator/common.py @@ -214,7 +214,6 @@ def common_compiler_flags(compiler, target): "-fdata-sections", "-ffunction-sections", - "-fshort-wchar", "-funsigned-char", "-fwrapv", # "-fmerge-all-constants", @@ -222,6 +221,8 @@ def common_compiler_flags(compiler, target): "-g3", "-gdwarf", ] + if target.identifier["platform"] not in ["hosted"]: + flags["ccflags"].append("-fshort-wchar") if compiler.startswith("gcc"): flags["ccflags"] += [ "-finline-limit=10000", From b5a66c6fe27bfb6304a59fa5c3e5c04416ca28d5 Mon Sep 17 00:00:00 2001 From: Benjamin Carrick Date: Thu, 2 Jul 2020 21:26:54 +0200 Subject: [PATCH 056/135] Added driver for LSM6DS33 Sensor --- README.md | 11 +- src/modm/driver/inertial/lsm6ds33.hpp | 447 +++++++++++++++++++++ src/modm/driver/inertial/lsm6ds33.lb | 41 ++ src/modm/driver/inertial/lsm6ds33_impl.hpp | 142 +++++++ 4 files changed, 636 insertions(+), 5 deletions(-) create mode 100644 src/modm/driver/inertial/lsm6ds33.hpp create mode 100644 src/modm/driver/inertial/lsm6ds33.lb create mode 100644 src/modm/driver/inertial/lsm6ds33_impl.hpp diff --git a/README.md b/README.md index 473001fb18..5d31921f71 100644 --- a/README.md +++ b/README.md @@ -210,38 +210,39 @@ can easily configure them for you specific needs. LM75 LP503X LSM303A +LSM6DS33 LTC2984 MAX6966 -MAX7219 +MAX7219 MCP23X17 MCP2515 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/inertial/lsm6ds33.hpp b/src/modm/driver/inertial/lsm6ds33.hpp new file mode 100644 index 0000000000..8cff8be54a --- /dev/null +++ b/src/modm/driver/inertial/lsm6ds33.hpp @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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_LSM6DS33_HPP +#define MODM_LSM6DS33_HPP + +#include +#include +#include +#include +#include "lis3_transport.hpp" + +namespace modm +{ + +/// @ingroup modm_driver_lsm6ds33 +struct lsm6ds33 +{ + enum class + Register : uint8_t + { + FIFO_CTRL1 = 0x06, ///< FIFO control register 1 + FIFO_CTRL2 = 0x07, ///< FIFO control register 2 + FIFO_CTRL3 = 0x08, ///< FIFO control register 3 + FIFO_CTRL4 = 0x09, ///< FIFO control register 4 + FIFO_CTRL5 = 0x0A, ///< FIFO control register 5 + + ORIENT_CFG_G = 0x0B, ///< Orientation config register + + INT1_CTRL = 0x0D, ///< Interrrupt 1 Control register + INT2_CTRL = 0x0E, ///< Interrrupt 1 Control register + + WHO_AM_I = 0x0F, ///< Contains 0x69 + + CTRL1 = 0x10, ///< Control register 1: rw + CTRL2 = 0x11, ///< Control register 2: rw + CTRL3 = 0x12, ///< Control register 3: rw + CTRL4 = 0x13, ///< Control register 4: rw + CTRL5 = 0x14, ///< Control register 5: rw + CTRL6 = 0x15, ///< Control register 6: rw + CTRL7 = 0x16, ///< Control register 7: rw + CTRL8 = 0x17, ///< Control register 8: rw + CTRL9 = 0x18, ///< Control register 9: rw + CTRL10 = 0x19, ///< Control register 10: rw + + STATUS = 0x1E, ///< Status Data Register + + OUT_TEMP_L = 0x20, ///< Temperature output register low + OUT_TEMP_H = 0x21, ///< Temperature output register high + + OUT_X_L_G = 0x22, ///< Angular rates output x-axis register low + OUT_X_H_G = 0x23, ///< Angular rates output x-axis register high + OUT_Y_L_G = 0x24, ///< Angular rates output y-axis register low + OUT_Y_H_G = 0x25, ///< Angular rates output y-axis register high + OUT_Z_L_G = 0x26, ///< Angular rates output z-axis register low + OUT_Z_H_G = 0x27, ///< Angular rates output z-axis register high + + OUT_X_L_XL = 0x28, ///< Acceleration output x-axis register low + OUT_X_H_XL = 0x29, ///< Acceleration output x-axis register high + OUT_Y_L_XL = 0x2A, ///< Acceleration output y-axis register low + OUT_Y_H_XL = 0x2B, ///< Acceleration output y-axis register high + OUT_Z_L_XL = 0x2C, ///< Acceleration output z-axis register low + OUT_Z_H_XL = 0x2D, ///< Acceleration output z-axis register high + + FIFO_STATUS1 = 0x3A, ///< Fifo status register 1 + FIFO_STATUS2 = 0x3B, ///< Fifo status register 2 + FIFO_STATUS3 = 0x3C, ///< Fifo status register 3 + FIFO_STATUS4 = 0x3D, ///< Fifo status register 4 + + FIFO_DATA_OUT_L = 0x3E, ///< Fifo data output register low + FIFO_DATA_OUT_H = 0x3F, ///< Fifo data output register high + + }; + +public: + /// Control register for the acceleration sensor + enum class + Control1 : uint8_t + { + ODR3_XL = Bit7, ///< Output data rate bit 3 + ODR2_XL = Bit6, ///< Output data rate bit 2 + ODR1_XL = Bit5, ///< Output data rate bit 1 + ODR0_XL = Bit4, ///< Output data rate bit 0 + + FS1_XL = Bit3, ///< Full scale selection bit 1 + FS0_XL = Bit2, ///< Full scale selection bit 0 + + BW1_XL = Bit1, ///< Anti-aliasing bandwidth bit 1 + BW0_XL = Bit0, ///< Anti-aliasing bandwidth bit 0 + + }; + MODM_FLAGS8(Control1); + + /// Output Data Rates for the Acceleration sensor + enum class + AccDataRate : uint8_t + { + Off = 0x00, ///< Turn off the acceleration sensor + Rate_13_Hz = 0x10, ///< 13 Hz + Rate_26_Hz = 0x20, ///< 26 Hz + Rate_52_Hz = 0x30, ///< 52 Hz + Rate_104_Hz = 0x40, ///< 104 Hz + Rate_208_Hz = 0x50, ///< 208 Hz + Rate_416_Hz = 0x60, ///< 416 Hz + Rate_833_Hz = 0x70, ///< 833 Hz + Rate_1666_Hz = 0x80, ///< 1666 Hz + Rate_3332_Hz = 0x90, ///< 3332 Hz + Rate_6664_Hz = 0xA0, ///< 6664 Hz + }; + typedef modm::Configuration AccDataRate_t; + + /// Full scale of the acceleration sensor data output + enum class + AccScale : uint8_t + { + Scale_2_G = 0x00, ///< +- 2g + Scale_4_G = 0x08, ///< +- 4g + Scale_8_G = 0x0C, ///< +- 8g + Scale_16_G = 0x04, ///< +- 16g + }; + typedef modm::Configuration AccScale_t; + + /// Anti aliasing filter bandwith for the acceleration sensor (only used when Control Register 4, Bit 7 is 1) + enum class + AccAABandwith : uint8_t + { + Bw_400_Hz = 0x00, ///< 400 Hz + Bw_200_Hz = 0x01, ///< 200 Hz + BW_100_Hz = 0x02, ///< 100 Hz + Bw_50_Hz = 0x03, ///< 50 Hz + }; + typedef modm::Configuration AccAABandwith_t; + +// ---------------------------------------------------------------------------- + + /// Control Register 2 for the Gyroscope + enum class + Control2 : uint8_t + { + ODR3_G = Bit7, ///< Output data rate bit 3 + ODR2_G = Bit6, ///< Output data rate bit 2 + ODR1_G = Bit5, ///< Output data rate bit 1 + ODR0_G = Bit4, ///< Output data rate bit 0 + + FS2_G = Bit3, ///< Full scale selection bit 2 + FS1_G = Bit2, ///< Full scale selection bit 1 + FS0_G = Bit1, ///< Full scale selection bit 0 + }; + MODM_FLAGS8(Control2); + + /// Output Data Rates for the gyroscope + enum class + GyroDataRate : uint8_t + { + Off = 0x00, ///< Turn off the gyroscope + Rate_13_Hz = 0x10, ///< 13 Hz + Rate_26_Hz = 0x20, ///< 26 Hz + Rate_52_Hz = 0x30, ///< 52 Hz + Rate_104_Hz = 0x40, ///< 104 Hz + Rate_208_Hz = 0x50, ///< 208 Hz + Rate_416_Hz = 0x60, ///< 416 Hz + Rate_833_Hz = 0x70, ///< 833 Hz + Rate_1666_Hz = 0x80, ///< 1666 Hz + }; + typedef modm::Configuration GyroDataRate_t; + + /// Full scale of the gyroscope data output + enum class + GyroScale : uint8_t + { + Scale_125_dps = 0x02, ///< 125 degrees per second + Scale_245_dps = 0x00, ///< 245 degrees per second + Scale_500_dps = 0x04, ///< 500 degrees per second + Scale_1000_dps = 0x08, ///< 1000 degrees per second + Scale_2000_dps = 0x0C, ///< 2000 degrees per secons + }; + typedef modm::Configuration GyroScale_t; + +// ---------------------------------------------------------------------------- + + /// Control Register 3 + enum class + Control3 : uint8_t + { + BOOT = Bit7, ///< Reboot memory content + BDU = Bit6, ///< Block data update (0 = continous (default), 1 = wait until registers read) + H_LACTIVE = Bit5, ///< Interrupt activation level (0 = high active(default), 1= low active) + PP_OD = Bit4, ///< Interrupt pin mode (0 = Push-Pull(default), 1 = Open-Drain) + SIM = Bit3, ///< SPI Interface Mode (0 = 4 Wire(default), 1 = 3 Wire) + IF_INC = Bit2, ///< I2C increment register on access (0 = disabled, 1= enabled(default)) + BLE = Bit1, ///< Endianess selection (0 = Little endian(default), 1 = Big endian. + SW_RESET = Bit0 ///< Software Reset + }; + MODM_FLAGS8(Control3); + +// ---------------------------------------------------------------------------- + + /// Control Register 4 + enum class + Control4 : uint8_t + { + XL_BW_SCAL_ODR = Bit7, ///< Manual selection of bandwidth for the acceleration sensor (0 = automatic, 1 = manual) + SLEEP_G = Bit6, ///< Gyroscope sleep mode enable (default = 0) + INT2_on_INT1 = Bit5, ///< Interrupt 2 output on Interrupt 1 pad (default = 0) + FIFO_TEMP_EN = Bit4 ///< Enable Temperature output as 4th fifo set + }; + MODM_FLAGS8(Control4); + +// ---------------------------------------------------------------------------- + + /// Control Register 6 + enum class + Control6 : uint8_t + { + TRIG_EN = Bit7, ///< Gyroscope data edge-sensitive trigger enable (default = 0) + LVL_EN = Bit6, ///< Gyroscope data level-sensitive trigger enable (default = 0) + LVL2_EN = Bit5, ///< Gyroscope level-sensitive latched enable. + XL_HM_MODE = Bit4 ///< Disable high performance mode for acceleration sensor (default = 0) + }; + MODM_FLAGS8(Control6); + +// ---------------------------------------------------------------------------- + + /// Control Register 7 + enum class + Control7 : uint8_t + { + G_HM_MODE = Bit7, ///< Disable high performance mode for gyroscope (default = 0)s + HP_G_EN = Bit6, ///< Enable high-pass filter for gyroscope (default = 0) + HP_CF_G1 = Bit5, ///< Gyroscope high-pass filter cutoff frequency bit 1 + HP_CF_G0 = Bit4, ///< Gyroscope high-pass filter cutoff frequuency bit 0 + HP_G_RST = Bit3 ///< Reset high-pass filter for gyroscope. + }; + MODM_FLAGS8(Control7); + + /// High-pass cutoff frequency selection for the gyroscope + enum class + GyroHPCutoff : uint8_t + { + Cutoff_0_0081_Hz = 0x00, ///< 0.0081 Hz + Cutoff_0_0324_Hz = 0x10, ///< 0.0324 Hz + Cutoff_2_07_Hz = 0x20, ///< 2.07 Hz + Cutoff_16_32_Hz = 0x30 ///< 16.32 Hz + }; + typedef modm::Configuration GyroHPCutoff_t; + +// ---------------------------------------------------------------------------- + + /// Control Register 8 + enum class + Control8: uint8_t + { + LPF_XL_EN = Bit7, ///< Enable the low-pass filter for the acceleration sensor + HP_CF_XL1 = Bit6, ///< Acceleration sensor high-pass filter cutoff frequency bit 1 + HP_CF_XL0 = Bit5, ///< Acceleration sensor high-pass filter cutoff frequency bit 0 + HP_SLOPE_XL_EN = Bit3 ///< High-pass or slope filter selection + }; + MODM_FLAGS8(Control8); + + /// High-pass cutoff frequency selection for the gyroscope + enum class + AccHPCutoff : uint8_t + { + Cutoff_DIV_50 = 0x00, ///< Acceleration Data Rate / 50 + Cutoff_DIV_100 = 0x10, ///< Acceleration Data Rate / 100 + Cutoff_DIV_9 = 0x20, ///< Acceleration Data Rate / 9 + Cutoff_DIV_400 = 0x30 ///< Acceleration Data Rate / 400 + }; + typedef modm::Configuration AccHPCutoff_t; + +// ---------------------------------------------------------------------------- + /// Control Register 9 + enum class + Control9: uint8_t + { + Z_EN_XL = Bit5, ///< Enable Z-Axis for the acceleration sensor (default 1) + X_EN_XL = Bit4, ///< Enable X-Axis for the acceleration sensor (default 1) + Y_EN_XL = Bit3 ///< Enable Y-Axis for the acceleration sensor (default 1) + }; + MODM_FLAGS8(Control9); + +// ---------------------------------------------------------------------------- + + /// Control Register 10 + enum class + Control10: uint8_t + { + Z_EN_G = Bit5, ///< Enable Z-Axis for the gyroscope (default 1) + X_EN_G = Bit4, ///< Enable X-Axis for the gyroscope (default 1) + Y_EN_G = Bit3 ///< Enable Y-Axis for the gyroscope (default 1) + }; + MODM_FLAGS8(Control10); + +// ---------------------------------------------------------------------------- + + /// STATUS is read-only + enum class + Status : uint8_t + { + EV_BOOT = Bit3, ///< Bootup is running + TDA = Bit2, ///< New temperature data available + GDA = Bit1, ///< New gyroscope data available + XLDA = Bit0 ///< New acceleration sensor data available + }; + MODM_FLAGS8(Status); + +protected: + // Conversion table to convert raw acceleration values to physical + static constexpr float accConvTable [4] = { + 0.000061035f, // Conversion for 2 g scale + 0.000488281f, // Conversion for 16 g scale + 0.00012207f, // Conversion for 4 g scale + 0.000244141f // Conversion for 8 g scale + }; + + static constexpr float gyroConvTable [4] = { + 0.007476807f, // Conversion for 245 dps scale + 0.015258789f, // Conversion for 500 dps scale + 0.030517578f, // Conversion for 1000 dps scale + 0.061035156f // Conversion for 2000 scale + }; + + /// @endcond +}; // struct lsm6ds33 + +/** + * \tparam I2cMaster I2cMaster interface + * + * \author Benjamin Carrick + * \ingroup modm_driver_lsm6ds33 + */ +template < class I2cMaster > +class Lsm6ds33 : public lsm6ds33, public Lis3TransportI2c +{ +public: + /** + * \brief Construct a driver object for the LSM6DS33 chip + * + * \param address The I2C device address of the sensor + */ + Lsm6ds33(uint8_t address = 0x6A); + + /** + * \brief Configures the acceleration sensor + * This method will setup the data rate and the scale with which the sensor will sample the acceleration data. + * A sample rate has to be set in order to turn on the sensor. + * + * \param accRate The sample rate for the acceleration sensor + * \param accScale The full scale of the acceleration data + * \return Whether the configuration was successful + */ + modm::ResumableResult + configureAccelerationSensor(AccDataRate accRate, AccScale accScale); + + /** + * \brief Configures the gyroscope + * This method will setup the data rate and the scale with which the sensor will sample the spin rates. + * A sample rate has to be set in order to turn on the sensor. + * + * \param accRate The sample rate for the gyroscope + * \param accScale The full scale of the spin rate data + * \return Whether the configuration was successful + */ + modm::ResumableResult + configureGyroscope(GyroDataRate gyroRate, GyroScale gyroScale); + + /** + * @brief Reads out the raw acceleration data into a given vector object. + * + * @param acceleration A reference to a Vector3i object the data will be written to + * @return Whether the sensor data have been read + */ + modm::ResumableResult + readAccelerationRaw(Vector3i& acceleration); + + /** + * @brief Reads out the raw gyroscope data into a given vector object. + * + * @param spinRates A reference to a Vector3i object the data will be written to + * @return Whether the sensor data have been read + */ + modm::ResumableResult + readGyroscopeRaw(Vector3i& spinRates); + + /** + * @brief Get the currently configured scale for the acceleration sensor + * + * @return AccScale The currently config acceleration data scale + */ + AccScale + getAccelerationScale(); + + /** + * @brief Get the currently configured scale for the gyroscope + * + * @return AccScale The currently config gyroscope data scale + */ + GyroScale + getGyroscopeScale(); + + + /** + * \brief Reads out the scaled acceleration data in g + * + * \param acceleration A reference to a Vector3f object the data will be written to + * \return Whether the sensor data have been read + */ + modm::ResumableResult + readAcceleration(Vector3f& acceleration); + + /** + * \brief Reads out the scaled spin rates from the gyroscope in degrees per second + * + * \param spinRates A reference to a Vector3f object the data will be written to + * \return Whether the sensor data have been read + */ + modm::ResumableResult + readGyroscope(Vector3f& spinRates); + +private: + /// The shadow for the control register 1 (accelerometer) + Control1_t control1Shadow; + /// The shadow for the control register 2 (gyro) + Control2_t control2Shadow; + /// The shadow for the control register 3 + Control3_t control3Shadow; + + /// The buffer for reading out the data registers + int16_t readBuffer[3]; + + /// Variable to hold the success of a transaction to the sensor + bool success; +}; + +} // namespace modm + +#include "lsm6ds33_impl.hpp" + +#endif // MODM_LSM6DS33_HPP diff --git a/src/modm/driver/inertial/lsm6ds33.lb b/src/modm/driver/inertial/lsm6ds33.lb new file mode 100644 index 0000000000..9333d75134 --- /dev/null +++ b/src/modm/driver/inertial/lsm6ds33.lb @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Benjamin Carrick +# +# 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:lsm6ds33" + module.description = """\ +# LSM6DS33 always-on 3D accelerometer and 3D gyroscope + +The LSM6DS33 is a system-in-package featuring a 3D digital accelerometer and +a 3D digital gyroscope performing at 1.25 mA (up to 1.6 kHz ODR) in high-performance mode. +has a full-scale acceleration range of ±2/±4/±8/±16 g and an angular rate range +of ±125/±245/±500/±1000/±2000 dps. +The LSM6DS33 includes an I2C serial bus interface that supports standard and +fast mode 100 kHz and 400 kHz. + +This driver only supports the raw data output of the sensor. +Functions like tap recognition, step counter, free fall recogition, etc. are not supported +""" + +def prepare(module, options): + module.depends( + ":architecture:register", + ":driver:lis3.transport", + ":math:utils", + ":math:geometry") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/inertial" + env.copy("lsm6ds33.hpp") + env.copy("lsm6ds33_impl.hpp") diff --git a/src/modm/driver/inertial/lsm6ds33_impl.hpp b/src/modm/driver/inertial/lsm6ds33_impl.hpp new file mode 100644 index 0000000000..5e6e458202 --- /dev/null +++ b/src/modm/driver/inertial/lsm6ds33_impl.hpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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_LSM6DS33_HPP +# error "Don't include this file directly, use 'lsm6ds33.hpp' instead!" +#endif + +#include + +// ---------------------------------------------------------------------------- +template < class I2cMaster > +modm::Lsm6ds33::Lsm6ds33( uint8_t address) +: Lis3TransportI2c(address) +{ + control1Shadow.value = 0x00; + control2Shadow.value = 0x00; + control3Shadow.value = 0x04; +} + +template < class I2cMaster > +modm::ResumableResult +modm::Lsm6ds33::configureAccelerationSensor(AccDataRate accRate, AccScale accScale) +{ + RF_BEGIN(); + AccDataRate_t::set(control1Shadow,accRate); + AccScale_t::set(control1Shadow,accScale); + RF_END_RETURN_CALL(this->write(static_cast(Register::CTRL1),control1Shadow.value)); +} + +template < class I2cMaster > +modm::ResumableResult +modm::Lsm6ds33::configureGyroscope(GyroDataRate gyroRate, GyroScale gyroScale) +{ + RF_BEGIN(); + GyroDataRate_t::set(control2Shadow,gyroRate); + GyroScale_t::set(control2Shadow,gyroScale); + RF_END_RETURN_CALL( this->write(static_cast(Register::CTRL2),control2Shadow.value)); +} + +template < class I2cMaster > +modm::ResumableResult +modm::Lsm6ds33::readAccelerationRaw(Vector3i& acceleration) +{ + RF_BEGIN(); + success = RF_CALL(this->read(static_cast(Register::OUT_X_L_XL),reinterpret_cast(readBuffer),6)); + if(success) + { + acceleration.x = readBuffer[0]; + acceleration.y = readBuffer[1]; + acceleration.z = readBuffer[2]; + } + RF_END_RETURN(success); +} + +template < class I2cMaster > +modm::ResumableResult +modm::Lsm6ds33::readGyroscopeRaw(Vector3i& spinRates) +{ + RF_BEGIN(); + success = RF_CALL(this->read(static_cast(Register::OUT_X_L_G),reinterpret_cast(readBuffer),6)); + if(success) + { + spinRates.x = readBuffer[0]; + spinRates.y = readBuffer[1]; + spinRates.z = readBuffer[2]; + } + RF_END_RETURN(success); +} + +template < class I2cMaster > +modm::lsm6ds33::AccScale +modm::Lsm6ds33::getAccelerationScale() +{ + return AccScale_t::get(control1Shadow); +} + +template < class I2cMaster > +modm::lsm6ds33::GyroScale +modm::Lsm6ds33::getGyroscopeScale() +{ + return GyroScale_t::get(control2Shadow); +} + +template < class I2cMaster > +modm::ResumableResult +modm::Lsm6ds33::readAcceleration(Vector3f& acceleration) +{ + RF_BEGIN(); + success = RF_CALL(this->read(static_cast(Register::OUT_X_L_XL),reinterpret_cast(readBuffer),6)); + + if(success) + { + //use the bitmask of the acceleration scale to index the conversion table; + uint8_t accScaleIndex = (static_cast(getAccelerationScale()))>>2; + float conversionValue = accConvTable[accScaleIndex]; + + acceleration.x = static_cast(readBuffer[0]) * conversionValue; + acceleration.y = static_cast(readBuffer[1]) * conversionValue; + acceleration.z = static_cast(readBuffer[2]) * conversionValue; + } + + RF_END_RETURN(success); +} + +template < class I2cMaster > +modm::ResumableResult +modm::Lsm6ds33::readGyroscope(Vector3f& acceleration) +{ + RF_BEGIN(); + success = RF_CALL(this->read(static_cast(Register::OUT_X_L_G),reinterpret_cast(readBuffer),6)); + + if(success) + { + GyroScale scale = getGyroscopeScale(); + float conversionValue; + //Handle the 125 dps special case + if(scale == GyroScale::Scale_125_dps) + { + conversionValue = 0.003814697f; + } + else + { + //use the bitmask of the gyro scale to index the conversion table; + uint8_t gyroScaleIndex = (static_cast(scale))>>2; + conversionValue = gyroConvTable[gyroScaleIndex]; + } + + acceleration.x = static_cast(readBuffer[0]) * conversionValue; + acceleration.y = static_cast(readBuffer[1]) * conversionValue; + acceleration.z = static_cast(readBuffer[2]) * conversionValue; + } + + RF_END_RETURN(success); +} \ No newline at end of file From e233708dc4d2065cdbeb8a6284c0bceff3777d35 Mon Sep 17 00:00:00 2001 From: Benjamin Carrick Date: Thu, 2 Jul 2020 21:32:22 +0200 Subject: [PATCH 057/135] Added an example for the LSM6DS33 --- examples/nucleo_f042k6/lsm6ds33/main.cpp | 76 +++++++++++++++++++++ examples/nucleo_f042k6/lsm6ds33/project.xml | 11 +++ 2 files changed, 87 insertions(+) create mode 100644 examples/nucleo_f042k6/lsm6ds33/main.cpp create mode 100644 examples/nucleo_f042k6/lsm6ds33/project.xml diff --git a/examples/nucleo_f042k6/lsm6ds33/main.cpp b/examples/nucleo_f042k6/lsm6ds33/main.cpp new file mode 100644 index 0000000000..0c839f9d24 --- /dev/null +++ b/examples/nucleo_f042k6/lsm6ds33/main.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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 + +using namespace Board; +using namespace std::chrono_literals; + +using I2cSda = GpioA10; +using I2cScl = GpioA9; + +int +main() +{ + Board::initialize(); + LedD13::setOutput(); + + MODM_LOG_INFO << "LSM6DS33 demo" << modm::endl; + + I2cMaster1::connect(); + I2cMaster1::initialize(); + + // Create a sensor object with the adress of the sensor built onto the Pololu AltIMU-10 v5 + modm::Lsm6ds33 sensor(0x6B); + + // Turn on and configure the acceleration sensor + bool accSuccess = RF_CALL_BLOCKING(sensor.configureAccelerationSensor(modm::lsm6ds33::AccDataRate::Rate_13_Hz, + modm::lsm6ds33::AccScale::Scale_16_G)); + // Turn on and configure the gyroscope + bool gyroSuccess = RF_CALL_BLOCKING(sensor.configureGyroscope(modm::lsm6ds33::GyroDataRate::Rate_13_Hz, + modm::lsm6ds33::GyroScale::Scale_125_dps)); + + if(!(accSuccess && gyroSuccess)) + { + MODM_LOG_INFO << "Sensor could not be configured!" << modm::endl; + } + + modm::Vector3f accVector; + modm::Vector3f gyroVector; + + while (true) + { + //Read the sensor data and print it out + accSuccess = RF_CALL_BLOCKING(sensor.readAcceleration(accVector)); + gyroSuccess = RF_CALL_BLOCKING(sensor.readGyroscope(gyroVector)); + if(accSuccess && gyroSuccess) + { + MODM_LOG_INFO << "Acceleration Vector:" << modm::endl; + MODM_LOG_INFO << "X: "<< accVector.x << " g" << modm::endl; + MODM_LOG_INFO << "Y: "<< accVector.y << " g" << modm::endl; + MODM_LOG_INFO << "Z: "<< accVector.z << " g" << modm::endl; + MODM_LOG_INFO << modm::endl; + + MODM_LOG_INFO << "Spin Rates Vector:" << modm::endl; + MODM_LOG_INFO << "X: "<< gyroVector.x << " deg/s" << modm::endl; + MODM_LOG_INFO << "Y: "<< gyroVector.y << " deg/s" << modm::endl; + MODM_LOG_INFO << "Z: "<< gyroVector.z << " deg/s" << modm::endl; + MODM_LOG_INFO << modm::endl; + + } + else + { + MODM_LOG_INFO << "Sensor could not be read!" << modm::endl; + } + modm::delay(1s); + } + return 0; +} diff --git a/examples/nucleo_f042k6/lsm6ds33/project.xml b/examples/nucleo_f042k6/lsm6ds33/project.xml new file mode 100644 index 0000000000..d2766324d6 --- /dev/null +++ b/examples/nucleo_f042k6/lsm6ds33/project.xml @@ -0,0 +1,11 @@ + + modm:nucleo-f042k6 + + + + + modm:build:scons + modm:driver:lsm6ds33 + modm:platform:i2c:1 + + From af2eb29f68d86a5c6cbb5fe192cb88b1d53fcc59 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 27 Apr 2020 15:47:17 +0200 Subject: [PATCH 058/135] [ext] Enable SAMD devices and update submodules --- .gitmodules | 3 +++ README.md | 4 +++- ext/microchip/sam | 1 + repo.lb | 1 + tools/scripts/docs_modm_io_generator.py | 5 ++++- tools/scripts/synchronize_docs.py | 2 ++ 6 files changed, 14 insertions(+), 2 deletions(-) create mode 160000 ext/microchip/sam diff --git a/.gitmodules b/.gitmodules index d604e01df7..4693242c2b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "ext/adamgreen/crashcatcher"] path = ext/adamgreen/crashcatcher url = https://github.com/modm-ext/CrashCatcher-partial.git +[submodule "ext/microchip/sam"] + path = ext/microchip/sam + url = https://github.com/modm-io/cmsis-header-sam.git diff --git a/README.md b/README.md index 5d31921f71..86638dc604 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,8 @@ git clone --recurse-submodules https://github.com/modm-io/modm.git ## Targets -modm can generate code for 530 AVR and 1959 +modm can generate code for 530 AVR, +163 SAM and 1959 STM32 devices, however, there are different levels of support and testing.
@@ -81,6 +82,7 @@ STM32 devices, however, there are different levels of support and testing. | STM32F2 | ★★★★ | STM32F3 | ★★★★★ | STM32F4 | ★★★★★ | | STM32F7 | ★★★★ | STM32L1 | ★★★★ | STM32L4 | ★★★★ | | STM32L4+ | ★★★★ | STM32G0 | ★★★★ | STM32G4 | ★★★★ | +| SAMD21 | ★ | | | | |
diff --git a/ext/microchip/sam b/ext/microchip/sam new file mode 160000 index 0000000000..9f08123fc9 --- /dev/null +++ b/ext/microchip/sam @@ -0,0 +1 @@ +Subproject commit 9f08123fc9c504cfe99928fb8f125e149c815e80 diff --git a/repo.lb b/repo.lb index db99cf8aa3..4810f89dfd 100644 --- a/repo.lb +++ b/repo.lb @@ -85,6 +85,7 @@ class DevicesCache(dict): "stm32g0", "stm32g4", "stm32l1", "stm32l4", "at90", "attiny", "atmega", + "samd21", "hosted"] device_file_names = [dfn for dfn in device_file_names if any(s in dfn for s in supported)] diff --git a/tools/scripts/docs_modm_io_generator.py b/tools/scripts/docs_modm_io_generator.py index 3b8545cc9e..7a186f5e5e 100755 --- a/tools/scripts/docs_modm_io_generator.py +++ b/tools/scripts/docs_modm_io_generator.py @@ -59,6 +59,9 @@ def get_targets(): elif target.platform == "hosted": short_id.naming_schema = "{platform}" + elif target.platform == "sam": + short_id.naming_schema = "{platform}{family}{series}" + short_id.set("platform", target.platform) # invalidate caches minimal_targets[short_id.string].append(target) @@ -93,7 +96,7 @@ def main(): # test list device_list = ["hosted-linux", "atmega328p-au", "stm32f103c8t6", "stm32g474cet6"] elif args.test2: - device_list = ["hosted-linux", "atmega328p-pu", "stm32f103zgt7", "stm32g474vet7"] + device_list = ["hosted-linux", "atmega328p-pu", "stm32f103zgt7", "stm32g474vet7", "samd21g18a-uu"] else: device_list = get_targets() diff --git a/tools/scripts/synchronize_docs.py b/tools/scripts/synchronize_docs.py index 7106ec1044..29e8efb066 100755 --- a/tools/scripts/synchronize_docs.py +++ b/tools/scripts/synchronize_docs.py @@ -124,6 +124,7 @@ def get_lbuild(root, target=None): targets -= ignored_devices avr_count = len([t for t in targets if t.startswith("at")]) stm_count = len([t for t in targets if t.startswith("stm32")]) +sam_count = len([t for t in targets if t.startswith("sam")]) # get the author count from authors import author_handles @@ -141,6 +142,7 @@ def get_lbuild(root, target=None): readme = readme_path.read_text() readme = replace(readme, "authorcount", author_count - 7) readme = replace(readme, "avrcount", avr_count) +readme = replace(readme, "samcount", sam_count) readme = replace(readme, "stmcount", stm_count) readme = replace(readme, "bsptable", bsp_table) readme = replace(readme, "drivertable", driver_table) From 5367b04438c413e8d4d77f17fda7aa39e9283230 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 24 Apr 2019 02:27:36 +0200 Subject: [PATCH 059/135] [ext] Add :cmsis:device module for SAM devices --- ext/microchip/device.hpp.in | 29 +++++++++++++++ ext/microchip/module.lb | 70 +++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 ext/microchip/device.hpp.in create mode 100644 ext/microchip/module.lb diff --git a/ext/microchip/device.hpp.in b/ext/microchip/device.hpp.in new file mode 100644 index 0000000000..5106855867 --- /dev/null +++ b/ext/microchip/device.hpp.in @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_DEVICE_HPP +#define MODM_DEVICE_HPP + +#define DONT_USE_CMSIS_INIT 1 +%% for define in defines +#define {{ define }} 1 +%% endfor + +#include +// Defines for example the modm_always_inline macro +#include + +// Include external device headers: +%% for header in headers +#include <{{ header }}> +%% endfor + +#endif // MODM_DEVICE_HPP diff --git a/ext/microchip/module.lb b/ext/microchip/module.lb new file mode 100644 index 0000000000..026f0a9cf9 --- /dev/null +++ b/ext/microchip/module.lb @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, 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/. +# ----------------------------------------------------------------------------- + +import re +from pathlib import Path + +# ----------------------------------------------------------------------------- +def init(module): + module.name = ":cmsis:device" + +def prepare(module, options): + device = options[":target"] + if device.identifier["platform"] != "sam": + return False + + module.depends(":cmsis:core") + return True + +pp = {} +def validate(env): + device = env[":target"] + name = device.partname.split("-")[0] + + + define = "__{}__".format(name.upper()) + family_file = None + for famfile in Path(localpath("sam")).glob("**/sam.h"): + content = famfile.read_text(encoding="utf-8", errors="replace") + match = re.findall(r"defined\((?P__SAM.*?__)\)", content) + if match is not None and define in match: + family_file = famfile.relative_to(localpath(".")) + + if family_file is None: + raise ValidateException("No device define found for '{}'!".format(device.partname)) + + family_folder = family_file.parent + device_header = "{}.h".format(name) + + global pp + pp = { + "define": define, + "folder": family_folder, + "device_header": device_header, + } + +def build(env): + global pp + env.collect(":build:path.include", "modm/ext") + env.collect(":build:path.include", "modm/ext/cmsis/device") + + env.outbasepath = "modm/ext/cmsis/device" + files = ["sam.h", "component-version.h", "pio", "instance", "component", pp["device_header"]] + for file in files: + env.copy(localpath(pp["folder"], file), file) + + env.substitutions = { + "headers": [pp["device_header"]], + "defines": [pp["define"]], + } + env.outbasepath = "modm/src/modm/platform" + env.template("device.hpp.in") From 9081d7a507d792cce6f824036ada150b65b744a5 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 26 Apr 2020 20:40:28 +0200 Subject: [PATCH 060/135] [build] Add BOSSA tool for SAM programming --- docs/src/guide/installation.md | 4 + tools/build_script_generator/module.lb | 7 ++ tools/build_script_generator/scons/module.lb | 5 ++ .../scons/resources/SConscript.in | 10 ++- .../scons/resources/build_target.py.in | 17 +++- .../scons/site_tools/avrdude.py | 2 +- .../scons/site_tools/bossac.py | 44 ++++++++++ tools/modm_tools/bossac.py | 81 +++++++++++++++++++ 8 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 tools/build_script_generator/scons/site_tools/bossac.py create mode 100644 tools/modm_tools/bossac.py diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md index b66c58be6f..aa75820db1 100644 --- a/docs/src/guide/installation.md +++ b/docs/src/guide/installation.md @@ -49,6 +49,10 @@ well: brew install arm-gcc-bin brew install openocd --HEAD +To program Microchip SAM devices via the bootloader, install the `bossac` tool: + + brew cask install bossa + We recommend the use of a graphical frontend for GDB called [gdbgui][]: pip3 install gdbgui diff --git a/tools/build_script_generator/module.lb b/tools/build_script_generator/module.lb index c722247900..db780748a8 100644 --- a/tools/build_script_generator/module.lb +++ b/tools/build_script_generator/module.lb @@ -117,6 +117,11 @@ def prepare(module, options): PathCollector(name="path.openocd", description="Search path for OpenOCD configuration files")) + if platform == "sam": + module.add_collector( + StringCollector(name="bossac.options", + description="Additional BOSSAc options")) + elif platform in ["avr"]: module.add_collector( StringCollector(name="default.avrdude.programmer", @@ -171,6 +176,8 @@ def build(env): if is_cortex_m: tools.update({"bmp", "openocd", "crashdebug", "gdb", "backend", "log", "build_id", "size"}) + if platform in ["sam"]: + tools.update({"bossac"}) elif platform in ["avr"]: tools.update({"avrdude"}) diff --git a/tools/build_script_generator/scons/module.lb b/tools/build_script_generator/scons/module.lb index c0486ea4a5..a98038aeb3 100644 --- a/tools/build_script_generator/scons/module.lb +++ b/tools/build_script_generator/scons/module.lb @@ -104,6 +104,8 @@ def build(env): if device["core"].startswith("cortex-m"): tools.update({"compiler_arm_none_eabi_gcc", "size", "log_itm", "artifact", "openocd", "openocd_remote", "bmp", "crashdebug", "dfu"}) + if device["platform"] in ["sam"]: + tools.update({"bossac"}) elif device["core"].startswith("avr"): tools.update({"compiler_avr_gcc", "size", "avrdude"}) else: # hosted @@ -185,6 +187,9 @@ def post_build(env): }) if subs["platform"] == "avr": subs.update(env.query("::avrdude_options")) + if subs["platform"] == "sam": + subs.update({"bossac_offset": env.get(":platform:cortex-m:linkerscript.flash_offset", None), + "bossac_options": " ".join(env.collector_values(":build:bossac.options"))}) # Set these substitutions for all templates env.substitutions = subs diff --git a/tools/build_script_generator/scons/resources/SConscript.in b/tools/build_script_generator/scons/resources/SConscript.in index 77d3000de9..03f930d1c6 100644 --- a/tools/build_script_generator/scons/resources/SConscript.in +++ b/tools/build_script_generator/scons/resources/SConscript.in @@ -108,13 +108,21 @@ env["CONFIG_AVRDUDE_PORT"] = "{{ avrdude_port }}" %% if avrdude_options env["CONFIG_AVRDUDE_OPTIONS"] = "{{ avrdude_options }}" %% endif -%% if avrdude_baudrate > 0 +%% if avrdude_baudrate env["CONFIG_AVRDUDE_BAUDRATE"] = "{{ avrdude_baudrate }}" %% endif %% elif core.startswith("cortex-m") env.Append(MODM_OPENOCD_CONFIGFILES="$BASEPATH/openocd.cfg") env.Append(MODM_OPENOCD_GDBINIT="$BASEPATH/openocd_gdbinit") env.Append(MODM_GDBINIT="$BASEPATH/gdbinit") +%% if platform == "sam" +%% if bossac_offset +env.Append(MODM_BOSSAC_OFFSET={{ bossac_offset }}) +%% endif +%% if bossac_options +env.Append(MODM_BOSSAC_OPTIONS="{{ bossac_options }}") +%% endif +%% endif %% endif # XPCC generator tool path diff --git a/tools/build_script_generator/scons/resources/build_target.py.in b/tools/build_script_generator/scons/resources/build_target.py.in index 30d673b10d..6d302224d7 100644 --- a/tools/build_script_generator/scons/resources/build_target.py.in +++ b/tools/build_script_generator/scons/resources/build_target.py.in @@ -40,23 +40,32 @@ def build_target(env, sources): env.Alias("artifact", env.CacheArtifact(program)) %% set artifact = ', "artifact"' if upload_with_artifact else '' - env.Alias("program", [env.ProgramOpenOcd(program){{ artifact }}]) + env.Alias("program-openocd", [env.ProgramOpenOcd(program){{ artifact }}]) env.Alias("program-remote", [env.ProgramGdbRemote(program){{ artifact }}]) env.Alias("program-bmp", [env.ProgramBMP(program){{ artifact }}]) env.Alias('program-dfu', [env.ProgramDFU(env.Bin(program)){{ artifact }}]) + %% if platform in ["sam"] + env.Alias("program-bossac", [env.ProgramBossac(env.Bin(program)){{ artifact }}]) + %% endif - env.Alias("debug", env.DebugOpenOcd(program)) + env.Alias("debug-openocd", env.DebugOpenOcd(program)) env.Alias("debug-remote", env.DebugGdbRemote(program)) env.Alias("debug-bmp", env.DebugBMP(program)) env.Alias("debug-coredump", env.DebugCoredump(program)) - env.Alias("reset", env.ResetOpenOcd()) + env.Alias("reset-openocd", env.ResetOpenOcd()) env.Alias("reset-bmp", env.ResetBMP()) env.Alias("reset-remote", env.ResetGdbRemote()) + # Default to OpenOCD + env.Alias("program", "program-openocd") + env.Alias("reset", "reset-openocd") + env.Alias("debug", "debug-openocd") + %% elif core.startswith("avr") - env.Alias("program", env.ProgramAvrdude(program)) + env.Alias("program-avrdude", env.ProgramAvrdude(program)) env.Alias("program-fuses", env.ProgramAvrdudeFuses(program)) + env.Alias("program", "program-avrdude") %% endif env.Alias("all", ["build", "size"]) diff --git a/tools/build_script_generator/scons/site_tools/avrdude.py b/tools/build_script_generator/scons/site_tools/avrdude.py index a480be0406..ef76f3fd2e 100644 --- a/tools/build_script_generator/scons/site_tools/avrdude.py +++ b/tools/build_script_generator/scons/site_tools/avrdude.py @@ -24,7 +24,7 @@ def call_avrdude(env, source, fuses=None): fuses=fuses) # ----------------------------------------------------------------------------- -def avrdude_flash(env, source, alias="avrdude_program"): +def avrdude_flash(env, source, alias="avrdude_flash"): def call_program(target, source, env): call_avrdude(env, source[0]) diff --git a/tools/build_script_generator/scons/site_tools/bossac.py b/tools/build_script_generator/scons/site_tools/bossac.py new file mode 100644 index 0000000000..8ec99b25e9 --- /dev/null +++ b/tools/build_script_generator/scons/site_tools/bossac.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, 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/. +# ----------------------------------------------------------------------------- + +import platform +from SCons.Script import * +from modm_tools import bossac + +# ----------------------------------------------------------------------------- +def bossac_program(env, source, alias="bossac_program"): + def call_program(target, source, env): + offset = env.get("MODM_BOSSAC_OFFSET", 0) + if offset < 0x1000: # Is 4kB the smallest SAM-BA bootloader? + raise ValueError("Refusing to overwrite bootloader!\n" + " Hint: Set `:platform:cortex-m:linkerscript.flash_offset` to bootloader size!") + else: + bossac.program(source=source[0], offset=offset, + port=ARGUMENTS.get("port", "auto"), + options=env.get("MODM_BOSSAC_OPTIONS")) + + action = Action(call_program, cmdstr="$PROGRAM_BOSSAC_STR") + return env.AlwaysBuild(env.Alias(alias, source, action)) + +# ----------------------------------------------------------------------------- +def generate(env, **kw): + # build messages + if ARGUMENTS.get("verbose") != "1": + env["PROGRAM_BOSSAC_STR"] = \ + "{0}.-------------- {1}$SOURCE\n" \ + "{0}'----BOSSAc---> {2}$CONFIG_DEVICE_NAME{3}" \ + .format("\033[;0;32m", "\033[;0;33m", "\033[;1;33m", "\033[;0;0m") + + env.AddMethod(bossac_program, "ProgramBossac") + +def exists(env): + return env.Detect("bossac") diff --git a/tools/modm_tools/bossac.py b/tools/modm_tools/bossac.py new file mode 100644 index 0000000000..2b22e67a11 --- /dev/null +++ b/tools/modm_tools/bossac.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, 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/. +# ----------------------------------------------------------------------------- + +""" +### BOSSA + +This tool simply wraps the `bossac` command line tool to guess the serial port. + +```sh +python3 modm/modm_tools/bossac.py -p auto --offset=0x2000 -e \\ + path/to/project.bin +``` + +(\* *only SAM targets*) +""" + +import os +import subprocess +if __name__ == "__main__": + import sys + sys.path.append(os.path.dirname(os.path.dirname(__file__))) + +from modm_tools import utils +from elftools.elf.elffile import ELFFile, NoteSection + +# ----------------------------------------------------------------------------- +def program(source, offset=None, port=None, erase=False, options=None): + command = ["bossac", "-b", "-R"] + + # Attempt to find a serial port automatically + if port == "auto": + port = utils.guess_serial_port("bossac") + + if port is not None: + command.append("--port={}".format(port)) + + if offset is not None: + command.append("--offset={}".format(offset)) + + if erase: + command.append("-e") + + command.extend(utils.listify(options)) + command.extend(["-v", "-w", str(source)]) + + command = " ".join(command) + # print(command) + subprocess.call(command, cwd=os.getcwd(), shell=True) + + +# ----------------------------------------------------------------------------- +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description='Program ELF file via BOSSA') + parser.add_argument( + dest="source", + metavar="BIN") + parser.add_argument( + "-p", + dest="port", + default="auto") + parser.add_argument( + "--offset", + dest="offset") + parser.add_argument( + "-e", + dest="erase", + default=False) + + args = parser.parse_args() + program(args.source, offset=args.offset, port=args.port, erase=args.erase) From 4327990368150dcc6564797cbc6c027ce53d22c2 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 5 Jul 2020 16:57:43 +0200 Subject: [PATCH 061/135] [test] Use Cortex-M runner for unit tests --- test/module.lb | 5 +++-- test/runner/{stm32.cpp => cortex-m.cpp} | 0 2 files changed, 3 insertions(+), 2 deletions(-) rename test/runner/{stm32.cpp => cortex-m.cpp} (100%) diff --git a/test/module.lb b/test/module.lb index f444a99eb2..865bb72787 100644 --- a/test/module.lb +++ b/test/module.lb @@ -22,6 +22,7 @@ def prepare(module, options): def build(env): env.collect("modm:build:path.include", "modm-test/src") + core = env[":target"].get_driver("core")["type"] + core = next(t for t in ("avr", "cortex-m", "hosted") if core.startswith(t)) env.outbasepath = "." - env.copy("runner/{}.cpp".format(env[":target"].identifier.platform), - "main.cpp") + env.copy("runner/{}.cpp".format(core), "main.cpp") diff --git a/test/runner/stm32.cpp b/test/runner/cortex-m.cpp similarity index 100% rename from test/runner/stm32.cpp rename to test/runner/cortex-m.cpp From 304f43c1c0acc7d490401371bf3cf2b337aa78c8 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 6 May 2019 03:48:56 +0200 Subject: [PATCH 062/135] [core] Add ATSAM port --- .../core/sam/linkerscript/sam_ram.ld.in | 50 +++++++++++++++++ src/modm/platform/core/sam/module.lb | 39 ++++++++++++++ .../platform/core/sam/startup_platform.c.in | 53 +++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 src/modm/platform/core/sam/linkerscript/sam_ram.ld.in create mode 100644 src/modm/platform/core/sam/module.lb create mode 100644 src/modm/platform/core/sam/startup_platform.c.in diff --git a/src/modm/platform/core/sam/linkerscript/sam_ram.ld.in b/src/modm/platform/core/sam/linkerscript/sam_ram.ld.in new file mode 100644 index 0000000000..8b1c33a64e --- /dev/null +++ b/src/modm/platform/core/sam/linkerscript/sam_ram.ld.in @@ -0,0 +1,50 @@ +%% import "../../cortex/linker.macros" as linker with context +{{ linker.copyright() }} + +{{ linker.prefix() }} + +%% if vector_table_location == "ram" +/* Round up the number of vector table entries to the nearest power-of-two and multiply by 8. */ +VECTOR_TABLE_ALIGNMENT = (1 << LOG2CEIL({{ number_of_interrupts + 16 }})) * 8; +/* compute the vector table offset from start of RAM */ +VECTOR_TABLE_OFFSET = ALIGN(TOTAL_STACK_SIZE, VECTOR_TABLE_ALIGNMENT); +%% else +VECTOR_TABLE_OFFSET = TOTAL_STACK_SIZE; +%% endif + +SECTIONS +{ +{{ linker.section_rom_start("FLASH") }} + +{{ linker.section_vector_rom("FLASH") }} + +{{ linker.section_stack("RAM", "VECTOR_TABLE_OFFSET - TOTAL_STACK_SIZE") }} + +{{ linker.section_vector_ram("RAM") }} + +{{ linker.section_rom("FLASH") }} + +{{ linker.section("FLASH", "fastcode") }} + +{{ linker.section_ram("RAM", "FLASH") }} + +{{ linker.section("RAM AT >FLASH", "fastdata") }} + +{{ linker.section_heap("RAM", "heap1") }} + +{{ linkerscript_sections | indent(first=True) }} + + /* TABLES! TABLES! ALL THE TABLES YOU COULD EVER WANT! TABLES! */ +{{ linker.section_table_zero("FLASH") }} + +{{ linker.section_table_copy("FLASH") }} + +{{ linker.section_table_extern("FLASH") }} + +%% set heap_table = [{"name": "heap1", "prop": "0x000f"}] +{{ linker.section_table_heap("FLASH", heap_table) }} + +{{ linker.section_rom_end("FLASH") }} + +{{ linker.section_debug() }} +} diff --git a/src/modm/platform/core/sam/module.lb b/src/modm/platform/core/sam/module.lb new file mode 100644 index 0000000000..e19ea01ecf --- /dev/null +++ b/src/modm/platform/core/sam/module.lb @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Ethan Slattery +# +# 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:core" + module.description = """ +# Microchip SAM core module + +Provides SAM specific linkerscripts and startup code. +""" + +def prepare(module, options): + if options[":target"].identifier.platform != "sam": + return False + + module.depends(":platform:cortex-m") + return True + + +def build(env): + env.substitutions = {"target": env[":target"].identifier} + env.outbasepath = "modm/src/modm/platform/core" + # startup helper code + env.template("startup_platform.c.in") + + +def post_build(env): + env.substitutions = env.query("::cortex-m:linkerscript") + env.outbasepath = "modm/link" + env.template("linkerscript/sam_ram.ld.in", "linkerscript.ld") diff --git a/src/modm/platform/core/sam/startup_platform.c.in b/src/modm/platform/core/sam/startup_platform.c.in new file mode 100644 index 0000000000..6bd7663bfb --- /dev/null +++ b/src/modm/platform/core/sam/startup_platform.c.in @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019, Ethan Slattery + * + * 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 "../device.hpp" + +/** + * This code should _only_ enable internal memories and nothing else. + * Since this is the first code executed after a reset, you do not + * have access to _any_ data stored in RAM, since it has not yet been + * initialized. + * In the worst case you won't even have access to the stack, if the + * memory containing the stack is not physically enabled yet. + * In that case, consider using inline assembly to manage stack access + * manually, until the memory is enabled. + */ +void +__modm_initialize_platform(void) +{ +%% if target.family == "d" +%% if target.series == "21" + + // Overwriting the default value of the NVMCTRL.CTRLB.MANW bit (errata reference 13134) + NVMCTRL->CTRLB.bit.MANW = 1; + + // Change default QOS values to have the best performance and correct USB behaviour + SBMATRIX->SFR[SBMATRIX_SLAVE_HMCRAMC0].reg = 2; + +#ifdef USB + USB->DEVICE.QOSCTRL.bit.CQOS = 2; + USB->DEVICE.QOSCTRL.bit.DQOS = 2; +#endif + + DMAC->QOSCTRL.bit.DQOS = 2; + DMAC->QOSCTRL.bit.FQOS = 2; + DMAC->QOSCTRL.bit.WRBQOS = 2; + +%% elif target.series == "21" +%% endif + +%% elif target.family == "l" +%% if target.series == "21" +%% endif +%% endif + +} From 8f8590538eec17b5b2e3c0262067efbbf9d87a4d Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 6 May 2019 05:00:28 +0200 Subject: [PATCH 063/135] [clock] Port ATSAM GCLK peripheral --- src/modm/platform/clock/sam/gclk.cpp.in | 20 ++++++++++ src/modm/platform/clock/sam/gclk.hpp | 35 +++++++++++++++++ src/modm/platform/clock/sam/gclk_impl.hpp.in | 30 +++++++++++++++ src/modm/platform/clock/sam/module.lb | 40 ++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 src/modm/platform/clock/sam/gclk.cpp.in create mode 100644 src/modm/platform/clock/sam/gclk.hpp create mode 100644 src/modm/platform/clock/sam/gclk_impl.hpp.in create mode 100644 src/modm/platform/clock/sam/module.lb diff --git a/src/modm/platform/clock/sam/gclk.cpp.in b/src/modm/platform/clock/sam/gclk.cpp.in new file mode 100644 index 0000000000..c53ba5b92d --- /dev/null +++ b/src/modm/platform/clock/sam/gclk.cpp.in @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013-2014, Kevin Läufer + * Copyright (c) 2014-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 "../device.hpp" +#include "gclk.hpp" + +namespace modm::platform +{ +uint16_t modm_fastdata delay_fcpu_MHz(1); +uint16_t modm_fastdata delay_ns_per_loop({{ loops * 1000 }}); +} diff --git a/src/modm/platform/clock/sam/gclk.hpp b/src/modm/platform/clock/sam/gclk.hpp new file mode 100644 index 0000000000..82e57430bb --- /dev/null +++ b/src/modm/platform/clock/sam/gclk.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019, Ethan Slattery + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include "../device.hpp" + +namespace modm::platform +{ + +/** + * Clock management + * + * \ingroup modm_platform_gclk + */ +class GenericClockController +{ +public: + template< uint32_t Core_Hz > + void + updateCoreFrequency(); +}; + +} + +#include "gclk_impl.hpp" diff --git a/src/modm/platform/clock/sam/gclk_impl.hpp.in b/src/modm/platform/clock/sam/gclk_impl.hpp.in new file mode 100644 index 0000000000..d1cfc9dc00 --- /dev/null +++ b/src/modm/platform/clock/sam/gclk_impl.hpp.in @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019, Ethan Slattery + * + * 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 + +namespace modm::platform +{ +/// @cond +extern uint16_t delay_fcpu_MHz; +extern uint16_t delay_ns_per_loop; +/// @endcond + +template< uint32_t Core_Hz > +void +GenericClockController::updateCoreFrequency() +{ + delay_fcpu_MHz = Core_Hz / 1'000'000; + delay_ns_per_loop = ::round({{loops}}000.f / (Core_Hz / 1'000'000)); +} + +} + diff --git a/src/modm/platform/clock/sam/module.lb b/src/modm/platform/clock/sam/module.lb new file mode 100644 index 0000000000..a4d1fadf54 --- /dev/null +++ b/src/modm/platform/clock/sam/module.lb @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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:gclk" + module.description = "Generic Clock Controller (GCLK)" + +def prepare(module, options): + if not options[":target"].has_driver("gclk:sam"): + return False + + module.depends(":cmsis:device") + return True + +def build(env): + device = env[":target"] + core = device.get_driver("core")["type"] + + if "m0" in core: + loops = 4 + elif "m7" in core: + loops = 1 + else: + loops = 3 + + env.substitutions = {"loops": loops} + env.outbasepath = "modm/src/modm/platform/clock" + env.copy("gclk.hpp") + env.template("gclk.cpp.in") + env.template("gclk_impl.hpp.in") From 411150e394d785842e168016257db49748491176 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 27 Apr 2020 00:04:49 +0200 Subject: [PATCH 064/135] [gpio] Implement GPIO driver for SAMD --- src/modm/platform/gpio/common/inverted.hpp | 2 +- src/modm/platform/gpio/sam/base.hpp.in | 103 ++++++++++++ src/modm/platform/gpio/sam/module.lb | 75 +++++++++ src/modm/platform/gpio/sam/peripherals.hpp.in | 23 +++ src/modm/platform/gpio/sam/pin.hpp.in | 112 +++++++++++++ src/modm/platform/gpio/sam/set.hpp.in | 148 ++++++++++++++++++ src/modm/platform/gpio/sam/unused.hpp.in | 81 ++++++++++ 7 files changed, 543 insertions(+), 1 deletion(-) create mode 100644 src/modm/platform/gpio/sam/base.hpp.in create mode 100644 src/modm/platform/gpio/sam/module.lb create mode 100644 src/modm/platform/gpio/sam/peripherals.hpp.in create mode 100644 src/modm/platform/gpio/sam/pin.hpp.in create mode 100644 src/modm/platform/gpio/sam/set.hpp.in create mode 100644 src/modm/platform/gpio/sam/unused.hpp.in diff --git a/src/modm/platform/gpio/common/inverted.hpp b/src/modm/platform/gpio/common/inverted.hpp index 96fb642356..3bcf0dfbde 100644 --- a/src/modm/platform/gpio/common/inverted.hpp +++ b/src/modm/platform/gpio/common/inverted.hpp @@ -44,7 +44,7 @@ class GpioInverted : public Pin using Input = GpioInverted; using IO = GpioInverted; using Type = typename Pin::Type; - static constexpr bool isInverted = Pin::isInverted ^ true; + static constexpr bool isInverted = not Pin::isInverted; public: using Pin::setOutput; diff --git a/src/modm/platform/gpio/sam/base.hpp.in b/src/modm/platform/gpio/sam/base.hpp.in new file mode 100644 index 0000000000..a2873316fd --- /dev/null +++ b/src/modm/platform/gpio/sam/base.hpp.in @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include "../device.hpp" +#include +#include +#include + +namespace modm::platform +{ + +/// @ingroup modm_platform_gpio +struct Gpio +{ + enum class + InputType + { + Floating = 0x0, ///< floating on input + PullUp = 0x1, ///< pull-up on input + PullDown = 0x2, ///< pull-down on input + }; + + enum class + OutputType + { + PushPull = 0x0, ///< push-pull on output + OpenDrain = 0x1, ///< open-drain on output + }; + + enum class + OutputSpeed + { + Low = 0, + Medium = 0x1, + High = 0x2, + VeryHigh = 0x3, ///< 30 pF (80 MHz Output max speed on 15 pF) + MHz2 = Low, + MHz25 = Medium, + MHz50 = High, + MHz100 = VeryHigh, + }; + + enum class + InputTrigger + { + RisingEdge, + FallingEdge, + BothEdges, + }; + + /// The Port a Gpio Pin is connected to. + enum class + Port + { +%% for port in ports + {{ port | upper }} = {{ port | modm.ord }}, +%% endfor + }; + + /// @cond + enum class + Signal + { + BitBang, + }; + /// @endcond + +protected: + /// @cond + /// I/O Direction Mode values for this specific pin. + enum class + Mode + { + Input = 0x0, + Output = 0x1, + AlternateFunction = 0x2, + Analog = 0x3, + Mask = 0x3, + }; + + static constexpr uint32_t + i(Mode mode) { return uint32_t(mode); } + // Enum Class To Integer helper functions. + static constexpr uint32_t + i(InputType pull) { return uint32_t(pull); } + static constexpr uint32_t + i(OutputType out) { return uint32_t(out); } + static constexpr uint32_t + i(OutputSpeed speed) { return uint32_t(speed); } + /// @endcond +}; + +} // namespace modm::platform diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb new file mode 100644 index 0000000000..70c53fa480 --- /dev/null +++ b/src/modm/platform/gpio/sam/module.lb @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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/. +# ----------------------------------------------------------------------------- + +import copy +from collections import defaultdict, OrderedDict + +def port_ranges(gpios): + ports = {p: (32, 0) for p in set(p["port"] for p in gpios)} + for gpio in gpios: + pin = int(gpio["pin"]) + pmin, pmax = ports[gpio["port"]] + ports[gpio["port"]] = (min(pin, pmin), max(pin, pmax)) + + ports = [{"name": k.upper(), "start": v[0], "width": v[1] - v[0] + 1} for k,v in ports.items()] + ports.sort(key=lambda p: p["name"]) + return ports + +bprops = {} + +# ----------------------------------------------------------------------------- +def init(module): + module.name = ":platform:gpio" + module.description = "General Purpose I/O (GPIO)" + +def prepare(module, options): + device = options[":target"] + if not device.has_driver("gpio:sam*"): + return False + + module.depends( + ":architecture:gpio", + ":cmsis:device", + ":math:utils") + return True + +def build(env): + device = env[":target"] + driver = device.get_driver("gpio") + bprops["ranges"] = port_ranges(driver["gpio"]) + bprops["ports"] = OrderedDict([(k, i) for i, k in enumerate([p["name"] for p in bprops["ranges"]])]) + + properties = {"target": device.identifier} + properties.update(bprops) + + env.substitutions = properties + env.outbasepath = "modm/src/modm/platform/gpio" + + for gpio in driver["gpio"]: + po, pi = gpio["port"], gpio["pin"] + properties["gpio"] = gpio + env.template("pin.hpp.in", "gpio_{}{}.hpp".format(po.upper(), pi)) + + # env.template("port.hpp.in") + # env.template("software_port.hpp.in") + env.template("set.hpp.in") + env.template("base.hpp.in") + env.template("unused.hpp.in") + + env.copy("../common/inverted.hpp", "inverted.hpp") + env.copy("../common/connector.hpp", "connector.hpp") + env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") + + # FIXME: Move to modm:platform:core! + env.outbasepath = "modm/src/modm/platform/core" + env.template("peripherals.hpp.in") diff --git a/src/modm/platform/gpio/sam/peripherals.hpp.in b/src/modm/platform/gpio/sam/peripherals.hpp.in new file mode 100644 index 0000000000..f87f1b776f --- /dev/null +++ b/src/modm/platform/gpio/sam/peripherals.hpp.in @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +namespace modm::platform +{ + +enum class +Peripheral +{ + BitBang, +}; + +} diff --git a/src/modm/platform/gpio/sam/pin.hpp.in b/src/modm/platform/gpio/sam/pin.hpp.in new file mode 100644 index 0000000000..44e38a0644 --- /dev/null +++ b/src/modm/platform/gpio/sam/pin.hpp.in @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017-2018, 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/. + */ +// ---------------------------------------------------------------------------- + +%% set port = gpio.port | upper +%% set reg = "GPIO" ~ port +%% set pin = gpio.pin +%% set pnr = gpio.port | modm.ord + +#pragma once + +#include "../device.hpp" +#include "base.hpp" +#include "set.hpp" + +namespace modm::platform +{ + +/// @cond +class Gpio{{ port ~ pin }}; +using GpioOutput{{ port ~ pin }} = Gpio{{ port ~ pin }}; +using GpioInput{{ port ~ pin }} = Gpio{{ port ~ pin }}; +/// @endcond + +/// IO class for Pin {{port ~ pin}} +/// @ingroup modm_platform_gpio +class Gpio{{ port ~ pin }} : public Gpio, public ::modm::GpioIO +{ + template + friend class GpioSet; + using PinSet = GpioSet; + friend class Adc; + friend class Adc1; friend class Adc2; + friend class Adc3; friend class Adc4; +public: + using Output = Gpio{{ port ~ pin }}; + using Input = Gpio{{ port ~ pin }}; + using IO = Gpio{{ port ~ pin }}; + using Type = Gpio{{ port ~ pin }}; + static constexpr bool isInverted = false; + static constexpr Port port = Port::{{port}}; ///< Port name + static constexpr uint8_t pin = {{pin|int}}; ///< Pin number + +protected: + /// Bitmask for registers that contain a 1bit value for every pin. + static constexpr uint32_t mask = 1ul << pin; + /// Bitmask for registers that contain a 2bit value for every pin. + // static constexpr uint32_t mask2 = 0x3 << (pin * 2); + /// Port Number. + static constexpr uint8_t port_nr = uint8_t(port); + /// Alternate Function register id. 0 for pin 0-7. 1 for pin 8-15. + // static constexpr uint8_t af_id = pin / 8; + /// Alternate Function offset. + // static constexpr uint8_t af_offset = (pin * 4) % 32; + /// Alternate Function register mask. + // static constexpr uint32_t af_mask = 0xf << af_offset; + +public: + /// @cond + inline static void setAlternateFunction(uint8_t) { + // {{reg}}->AFR[af_id] = ({{reg}}->AFR[af_id] & ~af_mask) | ((af & 0xf) << af_offset); + // {{reg}}->MODER = ({{reg}}->MODER & ~mask2) | (i(Mode::AlternateFunction) << (pin * 2)); + } + + /// Enable Analog Mode which is needed to use this pin as an ADC input. + inline static void setAnalogInput() { PinSet::setAnalogInput(); } + /// @endcond + +public: + // GpioOutput + // start documentation inherited + inline static void setOutput() { PinSet::setOutput(); } + inline static void setOutput(bool status) { PinSet::setOutput(status); } + inline static void set() { PinSet::set(); } + inline static void set(bool status) { PinSet::set(status); } + inline static void reset() { PinSet::reset(); } + inline static void toggle() { + if (isSet()) { reset(); } + else { set(); } + } + inline static bool isSet() { return (REG_PORT_OUT{{pnr}} & mask); } + // stop documentation inherited + inline static void configure(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::configure(type, speed); } + inline static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::setOutput(type, speed); } + // GpioInput + // start documentation inherited + inline static void setInput() { PinSet::setInput(); } + inline static bool read() { return (REG_PORT_IN{{pnr}} & mask); } + // end documentation inherited + inline static void configure(InputType type) { PinSet::configure(type); } + inline static void setInput(InputType type) { PinSet::setInput(type); } + // GpioIO + // start documentation inherited + inline static Direction getDirection() { + if (REG_PORT_DIR{{pnr}} & mask) + return Direction::In; + return Direction::Out; + } + // end documentation inherited + inline static void disconnect() { + setInput(InputType::Floating); + } +}; + +} // namespace modm::platform diff --git a/src/modm/platform/gpio/sam/set.hpp.in b/src/modm/platform/gpio/sam/set.hpp.in new file mode 100644 index 0000000000..bc1d89bab7 --- /dev/null +++ b/src/modm/platform/gpio/sam/set.hpp.in @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_GPIO_SET_HPP +#define MODM_STM32_GPIO_SET_HPP + +#include "../device.hpp" +#include "base.hpp" + +namespace modm +{ + +namespace platform +{ + +/// @ingroup modm_platform_gpio +template< class... Gpios > +class GpioSet : public Gpio +{ +protected: + static constexpr uint32_t inverteds[{{ ports | length }}] = { +%% for port in ports + (((Gpios::port == Port::{{port}} and Gpios::isInverted) ? Gpios::mask : 0) | ...), +%% endfor + }; + static constexpr uint32_t inverted(uint8_t id) { return inverteds[id]; } + + static constexpr uint32_t masks[{{ ports | length }}] = { +%% for port in ports + (((Gpios::port == Port::{{port}}) ? Gpios::mask : 0) | ...), +%% endfor + }; + static constexpr uint32_t mask(uint8_t id) { return masks[id]; } + static constexpr uint8_t numberOfPorts() { + uint8_t r{0}; + for (const auto &m: masks) r += (m) ? 1 : 0; + return r; + } +public: + static constexpr uint8_t width = sizeof...(Gpios); + static constexpr uint8_t number_of_ports = numberOfPorts(); +public: + static void setOutput() + { + %% for port, id in ports.items() + if constexpr (mask({{id}})) REG_PORT_DIRSET{{id}} = mask({{id}}); + %% endfor + } + + static void setOutput(bool status) + { + set(status); + setOutput(); + } + + static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) + { + configure(type, speed); + setOutput(); + } + + static void configure(OutputType , OutputSpeed = OutputSpeed::MHz50) + { + %% for port, id in ports.items() + if constexpr (mask({{id}})) { + // GPIO{{port}}->OSPEEDR = (GPIO{{port}}->OSPEEDR & ~mask2({{id}})) | (i(speed) * mask2({{id}}, 0b01)); + // GPIO{{port}}->OTYPER = (GPIO{{port}}->OTYPER & ~mask({{id}})) | (i(type) ? mask({{id}}) : 0); + } + %% endfor + } + + static void setInput() + { + %% for port, id in ports.items() + if constexpr (mask({{id}})) { + REG_PORT_DIRCLR{{id}} = mask({{id}}); + } + %% endfor + } + + static void setInput(InputType type) + { + configure(type); + setInput(); + } + + static void setAnalogInput() + { + %% for port, id in ports.items() + // if constexpr (mask({{id}})) GPIO{{port}}->MODER |= mask2({{id}}, i(Mode::Analog)); + %% endfor + } + + static void configure(InputType) + { + %% for port, id in ports.items() + if constexpr (mask({{id}})) { + // GPIO{{port}}->PUPDR = (GPIO{{port}}->PUPDR & ~mask2({{id}})) | (i(type) * mask2({{id}}, 0b01)); + } + %% endfor + } + + static void set() + { +%% for port, id in ports.items() + if constexpr (mask({{id}})) REG_PORT_OUTSET{{id}} = mask({{id}}); +%% endfor + } + + static void set(bool status) + { + if (status) set(); + else reset(); + } + + static void reset() + { +%% for port, id in ports.items() + if constexpr (mask({{id}})) REG_PORT_OUTCLR{{id}} = mask({{id}}); +%% endfor + } + + static void toggle() + { +%% for port, id in ports.items() + if constexpr (mask({{id}})) REG_PORT_OUTTGL{{id}} = mask({{id}}); +%% endfor + } + + static void disconnect() + { + // (Gpios::disconnect(), ...); + } +}; + +} // namespace platform + +} // namespace modm + +#endif // MODM_STM32_GPIO_SET_HPP diff --git a/src/modm/platform/gpio/sam/unused.hpp.in b/src/modm/platform/gpio/sam/unused.hpp.in new file mode 100644 index 0000000000..1be9a4d1af --- /dev/null +++ b/src/modm/platform/gpio/sam/unused.hpp.in @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include "base.hpp" +#include + +namespace modm::platform +{ + +/** + * @author Niklas Hauser + * @ingroup modm_platform_gpio + */ +class GpioUnused : public Gpio, public ::modm::GpioIO +{ +public: + using Output = GpioUnused; + using Input = GpioUnused; + using IO = GpioUnused; + using Type = GpioUnused; + static constexpr bool isInverted = false; + static constexpr Port port = Port(-1); + static constexpr uint8_t pin = uint8_t(-1); + static constexpr uint32_t mask = 0; + +protected: + /// @cond + static void setAlternateFunction(uint8_t) {} + static void setAnalogInput() {} + /// @endcond + +public: + // GpioOutput + // start documentation inherited + static void setOutput() {} + static void setOutput(bool) {} + static void set() {} + static void set(bool) {} + static void reset() {} + static void toggle() {} + static bool isSet() { return false; } + // stop documentation inherited + static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {} + static void setOutput(OutputType, OutputSpeed = OutputSpeed::MHz50) {} + + // GpioInput + // start documentation inherited + static void setInput() {} + static bool read() { return false; } + // end documentation inherited + static void configure(InputType) {} + static void setInput(InputType) {} + // External Interrupts + static void enableExternalInterrupt() {} + static void disableExternalInterrupt() {} + static void enableExternalInterruptVector(const uint32_t) {} + static void disableExternalInterruptVector() {} + static void setInputTrigger(const InputTrigger) {} + static bool getExternalInterruptFlag() { return false; } + /// Reset the interrupt flag in the interrupt routine. + static void acknowledgeExternalInterruptFlag() {} + + // GpioIO + // start documentation inherited + static Direction getDirection() { return Direction::Special; } + // end documentation inherited + static void lock() {} + static void disconnect() {} +}; + +} // namespace modm::platform From 1dd5c4a615d7ba6d4458b6009cd4ea7c579dbf7d Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 6 May 2019 03:49:11 +0200 Subject: [PATCH 065/135] [board] Add Feather-M0 and SAMD21-MINI board --- README.md | 12 ++-- src/modm/board/feather_m0/board.hpp | 81 +++++++++++++++++++++++++++ src/modm/board/feather_m0/board.xml | 14 +++++ src/modm/board/feather_m0/module.lb | 43 ++++++++++++++ src/modm/board/samd21_mini/board.hpp | 84 ++++++++++++++++++++++++++++ src/modm/board/samd21_mini/board.xml | 15 +++++ src/modm/board/samd21_mini/module.lb | 43 ++++++++++++++ 7 files changed, 287 insertions(+), 5 deletions(-) create mode 100644 src/modm/board/feather_m0/board.hpp create mode 100644 src/modm/board/feather_m0/board.xml create mode 100644 src/modm/board/feather_m0/module.lb create mode 100644 src/modm/board/samd21_mini/board.hpp create mode 100644 src/modm/board/samd21_mini/board.xml create mode 100644 src/modm/board/samd21_mini/module.lb diff --git a/README.md b/README.md index 86638dc604..c4eca83206 100644 --- a/README.md +++ b/README.md @@ -132,31 +132,33 @@ documentation. DISCO-L152RC DISCO-L476VG +FEATHER-M0 MEGA-2560-PRO MINI-F401 MINI-F411 -NUCLEO-F031K6 +NUCLEO-F031K6 NUCLEO-F042K6 NUCLEO-F103RB NUCLEO-F303K8 -NUCLEO-F303RE +NUCLEO-F303RE NUCLEO-F401RE NUCLEO-F411RE NUCLEO-F429ZI -NUCLEO-F446RE +NUCLEO-F446RE NUCLEO-F746ZG NUCLEO-G071RB NUCLEO-G474RE -NUCLEO-L152RE +NUCLEO-L152RE NUCLEO-L432KC NUCLEO-L476RG OLIMEXINO-STM32 -STM32F030F4P6-DEMO +SAMD21-MINI +STM32F030F4P6-DEMO diff --git a/src/modm/board/feather_m0/board.hpp b/src/modm/board/feather_m0/board.hpp new file mode 100644 index 0000000000..277da20516 --- /dev/null +++ b/src/modm/board/feather_m0/board.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016-2017, Sascha Schade + * Copyright (c) 2017-2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include + +using namespace modm::platform; + + +/// @ingroup modm_board_feather_m0 +namespace Board +{ + using namespace modm::literals; + +/// samd21g18a running at 48MHz generated from the external 32.768 KHz crystal +struct SystemClock { + static constexpr uint32_t Frequency = 48_MHz; + // static constexpr uint32_t Ahb = Frequency; + // static constexpr uint32_t Apba = Frequency; + // static constexpr uint32_t Apbb = Frequency; + // static constexpr uint32_t Apbc = Frequency; + + // static constexpr uint32_t Adc = Apb2; + + // static constexpr uint32_t SercomSlow = Apb2; + // static constexpr uint32_t Sercom0 = Apb2; + // static constexpr uint32_t Sercom1 = Apb2; + // static constexpr uint32_t Sercom2 = Apb2; + // static constexpr uint32_t Sercom3 = Apb2; + // static constexpr uint32_t Sercom4 = Apb2; + // static constexpr uint32_t Sercom5 = Apb2; + + // static constexpr uint32_t Apb1Timer = Apb1 * 2; + // static constexpr uint32_t Apb2Timer = Apb2 * 1; + // 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 bool inline + enable() + { + // GenericClockController::enableExternalCrystal(Frequency); + + // switch system clock to PLL output + // GenericClockController::enableSystemClock(ClockControl::SystemClockSource::Pll); + + // update frequencies for busy-wait delay functions + // GenericClockController::updateCoreFrequency(); + + return true; + } +}; + +// User LED (inverted, because connected to 3V3) +// using LedRed = GpioOutputA17; +// using Leds = SoftwareGpioPort< LedRed >; + +// using Button = GpioUnused; + +inline void +initialize() +{ + SystemClock::enable(); + SysTickTimer::initialize(); + + // LedGreen::setOutput(modm::Gpio::Low); +} + +} // Board namespace diff --git a/src/modm/board/feather_m0/board.xml b/src/modm/board/feather_m0/board.xml new file mode 100644 index 0000000000..c816c6f149 --- /dev/null +++ b/src/modm/board/feather_m0/board.xml @@ -0,0 +1,14 @@ + + + + ../../../../repo.lb + + + + + + + + modm:board:feather-m0 + + diff --git a/src/modm/board/feather_m0/module.lb b/src/modm/board/feather_m0/module.lb new file mode 100644 index 0000000000..ad1fa5d59c --- /dev/null +++ b/src/modm/board/feather_m0/module.lb @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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:feather-m0" + module.description = """\ +# Adafruit Feather-M0 + +At the Feather M0's heart is an ATSAMD21G18 ARM Cortex M0 processor, +clocked at 48 MHz and at 3.3V logic, the same one used in the new Arduino Zero. +This chip has a whopping 256K of FLASH (8x more than the Atmega328 or 32u4) +and 32K of RAM (16x as much)! This chip comes with built in USB so it has +USB-to-Serial program & debug capability built in with no need for an FTDI-like chip. + +https://www.adafruit.com/product/2772 +""" + +def prepare(module, options): + if not options[":target"].partname.startswith("samd21g18a"): + return False + + module.depends(":platform:gclk", ":platform:core", ":platform:clock") + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + # env.substitutions = {"board_has_logger": False} + # env.template("../board.cpp.in", "board.cpp") + env.copy('.') + + # env.outbasepath = "modm/openocd/modm/board/" + # env.copy(repopath("tools/openocd/modm/stm32f103_blue_pill.cfg"), "stm32f103_blue_pill.cfg") + # env.collect(":build:openocd.source", "modm/board/stm32f103_blue_pill.cfg"); diff --git a/src/modm/board/samd21_mini/board.hpp b/src/modm/board/samd21_mini/board.hpp new file mode 100644 index 0000000000..901662728d --- /dev/null +++ b/src/modm/board/samd21_mini/board.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016-2017, Sascha Schade + * Copyright (c) 2017-2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include + +using namespace modm::platform; + + +/// @ingroup modm_board_samd21_mini +namespace Board +{ +using namespace modm::literals; + +struct SystemClock +{ + static constexpr uint32_t Frequency = 1_MHz; + // static constexpr uint32_t Ahb = Frequency; + // static constexpr uint32_t Apba = Frequency; + // static constexpr uint32_t Apbb = Frequency; + // static constexpr uint32_t Apbc = Frequency; + + // static constexpr uint32_t Adc = Apb2; + + // static constexpr uint32_t SercomSlow = Apb2; + // static constexpr uint32_t Sercom0 = Apb2; + // static constexpr uint32_t Sercom1 = Apb2; + // static constexpr uint32_t Sercom2 = Apb2; + // static constexpr uint32_t Sercom3 = Apb2; + // static constexpr uint32_t Sercom4 = Apb2; + // static constexpr uint32_t Sercom5 = Apb2; + + // static constexpr uint32_t Apb1Timer = Apb1 * 2; + // static constexpr uint32_t Apb2Timer = Apb2 * 1; + // 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 bool inline + enable() + { + // GenericClockController::enableExternalCrystal(Frequency); + + // switch system clock to PLL output + // GenericClockController::enableSystemClock(ClockControl::SystemClockSource::Pll); + + // update frequencies for busy-wait delay functions + // GenericClockController::updateCoreFrequency(); + + return true; + } +}; + +// User LED (inverted, because connected to 3V3) +using LedD13 = GpioInverted; +using LedTx = GpioInverted; +using LedRx = GpioInverted; +// using Leds = SoftwareGpioPort< LedRed >; + +using Button = GpioUnused; + +inline void +initialize() +{ + SystemClock::enable(); + SysTickTimer::initialize(); + + LedTx::setOutput(modm::Gpio::Low); + LedRx::setOutput(modm::Gpio::Low); +} + +} // Board namespace diff --git a/src/modm/board/samd21_mini/board.xml b/src/modm/board/samd21_mini/board.xml new file mode 100644 index 0000000000..d57567cf23 --- /dev/null +++ b/src/modm/board/samd21_mini/board.xml @@ -0,0 +1,15 @@ + + + + ../../../../repo.lb + + + + + + + + + modm:board:samd21-mini + + diff --git a/src/modm/board/samd21_mini/module.lb b/src/modm/board/samd21_mini/module.lb new file mode 100644 index 0000000000..f6d243640c --- /dev/null +++ b/src/modm/board/samd21_mini/module.lb @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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:samd21-mini" + module.description = """ +# RobotDyn SAMD21 M0 MINI + +The SAMD21 MINI board is a small breakout board for the ATSAMD21G18 ARM +Cortex-M0 processor, clocked at 48 MHz and at 3.3V logic. + +See: https://robotdyn.com/samd21-m0-mini.html + +It can be bought for little cost from well known Chinese online stores. +""" + +def prepare(module, options): + if not options[":target"].partname.startswith("samd21g18a"): + return False + + module.depends(":platform:gclk", ":platform:core", ":platform:clock", + ":platform:gpio") + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + # env.substitutions = {"board_has_logger": False} + # env.template("../board.cpp.in", "board.cpp") + env.copy('.') + + # env.outbasepath = "modm/openocd/modm/board/" + # env.copy(repopath("tools/openocd/modm/stm32f103_blue_pill.cfg"), "stm32f103_blue_pill.cfg") + # env.collect(":build:openocd.source", "modm/board/stm32f103_blue_pill.cfg"); From 6dbeb59e436f95a387772cfe8bc8311b5f706aa4 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 27 Apr 2020 15:35:46 +0200 Subject: [PATCH 066/135] [examples] Add SAMD21 examples --- .circleci/config.yml | 4 ++++ examples/samd/blink/main.cpp | 35 +++++++++++++++++++++++++++++++++ examples/samd/blink/project.xml | 9 +++++++++ 3 files changed, 48 insertions(+) create mode 100644 examples/samd/blink/main.cpp create mode 100644 examples/samd/blink/project.xml diff --git a/.circleci/config.yml b/.circleci/config.yml index 25330c979a..9b1792df30 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -51,6 +51,10 @@ jobs: name: Generic Examples command: | (cd examples && ../tools/scripts/examples_compile.py generic) + - run: + name: Examples SAM Devices + command: | + (cd examples && ../tools/scripts/examples_compile.py samd) - run: name: Execute Python Scripts command: | diff --git a/examples/samd/blink/main.cpp b/examples/samd/blink/main.cpp new file mode 100644 index 0000000000..7472a0a5ec --- /dev/null +++ b/examples/samd/blink/main.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019, Ethan Slattery + * + * 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; +using namespace std::chrono_literals; + +int +main() +{ + Board::initialize(); + LedRx::set(); + + while (1) + { + LedTx::toggle(); + LedRx::toggle(); + modm::delay(500ms); + +#ifdef MODM_BOARD_HAS_LOGGER + static uint32_t counter(0); + MODM_LOG_INFO << "Loop counter: " << (counter++) << modm::endl; +#endif + } + + return 0; +} diff --git a/examples/samd/blink/project.xml b/examples/samd/blink/project.xml new file mode 100644 index 0000000000..0c80983a8b --- /dev/null +++ b/examples/samd/blink/project.xml @@ -0,0 +1,9 @@ + + modm:samd21-mini + + + + + modm:build:scons + + From 6c4a8d655f45bfefb498e4a3d8bfad7a2c597ac2 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 27 Apr 2020 23:00:07 +0200 Subject: [PATCH 067/135] [ci] Add SAMD devices to CI --- .circleci/config.yml | 24 ++++++++++++++++++++++++ test/all/{stm32.cpp => cortex-m.cpp} | 0 test/all/{stm32.xml => cortex-m.xml} | 0 test/all/ignored.txt | 5 +++++ test/all/run_all.py | 6 +++--- 5 files changed, 32 insertions(+), 3 deletions(-) rename test/all/{stm32.cpp => cortex-m.cpp} (100%) rename test/all/{stm32.xml => cortex-m.xml} (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9b1792df30..5bfd6c3a3a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -161,6 +161,23 @@ jobs: path: test/all/log destination: log + samd-compile-all: + docker: + - image: modm/modm-build:latest + steps: + - checkout + - run: + name: Checkout code and update modm tools + command: | + (git submodule sync && git submodule update --init --jobs 8) & pip3 install --upgrade --upgrade-strategy=eager modm & wait + - run: + name: Compile HAL for all SAMD + command: | + (cd test/all && python3 run_all.py samd) + - store_artifacts: + path: test/all/log + destination: log + stm32f0-compile-all: docker: - image: modm/modm-build:latest @@ -441,6 +458,12 @@ workflows: filters: branches: ignore: /^develop.*/ + - samd-compile-all: + requires: + - unittests-linux-generic + filters: + branches: + ignore: /^develop.*/ - stm32f0-compile-all: requires: - unittests-linux-generic @@ -518,6 +541,7 @@ workflows: ignore: /^develop.*/ requires: - avr-compile-all + - samd-compile-all - stm32f0-compile-all - stm32f1-compile-all - stm32f2-compile-all diff --git a/test/all/stm32.cpp b/test/all/cortex-m.cpp similarity index 100% rename from test/all/stm32.cpp rename to test/all/cortex-m.cpp diff --git a/test/all/stm32.xml b/test/all/cortex-m.xml similarity index 100% rename from test/all/stm32.xml rename to test/all/cortex-m.xml diff --git a/test/all/ignored.txt b/test/all/ignored.txt index 44ac365e5a..7a0b65ef99 100644 --- a/test/all/ignored.txt +++ b/test/all/ignored.txt @@ -98,3 +98,8 @@ stm32l151qch6 stm32l151zct6 stm32l152qch6 stm32l152zct6 +# FIXME: Missing cmsis-header for these ones +samd21e15c-uf +samd21e15c-uu +samd21e16c-uf +samd21e16c-uu diff --git a/test/all/run_all.py b/test/all/run_all.py index 54a695b781..31e9841da8 100644 --- a/test/all/run_all.py +++ b/test/all/run_all.py @@ -84,9 +84,9 @@ def run(self): if self.device.startswith("at"): lbuild_command = ["-c", "avr.xml"] shutil.copyfile("avr.cpp", os.path.join(tempdir, "main.cpp")) - elif self.device.startswith("stm32"): - lbuild_command = ["-c", "stm32.xml", "-D:::main_stack_size=512"] - shutil.copyfile("stm32.cpp", os.path.join(tempdir, "main.cpp")) + else: + lbuild_command = ["-c", "cortex-m.xml", "-D:::main_stack_size=512"] + shutil.copyfile("cortex-m.cpp", os.path.join(tempdir, "main.cpp")) if self.cache_dir: lbuild_command.append("-D:::cache_dir={}".format(self.cache_dir)) From 2f125fdc7af41c6484c1a03fb9bbd7fd4fe5c57d Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 5 Jul 2020 18:38:06 +0200 Subject: [PATCH 068/135] [scons] Use GCC-10 on hosted-darwin targets --- .../cmake/cmake_scripts/configure-gcc.cmake.in | 7 +++---- tools/build_script_generator/scons/resources/SConscript.in | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in b/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in index 564ff4a78a..23224fbb26 100644 --- a/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in +++ b/tools/build_script_generator/cmake/cmake_scripts/configure-gcc.cmake.in @@ -30,10 +30,9 @@ if(WIN32) %% if platform == "hosted" elseif(APPLE) # Using homebrew gcc on macOS - find_program(GCC_VERSION NAMES "gcc-9" "gcc-8" "gcc-7") - string(LENGTH ${GCC_VERSION} GCC_VERSION_LENGTH) - math(EXPR GCC_VERSION_LENGTH "${GCC_VERSION_LENGTH}-2") - string(SUBSTRING ${GCC_VERSION} ${GCC_VERSION_LENGTH} -1 GCC_VERSION) + find_program(GCC_VERSION NAMES "gcc-10" "gcc-9" "gcc-8" "gcc-7") + string(REGEX MATCH "gcc-[0-9]+" GCC_VERSION ${GCC_VERSION}) + string(SUBSTRING ${GCC_VERSION} 3 -1 GCC_VERSION) set(TOOL_EXECUTABLE_SUFFIX "${GCC_VERSION}") %% endif else() diff --git a/tools/build_script_generator/scons/resources/SConscript.in b/tools/build_script_generator/scons/resources/SConscript.in index 03f930d1c6..f6d29755cc 100644 --- a/tools/build_script_generator/scons/resources/SConscript.in +++ b/tools/build_script_generator/scons/resources/SConscript.in @@ -17,7 +17,7 @@ env["BUILDPATH"] = join(env["CONFIG_BUILD_BASE"], profile) env["BASEPATH"] = abspath(".") %% if family == "darwin" # Using homebrew gcc on macOS instead of clang -env["COMPILERSUFFIX"] = env.Detect(["gcc-9", "gcc-8", "gcc-7"])[3:] +env["COMPILERSUFFIX"] = env.Detect(["gcc-10", "gcc-9", "gcc-8", "gcc-7"])[3:] %% endif %% endif From dd00964575821d8210d140bdd73aaa06e9984ed0 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 5 Jul 2020 20:29:48 +0200 Subject: [PATCH 069/135] [docs] Update and fix small inconsistencies --- docs/src/guide/installation.md | 14 +++++++++----- src/modm/board/mini_f401/module.lb | 4 ++-- src/modm/board/mini_f411/module.lb | 4 ++-- src/modm/platform/fault/crashcatcher/module.md | 2 +- src/modm/platform/uart/cortex/module.md | 4 ++-- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md index aa75820db1..1d76d27755 100644 --- a/docs/src/guide/installation.md +++ b/docs/src/guide/installation.md @@ -73,17 +73,21 @@ some of these libraries as well, depending on what modm modules you use: ## Linux -For Ubuntu 18.04LTS, these commands install the basic build system: +For Ubuntu 20.04LTS, these commands install the basic build system: sudo apt-get install python3 python3-pip scons git sudo apt-get --no-install-recommends install doxygen pip3 install modm -scons now works with Python 3. Unfortunately, Ubuntu still defaults to Python 2. -For a less intrusive way to run all scons scripts with Python 3 add this to your -`.bashrc` or `.zshrc`: +!!! warning "Use Python 3!" + Ubuntu 18.04LTS unfortunately defaults to Python 2, however, our SCons tools + require Python 3. For a less intrusive way to run all scons scripts with + Python 3 add this to your `.bashrc` or `.zshrc`: - alias scons="/usr/bin/env python3 $(which scons)" + alias scons="/usr/bin/env python3 $(which scons)" + + If you get errors about missing `pyelftools`, check that you're really + running SCons with Python 3! Install the AVR toochain: diff --git a/src/modm/board/mini_f401/module.lb b/src/modm/board/mini_f401/module.lb index ac9a00b912..f584724831 100644 --- a/src/modm/board/mini_f401/module.lb +++ b/src/modm/board/mini_f401/module.lb @@ -29,10 +29,10 @@ descr_programming = """ Since the board has no St-Link programmer on the board, you must use DFU (Device Firmware Update (via USB)) to program the board. -A DFU command is integrated into the modm:build:scons tool and can be called as follows: +A DFU command is integrated into the `modm:build:scons` tool and can be called as follows: ``` -scons dfu +scons program-dfu ``` Alternatively you can go for an external programmer diff --git a/src/modm/board/mini_f411/module.lb b/src/modm/board/mini_f411/module.lb index 26757ada92..ee16ff2471 100644 --- a/src/modm/board/mini_f411/module.lb +++ b/src/modm/board/mini_f411/module.lb @@ -29,10 +29,10 @@ descr_programming = """ Since the board has no St-Link programmer on the board, you must use DFU (Device Firmware Update (via USB)) to program the board. -A DFU command is integrated into the modm:build:scons tool and can be called as follows: +A DFU command is integrated into the `modm:build:scons` tool and can be called as follows: ``` -scons dfu +scons program-dfu ``` Alternatively you can go for an external programmer diff --git a/src/modm/platform/fault/crashcatcher/module.md b/src/modm/platform/fault/crashcatcher/module.md index c5dbde2a67..483268af74 100644 --- a/src/modm/platform/fault/crashcatcher/module.md +++ b/src/modm/platform/fault/crashcatcher/module.md @@ -162,5 +162,5 @@ the corresponding ELF file automatically. # Copy data into coredump.txt touch coredump.txt # Start postmortem debugging of executable with this build id -scons postmortem firmware=59f08f7a37a7340799d9dba6b0c092bc3c9515c5 +scons debug-coredump firmware=59f08f7a37a7340799d9dba6b0c092bc3c9515c5 ``` diff --git a/src/modm/platform/uart/cortex/module.md b/src/modm/platform/uart/cortex/module.md index 251b2e9710..f9187b1b45 100644 --- a/src/modm/platform/uart/cortex/module.md +++ b/src/modm/platform/uart/cortex/module.md @@ -4,7 +4,7 @@ This module provides the ITM port 0 as a (buffered) Uart interface. You can use this class as a *transmit-only* alternative for logging: ```cpp -modm::IODeviceWrapper itm_device; modm::IOStream stream(itm_device); stream << "Hello World" << modm::endl; @@ -61,7 +61,7 @@ wrapper, which logs to a temporary file and displays it's contents on the terminal ``` - $ scons itm fcpu=64000000 + $ scons log-itm fcpu=64000000 .----OpenOCD--> Single Wire Viewer '------SWO----- stm32f103rbt From 11fa4eec55def471a78d85059f2c6f0d00035bfe Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 6 Jul 2020 00:37:08 +0200 Subject: [PATCH 070/135] [docs] Use verbose zipping to prevent CI timeout --- tools/scripts/docs_modm_io_generator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/scripts/docs_modm_io_generator.py b/tools/scripts/docs_modm_io_generator.py index 7a186f5e5e..e2bb6efc6a 100755 --- a/tools/scripts/docs_modm_io_generator.py +++ b/tools/scripts/docs_modm_io_generator.py @@ -124,7 +124,9 @@ def main(): # output_dir.rename(cwd / 'modm-api-docs') if args.compress: print("Zipping docs ...") - shutil.make_archive(str(cwd / 'modm-api-docs'), 'gztar', str(output_dir)) + # Zipping may take more than 10 minutes + os.system("tar -czvf {} {}".format(str(cwd / 'modm-api-docs.tar.gz'), str(output_dir))) + # shutil.make_archive(str(cwd / 'modm-api-docs'), 'gztar', str(output_dir)) else: final_output_dir = Path(args.output) if args.overwrite: From 303f2a8688a0e2d12746a8192303dedd52bdeb5c Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Tue, 7 Jul 2020 01:23:23 +0200 Subject: [PATCH 071/135] [docs] Fix verbose zipping to use relative path --- tools/scripts/docs_modm_io_generator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/scripts/docs_modm_io_generator.py b/tools/scripts/docs_modm_io_generator.py index e2bb6efc6a..e7cc74fce5 100755 --- a/tools/scripts/docs_modm_io_generator.py +++ b/tools/scripts/docs_modm_io_generator.py @@ -94,9 +94,9 @@ def main(): device_list = [] if args.test: # test list - device_list = ["hosted-linux", "atmega328p-au", "stm32f103c8t6", "stm32g474cet6"] + device_list = ["hosted-linux", "atmega328p-au", "stm32f103c8t6", "stm32g474cet6", "samd21g18a-uu"] elif args.test2: - device_list = ["hosted-linux", "atmega328p-pu", "stm32f103zgt7", "stm32g474vet7", "samd21g18a-uu"] + device_list = ["hosted-linux", "atmega328p-pu", "stm32f103zgt7", "stm32g474vet7"] else: device_list = get_targets() @@ -125,7 +125,7 @@ def main(): if args.compress: print("Zipping docs ...") # Zipping may take more than 10 minutes - os.system("tar -czvf {} {}".format(str(cwd / 'modm-api-docs.tar.gz'), str(output_dir))) + os.system("(cd {} && tar -czvf {} .)".format(str(output_dir), str(cwd / 'modm-api-docs.tar.gz'))) # shutil.make_archive(str(cwd / 'modm-api-docs'), 'gztar', str(output_dir)) else: final_output_dir = Path(args.output) From 58e9e464abeed0653576574d24935409936c1a0c Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Tue, 7 Jul 2020 01:11:35 +0200 Subject: [PATCH 072/135] [build] Use regex to extract tool docs --- tools/build_script_generator/module.lb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/build_script_generator/module.lb b/tools/build_script_generator/module.lb index db780748a8..f39491735b 100644 --- a/tools/build_script_generator/module.lb +++ b/tools/build_script_generator/module.lb @@ -10,7 +10,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # ----------------------------------------------------------------------------- -import os, sys +import os, sys, re from pathlib import Path # import all common code @@ -30,12 +30,13 @@ class BuildModuleDocs: for tool in tools: tpath = Path(repopath("tools/modm_tools/{}.py".format(tool))) doc = None + # Documentation is inside a Markdown file if tpath.with_suffix(".md").exists(): doc = tpath.with_suffix(".md").read_text() + # Documentation is inside the tool as a docstring elif tpath.exists(): - co = compile(tpath.read_bytes(), tpath, 'exec') - if co.co_consts and isinstance(co.co_consts[0], str): - doc = co.co_consts[0] + doc = re.search('"""(.*?)"""', tpath.read_text(), flags=re.DOTALL | re.MULTILINE) + if doc: doc = doc.group(1); if doc is not None: self._content += "\n\n\n" + doc.strip() From 483eba8a404efc897499c26e5bfbbc800b54d888 Mon Sep 17 00:00:00 2001 From: Benjamin Carrick Date: Mon, 6 Jul 2020 21:32:11 +0200 Subject: [PATCH 073/135] Implementation of the LIS3MDL Driver --- README.md | 11 +- src/modm/driver/inertial/lis3mdl.hpp | 294 ++++++++++++++++++++++ src/modm/driver/inertial/lis3mdl.lb | 36 +++ src/modm/driver/inertial/lis3mdl_impl.hpp | 111 ++++++++ 4 files changed, 447 insertions(+), 5 deletions(-) create mode 100644 src/modm/driver/inertial/lis3mdl.hpp create mode 100644 src/modm/driver/inertial/lis3mdl.lb create mode 100644 src/modm/driver/inertial/lis3mdl_impl.hpp diff --git a/README.md b/README.md index c4eca83206..7ada327031 100644 --- a/README.md +++ b/README.md @@ -211,41 +211,42 @@ can easily configure them for you specific needs. LIS302DL LIS3DSH +LIS3MDL LM75 LP503X LSM303A LSM6DS33 LTC2984 -MAX6966 +MAX6966 MAX7219 MCP23X17 MCP2515 NOKIA5110 NRF24 -TFT-DISPLAY +TFT-DISPLAY PAT9125EL PCA8574 PCA9535 PCA9548A PCA9685 -SIEMENS-S65 +SIEMENS-S65 SIEMENS-S75 SK6812 SK9822 SSD1306 SX1276 -TCS3414 +TCS3414 TCS3472 TLC594X TMP102 TMP175 VL53L0 -VL6180 +VL6180 WS2812 diff --git a/src/modm/driver/inertial/lis3mdl.hpp b/src/modm/driver/inertial/lis3mdl.hpp new file mode 100644 index 0000000000..b042f664af --- /dev/null +++ b/src/modm/driver/inertial/lis3mdl.hpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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_LIS3MDL_HPP +#define MODM_LIS3MDL_HPP + +#include +#include +#include +#include +#include "lis3_transport.hpp" + +namespace modm +{ + +/// @ingroup modm_driver_lis3mdl +struct lis3mdl +{ + enum class + Register : uint8_t + { + WHO_AM_I = 0x0F, ///< Contains 0x3D + + CTRL1 = 0x20, ///< Control register 1: rw + CTRL2 = 0x21, ///< Control register 2: rw + CTRL3 = 0x22, ///< Control register 3: rw + CTRL4 = 0x23, ///< Control register 4: rw + CTRL5 = 0x24, ///< Control register 5: rw + + STATUS = 0x27, ///< Status Data Register + + OUT_X_L = 0x28, ///< Angular rates output x-axis register low + OUT_X_H = 0x29, ///< Angular rates output x-axis register high + OUT_Y_L = 0x2A, ///< Angular rates output y-axis register low + OUT_Y_H = 0x2B, ///< Angular rates output y-axis register high + OUT_Z_L = 0x2C, ///< Angular rates output z-axis register low + OUT_Z_H = 0x2D, ///< Angular rates output z-axis register high + + OUT_TEMP_L = 0x2E, ///< Temperature output register low + OUT_TEMP_H = 0x2F, ///< Temperature output register high + + INT_CFG = 0x30, ///< Interrupt Configuration Register + INT_SRC = 0x31, ///< Interrupt Source Register + INT_THS_L = 0x33, ///< Interrupt Threshold Value Low + INT_THS_H = 0x34 ///< Interrupt Threshold Value High + }; + +public: + /// Control register 1 + enum class + Control1 : uint8_t + { + TEMP_EN = Bit7, ///< Temperature sensor enable + + OM1 = Bit6, ///< Operation mode bit 1 + OM0 = Bit5, ///< Operation mode bit 0 + + DO2 = Bit4, ///< Data output rate bit 2 + DO1 = Bit3, ///< Data output rate bit 1 + DO0 = Bit2, ///< Data output rate bit 0 + + FAST_ODR = Bit1, ///< Fast data acquisition (for rates higher than 80Hz) + ST = Bit0, ///< Self test enable + + }; + MODM_FLAGS8(Control1); + + /// Output Data Rates for the Magnetometer sensor + enum class + DataRate : uint8_t + { + Rate_0_625_Hz = 0x00, ///< 0.625 Hz + Rate_1_25_Hz = 0x04, ///< 1.25 Hz + Rate_2_5_Hz = 0x80, ///< 2.5 Hz + Rate_5_Hz = 0xC0, ///< 5 Hz + Rate_10_Hz = 0x10, ///< 10 Hz + Rate_20_Hz = 0x14, ///< 20 Hz + Rate_40_Hz = 0x18, ///< 40 Hz + Rate_80_Hz = 0x1C, ///< 80 Hz + Rate_115_Hz = 0x62, ///< 115 Hz + Rate_300_Hz = 0x42, ///< 300 Hz + Rate_569_Hz = 0x22, ///< 560 Hz + Rate_1000_Hz = 0x02, ///< 1000 Hz + }; + typedef modm::Configuration DataRate_t; + + /// Operation mode to be used with high speed data rates (>80 Hz) + enum class + HighSpeedModes : uint8_t + { + LowPower = 0x00, //< 1000 Hz + MediumPerformance = 0x01, //< 560 Hz + HighPerformance = 0x02, //< 300 Hz + UltraHighPerformance = 0x03 //< 115 Hz + }; + //This is the definition of the Highspeed modes for the Y and X axes. Z axis is in register 4 + typedef modm::Configuration HighSpeedModes_t; + +// ---------------------------------------------------------------------------- + + /// Control Register 2 + enum class + Control2 : uint8_t + { + FS1 = Bit6, ///< Full-Scale configuration bit 1 + FS0 = Bit5, ///< Full-Scale configuration bit 0 + REBOOT = Bit3, ///< Reboot memory content + SOFT_RST= Bit2, ///< Soft reset + }; + MODM_FLAGS8(Control2); + + /// Full scale of the magnetometer data output + enum class + Scale : uint8_t + { + Scale_4_gauss = 0x00, ///< 4 gauss + Scale_8_gauss = 0x40, ///< 8 gauss + Scale_12_gauss = 0x80, ///< 12 gauss + Scale_16_gauss = 0xC0, ///< 16 gauss + }; + typedef modm::Configuration Scale_t; + +// ---------------------------------------------------------------------------- + + /// Control Register 3 + enum class + Control3 : uint8_t + { + LP = Bit5, ///< Low Power Mode Enable + SIM = Bit2, ///< SPI Interface Mode (0 = 4 Wire(default), 1 = 3 Wire) + MD_1 = Bit1, ///< Sensor Operation Mode bit 1 + MD_0 = Bit0 ///< Sensor Operation Mode bit 0 + }; + MODM_FLAGS8(Control3); + + enum class + OperationMode : uint8_t + { + Off = 0x02, ///< Power down mode + Continous = 0x00, ///< Continous conversion mode + Single = 0x01, ///< Single shot mode + }; + typedef modm::Configuration OperationMode_t; + +// ---------------------------------------------------------------------------- + + /// Control Register 4 + enum class + Control4 : uint8_t + { + OMZ_1 = Bit3, ///< Operation Mode for z axis bit 1 + OMZ_0 = Bit2, ///< Operation Mode for z axis bit 1 + BLE = Bit1, ///< Bit order of the data output (0 = little endian, 1 = big endian) + }; + MODM_FLAGS8(Control4); + // The configuration for the high speed mode of the Z axis, see register 1 for Y and Z axes + typedef modm::Configuration HighSpeedModesZ_t; + +// ---------------------------------------------------------------------------- + + /// Control Register 5 + enum class + Control5 : uint8_t + { + FAST_READ = Bit7, ///< Fast read enable + BDU = Bit6, ///< Blocking read + }; + MODM_FLAGS8(Control5); + +// ---------------------------------------------------------------------------- + + /// STATUS is read-only + enum class + Status : uint8_t + { + ZYXOR = Bit7, ///< Overrun in Z,Y and X axis + ZOR = Bit6, ///< Overrun in Z axis + YOR = Bit5, ///< Overrun in Y axis + XOR = Bit4, ///< Overrun in X axis + ZYXDA = Bit3, ///< New data available in Z,Y and X axis + ZDA = Bit2, ///< New data available in Z axis + YDA = Bit1, ///< New data available in Y axis + XDA = Bit0 ///< New data available in X axis + + }; + MODM_FLAGS8(Status); + +protected: + // Conversion table to convert raw acceleration values to physical + static constexpr float convTable [4] = { + 0.00012207f, // Conversion for 4 gauss scale + 0.000244141f, // Conversion for 8 gauss scale + 0.000366211f, // Conversion for 12 gauss scale + 0.000488281f // Conversion for 16 gauss scale + }; + /// @endcond +}; // struct lis3mdl + +/** + * \tparam I2cMaster I2cMaster interface + * + * \author Benjamin Carrick + * \ingroup modm_driver_lis3mdl + */ +template < class I2cMaster > +class Lis3mdl : public lis3mdl, public Lis3TransportI2c +{ +public: + /** + * \brief Construct a driver object for the LIS3MDL chip + * + * \param address The I2C device address of the sensor + */ + Lis3mdl(uint8_t address = 0x1C); + + /** + * \brief Configures the sensor + * This method will setup the data rate and the scale with which the sensor will sample the data. + * + * \param rate The sample rate for the sensor + * \param scale The full scale of the output data + * \return Whether the configuration was successful + */ + modm::ResumableResult + configure(DataRate rate, Scale scale); + + /** + * @brief Sets the mode of the sensor + * + * @param mode The mode on which the magnetometer operates + * @return Whether the mode has been set + */ + modm::ResumableResult + setMode(OperationMode mode); + + + /** + * @brief Get the currently configured scale for the magnetometer + * + * @return The currently configured data scale + */ + Scale + getScale(); + + /** + * @brief Reads out the raw magnetometer into a given vector object. + * + * @param data A reference to a Vector3i object the data will be written to + * @return Whether the sensor data have been read + */ + modm::ResumableResult + readMagnetometerRaw(Vector3i& data); + + /** + * \brief Reads out the scaled magnetometer data in gauss + * + * \param data A reference to a Vector3f object the data will be written to + * \return Whether the sensor data have been read + */ + modm::ResumableResult + readMagnetometer(Vector3f& data); + +private: + /// The shadow for the control register 1 + Control1_t control1Shadow; + /// The shadow for the control register 2 + Control2_t control2Shadow; + /// The shadow for the control register 3 + Control3_t control3Shadow; + /// The shadow for the control register 4 + Control4_t control4Shadow; + /// The shadow for the control register 5 + Control5_t control5Shadow; + + /// The buffer for reading out the data registers + int16_t readBuffer[3]; + + /// Variable to hold the success of a transaction to the sensor + bool success; +}; + +} // namespace modm + +#include "lis3mdl_impl.hpp" + +#endif // MODM_LIS3MDL_HPP diff --git a/src/modm/driver/inertial/lis3mdl.lb b/src/modm/driver/inertial/lis3mdl.lb new file mode 100644 index 0000000000..c12d48b332 --- /dev/null +++ b/src/modm/driver/inertial/lis3mdl.lb @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Benjamin Carrick +# +# 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:lis3mdl" + module.description = """\ +# LIS3MDL Magnetic Sensor + +The LIS3MDL is an ultra-low-power high-performance three-axis magnetic sensor. +The sensor has user-selectable full scales of 4/ 8/ 12/ 16 gauss. +The LIS3MDL includes an I2C serial bus interface that supports standard and +fast mode 100 kHz and 400 kHz. +""" + +def prepare(module, options): + module.depends( + ":architecture:register", + ":driver:lis3.transport", + ":math:utils", + ":math:geometry") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/inertial" + env.copy("lis3mdl.hpp") + env.copy("lis3mdl_impl.hpp") diff --git a/src/modm/driver/inertial/lis3mdl_impl.hpp b/src/modm/driver/inertial/lis3mdl_impl.hpp new file mode 100644 index 0000000000..6e038c4e22 --- /dev/null +++ b/src/modm/driver/inertial/lis3mdl_impl.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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_LIS3MDL_HPP +# error "Don't include this file directly, use 'lis3mdl.hpp' instead!" +#endif + +#include + +// ---------------------------------------------------------------------------- +template < class I2cMaster > +modm::Lis3mdl::Lis3mdl( uint8_t address) +: Lis3TransportI2c(address) +{ + control1Shadow.value = 0x10; + control2Shadow.value = 0x00; + control3Shadow.value = 0x03; + control4Shadow.value = 0x00; + control5Shadow.value = 0x00; +} + +template < class I2cMaster > +modm::ResumableResult +modm::Lis3mdl::configure(DataRate rate, Scale scale) +{ + RF_BEGIN(); + + DataRate_t::set(control1Shadow,rate); + + // if FAST_ODR is requested, copy the highsped modes for the Z axis + if(control1Shadow & Control1::FAST_ODR) + { + HighSpeedModes hsMode = HighSpeedModes_t::get(control1Shadow); + HighSpeedModesZ_t::set(control4Shadow,hsMode); + } + + Scale_t::set(control2Shadow,scale); + + success = RF_CALL(this->write(static_cast(Register::CTRL1),control1Shadow.value)); + if (success) + { + success = RF_CALL(this->write(static_cast(Register::CTRL2),control2Shadow.value)); + if(success) + { + success = RF_CALL(this->write(static_cast(Register::CTRL4),control4Shadow.value)); + } + } + RF_END_RETURN(success); +} + + +template < class I2cMaster > +modm::ResumableResult +modm::Lis3mdl::setMode(OperationMode mode) +{ + RF_BEGIN(); + OperationMode_t::set(control3Shadow,mode); + RF_END_RETURN_CALL(this->write(static_cast(Register::CTRL3),control3Shadow.value)); +} + +template < class I2cMaster > +modm::lis3mdl::Scale +modm::Lis3mdl::getScale() +{ + return Scale_t::get(control2Shadow); +} + +template < class I2cMaster > +modm::ResumableResult +modm::Lis3mdl::readMagnetometerRaw(Vector3i& data) +{ + RF_BEGIN(); + success = RF_CALL(this->read(static_cast(Register::OUT_X_L),reinterpret_cast(readBuffer),6)); + if(success) + { + data.x = readBuffer[0]; + data.y = readBuffer[1]; + data.z = readBuffer[2]; + } + RF_END_RETURN(success); +} + + +template < class I2cMaster > +modm::ResumableResult +modm::Lis3mdl::readMagnetometer(Vector3f& data) +{ + RF_BEGIN(); + success = RF_CALL(this->read(static_cast(Register::OUT_X_L),reinterpret_cast(readBuffer),6)); + + if(success) + { + //use the bitmask of the scale to index the conversion table; + uint8_t scaleIndex = (static_cast(getScale()))>>5; + float conversionValue = convTable[scaleIndex]; + + data.x = static_cast(readBuffer[0]) * conversionValue; + data.y = static_cast(readBuffer[1]) * conversionValue; + data.z = static_cast(readBuffer[2]) * conversionValue; + } + + RF_END_RETURN(success); +} \ No newline at end of file From 0994a557a73b344673a105eaa2eaedb8a370418b Mon Sep 17 00:00:00 2001 From: Benjamin Carrick Date: Mon, 6 Jul 2020 21:33:11 +0200 Subject: [PATCH 074/135] Example for the LIS3MDL driver on the Nucleo F042K6 --- examples/nucleo_f042k6/lis3mdl/main.cpp | 73 ++++++++++++++++++++++ examples/nucleo_f042k6/lis3mdl/project.xml | 11 ++++ 2 files changed, 84 insertions(+) create mode 100644 examples/nucleo_f042k6/lis3mdl/main.cpp create mode 100644 examples/nucleo_f042k6/lis3mdl/project.xml diff --git a/examples/nucleo_f042k6/lis3mdl/main.cpp b/examples/nucleo_f042k6/lis3mdl/main.cpp new file mode 100644 index 0000000000..461a11c476 --- /dev/null +++ b/examples/nucleo_f042k6/lis3mdl/main.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020, Benjamin Carrick + * + * 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 + +using namespace Board; +using namespace std::chrono_literals; + +using I2cSda = GpioA10; +using I2cScl = GpioA9; + +int +main() +{ + Board::initialize(); + LedD13::setOutput(); + + MODM_LOG_INFO << "LIS3MDL demo" << modm::endl; + + I2cMaster1::connect(); + I2cMaster1::initialize(); + + // Create a sensor object with the adress of the sensor built onto the Pololu AltIMU-10 v5 + modm::Lis3mdl sensor(0x1E); + + // Turn on and configure the magnetometer + bool success = RF_CALL_BLOCKING(sensor.configure(modm::lis3mdl::DataRate::Rate_5_Hz, + modm::lis3mdl::Scale::Scale_8_gauss)); + + + if(!success) + { + MODM_LOG_INFO << "Sensor could not be configured!" << modm::endl; + } + + // Set the sensor to continous acquistion and turn on the temperature sensing + success = RF_CALL_BLOCKING(sensor.setMode(modm::lis3mdl::OperationMode::Continous)); + if(!success) + { + MODM_LOG_INFO << "Sensor could not be started!" << modm::endl; + } + + modm::Vector3f magVector; + + while (true) + { + //Read the sensor data and print it out + success = RF_CALL_BLOCKING(sensor.readMagnetometer(magVector)); + + if(success) + { + MODM_LOG_INFO << "Magnetic Vector:" << modm::endl; + MODM_LOG_INFO << "X: "<< magVector.x << " gauss" << modm::endl; + MODM_LOG_INFO << "Y: "<< magVector.y << " gauss" << modm::endl; + MODM_LOG_INFO << "Z: "<< magVector.z << " gauss" << modm::endl; + MODM_LOG_INFO << modm::endl; + } + else + { + MODM_LOG_INFO << "Sensor could not be read!" << modm::endl; + } + modm::delay(1s); + } + return 0; +} diff --git a/examples/nucleo_f042k6/lis3mdl/project.xml b/examples/nucleo_f042k6/lis3mdl/project.xml new file mode 100644 index 0000000000..e03311dcad --- /dev/null +++ b/examples/nucleo_f042k6/lis3mdl/project.xml @@ -0,0 +1,11 @@ + + modm:nucleo-f042k6 + + + + + modm:build:scons + modm:driver:lis3mdl + modm:platform:i2c:1 + + From f74e34255bc9371c5c429881388be5faf851843c Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Mon, 6 Jul 2020 10:05:14 +0200 Subject: [PATCH 075/135] [board] Make SAMD blink example work on Feather M0 --- examples/samd/blink/main.cpp | 5 +---- examples/samd/blink/project.xml | 2 +- src/modm/board/feather_m0/board.hpp | 12 +++++------- src/modm/board/feather_m0/board.xml | 1 + src/modm/board/feather_m0/module.lb | 3 ++- src/modm/board/samd21_mini/board.hpp | 1 + 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/samd/blink/main.cpp b/examples/samd/blink/main.cpp index 7472a0a5ec..fda59d82d5 100644 --- a/examples/samd/blink/main.cpp +++ b/examples/samd/blink/main.cpp @@ -17,12 +17,9 @@ int main() { Board::initialize(); - LedRx::set(); - while (1) { - LedTx::toggle(); - LedRx::toggle(); + LedD13::toggle(); modm::delay(500ms); #ifdef MODM_BOARD_HAS_LOGGER diff --git a/examples/samd/blink/project.xml b/examples/samd/blink/project.xml index 0c80983a8b..3bab6ef5a5 100644 --- a/examples/samd/blink/project.xml +++ b/examples/samd/blink/project.xml @@ -1,5 +1,5 @@ - modm:samd21-mini + modm:feather-m0 diff --git a/src/modm/board/feather_m0/board.hpp b/src/modm/board/feather_m0/board.hpp index 277da20516..41f955ed23 100644 --- a/src/modm/board/feather_m0/board.hpp +++ b/src/modm/board/feather_m0/board.hpp @@ -24,7 +24,8 @@ namespace Board using namespace modm::literals; /// samd21g18a running at 48MHz generated from the external 32.768 KHz crystal -struct SystemClock { +struct SystemClock +{ static constexpr uint32_t Frequency = 48_MHz; // static constexpr uint32_t Ahb = Frequency; // static constexpr uint32_t Apba = Frequency; @@ -63,11 +64,8 @@ struct SystemClock { } }; -// User LED (inverted, because connected to 3V3) -// using LedRed = GpioOutputA17; -// using Leds = SoftwareGpioPort< LedRed >; - -// using Button = GpioUnused; +using LedD13 = GpioInverted; +// using Leds = SoftwareGpioPort< LedD13 >; inline void initialize() @@ -75,7 +73,7 @@ initialize() SystemClock::enable(); SysTickTimer::initialize(); - // LedGreen::setOutput(modm::Gpio::Low); + LedD13::setOutput(modm::Gpio::Low); } } // Board namespace diff --git a/src/modm/board/feather_m0/board.xml b/src/modm/board/feather_m0/board.xml index c816c6f149..6f9820722c 100644 --- a/src/modm/board/feather_m0/board.xml +++ b/src/modm/board/feather_m0/board.xml @@ -7,6 +7,7 @@ + modm:board:feather-m0 diff --git a/src/modm/board/feather_m0/module.lb b/src/modm/board/feather_m0/module.lb index ad1fa5d59c..39bf56f595 100644 --- a/src/modm/board/feather_m0/module.lb +++ b/src/modm/board/feather_m0/module.lb @@ -29,7 +29,8 @@ def prepare(module, options): if not options[":target"].partname.startswith("samd21g18a"): return False - module.depends(":platform:gclk", ":platform:core", ":platform:clock") + module.depends(":platform:gclk", ":platform:core", ":platform:clock", + ":platform:gpio") return True def build(env): diff --git a/src/modm/board/samd21_mini/board.hpp b/src/modm/board/samd21_mini/board.hpp index 901662728d..456d46a29e 100644 --- a/src/modm/board/samd21_mini/board.hpp +++ b/src/modm/board/samd21_mini/board.hpp @@ -77,6 +77,7 @@ initialize() SystemClock::enable(); SysTickTimer::initialize(); + LedD13::setOutput(modm::Gpio::Low); LedTx::setOutput(modm::Gpio::Low); LedRx::setOutput(modm::Gpio::Low); } From 7b6f3425fd0e5b755151984a58f77a8647a42049 Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Wed, 8 Jul 2020 22:38:43 +0200 Subject: [PATCH 076/135] [gclk] Add system clock sources and flash latency --- src/modm/board/feather_m0/board.hpp | 28 ++++-- src/modm/platform/clock/sam/gclk.cpp.in | 96 ++++++++++++++++++++ src/modm/platform/clock/sam/gclk.hpp | 46 +++++++++- src/modm/platform/clock/sam/gclk_impl.hpp.in | 15 +++ 4 files changed, 175 insertions(+), 10 deletions(-) diff --git a/src/modm/board/feather_m0/board.hpp b/src/modm/board/feather_m0/board.hpp index 41f955ed23..41966d1354 100644 --- a/src/modm/board/feather_m0/board.hpp +++ b/src/modm/board/feather_m0/board.hpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016-2017, Sascha Schade * Copyright (c) 2017-2018, Niklas Hauser + * Copyright (c) 2020, Erik Henriksson * * This file is part of the modm project. * @@ -12,8 +13,10 @@ #pragma once +#include #include #include +#define MODM_BOARD_HAS_LOGGER using namespace modm::platform; @@ -52,18 +55,25 @@ struct SystemClock static bool inline enable() { - // GenericClockController::enableExternalCrystal(Frequency); - - // switch system clock to PLL output - // GenericClockController::enableSystemClock(ClockControl::SystemClockSource::Pll); - - // update frequencies for busy-wait delay functions - // GenericClockController::updateCoreFrequency(); - + GenericClockController::setFlashLatency(); + GenericClockController::initExternalCrystal(); + GenericClockController::initDFLL48MHz(); + GenericClockController::initOsc8MHz(); + GenericClockController::setSystemClock(ClockSource::DFLL48M); + GenericClockController::updateCoreFrequency(); return true; } }; + +namespace debug_logger +{ + using Rx = GpioInputA11; + using Tx = GpioOutputA10; +} + +using LoggerDevice = modm::IODeviceWrapper< Uart0, modm::IOBuffer::BlockIfFull >; + using LedD13 = GpioInverted; // using Leds = SoftwareGpioPort< LedD13 >; @@ -72,6 +82,8 @@ initialize() { SystemClock::enable(); SysTickTimer::initialize(); + Uart0::connect(); + Uart0::initialize(); LedD13::setOutput(modm::Gpio::Low); } diff --git a/src/modm/platform/clock/sam/gclk.cpp.in b/src/modm/platform/clock/sam/gclk.cpp.in index c53ba5b92d..1e6c79af36 100644 --- a/src/modm/platform/clock/sam/gclk.cpp.in +++ b/src/modm/platform/clock/sam/gclk.cpp.in @@ -1,6 +1,7 @@ /* * Copyright (c) 2013-2014, Kevin Läufer * Copyright (c) 2014-2017, Niklas Hauser + * Copyright (c) 2020, Erik Henriksson * * This file is part of the modm project. * @@ -12,9 +13,104 @@ #include "../device.hpp" #include "gclk.hpp" +#include "modm/math/units.hpp" namespace modm::platform { uint16_t modm_fastdata delay_fcpu_MHz(1); uint16_t modm_fastdata delay_ns_per_loop({{ loops * 1000 }}); } + +bool +modm::platform::GenericClockController::initOsc8MHz( + uint32_t waitCycles) +{ + SYSCTRL->OSC8M.bit.PRESC = 0x0; + SYSCTRL->OSC8M.bit.ONDEMAND = true; + SYSCTRL->OSC8M.bit.RUNSTDBY = false; + SYSCTRL->OSC8M.bit.ENABLE = true; + while (!SYSCTRL->PCLKSR.bit.OSC8MRDY && --waitCycles); + return waitCycles; +} + +bool +modm::platform::GenericClockController::initExternalCrystal( + uint32_t waitCycles) +{ + // Enable external crystal. + SYSCTRL->XOSC32K.reg = + SYSCTRL_XOSC32K_STARTUP( 0x6u ) | + SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K; + // separate call, as described in chapter 15.6.3 + SYSCTRL->XOSC32K.bit.ENABLE = 1; + while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY and --waitCycles); + + // Write Generic Clock Generator configuration + GCLK->GENCTRL.reg = + GCLK_GENCTRL_ID(uint32_t(ClockGenerator::ExternalCrystal32K)) | + GCLK_GENCTRL_SRC_XOSC32K | + GCLK_GENCTRL_IDC | + GCLK_GENCTRL_GENEN; + // Wait for synchronization. + while (GCLK->STATUS.bit.SYNCBUSY and --waitCycles); + return waitCycles; +} + +bool +modm::platform::GenericClockController::initDFLL48MHz( + uint32_t waitCycles) +{ + // // Put ExternalCrystal as source for the PLL + GCLK->CLKCTRL.reg = + GCLK_CLKCTRL_ID(uint32_t(ClockMux::DFLL48M)) | + GCLK_CLKCTRL_GEN(uint32_t(ClockGenerator::ExternalCrystal32K)) | + // GCLK_CLKCTRL_GEN_GCLK1 | + GCLK_CLKCTRL_CLKEN; + // Wait for synchronization. + while (GCLK->STATUS.bit.SYNCBUSY and --waitCycles); + + // Errata 1.2.1: Disable the OnDemand mode + SYSCTRL->DFLLCTRL.bit.ONDEMAND = 0; + // Wait for synchronization. + while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles); + + SYSCTRL->DFLLMUL.reg = + SYSCTRL_DFLLMUL_CSTEP( 31 ) | + SYSCTRL_DFLLMUL_FSTEP( 511 ) | + SYSCTRL_DFLLMUL_MUL((48_MHz + 32'768_Hz/2) / 32'768_Hz); + // Wait for synchronization. + while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles); + // Write full configuration to DFLL control register + SYSCTRL->DFLLCTRL.reg |= + SYSCTRL_DFLLCTRL_MODE | // Enable the closed loop mode + SYSCTRL_DFLLCTRL_WAITLOCK | // No output until DFLL is locked. + SYSCTRL_DFLLCTRL_QLDIS ; // Disable Quick lock + // Wait for synchronization. + while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles); + // Enable the DFLL + SYSCTRL->DFLLCTRL.bit.ENABLE = true; + // Wait for locks flags + while ( + !(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKC) || + !(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKF)); + // Wait for synchronization. + while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) + return waitCycles; +} + +bool +modm::platform::GenericClockController::setSystemClock( + ClockSource source, uint32_t waitCycles) +{ + GCLK->GENDIV.reg = + GCLK_GENDIV_ID(uint32_t(ClockGenerator::System)) | + GCLK_GENDIV_DIV(0u); + GCLK->GENCTRL.reg = + GCLK_GENCTRL_ID(uint32_t(ClockGenerator::System)) | + GCLK_GENCTRL_SRC(uint32_t(source)) | + GCLK_GENCTRL_IDC | + GCLK_GENCTRL_GENEN; + // Wait for synchronization. + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); + return waitCycles; +} diff --git a/src/modm/platform/clock/sam/gclk.hpp b/src/modm/platform/clock/sam/gclk.hpp index 82e57430bb..e4181a9c74 100644 --- a/src/modm/platform/clock/sam/gclk.hpp +++ b/src/modm/platform/clock/sam/gclk.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Ethan Slattery + * Copyright (c) 2020, Erik Henriksson * * This file is part of the modm project. * @@ -17,6 +18,13 @@ namespace modm::platform { +enum class +ClockSource : uint32_t +{ + OSC8M = GCLK_GENCTRL_SRC_OSC8M_Val, + DFLL48M = GCLK_GENCTRL_SRC_DFLL48M_Val +}; + /** * Clock management * @@ -25,9 +33,43 @@ namespace modm::platform class GenericClockController { public: - template< uint32_t Core_Hz > - void + template< uint32_t Core_Hz> + static uint32_t + setFlashLatency(); + + template< uint32_t Core_Hz > + static void updateCoreFrequency(); + + static bool + initOsc8MHz(uint32_t waitCycles = 2048); + + static bool + initExternalCrystal(uint32_t waitCycles = 2048); + + static bool + initDFLL48MHz(uint32_t waitCycles = 2048); + + static bool + setSystemClock( + ClockSource source = ClockSource::OSC8M, + uint32_t waitCycles = 2048); + +private: + enum class + ClockGenerator : uint32_t + { + System = 0, + ExternalCrystal32K = 1, + ULP32K = 2, + Internal8M = 3, + }; + + enum class + ClockMux : uint32_t + { + DFLL48M = 0, + }; }; } diff --git a/src/modm/platform/clock/sam/gclk_impl.hpp.in b/src/modm/platform/clock/sam/gclk_impl.hpp.in index d1cfc9dc00..722f82b27a 100644 --- a/src/modm/platform/clock/sam/gclk_impl.hpp.in +++ b/src/modm/platform/clock/sam/gclk_impl.hpp.in @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Ethan Slattery + * Copyright (c) 2020, Erik Henriksson * * This file is part of the modm project. * @@ -10,6 +11,7 @@ // ---------------------------------------------------------------------------- #include +#include "modm/math/units.hpp" namespace modm::platform { @@ -26,5 +28,18 @@ GenericClockController::updateCoreFrequency() delay_ns_per_loop = ::round({{loops}}000.f / (Core_Hz / 1'000'000)); } +template< uint32_t Core_Hz > +uint32_t +GenericClockController::setFlashLatency() +{ + // See table 41.11 (NVM Characteristics) in the datasheet + if (Core_Hz > 24_MHz) { + NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_HALF_Val; + } else { + NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_SINGLE_Val; + } + return Core_Hz; +} + } From 2ed785d401dacd434134815e312e59a46227561a Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Wed, 8 Jul 2020 22:39:21 +0200 Subject: [PATCH 077/135] [gpio] Add basic SAMD connect implementation --- src/modm/platform/gpio/sam/base.hpp.in | 22 ++++++- src/modm/platform/gpio/sam/module.lb | 56 ++++++++++++++++- src/modm/platform/gpio/sam/peripherals.hpp.in | 4 ++ src/modm/platform/gpio/sam/pin.hpp.in | 63 ++++++++++++++++++- src/modm/platform/gpio/sam/set.hpp.in | 4 +- src/modm/platform/gpio/sam/unused.hpp.in | 2 +- 6 files changed, 144 insertions(+), 7 deletions(-) diff --git a/src/modm/platform/gpio/sam/base.hpp.in b/src/modm/platform/gpio/sam/base.hpp.in index a2873316fd..da7cebec29 100644 --- a/src/modm/platform/gpio/sam/base.hpp.in +++ b/src/modm/platform/gpio/sam/base.hpp.in @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Niklas Hauser + * Copyright (c) 2020, Erik Henriksson * * This file is part of the modm project. * @@ -60,7 +61,7 @@ struct Gpio /// The Port a Gpio Pin is connected to. enum class - Port + PortName { %% for port in ports {{ port | upper }} = {{ port | modm.ord }}, @@ -72,9 +73,28 @@ struct Gpio Signal { BitBang, +%% for signal in all_signals + {{ signal }}, +%% endfor }; /// @endcond + enum class + SercomRxPad + { + Pad0 = 0x0, + Pad1 = 0x1, + Pad2 = 0x2, + Pad3 = 0x3, + }; + + enum class + SercomTxPad + { + Pad0 = 0x0, + Pad2 = 0x1, + }; + protected: /// @cond /// I/O Direction Mode values for this specific pin. diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb index 70c53fa480..0dff4f3499 100644 --- a/src/modm/platform/gpio/sam/module.lb +++ b/src/modm/platform/gpio/sam/module.lb @@ -3,6 +3,7 @@ # # Copyright (c) 2016-2018, Niklas Hauser # Copyright (c) 2017, Fabian Greif +# Copyright (c) 2020, Erik Henriksson # # This file is part of the modm project. # @@ -25,6 +26,30 @@ def port_ranges(gpios): ports.sort(key=lambda p: p["name"]) return ports +def translate(s): + name = "" + for part in s.split("_"): + name += part.capitalize() + return name + +def get_driver(s): + name = "None" + if "driver" in s: name = translate(s["driver"]); + if "instance" in s: name += translate(s["instance"]); + return name + +def get_name(s): + return translate(s["name"]) + +def extract_signals(signals): + afs = {} + for s in signals: + driver = get_driver(s) + name = get_name(s) + key = driver + name + afs[key] = {"driver": driver, "name": name, "af": s["af"]} + return afs + bprops = {} # ----------------------------------------------------------------------------- @@ -43,6 +68,34 @@ def prepare(module, options): ":math:utils") return True +def validate(env): + device = env[":target"] + driver = device.get_driver("gpio") + + all_signals = {} + for gpio in driver["gpio"]: + key = gpio["port"] + gpio["pin"] + raw_signals = gpio["signal"] if "signal" in gpio else [] + for signal in raw_signals: + if "af" in signal: + signal["af"] = [signal["af"]] + else: + signal["af"] = [] + gpio["signal"] = raw_signals + extracted_signals = extract_signals(raw_signals) + all_signals.update(extracted_signals) + signal_names = sorted(list(set(s["name"] for s in extracted_signals.values()))) + extracted_signals = OrderedDict([(name, sorted([s for s in extracted_signals.values() if s["name"] == name], key=lambda si:si["name"])) for name in signal_names]) + bprops[key] = { + "gpio": gpio, + "signals": extracted_signals + } + + all_peripherals = [s["driver"] for s in all_signals.values()] + + bprops["all_peripherals"] = sorted(list(set(all_peripherals))) + bprops["all_signals"] = sorted(list(set(s["name"] for s in all_signals.values()))) + def build(env): device = env[":target"] driver = device.get_driver("gpio") @@ -57,9 +110,8 @@ def build(env): for gpio in driver["gpio"]: po, pi = gpio["port"], gpio["pin"] - properties["gpio"] = gpio + properties.update(bprops[po + pi]) env.template("pin.hpp.in", "gpio_{}{}.hpp".format(po.upper(), pi)) - # env.template("port.hpp.in") # env.template("software_port.hpp.in") env.template("set.hpp.in") diff --git a/src/modm/platform/gpio/sam/peripherals.hpp.in b/src/modm/platform/gpio/sam/peripherals.hpp.in index f87f1b776f..6afb0f00dd 100644 --- a/src/modm/platform/gpio/sam/peripherals.hpp.in +++ b/src/modm/platform/gpio/sam/peripherals.hpp.in @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Niklas Hauser + * Copyright (c) 2020, Erik Henriksson * * This file is part of the modm project. * @@ -18,6 +19,9 @@ enum class Peripheral { BitBang, +%% for per in all_peripherals + {{ per }}, +%% endfor }; } diff --git a/src/modm/platform/gpio/sam/pin.hpp.in b/src/modm/platform/gpio/sam/pin.hpp.in index 44e38a0644..3361a57889 100644 --- a/src/modm/platform/gpio/sam/pin.hpp.in +++ b/src/modm/platform/gpio/sam/pin.hpp.in @@ -1,5 +1,6 @@ /* * Copyright (c) 2017-2018, Niklas Hauser + * Copyright (c) 2020, Erik Henriksson * * This file is part of the modm project. * @@ -45,7 +46,7 @@ public: using IO = Gpio{{ port ~ pin }}; using Type = Gpio{{ port ~ pin }}; static constexpr bool isInverted = false; - static constexpr Port port = Port::{{port}}; ///< Port name + static constexpr PortName port = PortName::{{port}}; ///< Port name static constexpr uint8_t pin = {{pin|int}}; ///< Pin number protected: @@ -107,6 +108,66 @@ public: inline static void disconnect() { setInput(InputType::Floating); } + template< Peripheral peripheral > + struct BitBang { static void connect(); + static_assert( + (peripheral == Peripheral::BitBang), + "Gpio{{ port ~ pin }}::BitBang only connects to software drivers!"); + }; + %% for signal_name, signal_group in signals.items() + template< Peripheral peripheral > + struct {{ signal_name }} { static void connect(); + static_assert( + %% for signal in signal_group + (peripheral == Peripheral::{{ signal.driver }}){% if loop.last %},{% else %} ||{% endif %} + %% endfor + "Gpio{{ port ~ pin }}::{{ signal_name }} only connects to {% for signal in signal_group %}{{signal.driver}}{% if not loop.last %} or {% endif %}{% endfor %}!"); + }; + %% endfor }; +/// @cond +template<> +struct Gpio{{ port ~ pin }}::BitBang +{ + using Gpio = Gpio{{ port ~ pin }}; + static constexpr Gpio::Signal Signal = Gpio::Signal::BitBang; + static constexpr int af = -1; + inline static void connect() {} +}; +%% for signal_group in signals.values() + %% for signal in signal_group +template<> +struct Gpio{{ port ~ pin }}::{{ signal.name }} +{ + using Gpio = Gpio{{ port ~ pin }}; + static constexpr Gpio::Signal Signal = Gpio::Signal::{{ signal.name }}; + %% if signal.driver.startswith("Sercom") + static constexpr Gpio::SercomRxPad RxPad = Gpio::SercomRxPad::{{ signal.name }}; + %% endif + %% if signal.driver.startswith("Sercom") and signal.name in ("Pad0", "Pad2") + static constexpr Gpio::SercomTxPad TxPad = Gpio::SercomTxPad::{{ signal.name }}; + %% endif + inline static void + connect() + { + //## FIXME: Support using alternate function (see stm32). + %% if signal.driver.startswith("Sercom") + PORT->Group[uint32_t(Gpio::port)].PINCFG[uint32_t(Gpio::pin)].bit.PMUXEN = true; + if (Gpio::pin & 1u) { + PORT->Group[uint32_t(Gpio::port)].PMUX[uint32_t(Gpio::pin) >> 1].reg |= PORT_PMUX_PMUXO_C; + } else { + PORT->Group[uint32_t(Gpio::port)].PMUX[uint32_t(Gpio::pin) >> 1].reg |= PORT_PMUX_PMUXE_C; + } + %% endif + } +}; + + %% endfor + +%% endfor + + +/// @endcond + } // namespace modm::platform diff --git a/src/modm/platform/gpio/sam/set.hpp.in b/src/modm/platform/gpio/sam/set.hpp.in index bc1d89bab7..c8df0bc327 100644 --- a/src/modm/platform/gpio/sam/set.hpp.in +++ b/src/modm/platform/gpio/sam/set.hpp.in @@ -28,14 +28,14 @@ class GpioSet : public Gpio protected: static constexpr uint32_t inverteds[{{ ports | length }}] = { %% for port in ports - (((Gpios::port == Port::{{port}} and Gpios::isInverted) ? Gpios::mask : 0) | ...), + (((Gpios::port == PortName::{{port}} and Gpios::isInverted) ? Gpios::mask : 0) | ...), %% endfor }; static constexpr uint32_t inverted(uint8_t id) { return inverteds[id]; } static constexpr uint32_t masks[{{ ports | length }}] = { %% for port in ports - (((Gpios::port == Port::{{port}}) ? Gpios::mask : 0) | ...), + (((Gpios::port == PortName::{{port}}) ? Gpios::mask : 0) | ...), %% endfor }; static constexpr uint32_t mask(uint8_t id) { return masks[id]; } diff --git a/src/modm/platform/gpio/sam/unused.hpp.in b/src/modm/platform/gpio/sam/unused.hpp.in index 1be9a4d1af..d6acaacdcf 100644 --- a/src/modm/platform/gpio/sam/unused.hpp.in +++ b/src/modm/platform/gpio/sam/unused.hpp.in @@ -29,7 +29,7 @@ public: using IO = GpioUnused; using Type = GpioUnused; static constexpr bool isInverted = false; - static constexpr Port port = Port(-1); + static constexpr PortName port = PortName(-1); static constexpr uint8_t pin = uint8_t(-1); static constexpr uint32_t mask = 0; From 04688bcbab6d914305c14eefab3f5a1d7fff786c Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Wed, 8 Jul 2020 22:39:35 +0200 Subject: [PATCH 078/135] [uart] Add basic SAMD UART driver --- src/modm/board/feather_m0/module.lb | 10 +- src/modm/platform/uart/sam/module.lb | 88 +++++++++++ src/modm/platform/uart/sam/uart.cpp.in | 109 +++++++++++++ src/modm/platform/uart/sam/uart.hpp.in | 101 ++++++++++++ src/modm/platform/uart/sam/uart_base.hpp.in | 49 ++++++ src/modm/platform/uart/sam/uart_hal.hpp.in | 124 +++++++++++++++ .../platform/uart/sam/uart_hal_impl.hpp.in | 144 ++++++++++++++++++ 7 files changed, 622 insertions(+), 3 deletions(-) create mode 100644 src/modm/platform/uart/sam/module.lb create mode 100644 src/modm/platform/uart/sam/uart.cpp.in create mode 100644 src/modm/platform/uart/sam/uart.hpp.in create mode 100644 src/modm/platform/uart/sam/uart_base.hpp.in create mode 100644 src/modm/platform/uart/sam/uart_hal.hpp.in create mode 100644 src/modm/platform/uart/sam/uart_hal_impl.hpp.in diff --git a/src/modm/board/feather_m0/module.lb b/src/modm/board/feather_m0/module.lb index 39bf56f595..9da7d07fe1 100644 --- a/src/modm/board/feather_m0/module.lb +++ b/src/modm/board/feather_m0/module.lb @@ -3,6 +3,7 @@ # # Copyright (c) 2016-2018, Niklas Hauser # Copyright (c) 2017, Fabian Greif +# Copyright (c) 2020, Erik Henriksson # # This file is part of the modm project. # @@ -30,13 +31,16 @@ def prepare(module, options): return False module.depends(":platform:gclk", ":platform:core", ":platform:clock", - ":platform:gpio") + ":platform:gpio", ":platform:uart:0", ":debug") return True def build(env): env.outbasepath = "modm/src/modm/board" - # env.substitutions = {"board_has_logger": False} - # env.template("../board.cpp.in", "board.cpp") + env.substitutions = { + "with_logger": True, + "with_assert": False, + } + env.template("../board.cpp.in", "board.cpp") env.copy('.') # env.outbasepath = "modm/openocd/modm/board/" diff --git a/src/modm/platform/uart/sam/module.lb b/src/modm/platform/uart/sam/module.lb new file mode 100644 index 0000000000..707b7383da --- /dev/null +++ b/src/modm/platform/uart/sam/module.lb @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# Copyright (c) 2017, Erik Henriksson +# +# 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/. +# ----------------------------------------------------------------------------- + +props = {} + +class Instance(Module): + def __init__(self, driver, instance): + self.driver = driver + self.instance = int(instance) + + def init(self, module): + module.name = str(self.instance) + module.description = "Instance {}".format(self.instance) + + def prepare(self, module, options): + module.depends(":platform:uart") + return True + + def build(self, env): + device = env[":target"].identifier + global props + props["id"] = self.instance + props["driver"] = self.driver + props["features"] = self.driver["feature"] if "feature" in self.driver else [] + props["uart_name"] = 'Uart' + props["sercom_name"] = self.driver["name"].capitalize() + + env.substitutions = props + env.outbasepath = "modm/src/modm/platform/uart" + + env.template("uart_hal.hpp.in", "uart_hal_{}.hpp".format(self.instance)) + env.template("uart_hal_impl.hpp.in", "uart_hal_{}_impl.hpp".format(self.instance)) + env.template("uart.hpp.in", "uart_{}.hpp".format(self.instance)) + env.template("uart.cpp.in", "uart_{}.cpp".format(self.instance)) + + props["instances"].append(self.instance) + + +def init(module): + module.name = ":platform:uart" + module.description = "Universal Asynchronous Receiver Transmitter (UART)" + +def prepare(module, options): + device = options[":target"] + if not (device.has_driver("sercom:*")): + return False + + module.depends( + ":architecture:atomic", + ":architecture:interrupt", + ":architecture:register", + ":architecture:uart", + ":math:algorithm", + ":cmsis:device", + ":platform:gpio") + + global props + drivers = options[":target"].get_all_drivers("sercom") + props["extended_driver"] = ("extended" in drivers[0]["type"]) + props["over8_sampling"] = ("feature" in drivers[0]) and ("over8" in drivers[0]["feature"]) + props["tcbgt"] = ("feature" in drivers[0]) and ("tcbgt" in drivers[0]["feature"]) + props["instances"] = [] + + for driver in drivers: + for instance in driver["instance"]: + module.add_submodule(Instance(driver, instance)) + + props["target"] = device.identifier + return True + +def build(env): + device = env[":target"] + + global props + env.substitutions = props + env.outbasepath = "modm/src/modm/platform/uart" + env.template("uart_base.hpp.in") diff --git a/src/modm/platform/uart/sam/uart.cpp.in b/src/modm/platform/uart/sam/uart.cpp.in new file mode 100644 index 0000000000..f50854773f --- /dev/null +++ b/src/modm/platform/uart/sam/uart.cpp.in @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2009, Martin Rosekeit + * Copyright (c) 2009-2011, Fabian Greif + * Copyright (c) 2010-2011, 2013, Georgi Grinshpun + * Copyright (c) 2013-2014, Sascha Schade + * Copyright (c) 2013, 2016, Kevin Läufer + * Copyright (c) 2013-2017, Niklas Hauser + * Copyright (c) 2018, Lucas Mösch + * Copyright (c) 2020, Erik Henriksson + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +%% set name = uart_name ~ id +%% set hal = uart_name ~ "Hal" ~ id + +#include "../device.hpp" +#include "uart_hal_{{ id }}.hpp" +#include "uart_{{ id }}.hpp" + +void +modm::platform::{{ name }}::writeBlocking(uint8_t data) +{ + while(!{{ hal }}::isTransmitRegisterEmpty()); + {{ hal }}::write(data); +} + +void +modm::platform::{{ name }}::writeBlocking(const uint8_t *data, std::size_t length) +{ + while (length-- != 0) { + writeBlocking(*data++); + } +} + +void +modm::platform::{{ name }}::flushWriteBuffer() +{ + return; +} + +bool +modm::platform::{{ name }}::write(uint8_t data) +{ + if({{ hal }}::isTransmitRegisterEmpty()) { + {{ hal }}::write(data); + return true; + } else { + return false; + } +} + +std::size_t +modm::platform::{{ name }}::write(const uint8_t *data, std::size_t length) +{ + uint32_t i = 0; + for (; i < length; ++i) + { + if (!write(*data++)) { + return i; + } + } + return i; +} + +bool +modm::platform::{{ name }}::isWriteFinished() +{ + return {{ hal }}::isTransmitRegisterEmpty(); +} + +std::size_t +modm::platform::{{ name }}::discardTransmitBuffer() +{ + return 0; +} + +bool +modm::platform::{{ name }}::read(uint8_t &data) +{ + if({{ hal }}::isReceiveRegisterNotEmpty()) { + {{ hal }}::read(data); + return true; + } else { + return false; + } +} + +std::size_t +modm::platform::{{ name }}::read(uint8_t *data, std::size_t length) +{ + (void)length; // avoid compiler warning + if(read(*data)) { + return 1; + } else { + return 0; + } +} + +std::size_t +modm::platform::{{ name }}::discardReceiveBuffer() +{ + return 0; +} diff --git a/src/modm/platform/uart/sam/uart.hpp.in b/src/modm/platform/uart/sam/uart.hpp.in new file mode 100644 index 0000000000..c57bf4fea9 --- /dev/null +++ b/src/modm/platform/uart/sam/uart.hpp.in @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2009-2012, Fabian Greif + * Copyright (c) 2010, Martin Rosekeit + * Copyright (c) 2011, Georgi Grinshpun + * Copyright (c) 2011, 2013-2017, Niklas Hauser + * Copyright (c) 2012, Sascha Schade + * Copyright (c) 2013, 2016, Kevin Läufer + * Copyright (c) 2020, Erik Henriksson + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +%% set hal = uart_name ~ "Hal" ~ id +%% set name = uart_name ~ id +%% set sercom = sercom_name ~ id + +#pragma once + +#include +#include +#include "uart_base.hpp" +#include "uart_hal_{{ id }}.hpp" + +namespace modm +{ + +namespace platform +{ + +/** + * Universal asynchronous receiver transmitter ({{ uart_name | upper ~ id }}) + * + * @author Kevin Laeufer + * @author Niklas Hauser + * @ingroup modm_platform_uart modm_platform_uart_{{id}} + */ +class {{ name }} : public UartBase, public ::modm::Uart +{ +public: + template< template class RxPin, template class TxPin > + static void + connect() + { + using Connector = GpioConnector; + using Rx = typename Connector::template GetSignal< Gpio::Signal::Pad3 >; + using Tx = typename Connector::template GetSignal< Gpio::Signal::Pad2 >; + static_assert(Connector::template IsValid, + "{{ name }}::connect() requires Rx on {{ sercom }}Pad3"); + static_assert(Connector::template IsValid, + "{{ name }}::connect() requires Tx on {{ sercom }}Pad2"); + Connector::connect(); + } + + template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) > + static void modm_always_inline + initialize(Parity parity = Parity::Disabled) + { + {{ hal }}::initialize(parity); + {{ hal }}::setTransmitterEnable(true); + {{ hal }}::setReceiverEnable(true); + } + + static void + writeBlocking(uint8_t data); + + static void + writeBlocking(const uint8_t *data, std::size_t length); + + static void + flushWriteBuffer(); + + static bool + write(uint8_t data); + + static std::size_t + write(const uint8_t *data, std::size_t length); + + static bool + isWriteFinished(); + + static std::size_t + discardTransmitBuffer(); + + static bool + read(uint8_t &data); + + static std::size_t + read(uint8_t *buffer, std::size_t length); + + static std::size_t + discardReceiveBuffer(); +}; + +} // namespace platform + +} // namespace modm diff --git a/src/modm/platform/uart/sam/uart_base.hpp.in b/src/modm/platform/uart/sam/uart_base.hpp.in new file mode 100644 index 0000000000..1ee1081452 --- /dev/null +++ b/src/modm/platform/uart/sam/uart_base.hpp.in @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013, Kevin Läufer + * Copyright (c) 2014-2018, Niklas Hauser + * Copyright (c) 2017, Sascha Schade + * Copyright (c) 2020, Erik Henriksson + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include "../device.hpp" +#include +#include +#include + + +namespace modm +{ +namespace platform +{ +/** + * Base class for the UART classes + * + * Provides some common enum that do not depend on the specific UART. + * + * @author Kevin Laeufer + * @ingroup modm_platform_uart + */ +class UartBase +{ +public: + enum class Parity : uint32_t + { + Even = 0, + Odd = 1, + Disabled = 2, + }; +}; + +} // namespace platform + +} // namespace modm diff --git a/src/modm/platform/uart/sam/uart_hal.hpp.in b/src/modm/platform/uart/sam/uart_hal.hpp.in new file mode 100644 index 0000000000..bb095f7e5c --- /dev/null +++ b/src/modm/platform/uart/sam/uart_hal.hpp.in @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013, Sascha Schade + * Copyright (c) 2013-2014, 2016, Kevin Läufer + * Copyright (c) 2013-2017, Niklas Hauser + * Copyright (c) 2020, Erik Henriksson + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include "../device.hpp" +#include "uart_base.hpp" +#include "modm/platform/core/peripherals.hpp" + +namespace modm +{ + +namespace platform +{ + +/** + * Universal asynchronous receiver transmitter ({{ uart_name ~ "Hal" ~ id }}) + * + * Not available on the low- and medium density devices. + * + * Very basic implementation that exposes more hardware features than + * the regular Usart classes. + * + * @author Kevin Laeufer + * @ingroup modm_platform_uart + */ +class {{ uart_name ~ "Hal" ~ id }} : public UartBase +{ +private: + /** + * Disable Parity or Enable Odd/Even Parity + * + * This method assumes 8 databits + 1 parity bit + * Remember to enable the clock but not the UART peripheral + * before setting the party. + */ + static inline void + setParity(const Parity parity); + +public: + /// Enables the hw module + static inline void + enable(); + + /// Disables the hw module + static inline void + disable(); + + /// RResets the hw module + static inline void + reset(); + + /** + * Initialize Uart HAL Peripheral + * + * Enables clocks, the UART peripheral (but neither TX nor RX) + * Sets baudrate and parity. + */ + template< class SystemClock, baudrate_t baudrate > + static void + initialize( Parity parity = Parity::Disabled); + + /** + * \brief Write a single byte to the transmit register + * + * @warning This method does NOT do any sanity checks!! + * It is your responsibility to check if the register + * is empty! + */ + static inline void + write(uint8_t data); + + /** + * Saves the value of the receive register to data + * + * @warning This method does NOT do any sanity checks!! + * It is your responsibility to check if the register + * contains something useful! + */ + static inline void + read(uint8_t &data); + + /// Enable/Disable Transmitter + static inline void + setTransmitterEnable(const bool enable); + + /// Enable/Disable Receiver + static inline void + setReceiverEnable(bool enable); + + /// Set the UE (USART enable) bit + static inline void + enableOperation(); + + /// Clear the UE (USART enable) bit + static inline void + disableOperation(); + + /// Returns true if data has been received + static inline bool + isReceiveRegisterNotEmpty(); + + /// Returns true if data can be written + static inline bool + isTransmitRegisterEmpty(); +}; + +} // namespace platform + +} // namespace modm + +#include "uart_hal_{{ id }}_impl.hpp" diff --git a/src/modm/platform/uart/sam/uart_hal_impl.hpp.in b/src/modm/platform/uart/sam/uart_hal_impl.hpp.in new file mode 100644 index 0000000000..c8de01973f --- /dev/null +++ b/src/modm/platform/uart/sam/uart_hal_impl.hpp.in @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013-2014, 2016, Kevin Läufer + * Copyright (c) 2013-2017, Niklas Hauser + * Copyright (c) 2017, Fabian Greif + * Copyright (c) 2017, Sascha Schade + * Copyright (c) 2018, Christopher Durand + * Copyright (c) 2018, Lucas Mösch + * Copyright (c) 2020, Erik Henriksson + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +%% set name = uart_name ~ "Hal" ~ id +%% set sercom = sercom_name ~ id +%% set peripheral = sercom | upper + +// ---------------------------------------------------------------------------- +void +modm::platform::{{ name }}::setParity(const Parity parity) +{ + if (parity != Parity::Disabled) { + {{ peripheral }}->USART.CTRLA.bit.FORM = 0x1; // Enable parity. + {{ peripheral }}->USART.CTRLB.bit.PMODE = uint32_t(parity); + } else { + {{ peripheral }}->USART.CTRLA.bit.FORM = 0x0; // Disable parity. + } +} + +void +modm::platform::{{ name }}::enable() +{ + {{ peripheral }}->USART.CTRLA.bit.ENABLE = true; // Uart Enable + while({{ peripheral }}->USART.SYNCBUSY.bit.ENABLE); // Wait for sync. +} + +void +modm::platform::{{ name }}::disable() +{ + {{ peripheral }}->USART.CTRLA.bit.ENABLE = false; // Uart Disable + while({{ peripheral }}->USART.SYNCBUSY.bit.ENABLE); // Wait for sync. +} + +void +modm::platform::{{ name }}::reset() +{ + {{ peripheral }}->USART.CTRLA.bit.SWRST = true; // Uart Reset + while({{ peripheral }}->USART.SYNCBUSY.bit.SWRST); // Wait for sync. +} + +template +void modm_always_inline +modm::platform::{{ name }}::initialize(Parity parity) +{ + // Enable peripheral clock in power manager. + PM->APBCMASK.bit.{{ peripheral }}_ = true; + // Init clock using GCLK0 as source. + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_{{ peripheral }}_CORE | + GCLK_CLKCTRL_GEN_GCLK0 | + GCLK_CLKCTRL_CLKEN ; + while (GCLK->STATUS.bit.SYNCBUSY); + // Reset USART + reset(); + // Set clock mode internal + {{ peripheral }}->USART.CTRLA.bit.MODE = 0x1; + // Set asynchronous mode + {{ peripheral }}->USART.CTRLA.bit.CMODE = 0x0; + // Set rx/tx pins + {{ peripheral }}->USART.CTRLA.bit.RXPO = 0x3; // Pad3 + {{ peripheral }}->USART.CTRLA.bit.TXPO = 0x1; // Pad2; + // Set character size to 8 bits + {{ peripheral }}->USART.CTRLB.bit.CHSIZE = 0x0; + // Set Data order to LSB first + {{ peripheral }}->USART.CTRLA.bit.DORD = 0x1; + // Set parity + setParity(parity); + // Set stop bits to 1 + {{ peripheral }}->USART.CTRLB.bit.SBMODE = 0x0; + // fractional baud rate + {{ peripheral }}->USART.CTRLA.bit.SAMPR = 0x1; + // set baudrate (multiply by 8 to calc fractional part) + uint16_t baudTimes8 = (SystemClock::Frequency * 8) / (16 * baudrate); + SERCOM0->USART.BAUD.FRAC.BAUD = baudTimes8 / 8; + SERCOM0->USART.BAUD.FRAC.FP = baudTimes8 % 8; + // uint16_t baud = SystemClock::Frequency / (16 * baudrate); + // SERCOM0->USART.BAUD.reg = baud; + setReceiverEnable(true); + setTransmitterEnable(true); + // Enable USART + enable(); +} + + +void +modm::platform::{{ name }}::write(uint8_t data) +{ + {{ peripheral }}->USART.DATA.reg = data; +} + +void +modm::platform::{{ name }}::read(uint8_t &data) +{ + data = {{ peripheral }}->USART.DATA.reg; +} + +void +modm::platform::{{ name }}::setTransmitterEnable(const bool enable) +{ + {{ peripheral }}->USART.CTRLB.bit.TXEN = enable; +} + +void +modm::platform::{{ name }}::setReceiverEnable(bool enable) +{ + {{ peripheral }}->USART.CTRLB.bit.RXEN = enable; +} + +void +modm::platform::{{ name }}::enableOperation() +{ + enable(); +} + +void +modm::platform::{{ name }}::disableOperation() +{ + disable(); +} + +bool +modm::platform::{{ name }}::isReceiveRegisterNotEmpty() +{ + return {{ peripheral }}->USART.INTFLAG.bit.RXC; +} + +bool +modm::platform::{{ name }}::isTransmitRegisterEmpty() +{ + return {{ peripheral }}->USART.INTFLAG.bit.DRE; +} From 9f75518df25be5d6f10344b1d269964b286702f5 Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Tue, 14 Jul 2020 18:20:06 +0200 Subject: [PATCH 079/135] [gpio] Make inputs work for SAM and add Feather M0 pin defs --- examples/samd/blink/main.cpp | 2 +- ext/modm-devices | 2 +- src/modm/board/feather_m0/board.hpp | 52 ++++++++++++++++++++------- src/modm/board/feather_m0/board.xml | 2 +- src/modm/platform/gpio/sam/module.lb | 12 +++---- src/modm/platform/gpio/sam/pin.hpp.in | 15 ++------ src/modm/platform/gpio/sam/set.hpp.in | 29 ++++++--------- 7 files changed, 61 insertions(+), 53 deletions(-) diff --git a/examples/samd/blink/main.cpp b/examples/samd/blink/main.cpp index fda59d82d5..cf4f826f7c 100644 --- a/examples/samd/blink/main.cpp +++ b/examples/samd/blink/main.cpp @@ -19,7 +19,7 @@ main() Board::initialize(); while (1) { - LedD13::toggle(); + Led::toggle(); modm::delay(500ms); #ifdef MODM_BOARD_HAS_LOGGER diff --git a/ext/modm-devices b/ext/modm-devices index fdb70e01e8..961649d13d 160000 --- a/ext/modm-devices +++ b/ext/modm-devices @@ -1 +1 @@ -Subproject commit fdb70e01e8a5cd71a6293e5cd22900a89bb541dc +Subproject commit 961649d13dc9b9ad52d7b5dcfb7e5f12b1ff4fde diff --git a/src/modm/board/feather_m0/board.hpp b/src/modm/board/feather_m0/board.hpp index 41966d1354..c5644e8214 100644 --- a/src/modm/board/feather_m0/board.hpp +++ b/src/modm/board/feather_m0/board.hpp @@ -24,7 +24,42 @@ using namespace modm::platform; /// @ingroup modm_board_feather_m0 namespace Board { - using namespace modm::literals; + +using namespace modm::literals; + +using ARef = GpioA03; +using A0 = GpioA02; +using A1 = GpioB08; +using A2 = GpioB09; +using A3 = GpioA04; +using A4 = GpioA05; +using A5 = GpioB02; + +using Sck = GpioB11; +using Mosi = GpioB10; +using Miso = GpioA12; + +using Rx = GpioA11; +using Tx = GpioA10; + +using D13 = GpioA17; +using D12 = GpioA19; +using D11 = GpioA16; +using D10 = GpioA18; +using D9 = GpioA07; +using D6 = GpioA20; +using D5 = GpioA15; + +using Sda = GpioA23; +using Scl = GpioA22; + +// For RFM69 / LoRa boards +using RadioRst = GpioA08; +using RadioIrq = GpioA09; +using RadioCs = GpioA06; + +// This is the red LED by the USB jack. +using Led = D13; /// samd21g18a running at 48MHz generated from the external 32.768 KHz crystal struct SystemClock @@ -65,16 +100,7 @@ struct SystemClock } }; - -namespace debug_logger -{ - using Rx = GpioInputA11; - using Tx = GpioOutputA10; -} - using LoggerDevice = modm::IODeviceWrapper< Uart0, modm::IOBuffer::BlockIfFull >; - -using LedD13 = GpioInverted; // using Leds = SoftwareGpioPort< LedD13 >; inline void @@ -82,10 +108,10 @@ initialize() { SystemClock::enable(); SysTickTimer::initialize(); - Uart0::connect(); - Uart0::initialize(); + Uart0::connect(); + Uart0::initialize(); - LedD13::setOutput(modm::Gpio::Low); + Led::setOutput(modm::Gpio::Low); } } // Board namespace diff --git a/src/modm/board/feather_m0/board.xml b/src/modm/board/feather_m0/board.xml index 6f9820722c..59ed433d3e 100644 --- a/src/modm/board/feather_m0/board.xml +++ b/src/modm/board/feather_m0/board.xml @@ -6,7 +6,7 @@ - + diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb index 0dff4f3499..1f4c0a4ef8 100644 --- a/src/modm/platform/gpio/sam/module.lb +++ b/src/modm/platform/gpio/sam/module.lb @@ -47,7 +47,11 @@ def extract_signals(signals): driver = get_driver(s) name = get_name(s) key = driver + name - afs[key] = {"driver": driver, "name": name, "af": s["af"]} + afs[key] = { + "driver": driver, + "name": name, + "function": s["function"] if len(s["function"]) == 1 else None + } return afs bprops = {} @@ -76,13 +80,9 @@ def validate(env): for gpio in driver["gpio"]: key = gpio["port"] + gpio["pin"] raw_signals = gpio["signal"] if "signal" in gpio else [] - for signal in raw_signals: - if "af" in signal: - signal["af"] = [signal["af"]] - else: - signal["af"] = [] gpio["signal"] = raw_signals extracted_signals = extract_signals(raw_signals) + gpio["pmux"] = "PMUXO" if int(gpio["pin"]) % 2 else "PMUXE" all_signals.update(extracted_signals) signal_names = sorted(list(set(s["name"] for s in extracted_signals.values()))) extracted_signals = OrderedDict([(name, sorted([s for s in extracted_signals.values() if s["name"] == name], key=lambda si:si["name"])) for name in signal_names]) diff --git a/src/modm/platform/gpio/sam/pin.hpp.in b/src/modm/platform/gpio/sam/pin.hpp.in index 3361a57889..f56bd61416 100644 --- a/src/modm/platform/gpio/sam/pin.hpp.in +++ b/src/modm/platform/gpio/sam/pin.hpp.in @@ -142,23 +142,12 @@ struct Gpio{{ port ~ pin }}::{{ signal.name }} { using Gpio = Gpio{{ port ~ pin }}; static constexpr Gpio::Signal Signal = Gpio::Signal::{{ signal.name }}; - %% if signal.driver.startswith("Sercom") - static constexpr Gpio::SercomRxPad RxPad = Gpio::SercomRxPad::{{ signal.name }}; - %% endif - %% if signal.driver.startswith("Sercom") and signal.name in ("Pad0", "Pad2") - static constexpr Gpio::SercomTxPad TxPad = Gpio::SercomTxPad::{{ signal.name }}; - %% endif inline static void connect() { - //## FIXME: Support using alternate function (see stm32). - %% if signal.driver.startswith("Sercom") + %% if signal.function PORT->Group[uint32_t(Gpio::port)].PINCFG[uint32_t(Gpio::pin)].bit.PMUXEN = true; - if (Gpio::pin & 1u) { - PORT->Group[uint32_t(Gpio::port)].PMUX[uint32_t(Gpio::pin) >> 1].reg |= PORT_PMUX_PMUXO_C; - } else { - PORT->Group[uint32_t(Gpio::port)].PMUX[uint32_t(Gpio::pin) >> 1].reg |= PORT_PMUX_PMUXE_C; - } + PORT->Group[uint32_t(Gpio::port)].PMUX[uint32_t(Gpio::pin) >> 1].reg = PORT_PMUX_{{ gpio.pmux }}_{{ signal.function | upper }}; %% endif } }; diff --git a/src/modm/platform/gpio/sam/set.hpp.in b/src/modm/platform/gpio/sam/set.hpp.in index c8df0bc327..d763eb8b7d 100644 --- a/src/modm/platform/gpio/sam/set.hpp.in +++ b/src/modm/platform/gpio/sam/set.hpp.in @@ -44,6 +44,8 @@ protected: for (const auto &m: masks) r += (m) ? 1 : 0; return r; } + static constexpr PORT_PINCFG_Type *configs[] = + {(PORT_PINCFG_Type*) &PORT->Group[uint32_t(Gpios::port)].PINCFG[uint32_t(Gpios::pin)] ...}; public: static constexpr uint8_t width = sizeof...(Gpios); static constexpr uint8_t number_of_ports = numberOfPorts(); @@ -61,20 +63,13 @@ public: setOutput(); } - static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) + static void setOutput(OutputType, OutputSpeed) { - configure(type, speed); setOutput(); } static void configure(OutputType , OutputSpeed = OutputSpeed::MHz50) { - %% for port, id in ports.items() - if constexpr (mask({{id}})) { - // GPIO{{port}}->OSPEEDR = (GPIO{{port}}->OSPEEDR & ~mask2({{id}})) | (i(speed) * mask2({{id}}, 0b01)); - // GPIO{{port}}->OTYPER = (GPIO{{port}}->OTYPER & ~mask({{id}})) | (i(type) ? mask({{id}}) : 0); - } - %% endfor } static void setInput() @@ -92,20 +87,18 @@ public: setInput(); } - static void setAnalogInput() + static void configure(InputType type) { - %% for port, id in ports.items() - // if constexpr (mask({{id}})) GPIO{{port}}->MODER |= mask2({{id}}, i(Mode::Analog)); - %% endfor + for (PORT_PINCFG_Type* config : configs) { + set(type == InputType::PullUp); + config->reg = + PORT_PINCFG_INEN | + (type != InputType::Floating) << PORT_PINCFG_PULLEN_Pos; + } } - static void configure(InputType) + static void setAnalogInput() { - %% for port, id in ports.items() - if constexpr (mask({{id}})) { - // GPIO{{port}}->PUPDR = (GPIO{{port}}->PUPDR & ~mask2({{id}})) | (i(type) * mask2({{id}}, 0b01)); - } - %% endfor } static void set() From 5f71fb4bc328d78f9e9585a78b8039affcd7176c Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Tue, 14 Jul 2020 18:28:47 +0200 Subject: [PATCH 080/135] synchronize the modm documentation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ada327031..f3282a1547 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ git clone --recurse-submodules https://github.com/modm-io/modm.git ## Targets -modm can generate code for 530 AVR, +modm can generate code for 506 AVR, 163 SAM and 1959 STM32 devices, however, there are different levels of support and testing. @@ -82,7 +82,7 @@ STM32 devices, however, there are different levels of support and testing. | STM32F2 | ★★★★ | STM32F3 | ★★★★★ | STM32F4 | ★★★★★ | | STM32F7 | ★★★★ | STM32L1 | ★★★★ | STM32L4 | ★★★★ | | STM32L4+ | ★★★★ | STM32G0 | ★★★★ | STM32G4 | ★★★★ | -| SAMD21 | ★ | | | | | +| SAMD21 | ★★ | | | | |
From fecc5e068abad78937a2f35d53859e4359e34f6a Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Mon, 20 Jul 2020 12:14:27 +0200 Subject: [PATCH 081/135] [board] Enable modm_abandon reporter on Feather-M0 --- src/modm/board/feather_m0/board.hpp | 2 +- src/modm/board/feather_m0/module.lb | 2 +- src/modm/platform/gpio/sam/module.lb | 1 + .../platform/gpio/sam/software_port.hpp.in | 90 +++++++++++++++++++ 4 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 src/modm/platform/gpio/sam/software_port.hpp.in diff --git a/src/modm/board/feather_m0/board.hpp b/src/modm/board/feather_m0/board.hpp index c5644e8214..4a669e354d 100644 --- a/src/modm/board/feather_m0/board.hpp +++ b/src/modm/board/feather_m0/board.hpp @@ -101,7 +101,7 @@ struct SystemClock }; using LoggerDevice = modm::IODeviceWrapper< Uart0, modm::IOBuffer::BlockIfFull >; -// using Leds = SoftwareGpioPort< LedD13 >; +using Leds = SoftwareGpioPort< Led >; inline void initialize() diff --git a/src/modm/board/feather_m0/module.lb b/src/modm/board/feather_m0/module.lb index 9da7d07fe1..0449076ed2 100644 --- a/src/modm/board/feather_m0/module.lb +++ b/src/modm/board/feather_m0/module.lb @@ -38,7 +38,7 @@ def build(env): env.outbasepath = "modm/src/modm/board" env.substitutions = { "with_logger": True, - "with_assert": False, + "with_assert": True, } env.template("../board.cpp.in", "board.cpp") env.copy('.') diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb index 1f4c0a4ef8..39deee0e8e 100644 --- a/src/modm/platform/gpio/sam/module.lb +++ b/src/modm/platform/gpio/sam/module.lb @@ -116,6 +116,7 @@ def build(env): # env.template("software_port.hpp.in") env.template("set.hpp.in") env.template("base.hpp.in") + env.template("software_port.hpp.in") env.template("unused.hpp.in") env.copy("../common/inverted.hpp", "inverted.hpp") diff --git a/src/modm/platform/gpio/sam/software_port.hpp.in b/src/modm/platform/gpio/sam/software_port.hpp.in new file mode 100644 index 0000000000..6e1a06a985 --- /dev/null +++ b/src/modm/platform/gpio/sam/software_port.hpp.in @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_GPIO_SOFTWARE_PORT_HPP +#define MODM_STM32_GPIO_SOFTWARE_PORT_HPP + +#include "set.hpp" +#include + +namespace modm +{ + +namespace platform +{ + +/** + * Create an up to 32-bit port from arbitrary pins. + * + * This class optimizes the data type for the `read()` and `write()` methods. + * Supplying up to 8 Gpios will use `uint8_t`, up to 16 Gpios `uint16_t` and + * up to 32 Gpios `uint32_t`. + * + * @note Since the bit order is explicitly given by the order of template arguments, + * this class only supports `DataOrder::Normal`. + * If you need reverse bit order, reverse the order of `Gpios`! + * + * @tparam Gpios Up to 32 GpioIO classes, ordered MSB to LSB + * + * @author Niklas Hauser + * @ingroup modm_platform_gpio + */ +template< class... Gpios > +class SoftwareGpioPort : public ::modm::GpioPort, public GpioSet +{ + using Set = GpioSet; +public: + using Set::width; + static_assert(width <= 32, "Only a maximum of 32 pins are supported by this Port!"); + using PortType = std::conditional_t< (width > 8), + std::conditional_t< (width > 16), + uint32_t, + uint16_t >, + uint8_t >; + static constexpr DataOrder getDataOrder() + { return ::modm::GpioPort::DataOrder::Normal; } + +protected: + static constexpr int8_t shift_masks[{{ ports | length }}][width] = { +%% for port in ports + {(Gpios::port == Set::Port::{{port}} ? Gpios::pin : -1)...}, +%% endfor + }; + static constexpr int8_t shift_mask(uint8_t id, uint8_t pos) { return shift_masks[id][width - 1 - pos]; } + using Set::mask; + using Set::inverted; + +public: + static PortType isSet() + { + PortType r{0}; + // FIXME: Implement + return r; + } + + static void write(PortType data) + { + // FIXME: Implement + } + + static PortType read() + { + PortType r{0}; + // FIXME: Implement + return r; + } +}; + +} // namespace platform + +} // namespace modm + +#endif // MODM_STM32_GPIO_SOFTWARE_PORT_HPP From bad229aa257871561573e63759cd4c56137ac3ba Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Mon, 20 Jul 2020 12:17:35 +0200 Subject: [PATCH 082/135] [clock] Connect peripheral clocks via API --- src/modm/platform/clock/sam/gclk.cpp.in | 6 +- src/modm/platform/clock/sam/gclk.hpp | 65 ++++++++++++++++--- src/modm/platform/clock/sam/gclk_impl.hpp.in | 10 +++ src/modm/platform/uart/sam/module.lb | 4 +- src/modm/platform/uart/sam/uart_hal.hpp.in | 1 + .../platform/uart/sam/uart_hal_impl.hpp.in | 5 +- 6 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/modm/platform/clock/sam/gclk.cpp.in b/src/modm/platform/clock/sam/gclk.cpp.in index 1e6c79af36..c71f750c5b 100644 --- a/src/modm/platform/clock/sam/gclk.cpp.in +++ b/src/modm/platform/clock/sam/gclk.cpp.in @@ -40,7 +40,9 @@ modm::platform::GenericClockController::initExternalCrystal( // Enable external crystal. SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP( 0x6u ) | - SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K; + SYSCTRL_XOSC32K_XTALEN | + SYSCTRL_XOSC32K_RUNSTDBY | + SYSCTRL_XOSC32K_EN32K; // separate call, as described in chapter 15.6.3 SYSCTRL->XOSC32K.bit.ENABLE = 1; while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY and --waitCycles); @@ -94,7 +96,7 @@ modm::platform::GenericClockController::initDFLL48MHz( !(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKC) || !(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKF)); // Wait for synchronization. - while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) + while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles); return waitCycles; } diff --git a/src/modm/platform/clock/sam/gclk.hpp b/src/modm/platform/clock/sam/gclk.hpp index e4181a9c74..525cca21a9 100644 --- a/src/modm/platform/clock/sam/gclk.hpp +++ b/src/modm/platform/clock/sam/gclk.hpp @@ -25,6 +25,59 @@ ClockSource : uint32_t DFLL48M = GCLK_GENCTRL_SRC_DFLL48M_Val }; +enum class +ClockGenerator : uint32_t +{ + System = 0, + ExternalCrystal32K = 1, + ULP32K = 2, + Internal8M = 3, +}; + +enum class ClockPeripheral : uint32_t { + Dfll48 = GCLK_CLKCTRL_ID_DFLL48_Val, + Fdpll = GCLK_CLKCTRL_ID_FDPLL_Val, + Fdpll32K = GCLK_CLKCTRL_ID_FDPLL32K_Val, + Wdt = GCLK_CLKCTRL_ID_WDT_Val, + Rtc = GCLK_CLKCTRL_ID_RTC_Val, + Eic = GCLK_CLKCTRL_ID_EIC_Val, + Usb = GCLK_CLKCTRL_ID_USB_Val, + Evsys0 = GCLK_CLKCTRL_ID_EVSYS_0_Val, + Evsys1 = GCLK_CLKCTRL_ID_EVSYS_1_Val, + Evsys2 = GCLK_CLKCTRL_ID_EVSYS_2_Val, + Evsys3 = GCLK_CLKCTRL_ID_EVSYS_3_Val, + Evsys4 = GCLK_CLKCTRL_ID_EVSYS_4_Val, + Evsys5 = GCLK_CLKCTRL_ID_EVSYS_5_Val, + Evsys6 = GCLK_CLKCTRL_ID_EVSYS_6_Val, + Evsys7 = GCLK_CLKCTRL_ID_EVSYS_7_Val, + Evsys8 = GCLK_CLKCTRL_ID_EVSYS_8_Val, + Evsys9 = GCLK_CLKCTRL_ID_EVSYS_9_Val, + Evsys10 = GCLK_CLKCTRL_ID_EVSYS_10_Val, + Evsys11 = GCLK_CLKCTRL_ID_EVSYS_11_Val, + SercomXSlow = GCLK_CLKCTRL_ID_SERCOMX_SLOW_Val, + Sercom0 = GCLK_CLKCTRL_ID_SERCOM0_CORE_Val, + Sercom1 = GCLK_CLKCTRL_ID_SERCOM1_CORE_Val, + Sercom2 = GCLK_CLKCTRL_ID_SERCOM2_CORE_Val, + Sercom3 = GCLK_CLKCTRL_ID_SERCOM3_CORE_Val, + Sercom4 = GCLK_CLKCTRL_ID_SERCOM4_CORE_Val, + Sercom5 = GCLK_CLKCTRL_ID_SERCOM5_CORE_Val, + Tcc0 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val, + Tcc1 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val, + Tcc2 = GCLK_CLKCTRL_ID_TCC2_TC3_Val, + Tc3 = GCLK_CLKCTRL_ID_TCC2_TC3_Val, + Tc4 = GCLK_CLKCTRL_ID_TC4_TC5_Val, + Tc5 = GCLK_CLKCTRL_ID_TC4_TC5_Val, + Tc6 = GCLK_CLKCTRL_ID_TC6_TC7_Val, + Tc7 = GCLK_CLKCTRL_ID_TC6_TC7_Val, + Adc = GCLK_CLKCTRL_ID_ADC_Val, + AcDig = GCLK_CLKCTRL_ID_AC_DIG_Val, + AcAna = GCLK_CLKCTRL_ID_AC_ANA_Val, + Dac = GCLK_CLKCTRL_ID_DAC_Val, + Ptc = GCLK_CLKCTRL_ID_PTC_Val, + I2s0 = GCLK_CLKCTRL_ID_I2S_0_Val, + I2s1 = GCLK_CLKCTRL_ID_I2S_1_Val +}; + /** * Clock management * @@ -55,15 +108,11 @@ class GenericClockController ClockSource source = ClockSource::OSC8M, uint32_t waitCycles = 2048); + template< ClockPeripheral peripheral > + static void + connect(ClockGenerator clockGen); + private: - enum class - ClockGenerator : uint32_t - { - System = 0, - ExternalCrystal32K = 1, - ULP32K = 2, - Internal8M = 3, - }; enum class ClockMux : uint32_t diff --git a/src/modm/platform/clock/sam/gclk_impl.hpp.in b/src/modm/platform/clock/sam/gclk_impl.hpp.in index 722f82b27a..b0ab9b9c89 100644 --- a/src/modm/platform/clock/sam/gclk_impl.hpp.in +++ b/src/modm/platform/clock/sam/gclk_impl.hpp.in @@ -41,5 +41,15 @@ GenericClockController::setFlashLatency() return Core_Hz; } + template< ClockPeripheral peripheral > + void + GenericClockController::connect(ClockGenerator clockGen) + { + GCLK->CLKCTRL.reg = + GCLK_CLKCTRL_CLKEN | + GCLK_CLKCTRL_GEN(uint32_t(clockGen)) | + GCLK_CLKCTRL_ID(uint32_t(peripheral)); + } + } diff --git a/src/modm/platform/uart/sam/module.lb b/src/modm/platform/uart/sam/module.lb index 707b7383da..40126849d4 100644 --- a/src/modm/platform/uart/sam/module.lb +++ b/src/modm/platform/uart/sam/module.lb @@ -24,7 +24,6 @@ class Instance(Module): module.description = "Instance {}".format(self.instance) def prepare(self, module, options): - module.depends(":platform:uart") return True def build(self, env): @@ -63,7 +62,8 @@ def prepare(module, options): ":architecture:uart", ":math:algorithm", ":cmsis:device", - ":platform:gpio") + ":platform:gpio", + ":platform:gclk") global props drivers = options[":target"].get_all_drivers("sercom") diff --git a/src/modm/platform/uart/sam/uart_hal.hpp.in b/src/modm/platform/uart/sam/uart_hal.hpp.in index bb095f7e5c..b189ac89a7 100644 --- a/src/modm/platform/uart/sam/uart_hal.hpp.in +++ b/src/modm/platform/uart/sam/uart_hal.hpp.in @@ -17,6 +17,7 @@ #include #include "../device.hpp" #include "uart_base.hpp" +#include "modm/platform/clock/gclk.hpp" #include "modm/platform/core/peripherals.hpp" namespace modm diff --git a/src/modm/platform/uart/sam/uart_hal_impl.hpp.in b/src/modm/platform/uart/sam/uart_hal_impl.hpp.in index c8de01973f..1fd329c722 100644 --- a/src/modm/platform/uart/sam/uart_hal_impl.hpp.in +++ b/src/modm/platform/uart/sam/uart_hal_impl.hpp.in @@ -58,10 +58,7 @@ modm::platform::{{ name }}::initialize(Parity parity) { // Enable peripheral clock in power manager. PM->APBCMASK.bit.{{ peripheral }}_ = true; - // Init clock using GCLK0 as source. - GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_{{ peripheral }}_CORE | - GCLK_CLKCTRL_GEN_GCLK0 | - GCLK_CLKCTRL_CLKEN ; + GenericClockController::connect(ClockGenerator::System); while (GCLK->STATUS.bit.SYNCBUSY); // Reset USART reset(); From cbce42883c3ac74071ef560aeb048ec3d4bef4f6 Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Mon, 20 Jul 2020 12:18:22 +0200 Subject: [PATCH 083/135] [extint] Add external interrupt support for SAM --- examples/samd/interrupt/main.cpp | 41 +++++++++ examples/samd/interrupt/project.xml | 10 +++ src/modm/platform/extint/sam/extint.cpp | 58 ++++++++++++ src/modm/platform/extint/sam/extint.hpp | 95 ++++++++++++++++++++ src/modm/platform/extint/sam/extint_impl.hpp | 39 ++++++++ src/modm/platform/extint/sam/module.lb | 31 +++++++ src/modm/platform/gpio/sam/base.hpp.in | 8 +- src/modm/platform/gpio/sam/module.lb | 4 +- src/modm/platform/gpio/sam/pin.hpp.in | 14 +++ 9 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 examples/samd/interrupt/main.cpp create mode 100644 examples/samd/interrupt/project.xml create mode 100644 src/modm/platform/extint/sam/extint.cpp create mode 100644 src/modm/platform/extint/sam/extint.hpp create mode 100644 src/modm/platform/extint/sam/extint_impl.hpp create mode 100644 src/modm/platform/extint/sam/module.lb diff --git a/examples/samd/interrupt/main.cpp b/examples/samd/interrupt/main.cpp new file mode 100644 index 0000000000..481f0afd50 --- /dev/null +++ b/examples/samd/interrupt/main.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * 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; +using namespace std::chrono_literals; + +static volatile bool blink = true; + +void +isr() { + blink = !blink; + // Kids, please don't do serial logging in ISRs... + MODM_LOG_DEBUG << "blink: " << (blink ? "true" : "false") << modm::endl; +} + +int +main() +{ + Board::initialize(); + ExternalInterrupt::initialize(); + ExtInt<3>::initialize(&isr); + ExtInt<3>::connectPin(); + while (1) { + if (blink) { + Led::toggle(); + } else { + Led::set(0); + } + modm::delay(100ms); + } + return 0; +} diff --git a/examples/samd/interrupt/project.xml b/examples/samd/interrupt/project.xml new file mode 100644 index 0000000000..1c53a440cb --- /dev/null +++ b/examples/samd/interrupt/project.xml @@ -0,0 +1,10 @@ + + modm:feather-m0 + + + + + modm:build:scons + modm:platform:extint + + diff --git a/src/modm/platform/extint/sam/extint.cpp b/src/modm/platform/extint/sam/extint.cpp new file mode 100644 index 0000000000..fbe51d70d6 --- /dev/null +++ b/src/modm/platform/extint/sam/extint.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * 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 "extint.hpp" + +void isr(); + +namespace modm +{ +namespace platform +{ + +void +ExternalInterrupt::initialize(ClockGenerator clockGen, int priority) { + NVIC_DisableIRQ(EIC_IRQn); + NVIC_ClearPendingIRQ(EIC_IRQn); + NVIC_SetPriority(EIC_IRQn, priority); + NVIC_EnableIRQ(EIC_IRQn); + + GenericClockController::connect(clockGen); + + // Enable EIC + EIC->CTRL.bit.ENABLE = 1; + int waitCycles = 2048; + while (EIC->STATUS.bit.SYNCBUSY == 1 && --waitCycles); +} + +std::array, 16> ExternalInterrupt::handlers_ = {}; + +// FIXME: Figure out if it is worth using function pointers here instead +// to get rid of std::function overhead. +MODM_ISR(EIC) { + uint32_t int_flag = EIC->INTFLAG.reg; + uint32_t flags = int_flag << 16; + auto handler = ExternalInterrupt::handlers_.end() - 1; + do { + uint8_t leading_zeros = __builtin_clz(flags); + handler -= leading_zeros; + flags <<= leading_zeros; + if (flags & 1u << 31 && *handler) { + (*handler)(); + flags &= ~(1u << 31); + } + } while (flags); + EIC->INTFLAG.reg |= int_flag; +} + + +} // namespace platform +} // namespace modm diff --git a/src/modm/platform/extint/sam/extint.hpp b/src/modm/platform/extint/sam/extint.hpp new file mode 100644 index 0000000000..8e5e36b371 --- /dev/null +++ b/src/modm/platform/extint/sam/extint.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * 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 + +#pragma once + +namespace modm +{ + +namespace platform +{ + +MODM_ISR_DECL(EIC); + +/** + * External Interrupt handler for SAMD devices. + * + * @author Erik Henriksson + * @ingroup modm_platform_extint + */ +class ExternalInterrupt { + friend void EIC_IRQHandler(void); +public: + /** + * Initializes the External Interrupt handler. + * + * @param clockGen + * The clock generator to use for the peripheral. If any interrupts are to + * be used to akeup the CPU from standby mode, make sure this clock is + * actually running in standby. Defaults to external 32.768kHz crystal osc. + */ + static void initialize( + ClockGenerator clockGen = ClockGenerator::ExternalCrystal32K, + int priority = (1ul << __NVIC_PRIO_BITS) - 1ul); + + protected: + static std::array, 16> handlers_; +}; + +/** + * External Interrupt instance for SAMD devices. + * + * @author Erik Henriksson + * @ingroup modm_platform_extint + */ +template class ExtInt : private ExternalInterrupt +{ +public: + /** + * Initializes the External Interrupt instance. + * + * @param handler + * Function that will be called for any interrupts. + * @param trigger + * Specifies the edge detection trigger to use (default is rising edge). + * @param wakeupEnabled + * If true (default), allows the CPU to wakeup from interrupt + * from this instance. + */ + static void initialize( + std::function handler, + Gpio::InputTrigger trigger = Gpio::InputTrigger::RisingEdge, + bool wakeupEnabled = true); + + /** + * Connects a GPIO pin to this External Interrupt instance. + * + * @tparam Pin + * The GPIO pin to connect this instance to. + */ + template + static void + connectPin() { + Pin::template connectInterrupt(); + } +}; + +} // namespace platform + +} // namespace modm + +#include "extint_impl.hpp" \ No newline at end of file diff --git a/src/modm/platform/extint/sam/extint_impl.hpp b/src/modm/platform/extint/sam/extint_impl.hpp new file mode 100644 index 0000000000..c9ccd0a35d --- /dev/null +++ b/src/modm/platform/extint/sam/extint_impl.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +namespace modm +{ +namespace platform +{ + +template +void +ExtInt::initialize( + std::function handler, + Gpio::InputTrigger trigger, + bool wakeupEnabled) { + handlers_[instance] = handler; + if (wakeupEnabled) { + EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN(1u << instance); + } else { + EIC->WAKEUP.reg &= ~EIC_WAKEUP_WAKEUPEN(1u << instance); + } + constexpr int sensePos = instance*EIC_CONFIG_SENSE1_Pos; + EIC->CONFIG[instance & 0x8].reg &= EIC_CONFIG_SENSE0_Msk << sensePos; + EIC->CONFIG[instance & 0x8].reg |= uint32_t(trigger) << sensePos; + EIC->INTENSET.vec.EXTINT |= 1u << instance; +} + + +} // namespace platform +} // namespace modm diff --git a/src/modm/platform/extint/sam/module.lb b/src/modm/platform/extint/sam/module.lb new file mode 100644 index 0000000000..1ae2501fb9 --- /dev/null +++ b/src/modm/platform/extint/sam/module.lb @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Erik Henriksson +# +# 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/. +# ----------------------------------------------------------------------------- + +import sys + +def init(module): + module.name = ":platform:extint" + module.description = "External Interrupt" + +def prepare(module, options): + if options[":target"].identifier.platform != "sam": + return False + + module.depends(":cmsis:device", ":platform:clock", ":platform:gpio") + + return True + +def build(env): + env.outbasepath = "modm/src/modm/platform/extint" + env.copy("extint.hpp") + env.copy("extint_impl.hpp") + env.copy("extint.cpp") diff --git a/src/modm/platform/gpio/sam/base.hpp.in b/src/modm/platform/gpio/sam/base.hpp.in index da7cebec29..83c5baffe1 100644 --- a/src/modm/platform/gpio/sam/base.hpp.in +++ b/src/modm/platform/gpio/sam/base.hpp.in @@ -54,9 +54,11 @@ struct Gpio enum class InputTrigger { - RisingEdge, - FallingEdge, - BothEdges, + RisingEdge = EIC_CONFIG_SENSE0_RISE_Val, + FallingEdge = EIC_CONFIG_SENSE0_FALL_Val, + BothEdges = EIC_CONFIG_SENSE0_BOTH_Val, + High = EIC_CONFIG_SENSE0_HIGH_Val, + Low = EIC_CONFIG_SENSE0_LOW_Val, }; /// The Port a Gpio Pin is connected to. diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb index 39deee0e8e..15e2ab7e06 100644 --- a/src/modm/platform/gpio/sam/module.lb +++ b/src/modm/platform/gpio/sam/module.lb @@ -86,9 +86,11 @@ def validate(env): all_signals.update(extracted_signals) signal_names = sorted(list(set(s["name"] for s in extracted_signals.values()))) extracted_signals = OrderedDict([(name, sorted([s for s in extracted_signals.values() if s["name"] == name], key=lambda si:si["name"])) for name in signal_names]) + extints = sorted({"pin": signal["name"][6:], "function": signal["function"]} for signal in raw_signals if signal["name"].startswith("extint")) bprops[key] = { "gpio": gpio, - "signals": extracted_signals + "signals": extracted_signals, + "extints": extints } all_peripherals = [s["driver"] for s in all_signals.values()] diff --git a/src/modm/platform/gpio/sam/pin.hpp.in b/src/modm/platform/gpio/sam/pin.hpp.in index f56bd61416..81770a5e1c 100644 --- a/src/modm/platform/gpio/sam/pin.hpp.in +++ b/src/modm/platform/gpio/sam/pin.hpp.in @@ -40,6 +40,7 @@ class Gpio{{ port ~ pin }} : public Gpio, public ::modm::GpioIO friend class Adc; friend class Adc1; friend class Adc2; friend class Adc3; friend class Adc4; + template friend class ExtInt; public: using Output = Gpio{{ port ~ pin }}; using Input = Gpio{{ port ~ pin }}; @@ -124,6 +125,19 @@ public: "Gpio{{ port ~ pin }}::{{ signal_name }} only connects to {% for signal in signal_group %}{{signal.driver}}{% if not loop.last %} or {% endif %}{% endfor %}!"); }; %% endfor + +protected: + %% for extint in extints + template< int instance > + inline static void connectInterrupt() { + static_assert( + instance == {{ extint.pin }}, + "Gpio{{ port ~ pin }} only connects to ExtInt<{{ extint.pin }}>"); + PORT->Group[uint32_t(port)].PINCFG[uint32_t(pin)].bit.PMUXEN = true; + PORT->Group[uint32_t(port)].PMUX[uint32_t(pin) >> 1].reg = + PORT_PMUX_{{ gpio.pmux }}_{{ extint.function | upper }}; + } + %% endfor }; /// @cond From 026f82da86dcafa5e60780684f9b3f0589c179b3 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Jun 2020 02:44:50 +0200 Subject: [PATCH 084/135] [uart] Add more interface methods --- src/modm/architecture/interface/uart.hpp | 16 +++++++ src/modm/platform/uart/stm32/uart.cpp.in | 42 +++++++++++++++++++ src/modm/platform/uart/stm32/uart.hpp.in | 18 +++++++- src/modm/platform/uart/stm32/uart_base.hpp.in | 17 ++++++++ src/modm/platform/uart/stm32/uart_hal.hpp.in | 12 +++--- .../platform/uart/stm32/uart_hal_impl.hpp.in | 25 +++++++++++ 6 files changed, 123 insertions(+), 7 deletions(-) diff --git a/src/modm/architecture/interface/uart.hpp b/src/modm/architecture/interface/uart.hpp index 37814a524e..7bbd0a9f46 100644 --- a/src/modm/architecture/interface/uart.hpp +++ b/src/modm/architecture/interface/uart.hpp @@ -127,6 +127,10 @@ class Uart : public ::modm::PeripheralDriver static std::size_t read(uint8_t *data, std::size_t length); + /// @return the size of the receive FIFO queue. + static std::size_t + receiveBufferSize(); + /** * Empty the receive FIFO queue and hardware buffer. * @@ -135,6 +139,10 @@ class Uart : public ::modm::PeripheralDriver static std::size_t discardReceiveBuffer(); + /// @return the size of the transmit FIFO queue. + static std::size_t + transmitBufferSize(); + /** * Empty the transmit FIFO queue and hardware buffer. * @@ -142,6 +150,14 @@ class Uart : public ::modm::PeripheralDriver */ static std::size_t discardTransmitBuffer(); + + /// @return `true` if an error occured during any write or read + static bool + hasError(); + + /// Reset the sticky error indication + static void + clearError(); #endif }; diff --git a/src/modm/platform/uart/stm32/uart.cpp.in b/src/modm/platform/uart/stm32/uart.cpp.in index bd64090737..2246b5e1c8 100644 --- a/src/modm/platform/uart/stm32/uart.cpp.in +++ b/src/modm/platform/uart/stm32/uart.cpp.in @@ -115,6 +115,16 @@ modm::platform::{{ name }}::isWriteFinished() %% endif } +std::size_t +modm::platform::{{ name }}::transmitBufferSize() +{ +%% if options["buffered"] + return txBuffer.getSize(); +%% else + return {{ hal }}::isTransmitRegisterEmpty() ? 0 : 1; +%% endif +} + std::size_t modm::platform::{{ name }}::discardTransmitBuffer() { @@ -178,6 +188,16 @@ modm::platform::{{ name }}::read(uint8_t *data, std::size_t length) %% endif } +std::size_t +modm::platform::{{ name }}::receiveBufferSize() +{ +%% if options["buffered"] + return rxBuffer.getSize(); +%% else + return {{ hal }}::isReceiveRegisterNotEmpty() ? 1 : 0; +%% endif +} + std::size_t modm::platform::{{ name }}::discardReceiveBuffer() { @@ -193,6 +213,27 @@ modm::platform::{{ name }}::discardReceiveBuffer() %% endif } +bool +modm::platform::{{ name }}::hasError() +{ + return {{ hal }}::getInterruptFlags().any( + {{ hal }}::InterruptFlag::ParityError | +#ifdef USART_ISR_NE + {{ hal }}::InterruptFlag::NoiseError | +#endif + {{ hal }}::InterruptFlag::OverrunError | {{ hal }}::InterruptFlag::FramingError); +} +void +modm::platform::{{ name }}::clearError() +{ + return {{ hal }}::acknowledgeInterruptFlags( + {{ hal }}::InterruptFlag::ParityError | +#ifdef USART_ISR_NE + {{ hal }}::InterruptFlag::NoiseError | +#endif + {{ hal }}::InterruptFlag::OverrunError | {{ hal }}::InterruptFlag::FramingError); +} + %% if options["buffered"] %% set hal = "modm::platform::" ~ hal @@ -219,5 +260,6 @@ MODM_ISR({{ name | upper }}) txBuffer.pop(); } } + {{ hal }}::acknowledgeInterruptFlags({{ hal }}::InterruptFlag::OverrunError); } %% endif diff --git a/src/modm/platform/uart/stm32/uart.hpp.in b/src/modm/platform/uart/stm32/uart.hpp.in index ff135e0662..4addc9241c 100644 --- a/src/modm/platform/uart/stm32/uart.hpp.in +++ b/src/modm/platform/uart/stm32/uart.hpp.in @@ -50,6 +50,7 @@ private: %% endif public: + using Hal = {{ hal }}; // Expose jinja template parameters to be checked by e.g. drivers or application static constexpr size_t RxBufferSize = {{ options["buffer.rx"] }}; static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }}; @@ -57,7 +58,8 @@ public: public: template< template class... Signals > static void - connect(Gpio::InputType InputTypeRx = Gpio::InputType::PullUp) + connect(Gpio::InputType InputTypeRx = Gpio::InputType::PullUp, + Gpio::OutputType OutputTypeTx = Gpio::OutputType::PushPull) { using Connector = GpioConnector; using Tx = typename Connector::template GetSignal< Gpio::Signal::Tx >; @@ -67,7 +69,7 @@ public: "{{ name }}::connect() requires one Tx and/or one Rx signal!"); // Connector::disconnect(); - Tx::setOutput(Gpio::OutputType::PushPull); + Tx::setOutput(OutputTypeTx); Rx::setInput(InputTypeRx); Connector::connect(); } @@ -113,6 +115,9 @@ public: static bool isWriteFinished(); + static std::size_t + transmitBufferSize(); + static std::size_t discardTransmitBuffer(); @@ -122,9 +127,18 @@ public: static std::size_t read(uint8_t *buffer, std::size_t length); + static std::size_t + receiveBufferSize(); + static std::size_t discardReceiveBuffer(); + static bool + hasError(); + + static void + clearError(); + %% if id in shared_irq_ids static void irq(); diff --git a/src/modm/platform/uart/stm32/uart_base.hpp.in b/src/modm/platform/uart/stm32/uart_base.hpp.in index fc2b6a0d9f..917d6309d1 100644 --- a/src/modm/platform/uart/stm32/uart_base.hpp.in +++ b/src/modm/platform/uart/stm32/uart_base.hpp.in @@ -77,6 +77,10 @@ public: %% set reg = "ISR" /// Set if match character is received. CharacterMatch = USART_{{reg}}_CMF, +#ifdef USART_{{reg}}_NE + /// Set if noise was detected on the input. + NoiseError = USART_{{reg}}_NE, +#endif %% else %% set reg = "SR" %% endif @@ -128,6 +132,19 @@ public: Output = USART_CR2_LBCL, }; + enum class + WordLength : uint32_t + { +#ifdef USART_CR1_M1 + Bit7 = USART_CR1_M1, + Bit8 = 0, + Bit9 = USART_CR1_M0, +#else + Bit8 = 0, + Bit9 = USART_CR1_M, +#endif + }; + enum class SpiClock : uint32_t { diff --git a/src/modm/platform/uart/stm32/uart_hal.hpp.in b/src/modm/platform/uart/stm32/uart_hal.hpp.in index 969aefbe80..806b8f5f08 100644 --- a/src/modm/platform/uart/stm32/uart_hal.hpp.in +++ b/src/modm/platform/uart/stm32/uart_hal.hpp.in @@ -81,13 +81,15 @@ public: initialize( Parity parity = Parity::Disabled); %% endif + static inline void + setWordLength(WordLength length); /** - * Initialize Uart HAL Peripheral - * - * Enables clocks, the UART peripheral (but neither TX nor RX) - * Sets raw brr, parity and oversampling mode. - */ + * Initialize Uart HAL Peripheral + * + * Enables clocks, the UART peripheral (but neither TX nor RX) + * Sets raw brr, parity and oversampling mode. + */ static void initializeWithBrr(uint16_t brr, %% if target["family"] == "f1" diff --git a/src/modm/platform/uart/stm32/uart_hal_impl.hpp.in b/src/modm/platform/uart/stm32/uart_hal_impl.hpp.in index b2c0aa1618..e4b2356641 100644 --- a/src/modm/platform/uart/stm32/uart_hal_impl.hpp.in +++ b/src/modm/platform/uart/stm32/uart_hal_impl.hpp.in @@ -176,6 +176,31 @@ modm::platform::{{ name }}::setSpiDataMode(SpiDataMode mode) %% endif } +void +modm::platform::{{ name }}::setWordLength(WordLength length) +{ +%% if "extended" in driver["type"] + const bool usartEnabled = ({{ peripheral }}->CR1 & USART_CR1_UE); + if(usartEnabled) { + disableOperation(); + } +%% endif + + {{ peripheral }}->CR1 = +#ifdef USART_CR1_M1 + ({{ peripheral }}->CR1 & ~(USART_CR1_M0 | USART_CR1_M1)) +#else + ({{ peripheral }}->CR1 & ~USART_CR1_M) +#endif + | static_cast(length); + +%% if "extended" in driver["type"] + if(usartEnabled) { + enableOperation(); + } +%% endif +} + void modm::platform::{{ name }}::setLastBitClockPulse(LastBitClockPulse pulse) { From 8820d6b36eeabfc6b97e2578104697681175237e Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Tue, 9 Jun 2020 00:31:48 +0200 Subject: [PATCH 085/135] [communication] Reimplement AMNB protocol --- examples/nucleo_g071rb/amnb/main.cpp | 132 +++ examples/nucleo_g071rb/amnb/project.xml | 19 + src/modm/communication/amnb.hpp | 89 +- src/modm/communication/amnb/constants.hpp | 107 --- src/modm/communication/amnb/handler.hpp.in | 329 +++++++ src/modm/communication/amnb/interface.hpp | 209 ----- src/modm/communication/amnb/interface.hpp.in | 291 ++++++ .../communication/amnb/interface_impl.hpp | 389 -------- src/modm/communication/amnb/message.hpp.in | 279 ++++++ src/modm/communication/amnb/module.lb | 42 + src/modm/communication/amnb/module.md | 88 ++ src/modm/communication/amnb/node.hpp | 847 +++++++----------- src/modm/communication/amnb/node_impl.hpp | 482 ---------- .../communication/amnb/interface_test.cpp | 299 +++++++ .../communication/amnb/interface_test.hpp | 23 + test/modm/communication/amnb/message_test.cpp | 281 ++++++ test/modm/communication/amnb/message_test.hpp | 29 + test/modm/communication/amnb/node_test.cpp | 295 ++++++ test/modm/communication/amnb/node_test.hpp | 24 + test/modm/communication/module.lb | 14 + test/modm/mock/module.lb | 13 + test/modm/mock/shared_medium.hpp | 125 +++ 22 files changed, 2584 insertions(+), 1822 deletions(-) create mode 100644 examples/nucleo_g071rb/amnb/main.cpp create mode 100644 examples/nucleo_g071rb/amnb/project.xml delete mode 100644 src/modm/communication/amnb/constants.hpp create mode 100644 src/modm/communication/amnb/handler.hpp.in delete mode 100644 src/modm/communication/amnb/interface.hpp create mode 100644 src/modm/communication/amnb/interface.hpp.in delete mode 100644 src/modm/communication/amnb/interface_impl.hpp create mode 100644 src/modm/communication/amnb/message.hpp.in create mode 100644 src/modm/communication/amnb/module.lb create mode 100644 src/modm/communication/amnb/module.md delete mode 100644 src/modm/communication/amnb/node_impl.hpp create mode 100644 test/modm/communication/amnb/interface_test.cpp create mode 100644 test/modm/communication/amnb/interface_test.hpp create mode 100644 test/modm/communication/amnb/message_test.cpp create mode 100644 test/modm/communication/amnb/message_test.hpp create mode 100644 test/modm/communication/amnb/node_test.cpp create mode 100644 test/modm/communication/amnb/node_test.hpp create mode 100644 test/modm/mock/shared_medium.hpp diff --git a/examples/nucleo_g071rb/amnb/main.cpp b/examples/nucleo_g071rb/amnb/main.cpp new file mode 100644 index 0000000000..41784b7105 --- /dev/null +++ b/examples/nucleo_g071rb/amnb/main.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019, 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 +#include + +using namespace Board; +using namespace std::chrono_literals; +using namespace modm::amnb; +// ---------------------------------------------------------------------------- + +const Listener listeners[] = +{ + {1, +[](uint8_t sender, const uint32_t& data) + { + MODM_LOG_INFO << "Node2 and Node3 received Broadcast 1 from '" << sender; + MODM_LOG_INFO << "': " << data << modm::endl; + } + }, + {2, [](uint8_t sender) + { + MODM_LOG_INFO << "Node2 and Node3 received Broadcast 2 from '" << sender << "'" << modm::endl; + } + }, +}; +const Action actions[] = +{ + {1, +[]() -> Response + { + static uint8_t counter{0}; + MODM_LOG_INFO << "Node1 and Node3 received Action 1" << modm::endl; + return counter++; + } + }, + {2, +[](const uint32_t& data) -> Response + { + static uint8_t counter{0}; + MODM_LOG_INFO << "Node1 and Node3 received Action 2 with argument: " << data << modm::endl; + return ErrorResponse(counter++); + } + }, +}; + +// Two nodes on the same device on different UARTs of course! +DeviceWrapper device1; +DeviceWrapper device2; +DeviceWrapper device3; +Node node1(device1, 1, actions); +Node node2(device2, 2, listeners); +Node node3(device3, 3, actions, listeners); + +// You need to connect D1 with D15 and with A0 +using PinNode1 = GpioC4; // D1 +using PinNode2 = GpioB8; // D15 +using PinNode3 = GpioA0; // A0 + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + LedD13::setOutput(); + + Usart1::connect(); + Usart1::initialize(); + // Use Single-Wire Half-Duplex Mode + PinNode1::configure(Gpio::OutputType::OpenDrain); + PinNode1::configure(Gpio::InputType::PullUp); + USART1->CR1 &= ~USART_CR1_UE; + USART1->CR3 = USART_CR3_HDSEL; + USART1->CR1 |= USART_CR1_UE; + + Usart3::connect(); + Usart3::initialize(); + // Use Single-Wire Half-Duplex Mode + PinNode2::configure(Gpio::OutputType::OpenDrain); + PinNode2::configure(Gpio::InputType::PullUp); + USART3->CR1 &= ~USART_CR1_UE; + USART3->CR3 = USART_CR3_HDSEL; + USART3->CR1 |= USART_CR1_UE; + + Usart4::connect(); + Usart4::initialize(); + // Use Single-Wire Half-Duplex Mode + PinNode3::configure(Gpio::OutputType::OpenDrain); + PinNode3::configure(Gpio::InputType::PullUp); + USART4->CR1 &= ~USART_CR1_UE; + USART4->CR3 = USART_CR3_HDSEL; + USART4->CR1 |= USART_CR1_UE; + + + modm::ShortPeriodicTimer tmr{1s}; + uint32_t counter{0}; + + while (true) + { + node1.update(); + node2.update(); + node3.update(); + + if (tmr.execute()) + { + LedD13::toggle(); + node1.broadcast(1, counter++); + node3.broadcast(2); + + { + modm::ResumableResult< Result > res{0}; + while((res = node2.request(1, 1)).getState() == modm::rf::Running) + { node1.update(); node2.update(); node3.update(); } + MODM_LOG_INFO << "Node1 responded with: " << res.getResult().error(); + MODM_LOG_INFO << " " << *res.getResult().result() << modm::endl; + } + { + modm::ResumableResult< Result > res{0}; + while((res = node1.request(3, 2, counter)).getState() == modm::rf::Running) + { node1.update(); node2.update(); node3.update(); } + MODM_LOG_INFO << "Node3 responded with: " << res.getResult().error(); + MODM_LOG_INFO << " " << *res.getResult().resultError() << modm::endl; + } + } + } + + return 0; +} diff --git a/examples/nucleo_g071rb/amnb/project.xml b/examples/nucleo_g071rb/amnb/project.xml new file mode 100644 index 0000000000..740d681a8f --- /dev/null +++ b/examples/nucleo_g071rb/amnb/project.xml @@ -0,0 +1,19 @@ + + modm:nucleo-g071rb + + + + + + + + + + modm:platform:gpio + modm:communication:amnb + modm:platform:uart:1 + modm:platform:uart:3 + modm:platform:uart:4 + modm:build:scons + + diff --git a/src/modm/communication/amnb.hpp b/src/modm/communication/amnb.hpp index 185e4e9043..ec8f1630aa 100644 --- a/src/modm/communication/amnb.hpp +++ b/src/modm/communication/amnb.hpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2010-2011, Fabian Greif - * Copyright (c) 2011-2015, Niklas Hauser + * Copyright (c) 2020, Niklas Hauser * * This file is part of the modm project. * @@ -10,90 +9,4 @@ */ // ---------------------------------------------------------------------------- -/** - * @ingroup modm_communication - * @defgroup amnb Asynchronous Multi-Node Bus (AMNB) - * - * @section amnb_intro Introduction - * - * The AMNB (**A**synchronous **M**ulti-**N**ode **B**us) is a - * multi-master bus system, using p-persitent CSMA to send messages. - * - * One bus can be populated with up to 64 nodes. The nodes can be queried for - * data and they will respond like an SAB Slave, and can query data from other - * nodes like an SAB Master, or they can just broadcast a message. - * Each node can listen to all the responses and broadcasts and store that - * information for its purpose. - * - * Action callbacks to query requests can be defined as well as universal - * callbacks to any transmitted messaged (Listener callbacks). - * As an optional advanced feature, error handling callbacks can also be defined, - * which fire if messages have not been able to be sent, or requests timed out - * or misbehaved in other manners, or other nodes query unavailable information. - * - * @section amnb_protocol Protocol - * - * Features: - * - Maximum payload length is 32 byte. - * - CRC8 (1-Wire) - * - * @subsection structure Structure - * -@verbatim -+------+--------+--------+---------+--------------+-----+ -| SYNC | LENGTH | HEADER | COMMAND | ... DATA ... | CRC | -+------+--------+--------+---------+--------------+-----+ -@endverbatim - * - * - `SYNC` - Synchronization byte (always 0x54) - * - `LENGTH` - Length of the payload (without header, command and CRC byte) - * - `HEADER` - Address of the slave and two flag bits - * - `COMMAND` - Command code - * - `DATA` - Up to 32 byte of payload - * - `CRC` - CRC-8 checksum (iButton) - * - * @subsubsection header Header - * -@verbatim - 7 6 5 4 3 2 1 0 -+---+---+---+---+---+---+---+---+ -| Flags | ADDRESS | -+---+---+---+---+---+---+---+---+ - - Flags | Meaning ---------+--------- - 0 0 | data broadcast by a node - 0 1 | data request by a node - 1 0 | negative response from the node (NACK) - 1 1 | positive response from the node (ACK) -@endverbatim - * - * When transmitting, the *second bit* determines, whether or not to expect an - * answer from the addressed node. - * To just send information without any need for acknowledgment, use a broadcast. - * When a node is responding, the *second bit* has to following meaning: - * - * - `true` - Message is an positive response and may contain a payload - * - `false` - Message signals an error condition and carries only one byte of - * payload. This byte is an error code. - * - * @section amnb_electric Electrical characteristics - * - * Between different boards CAN transceivers are used. Compared to RS485 the - * CAN transceivers have the advantage to work without a separate direction input. - * You can just connected the transceiver directly to the UART of your - * microcontroller. - * These are identical to the SAB CAN electrical characteristics. - * You have to use the CAN transceivers, otherwise it cannot be determined, if - * the bus is busy or available for transmission. - * - * @author Fabian Greif - * @author Niklas Hauser - */ - -#ifndef MODM_AMNB_HPP -#define MODM_AMNB_HPP - #include "amnb/node.hpp" - -#endif // MODM_AMNB_HPP diff --git a/src/modm/communication/amnb/constants.hpp b/src/modm/communication/amnb/constants.hpp deleted file mode 100644 index 96e81f0912..0000000000 --- a/src/modm/communication/amnb/constants.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2011-2013, Niklas Hauser - * Copyright (c) 2013, Sascha Schade - * - * 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_AMNB_CONSTANTS_HPP -#define MODM_AMNB_CONSTANTS_HPP - -#include - -namespace modm -{ - namespace amnb - { - /** - * \brief Error code - * - * Error codes below 0x20 are reserved for the system. Every other - * code may be used by user. - * - * \ingroup amnb - */ - enum Error - { - /** - * \brief Universal error code - * - * Use this code if you're not sure what error to signal. If - * the user should react to the error code, create a specific - * one for the given problem. - */ - ERROR_GENERAL_ERROR = 0x00, - ERROR_ACTION_NO_ACTION = 0x01, ///< No corresponding action found for this command - - /** - * \brief Unexpected payload length - * - * The payload length of the received message differs from the - * expected length for the given command. - */ - ERROR_ACTION_WRONG_PAYLOAD_LENGTH = 0x02, - - /** - * \brief No response given by the user - * - * This error code is generated when no response method is called - * by the user during an action callback. - */ - ERROR_ACTION_NO_RESPONSE = 0x03, - - ERROR_QUERY_ERROR_CODE = 0x04, ///< Query answer contains an more detailed error code - - ERROR_QUERY_TIMEOUT = 0x05, ///< Query timed out - - ERROR_QUERY_WRONG_PAYLOAD_LENGTH = 0x06, ///< Query answer has wrong payload length - - ERROR_QUERY_IN_PROGRESS = 0x07, ///< Query is already in progress - - ERROR_TRANSMITTER_BUSY = 0x08, ///< Interface is currently transmitting - - ERROR_MESSAGE_OVERWRITTEN = 0x09, ///< Another message will be transmitted before this one - }; - - /** - * \brief Flags - * \ingroup amnb - */ - enum Flags - { - BROADCAST = 0x00, ///< send a message without getting a response - REQUEST = 0x40, ///< request data from a node - NACK = 0x80, ///< data request Negative ACKnowledge - ACK = 0xc0 ///< data request positive ACKnowledge - }; - - /** - * \brief Maximum length for the payload - * \ingroup amnb - */ - const uint8_t maxPayloadLength = 32; - - /** - * \internal - * \brief Universal base class for the AMNB interface - * \ingroup amnb - */ - const uint8_t syncByte = 0x54; - - /** - * \internal - * \brief Initial value for the CRC8 calculation - * \ingroup amnb - */ - const uint8_t crcInitialValue = 0x00; - } -} - -#endif // MODM_AMNB_CONSTANTS_HPP diff --git a/src/modm/communication/amnb/handler.hpp.in b/src/modm/communication/amnb/handler.hpp.in new file mode 100644 index 0000000000..bad628b7a4 --- /dev/null +++ b/src/modm/communication/amnb/handler.hpp.in @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include "message.hpp" +#include + +namespace modm::amnb +{ + +/// @ingroup modm_communication_amnb +enum class Error : uint8_t +{ + Ok = 0, + + RequestTimeout = 1, + WrongArgumentSize = 2, + NoAction = 3, + ResponseAllocationFailed = 4, + RequestAllocationFailed = 5, + UserError = 6, + Unknown = 7, +}; + +/// @ingroup modm_communication_amnb +struct Listener +{ + inline + Listener(int command, void(*listener)(uint8_t)) + : command(command), callback((void*)listener), + redirect([](const Message &msg, void *cb) + { + reinterpret_cast(cb)(msg.address()); + }) + {} + + template + Listener(int command, void(*listener)(uint8_t, const T&)) + : command(command), callback((void*)listener), + redirect([](const Message &msg, void* cb) + { + if (const T* arg = msg.get(); arg and msg.length() == sizeof(T)) + reinterpret_cast(cb)(msg.address(), *arg); + }) + {} + +protected: + const uint8_t command; + void *const callback; + void (*const redirect)(const Message &msg, void *cb); + + inline + void call(const Message &msg) const + { + redirect(msg, callback); + } + + template< size_t, size_t > friend class Node; +}; + +/// @ingroup modm_communication_amnb +struct Response +{ + inline + Response() + : msg(0, 0, Type::Response) {} + + template + Response(T retval) + : msg(0, 0, sizeof(T), Type::Response) + { + if (T* arg = msg.get(); arg) *arg = retval; + else { + msg = Message(0, 0, 1, Type::Error); + *msg.get() = Error::ResponseAllocationFailed; + } + } + + Message msg; +}; + +/// @ingroup modm_communication_amnb +template< typename T > +inline Response +ErrorResponse(T error) +{ + Response res(error); + res.msg.setType(Type::UserError); + return res; +} + +/// @ingroup modm_communication_amnb +struct Action +{ + inline + Action(int command, Response(*action)()) + : command(command), callback((void*)action), + redirect([](const Message &msg, void *cb) -> Message + { + if (msg.length() == 0) + return reinterpret_cast(cb)().msg; + return Response(Error::WrongArgumentSize).msg; + }) + {} + + template + Action(int command, Response(*action)(const T&)) + : command(command), callback((void*)action), + redirect([](const Message &msg, void* cb) -> Message + { + const T* arg = msg.get(); + if (arg == nullptr) + return Response(Error::ResponseAllocationFailed).msg; + if (msg.length() < sizeof(T)) + return Response(Error::WrongArgumentSize).msg; + return reinterpret_cast(cb)(*arg).msg; + }) + {} + + inline + Action(int command, void(*action)()) + : command(command), callback((void*)action), + redirect([](const Message &msg, void *cb) -> Message + { + if (msg.length() == 0) { + reinterpret_cast(cb)(); + return Response().msg; + } + return Response(Error::WrongArgumentSize).msg; + }) + {} + + template + Action(int command, void(*action)(const T&)) + : command(command), callback((void*)action), + redirect([](const Message &msg, void* cb) -> Message + { + + const T* arg = msg.get(); + if (arg == nullptr) + return Response(Error::ResponseAllocationFailed).msg; + if (msg.length() < sizeof(T)) + return Response(Error::WrongArgumentSize).msg; + reinterpret_cast(cb)(*arg); + return Response().msg; + }) + {} + +protected: + const uint8_t command; + void *const callback; + Message (*const redirect)(const Message &msg, void *cb); + + inline + Message call(const Message &msg) const + { + return redirect(msg, callback); + } + + template< size_t, size_t > friend class Node; +}; + +/// @ingroup modm_communication_amnb +template< class ReturnType = void, class ErrorType = void > +struct Result +{ + Result() = default; + + bool + hasError() const { return syserr != Error::Ok; } + + Error + error() const { return syserr; } + + const ErrorType* + resultError() const { return errval; } + + const ReturnType* + result() const { return retval; } + +protected: + Result(Message &msg) + { + switch(msg.type()) + { + case Type::Response: + if (retval = msg.get(); not retval) + syserr = Error::ResponseAllocationFailed; + break; + case Type::UserError: + if (errval = msg.get(); errval) + syserr = Error::UserError; + else + syserr = Error::ResponseAllocationFailed; + break; + case Type::Error: + syserr = *msg.get(); + break; + default: + syserr = Error::Unknown; + } + } + union { + const ReturnType *retval{nullptr}; + const ErrorType *errval; + }; + Error syserr{Error::Ok}; + + template< size_t, size_t > friend class Node; +}; + +/// @cond +template< class ReturnType> +struct Result +{ + Result() = default; + + bool + hasError() const { return syserr != Error::Ok; } + + Error + error() const { return syserr; } + + const Error* + resultError() const { return &syserr; } + + const ReturnType* + result() const { return retval; } + +protected: + Result(Message &msg) + { + switch(msg.type()) + { + case Type::Response: + retval = msg.get(); + if (not retval) syserr = Error::ResponseAllocationFailed; + break; + case Type::Error: + syserr = *msg.get(); + break; + case Type::UserError: + syserr = Error::UserError; + msg.deallocate(); + break; + default: + syserr = Error::Unknown; + } + } + const ReturnType *retval{nullptr}; + Error syserr{Error::Ok}; + + template< size_t, size_t > friend class Node; +}; + +template<> +struct Result +{ + Result() = default; + + bool + hasError() const { return syserr != Error::Ok; } + + Error + error() const { return syserr; } + + const Error* + resultError() const { return &syserr; } + + const Error* + result() const { return &syserr; } + +protected: + Result(Message &msg) + { + switch(msg.type()) + { + case Type::Response: + break; + case Type::Error: + syserr = *msg.get(); + break; + case Type::UserError: + syserr = Error::UserError; + break; + default: + syserr = Error::Unknown; + } + msg.deallocate(); + } + Error syserr{Error::Ok}; + + template< size_t, size_t > friend class Node; +}; +/// @endcond + +} + +%% if with_io +#include + +inline modm::IOStream& +operator << (modm::IOStream& s, const modm::amnb::Error error) +{ + using namespace modm::amnb; + switch(error) + { + case Error::Ok: s << "Ok"; break; + case Error::NoAction: s << "NoAction"; break; + case Error::RequestTimeout: s << "RequestTimeout"; break; + case Error::WrongArgumentSize: s << "WrongArgumentSize"; break; + case Error::RequestAllocationFailed: s << "RequestAllocationFailed"; break; + case Error::ResponseAllocationFailed: s << "ResponseAllocationFailed"; break; + case Error::UserError: s << "UserError"; break; + case Error::Unknown: s << "Unknown"; break; + } + return s; +} +%% endif + diff --git a/src/modm/communication/amnb/interface.hpp b/src/modm/communication/amnb/interface.hpp deleted file mode 100644 index 56b69c9c03..0000000000 --- a/src/modm/communication/amnb/interface.hpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2009, Georgi Grinshpun - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2011-2013, 2015-2016, Niklas Hauser - * Copyright (c) 2013, Sascha Schade - * - * 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_AMNB_INTERFACE_HPP -#define MODM_AMNB_INTERFACE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "constants.hpp" - -#define AMNB_TIMING_DEBUG 0 - -namespace modm -{ - namespace amnb - { - /** - * \internal - * \brief Universal base class for the AMNB interface - * - * \see - * Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products - * \ingroup amnb - */ - uint8_t - crcUpdate(uint8_t crc, uint8_t data); - - /** - * \brief AMNB interface - * - * \author Niklas Hauser - * - * \ingroup amnb - */ - template - class Interface - { - public: - /** - * \brief Initialize the interface - * - * \param seed for the random number generator - */ - static void - initialize(int seed); - - /** - * \brief Send a message - * - * \param address receiver address - * \param flags see modm::amnb::Flags - * \param command command byte - * \param *payload data field - * \param payloadLength size of the data field - */ - static bool - sendMessage(uint8_t address, Flags flags, uint8_t command, - const void *payload, uint8_t payloadLength); - - /** - * \brief Send a message - */ - template - static bool modm_always_inline - sendMessage(uint8_t address, Flags flags, uint8_t command, - const T& payload); - - /** - * \brief Send a empty message - */ - static bool modm_always_inline - sendMessage(uint8_t address, Flags flags, uint8_t command); - - /** - * \brief Check if a message was received - * - * Reset the status with a call of dropMessage(). - */ - static modm_always_inline bool - isMessageAvailable(); - - static modm_always_inline uint8_t - getTransmittedAddress(); - - static modm_always_inline uint8_t - getTransmittedCommand(); - - static modm_always_inline Flags - getTransmittedFlags(); - - static modm_always_inline uint8_t - getAddress(); - - static modm_always_inline uint8_t - getCommand(); - - static modm_always_inline bool - isResponse(); - - /** - * \brief Check if the message is an ACK or NACK - * \return \c true if the message is an ACK, \c false on NACK. - */ - static modm_always_inline bool - isAcknowledge(); - - /** - * \return \c true you are allowed to send right now - */ - static modm_always_inline bool - isBusAvailable(); - - /** - * \brief Check if there has been an error during transmission - * \return \c true if the message has been transmitted without - * collision. - */ - static modm_always_inline bool - messageTransmitted(); - - /** - * \brief Access the data of a received message - * - * Data access is only valid after isMessageAvailable() returns - * \c true and before any call of dropMessage() or update() - */ - static modm_always_inline const uint8_t * - getPayload(); - - /** - * \return Size of the received message. Zero if no message - * is available at the moment. - */ - static modm_always_inline uint8_t - getPayloadLength(); - - /** - * \brief End procession of the current message - */ - static void - dropMessage(); - - /** - * \brief Update internal status - * - * Has to be called periodically. Decodes received messages. - */ - static void - update(); - -#if AMNB_TIMING_DEBUG - static modm::ShortDuration latency; - static uint8_t collisions; -#endif - - private: - static bool - writeMessage(); - - enum State - { - SYNC, - LENGTH, - DATA - }; - - static uint8_t rx_buffer[maxPayloadLength + 3]; - static uint8_t tx_buffer[maxPayloadLength + 4]; - static uint8_t crc; - static uint8_t position; - static uint8_t length; - static uint8_t lengthOfReceivedMessage; - static uint8_t lengthOfTransmitMessage; - static modm::ShortTimeout resetTimer; - static const uint8_t resetTimeout = 4; - - static bool rescheduleTransmit; - static bool hasMessageToSend; - static bool messageSent; - static bool transmitting; - static modm::PreciseTimeout rescheduleTimer; - static uint8_t rescheduleTimeout; - - static State state; - }; - } -} - -#include "interface_impl.hpp" - -#endif // MODM_AMNB_INTERFACE_HPP diff --git a/src/modm/communication/amnb/interface.hpp.in b/src/modm/communication/amnb/interface.hpp.in new file mode 100644 index 0000000000..e9dc8944d5 --- /dev/null +++ b/src/modm/communication/amnb/interface.hpp.in @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include "message.hpp" +#include +#include +#include + +namespace modm::amnb +{ + +enum class InterfaceStatus : uint8_t +{ + Ok = 0, + + HeaderInvalid, + DataInvalid, + MediumBusy, + MediumEmpty, + + SyncReadFailed, + HeaderReadFailed, + DataReadFailed, + AllocationFailed, + + SyncWriteFailed, + HeaderWriteFailed, + DataWriteFailed, +}; + +/// @ingroup modm_communication_amnb +class Device +{ +public: + virtual bool + hasReceived() = 0; + + virtual modm::ResumableResult + write(uint8_t data) = 0; + + virtual modm::ResumableResult + read(uint8_t *data) = 0; +}; + +/// @ingroup modm_communication_amnb +template< class Uart, uint16_t TimeoutUsTx = 1000, uint16_t TimeoutUsRx = 10'000 > +class DeviceWrapper : public Device, modm::Resumable<2> +{ +public: + bool + hasReceived() final + { + return Uart::receiveBufferSize() > 0; + } + + modm::ResumableResult + write(uint8_t data) final + { + RF_BEGIN(0); + RF_WAIT_UNTIL(Uart::write(data)); + + timeout.restart(std::chrono::microseconds(TimeoutUsTx)); + RF_WAIT_UNTIL(Uart::read(rx_data) or Uart::hasError() or timeout.isExpired()); + if (timeout.isExpired() or Uart::hasError() or rx_data != data) + { + Uart::discardTransmitBuffer(); + Uart::discardReceiveBuffer(); + Uart::clearError(); + RF_RETURN(false); + } + RF_END_RETURN(true); + } + + modm::ResumableResult + read(uint8_t *data) final + { + RF_BEGIN(1); + timeout.restart(std::chrono::microseconds(TimeoutUsRx)); + RF_WAIT_UNTIL(Uart::read(*data) or Uart::hasError() or timeout.isExpired()); + if (timeout.isExpired() or Uart::hasError()) + { + Uart::discardReceiveBuffer(); + Uart::clearError(); + RF_RETURN(false); + } + RF_END_RETURN(true); + } + +protected: + modm::ShortPreciseTimeout timeout; + uint8_t rx_data; +}; + +/// @ingroup modm_communication_amnb +template< size_t MaxHeapAllocation = 0 > +class Interface : modm::Resumable<6> +{ +public: + Interface(Device &device) + : device(device) {} + + bool + isMediumBusy() const + { return isReceiving or device.hasReceived(); } + + modm::ResumableResult + transmit(const Message *message) + { + RF_BEGIN(0); + + if (isMediumBusy()) + RF_RETURN(InterfaceStatus::MediumBusy); + isTransmitting = true; + + tx_data = STX; + if (not RF_CALL(write())) RF_RETURN(InterfaceStatus::SyncWriteFailed); + if (not RF_CALL(write())) RF_RETURN(InterfaceStatus::SyncWriteFailed); + + for (tx_index = 0; tx_index < message->headerLength(); tx_index++) + if (not RF_CALL(write_escaped(message->self()[tx_index]))) + RF_RETURN(InterfaceStatus::HeaderWriteFailed); + + for (tx_index = 0; tx_index < message->dataLength(); tx_index++) + if (not RF_CALL(write_escaped(message->get()[tx_index]))) + RF_RETURN(InterfaceStatus::DataWriteFailed); + + isTransmitting = false; + RF_END_RETURN(InterfaceStatus::Ok); + } + + modm::ResumableResult + receiveHeader(Message *message) + { + RF_BEGIN(1); + + if (isTransmitting) + RF_RETURN(InterfaceStatus::MediumBusy); + + if (not device.hasReceived()) + RF_RETURN(InterfaceStatus::MediumEmpty); + + if (not RF_CALL(read())) RF_RETURN(InterfaceStatus::SyncReadFailed); + if (rx_data != STX) RF_RETURN(InterfaceStatus::SyncReadFailed); + isReceiving = true; + if (not RF_CALL(read())) { + isReceiving = false; + RF_RETURN(InterfaceStatus::SyncReadFailed); + } + if (rx_data != STX) { + isReceiving = false; + RF_RETURN(InterfaceStatus::SyncReadFailed); + } + + for(rx_index = 0; rx_index < message->SMALL_HEADER_SIZE; rx_index++) + { + if (not RF_CALL(read_escaped())) RF_RETURN(InterfaceStatus::HeaderReadFailed); + message->self()[rx_index] = rx_data; + } + for(; rx_index < message->headerLength(); rx_index++) + { + if (not RF_CALL(read_escaped())) RF_RETURN(InterfaceStatus::HeaderReadFailed); + message->self()[rx_index] = rx_data; + } + + if (not message->isHeaderValid()) { + isReceiving = false; + RF_RETURN(InterfaceStatus::HeaderInvalid); + } + if (not message->isLarge()) isReceiving = false; + RF_END_RETURN(InterfaceStatus::Ok); + } + + modm::ResumableResult + receiveData(Message *message, bool allocate=true) + { + RF_BEGIN(1); // same index as receiveHeader!!! + + if (not message->isLarge()) RF_RETURN(InterfaceStatus::Ok); + + if ( (rx_allocated = allocate and (message->dataLength() <= MaxHeapAllocation)) ) + rx_allocated = message->allocate(); + + for(rx_index = 0; rx_index < message->dataLength(); rx_index++) + { + if (not RF_CALL(read_escaped())) RF_RETURN(InterfaceStatus::DataReadFailed); + if (rx_allocated) message->get()[rx_index] = rx_data; + } + isReceiving = false; + if (allocate and not rx_allocated) RF_RETURN(InterfaceStatus::AllocationFailed); + + RF_END_RETURN(message->isDataValid() ? InterfaceStatus::Ok : InterfaceStatus::DataInvalid); + } + +protected: + modm::ResumableResult + write_escaped(uint8_t data) + { + RF_BEGIN(2); + if (data == STX or data == DLE) { + tx_data = DLE; + if (not RF_CALL(write())) RF_RETURN(false); + tx_data = data ^ 0x20; + } + else { + tx_data = data; + } + RF_END_RETURN_CALL(write()); + } + + modm::ResumableResult + read_escaped() + { + RF_BEGIN(3); + if (not RF_CALL(read())) RF_RETURN(false); + if (rx_data == DLE) { + if (not RF_CALL(read())) RF_RETURN(false); + rx_data ^= 0x20; + } + RF_END_RETURN(true); + } + + modm::ResumableResult + write() + { + RF_BEGIN(4); + if (RF_CALL(device.write(tx_data))) + RF_RETURN(true); + isTransmitting = false; + RF_END_RETURN(false); + } + + modm::ResumableResult + read() + { + RF_BEGIN(5); + if (RF_CALL(device.read(&rx_data))) + RF_RETURN(true); + isReceiving = false; + RF_END_RETURN(false); + } + +protected: + Device &device; + uint16_t tx_index; + uint16_t rx_index; + uint8_t tx_data; + uint8_t rx_data; + bool rx_allocated; + bool isReceiving{false}; + bool isTransmitting{false}; + static constexpr uint8_t STX{0x7E}; + static constexpr uint8_t DLE{0x7D}; +}; + +} // namespace modm::amnb + +%% if with_io +#include + +inline modm::IOStream& +operator << (modm::IOStream& s, const modm::amnb::InterfaceStatus status) +{ + using namespace modm::amnb; + switch(status) + { + case InterfaceStatus::Ok: s << "Ok"; break; + case InterfaceStatus::HeaderInvalid: s << "HeaderInvalid"; break; + case InterfaceStatus::DataInvalid: s << "DataInvalid"; break; + case InterfaceStatus::MediumBusy: s << "MediumBusy"; break; + case InterfaceStatus::MediumEmpty: s << "MediumEmpty"; break; + case InterfaceStatus::SyncReadFailed: s << "SyncReadFailed"; break; + case InterfaceStatus::HeaderReadFailed: s << "HeaderReadFailed"; break; + case InterfaceStatus::DataReadFailed: s << "DataReadFailed"; break; + case InterfaceStatus::AllocationFailed: s << "AllocationFailed"; break; + case InterfaceStatus::SyncWriteFailed: s << "SyncWriteFailed"; break; + case InterfaceStatus::HeaderWriteFailed: s << "HeaderWriteFailed"; break; + case InterfaceStatus::DataWriteFailed: s << "DataWriteFailed"; break; + } + return s; +} +%% endif diff --git a/src/modm/communication/amnb/interface_impl.hpp b/src/modm/communication/amnb/interface_impl.hpp deleted file mode 100644 index 320344d7db..0000000000 --- a/src/modm/communication/amnb/interface_impl.hpp +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2009-2010, Martin Rosekeit - * Copyright (c) 2009-2012, Fabian Greif - * Copyright (c) 2011-2013, 2015, Niklas Hauser - * Copyright (c) 2012-2013, Sascha Schade - * - * 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_AMNB_INTERFACE_HPP -# error "Don't include this file directly, use 'interface.hpp' instead!" -#endif - -#include - -// ---------------------------------------------------------------------------- -#ifdef __AVR__ -# include -#endif - -uint8_t -modm::amnb::crcUpdate(uint8_t crc, uint8_t data) -{ -#ifdef __AVR__ - return _crc_ibutton_update(crc, data); -#else - crc = crc ^ data; - for (uint_fast8_t i = 0; i < 8; ++i) - { - if (crc & 0x01) { - crc = (crc >> 1) ^ 0x8C; - } - else { - crc >>= 1; - } - } - return crc; -#endif -} - -// ---------------------------------------------------------------------------- -template typename modm::amnb::Interface::State \ - modm::amnb::Interface::state = SYNC; - -template -uint8_t modm::amnb::Interface::rx_buffer[maxPayloadLength + 3]; - -template -uint8_t modm::amnb::Interface::tx_buffer[maxPayloadLength + 4]; - -template -uint8_t modm::amnb::Interface::crc; - -template -uint8_t modm::amnb::Interface::position; - -template -uint8_t modm::amnb::Interface::length; - -template -uint8_t modm::amnb::Interface::lengthOfReceivedMessage = 0; - -template -uint8_t modm::amnb::Interface::lengthOfTransmitMessage = 0; - -template -bool modm::amnb::Interface::hasMessageToSend = false; - -template -bool modm::amnb::Interface::rescheduleTransmit = false; - -template -bool modm::amnb::Interface::transmitting = false; - -template -modm::ShortTimeout modm::amnb::Interface::resetTimer; - -template -modm::Timeout modm::amnb::Interface::rescheduleTimer; - -template -uint8_t modm::amnb::Interface::rescheduleTimeout; - -template -bool modm::amnb::Interface::messageSent = false; - -#if AMNB_TIMING_DEBUG -template -modm::ShortDuration modm::amnb::Interface::latency; - -template -uint8_t modm::amnb::Interface::collisions; -#endif - -// ---------------------------------------------------------------------------- - -template -void -modm::amnb::Interface::initialize(int seed) -{ - srand(seed); - rescheduleTimeout = static_cast(rand()) % TIMEOUT; - state = SYNC; -} - -// ---------------------------------------------------------------------------- - -template -bool -modm::amnb::Interface::writeMessage() -{ - uint8_t check; - transmitting = true; - Device::resetErrorFlags(); - - for (uint_fast8_t i=0; i < lengthOfTransmitMessage; ++i) { - Device::write(tx_buffer[i]); - - // try and read the transmitted byte back but do not wait infinity - uint16_t count(0); - while (!Device::read(check) && (++count <= 1000)) ; - - // if the read timed out or framing error occurred or content mismatch - if ((count > 1000) || Device::readErrorFlags() || (check != tx_buffer[i])) { - // stop transmitting, signal the collision - transmitting = false; - rescheduleTransmit = true; - Device::resetErrorFlags(); - // and wait for a random amount of time before sending again - rescheduleTimer.restart(std::chrono::microseconds(rescheduleTimeout)); -#if AMNB_TIMING_DEBUG - ++collisions; -#endif - return false; - } - } - -#if AMNB_TIMING_DEBUG - latency = modm::Clock::now() - latency; -#endif - - messageSent = true; - hasMessageToSend = false; - transmitting = false; - return true; -} - -template -bool -modm::amnb::Interface::sendMessage(uint8_t address, Flags flags, - uint8_t command, - const void *payload, uint8_t payloadLength) -{ - // dont overwrite the buffer when transmitting - if (transmitting) return false; - - hasMessageToSend = false; - messageSent = false; - uint8_t crc; - -#if AMNB_TIMING_DEBUG - latency = modm::Clock::now(); -#endif - - tx_buffer[0] = syncByte; - tx_buffer[1] = payloadLength; - tx_buffer[2] = address | flags; - tx_buffer[3] = command; - - crc = crcUpdate(crcInitialValue, payloadLength); - crc = crcUpdate(crc, address | flags); - crc = crcUpdate(crc, command); - - const uint8_t *ptr = static_cast(payload); - uint_fast8_t i=0; - for (; i < payloadLength; ++i) - { - crc = crcUpdate(crc, *ptr); - tx_buffer[i+4] = *ptr; - ptr++; - } - - tx_buffer[i+4] = crc; - - lengthOfTransmitMessage = payloadLength + 5; - hasMessageToSend = true; - return true; -} - -template template -bool -modm::amnb::Interface::sendMessage(uint8_t address, Flags flags, - uint8_t command, - const T& payload) -{ - return sendMessage(address, flags, - command, - reinterpret_cast(&payload), sizeof(T)); -} - -template -bool -modm::amnb::Interface::sendMessage(uint8_t address, Flags flags, uint8_t command) -{ - return sendMessage(address, flags, - command, - 0, 0); -} - -// ---------------------------------------------------------------------------- - -template -bool -modm::amnb::Interface::isMessageAvailable() -{ - return (lengthOfReceivedMessage != 0); -} - -template -uint8_t -modm::amnb::Interface::getTransmittedAddress() -{ - return (tx_buffer[0] & 0x3f); -} - -template -uint8_t -modm::amnb::Interface::getTransmittedCommand() -{ - return tx_buffer[1]; -} - -template -modm::amnb::Flags -modm::amnb::Interface::getTransmittedFlags() -{ - return static_cast(tx_buffer[0] & 0xc0); -} - -template -uint8_t -modm::amnb::Interface::getAddress() -{ - return (rx_buffer[0] & 0x3f); -} - -template -uint8_t -modm::amnb::Interface::getCommand() -{ - return rx_buffer[1]; -} - -template -bool -modm::amnb::Interface::isResponse() -{ - return (rx_buffer[0] & 0x80); -} - -template -bool -modm::amnb::Interface::isAcknowledge() -{ - return (rx_buffer[0] & 0x40); -} - -template -bool -modm::amnb::Interface::isBusAvailable() -{ - return (state == SYNC) && !transmitting && !rescheduleTransmit; -} - -template -bool -modm::amnb::Interface::messageTransmitted() -{ - return messageSent; -} - -template -const uint8_t* -modm::amnb::Interface::getPayload() -{ - return rx_buffer+2; -} - -template -uint8_t -modm::amnb::Interface::getPayloadLength() -{ - return (lengthOfReceivedMessage - 3); -} - -template -void -modm::amnb::Interface::dropMessage() -{ - lengthOfReceivedMessage = 0; -} - -// ---------------------------------------------------------------------------- - -template -void -modm::amnb::Interface::update() -{ - uint8_t byte; - while (Device::read(byte)) - { - if (Device::readErrorFlags()) - { - // collision has been detected - rescheduleTransmit = true; - Device::resetErrorFlags(); - - // erase the message in the buffer - Device::flushReceiveBuffer(); - - // and wait for a random amount of time before sending again - rescheduleTimeout = static_cast(rand()) % TIMEOUT; - rescheduleTimer.restart(std::chrono::microseconds(rescheduleTimeout)); - state = SYNC; -#if AMNB_TIMING_DEBUG - ++collisions; -#endif - return; - } - - switch (state) - { - case SYNC: - if (byte == syncByte) state = LENGTH; - break; - - case LENGTH: - if (byte > maxPayloadLength) { - state = SYNC; - } - else { - length = byte + 3; // +3 for header, command and crc byte - position = 0; - crc = crcUpdate(crcInitialValue, byte); - state = DATA; - } - break; - - case DATA: - rx_buffer[position++] = byte; - crc = crcUpdate(crc, byte); - - if (position >= length) - { - if (crc == 0) lengthOfReceivedMessage = length; - state = SYNC; - } - break; - - default: - state = SYNC; - break; - } - - resetTimer.restart(std::chrono::microseconds(resetTimeout)); - } - if ((state != SYNC) && resetTimer.isExpired()) state = SYNC; - - // check if we have waited for a random amount of time - if (rescheduleTransmit && rescheduleTimer.isExpired()) rescheduleTransmit = false; - - if (hasMessageToSend && !rescheduleTransmit && !transmitting && (state == SYNC)) { - // if channel is free, send with probability P - if (rescheduleTimeout < static_cast(2.56f * PROBABILITY)) { - writeMessage(); - } - // otherwise reschedule - else { - rescheduleTransmit = true; - rescheduleTimer.restart(std::chrono::microseconds(rescheduleTimeout % TIMEOUT)); - } - rescheduleTimeout = static_cast(rand()); - } -} diff --git a/src/modm/communication/amnb/message.hpp.in b/src/modm/communication/amnb/message.hpp.in new file mode 100644 index 0000000000..b75551372a --- /dev/null +++ b/src/modm/communication/amnb/message.hpp.in @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include + +namespace modm::amnb +{ + +/// @ingroup modm_communication_amnb +enum class Type: uint8_t +{ + Broadcast = 0b000 << 5, ///< Message Broadcast + Request = 0b010 << 5, ///< Action request + Response = 0b011 << 5, ///< Positive Response with a custom user payload + Error = 0b100 << 5, ///< Negative Response with a system error code + UserError = 0b101 << 5, ///< Negative Response with a custom user payload +}; + +/// @ingroup modm_communication_amnb +class modm_packed modm_aligned(4) Message +{ +public: + inline Message() = default; + inline Message(uint8_t address, uint8_t command, uint16_t length, Type type=Type::Broadcast) + : header{0, address, command, uint8_t(uint8_t(type) | + ((length <= SMALL_LENGTH) ? length : LENGTH_MASK))}, + storage{(length <= SMALL_LENGTH) ? uint16_t(0) : length} {} + + inline Message(uint16_t length) : Message(0, 0, length) {} + inline Message(uint8_t address, uint8_t command, Type type=Type::Broadcast) + : Message(address, command, 0, type) {} + + inline Message(const Message& m) { *this = m; } + inline Message(Message&& m) { *this = std::move(m); } + + inline ~Message() { deallocate(); } + + inline Message& operator=(const Message& m) + { + deallocate(); + header = m.header; + if (isLarge()) { + storage.large = m.storage.large; + if (storage.large.data) (*storage.large.data)++; + } + else storage.small = m.storage.small; + return *this; + } + inline Message& operator=(Message&& m) + { + deallocate(); + header = m.header; + if (isLarge()) { + storage.large = m.storage.large; + m.storage.large.data = nullptr; + } + else storage.small = m.storage.small; + m.header.type_length &= ~LENGTH_MASK; + return *this; + } + +public: + inline uint8_t address() const { return header.address; } + inline void setAddress(uint8_t address) { header.address = address; } + + inline uint8_t command() const { return header.command; } + inline void setCommand(uint8_t command) { header.command = command; } + + inline uint16_t length() const + { + if (isLarge()) return storage.large.length; + return std::min(smallLength(), SMALL_LENGTH); + } + inline void setLength(uint16_t length) + { + deallocate(); + if (length <= SMALL_LENGTH) { + header.type_length = (header.type_length & ~LENGTH_MASK) | length; + } + else { + header.type_length |= LENGTH_MASK; + storage.large.length = length; + storage.large.data = nullptr; + } + } + + inline Type type() const + { return Type(header.type_length & TYPE_MASK); } + inline void setType(Type type) + { header.type_length = (header.type_length & ~TYPE_MASK) | uint8_t(type); } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" + template + T* get() + { + if (isLarge()) { + if (not allocate() or sizeof(T) > storage.large.length) return nullptr; + return reinterpret_cast(storage.large.data + LARGE_DATA_OFFSET); + } + if (not smallLength() or sizeof(T) > smallLength()) return nullptr; + return reinterpret_cast(storage.small.data); + } + + template + const T* get() const + { + if (isLarge()) { + if (sizeof(T) > storage.large.length or not storage.large.data) return nullptr; + return reinterpret_cast(storage.large.data + LARGE_DATA_OFFSET); + } + if (not smallLength() or sizeof(T) > smallLength()) return nullptr; + return reinterpret_cast(storage.small.data); + } +#pragma GCC diagnostic pop + +protected: + inline bool isHeaderValid() const + { return crcHeader() == header.crc; } + + inline bool isDataValid() const + { + if (not isHeaderValid()) return false; + if (dataLength()) return (crcData() == storage.large.crc); + return true; + } + + inline void setValid() + { + if (isLarge() and allocate()) { + storage.large.crc = crcData(); + } + header.crc = crcHeader(); + } + +protected: + inline uint8_t + crcHeader() const + { + return modm::math::crc8_ccitt(&header.address, headerLength()-1); + } + + inline uint16_t + crcData() const + { + return modm::math::crc16_ccitt(storage.large.data + LARGE_DATA_OFFSET, dataLength()); + } + + inline uint8_t smallLength() const + { + return header.type_length & LENGTH_MASK; + } + + inline bool isLarge() const + { + return smallLength() > SMALL_LENGTH; + } + + inline uint8_t headerLength() const + { + if (isLarge()) return LARGE_HEADER_SIZE; + return SMALL_HEADER_SIZE + std::min(smallLength(), SMALL_LENGTH); + } + + inline uint8_t dataLength() const + { + return isLarge() ? storage.large.length : 0; + } + + inline bool allocate() + { + if (isLarge() and not storage.large.data) { +%% if with_heap + storage.large.data = new (std::nothrow) uint8_t[storage.large.length + LARGE_DATA_OFFSET]; + if (storage.large.data) { + *storage.large.data = 1; + return true; + } +%% endif + return false; + } + return true; + } + inline void deallocate() + { +%% if with_heap + if (isLarge() and storage.large.data) { + if (*storage.large.data <= 1) delete storage.large.data; + else (*storage.large.data)--; + storage.large.data = nullptr; + } +%% endif + } + inline uint8_t* self() + { return &header.crc; } + inline const uint8_t* self() const + { return &header.crc; } + +protected: + static constexpr uint8_t TYPE_MASK{0xE0}; + static constexpr uint8_t LENGTH_MASK{0x1F}; + static constexpr uint8_t SMALL_LENGTH{28}; +%% if target.platform == "avr" + static constexpr uint8_t LARGE_DATA_OFFSET{1}; +%% else + static constexpr uint8_t LARGE_DATA_OFFSET{4}; +%% endif + + static constexpr uint8_t SMALL_HEADER_SIZE{4}; + static constexpr uint8_t LARGE_HEADER_SIZE{8}; + +public: + static constexpr uint8_t SizeSmall{SMALL_LENGTH}; + static constexpr uint16_t SizeMax{8*1024}; + +protected: + // NOTE: This arrangement allows this data structure to be 4-byte aligned + // so that any Cortex-M0 device has no issues with unaligned access + // of neither the header values nor the data cast to another struct! + struct modm_packed Header { + uint8_t crc{0}; // 0 + uint8_t address{0}; // 1 + uint8_t command{0}; // 2 + uint8_t type_length{0}; // 3 + } header; + union modm_packed Storage { + inline Storage(uint16_t length=0) + : large{length,0,nullptr} {} + + struct modm_packed { + uint16_t length; // 4 5 + uint16_t crc; // 6 7 + uint8_t *data; // 8... 4-byte aligned + } large; + struct modm_packed { + uint8_t data[SMALL_LENGTH]; // 4... 4-byte aligned + } small; + } storage; + +private: + template< size_t > friend class Interface; + template< size_t, size_t > friend class Node; + template< class, class > friend class Result; +}; +static_assert(sizeof(Message) == 32, "modm::amnb::Message must be memory-packed!"); + +} // namespace modm::amnb + +%% if with_io +#include + +inline modm::IOStream& +operator << (modm::IOStream& s, const modm::amnb::Type type) +{ + using namespace modm::amnb; + switch(type) + { + case Type::Broadcast: s << "Broadcast"; break; + case Type::Request: s << "Request"; break; + case Type::UserError: s << "UserError"; break; + case Type::Error: s << "Error"; break; + case Type::Response: s << "Response"; break; + } + return s; +} +%% endif diff --git a/src/modm/communication/amnb/module.lb b/src/modm/communication/amnb/module.lb new file mode 100644 index 0000000000..2f7fbdbed3 --- /dev/null +++ b/src/modm/communication/amnb/module.lb @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, 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 = ":communication:amnb" + module.description = FileReader("module.md") + +def prepare(module, options): + module.depends( + ":math:utils", + ":processing:resumable", + ":processing:timer", + ":container") + + module.add_option( + BooleanOption(name="with_heap", + description="Allow large messages to allocate on the heap.", + default=True)) + + return True + +def build(env): + env.substitutions = { + "with_io": env.has_module(":io"), + "target": env[":target"].identifier, + "with_heap": env["with_heap"] + } + env.outbasepath = "modm/src/modm/communication/amnb" + env.template("message.hpp.in") + env.template("interface.hpp.in") + env.template("handler.hpp.in") + env.copy("node.hpp") + env.copy("../amnb.hpp") diff --git a/src/modm/communication/amnb/module.md b/src/modm/communication/amnb/module.md new file mode 100644 index 0000000000..263307638d --- /dev/null +++ b/src/modm/communication/amnb/module.md @@ -0,0 +1,88 @@ +# Asynchronous Multi-Node Bus (AMNB) + +The AMNB (**A**synchronous **M**ulti-**N**ode **B**us) is a multi-master bus +system, using p-persistent CSMA/CD to send messages. + +One bus can be populated with up to 256 (logical) nodes. The nodes can be +queried for data and they will respond like an SAB Slave, and can query data +from other nodes like an SAB Master, or they can just broadcast a message. Each +node can listen to all the responses and broadcasts and store that information +for its purpose. + +Action callbacks to query requests can be defined as well as universal callbacks +to any transmitted messaged (Listener callbacks). As an optional advanced +feature, error handling callbacks can also be defined, which fire if messages +have not been able to be sent, or requests timed out or misbehaved in other +manners, or other nodes query unavailable information. + + +## Protocol Structure + +Message without data: + +- `SYNC`: Synchronization sequence. +- `CRC8`: checksum for the header +- `ADDR`: Node address. +- `TYPE`: Message type . +- `COMMAND`: Command code. + +``` + 1 2 3 4 5 6 ++------+------+------+---------+------+ +| SYNC | CRC8 | ADDR | COMMAND | TYPE | ++------+------+------+---------+------+ +``` + +Message with <=28B data: + +- `CRC8`: checksum for the header and data. +- `TYPE/LENGTH`: Message type and length. + +``` + 1 2 3 4 5 6 7 ... ++------+------+------+---------+-------------+--------------------+ +| SYNC | CRC8 | ADDR | COMMAND | TYPE/LENGTH | ... <=28B DATA ... | ++------+------+------+---------+-------------+--------------------+ +``` + +Message with <8kB data: + +- `CRC16`: checksum for the data. +- `DATA`: Up to 8kB payload (nodes may support *much* less though!) + +``` + 1 2 3 4 5 6 7 8 9 10 11 ... ++------+------+------+---------+------+--------+-------+-------------------+ +| SYNC | CRC8 | ADDR | COMMAND | TYPE | LENGTH | CRC16 | ... <8kB DATA ... | ++------+------+------+---------+------+--------+-------+-------------------+ +``` + + +## Message Types + +| Type | Meaning | +|:----:|:---------------------------------------| +| 000 | Data broadcast by a node | +| 010 | Data request by a node | +| 100 | Negative response from the node (NACK) | +| 101 | Positive response from the node (ACK) | + +When transmitting, the *second bit* determines, whether or not to expect an +answer from the addressed node. To just send information without any need for +acknowledgment, use a broadcast. + +When a node is responding, the *second bit* has the following meaning: + +- `true`: Message is an positive response and may contain a payload +- `false`: Message signals an error condition and carries only one byte of + payload. This byte is an error code. + + +## Electrical characteristics + +Between different boards CAN transceivers are used. Compared to RS485 the CAN +transceivers have the advantage to work without a separate direction input. You +can just connected the transceiver directly to the UART of your microcontroller. +These are identical to the SAB CAN electrical characteristics. You have to use +the CAN transceivers, otherwise it cannot be determined, if the bus is busy or +available for transmission. diff --git a/src/modm/communication/amnb/node.hpp b/src/modm/communication/amnb/node.hpp index 7f9e790c63..4ee5c21a28 100644 --- a/src/modm/communication/amnb/node.hpp +++ b/src/modm/communication/amnb/node.hpp @@ -1,7 +1,5 @@ /* - * Copyright (c) 2011, Fabian Greif - * Copyright (c) 2011-2013, 2015-2016, Niklas Hauser - * Copyright (c) 2012-2013, Sascha Schade + * Copyright (c) 2020, Niklas Hauser * * This file is part of the modm project. * @@ -11,559 +9,314 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_AMNB_NODE_HPP -#define MODM_AMNB_NODE_HPP - -#include -#include -#include +#pragma once #include "interface.hpp" +#include "handler.hpp" +#include +#include -namespace modm +namespace modm::amnb { - namespace amnb + +/** + * @author Niklas Hauser + * @ingroup modm_communication_amnb + */ +template < size_t TxBufferSize = 2, size_t MaxHeapAllocation = 0 > +class Node : public modm::Resumable<6> +{ + static_assert(2 <= TxBufferSize, "TxBuffer must have at least two messages!"); +public: + Node(Device &device, uint8_t address): interface(device), address(address) + { setSeed(); } + + template< size_t Actions > + Node(Device &device, uint8_t address, const Action (&actions)[Actions]) + : interface(device), actionList(actions), actionCount(Actions), address(address) { - /** - * \internal - * \brief Interface used to transmit data through a slave object - * - * \ingroup amnb - */ - class Transmitter - { - public: - virtual void - send(bool acknowledge, const void *payload, uint8_t payloadLength) = 0; - }; - - /** - * \brief Response object for an action call - * - * \ingroup amnb - */ - class Response - { - template - friend class Node; - - public: - /** - * \brief Signal an error condition - * - * \param errorCode Error code. Values below 0x20 are reserved - * for the system, feel free to use any other - * value for specific error conditions. - * - * \see modm::amnb::Error - */ - void - error(uint8_t errorCode = ERROR_GENERAL_ERROR); - - /** - * \brief Send a response without any data - */ - void - send(); - - /** - * \brief Send a response with an attached payload - * - * \param payload Pointer to the payload - * \param length Number of bytes - */ - void - send(const void *payload, std::size_t length); - - /** - * \brief Send a response with an attached payload - */ - template - modm_always_inline void - send(const T& payload); - - protected: - Response(Transmitter *parent); - - Response(const Response&); - - Response& - operator = (const Response&); - - Transmitter *transmitter; - bool triggered; ///< is used by amnp::Node to check that a response was send - }; - - /** - * \brief Base-class for every object which should be used inside - * a callback. - * - * Example: - * \code - * class Sensor : public modm::amnb::Callable - * { - * public: - * void - * sendValue(modm::amnb::Response& response) - * { - * response.send(this->value); - * } - * - * // ... - * - * private: - * int8_t value; - * }; - * \endcode - * - * A complete example is available in the \c example/amnb folder. - * - * \see modm::amnb::Node - * \ingroup amnb - */ - struct Callable - { - }; - - /** - * \brief Possible Action - * - * \see AMNB_ACTION() - * \ingroup amnb - */ - struct Action + static_assert(Actions <= 0xff, "Actions list must be smaller than 255!"); + setSeed(); + } + + template< size_t Listeners > + Node(Device &device, uint8_t address, const Listener (&listeners)[Listeners]) + : interface(device), listenerList(listeners), listenerCount(Listeners), address(address) + { + static_assert(Listeners <= 0xff, "Listeners list must be smaller than 255!"); + setSeed(); + } + + template< size_t Actions, size_t Listeners > + Node(Device &device, uint8_t address, const Action (&actions)[Actions], const Listener (&listeners)[Listeners]) + : interface(device), actionList(actions), listenerList(listeners), + actionCount(Actions), listenerCount(Listeners), address(address) + { + static_assert(Actions <= 0xff, "Actions list must be smaller than 255!"); + static_assert(Listeners <= 0xff, "Listeners list must be smaller than 255!"); + setSeed(); + } + + void + setAddress(uint8_t address) + { + this->address = address; + setSeed(); + } + +public: + bool + broadcast(uint8_t command) + { + if (tx_queue.isFull()) return false; + return tx_queue.push(std::move(Message(address, command, Type::Broadcast))); + } + template< typename T > + bool + broadcast(uint8_t command, const T &argument) + { + if (tx_queue.isFull()) return false; + Message msg(address, command, sizeof(T), Type::Broadcast); + T* t = msg.get(); + if (t == nullptr) return false; + *t = argument; + return tx_queue.push(std::move(msg)); + } + + template< class ReturnType = void, class ErrorType = void > + modm::ResumableResult< Result > + request(uint8_t from, uint8_t command) + { + RF_BEGIN(0); + request_msg = Message(from, command, Type::Request); + RF_CALL(request()); + RF_END_RETURN(request_msg); + } + + template< class ReturnType = void, class ErrorType = void, class T > + modm::ResumableResult< Result > + request(uint8_t from, uint8_t command, const T &argument) + { + RF_BEGIN(1); { - typedef void (Callable::*Callback)(Response& response, const void *payload); - - inline void - call(Response& response, const void *payload); - - uint8_t command; - uint8_t payloadLength; //!< Payload length in bytes - Callable *object; - Callback function; //!< Method callActionback - }; - - /** - * \brief Possible Listener - * - * \see AMNB_LISTEN() - * \ingroup amnb - */ - struct Listener + request_msg = Message(from, command, sizeof(T), Type::Request); + T* arg = request_msg.get(); + if constexpr (sizeof(T) > Message::SMALL_LENGTH) { + if (arg == nullptr) { + request_msg = Response(Error::RequestAllocationFailed).msg; + RF_RETURN(request_msg); + } + } + *arg = argument; + } + RF_CALL(request()); + RF_END_RETURN(request_msg); + } + +public: + void + update() + { + transmit(); + receive(); + } + +protected: + modm::ResumableResult + transmit() + { + RF_BEGIN(2); + while(1) { - typedef void (Callable::*Callback)(const void *payload, const uint8_t length, const uint8_t sender); - - inline void - call(const void *payload, const uint8_t length, const uint8_t sender); - - uint8_t address; //!< Address of transmitting node - uint8_t command; - Callable *object; - Callback function; //!< Method callActionback - }; - - /** - * \brief Possible Error - * - * \see AMNB_ERROR() - * \ingroup amnb - */ - struct ErrorHandler + if (not tx_queue.isEmpty()) + { + RF_WAIT_WHILE(isResumableRunning(3)); + RF_CALL(send(tx_queue.get())); + tx_queue.pop(); + } + RF_YIELD(); + } + RF_END(); + } + + modm::ResumableResult + send(Message &msg) + { + RF_BEGIN(3); + + msg.setValid(); + tx_counter = std::min(10, msg.command() >> (8 - PRIORITY_BITS)); + while(1) { - typedef void (Callable::*Callback)(Flags type, const uint8_t errorCode); - - inline void - call(Flags type, const uint8_t errorCode); - - uint8_t address; //!< Node address of message - uint8_t command; //!< Command of message - Callable *object; - Callback function; //!< Method callActionback - }; - - /** - * \brief AMNB Node - * - * \code - * typedef modm::amnb::Node< modm::amnb::Interface< modm::BufferedUart0 > > Node; - * - * FLASH_STORAGE(modm::amnb::Action actionList[]) = - * { - * AMNB_ACTION(0x57, object, Object::method1, 0), - * AMNB_ACTION(0x03, object, Object::method2, 2), - * }; - * // optional - * FLASH_STORAGE(modm::amnb::Listener listenList[]) = - * { - * AMNB_LISTEN(0x29, 0x46, object, Object::method) - * }; - * // also optional - * FLASH_STORAGE(modm::amnb::ErrorHandler errorHandlerList[]) = - * { - * AMNB_LISTEN(0x29, 0x46, object, Object::method) - * }; - * - * int - * main() - * { - * // initialize the interface - * Node node(0x02, - * modm::accessor::asFlash(actionList), - * sizeof(actionList) / sizeof(modm::amnb::Action), - * // optional - * modm::accessor::asFlash(listenList), - * sizeof(listenList) / sizeof(modm::amnb::Listener), - * modm::accessor::asFlash(errorHandlerList), - * sizeof(errorHandlerList) / sizeof(modm::amnb::ErrorHandler)); - * - * while (true) - * { - * node.update(); - * } - * } - * \endcode - * - * A complete example is available in the \c example/amnb folder. - * - * \author Fabian Greif, Niklas Hauser - * \ingroup amnb - */ - template - class Node : protected Transmitter + while (interface.isMediumBusy()) + { + RF_WAIT_WHILE(interface.isMediumBusy()); + reschedule(RESCHEDULE_MASK_SHORT); + RF_WAIT_UNTIL(tx_timer.isExpired()); + } + + if (RF_CALL(interface.transmit(&msg)) == InterfaceStatus::Ok) + break; + + if (--tx_counter == 0) break; + + // a collision or other write issue occurred + RF_WAIT_WHILE(interface.isMediumBusy()); + reschedule(RESCHEDULE_MASK_LONG); + RF_WAIT_UNTIL(tx_timer.isExpired()); + } + RF_END(); + } + + modm::ResumableResult + request() + { + RF_BEGIN(4); + + RF_WAIT_WHILE(isResumableRunning(3)); + response_status = ResponseStatus::Waiting; + RF_CALL(send(request_msg)); + + response_timer.restart(1s); + RF_WAIT_UNTIL((response_status == ResponseStatus::Received) or response_timer.isExpired()); + response_status = ResponseStatus::NotWaiting; + + if (response_timer.isExpired()) { + request_msg = Message(address, request_msg.command(), 1, Type::Error); + *request_msg.get() = Error::RequestTimeout; + } + + RF_END(); + } + + modm::ResumableResult + receive() + { + RF_BEGIN(5); + while(1) { - public: - /** - * \brief Initialize the node - * - * \param address Own address - * \param actionList List of all action callbacks, need to be - * stored in flash-memory - * \param actionCount Number of entries in \a actionList - * \param listenList List of all listener callbacks, need to be - * stored in flash-memory - * \param listenCount Number of entries in \a listenList - * \param errorHandlerList List of all error handler callbacks, - * need to be stored in flash-memory - * \param errorHandlerCount Number of entries in \a errorHandlerList - * - * \see AMNB_ACTION() - * \see AMNB_LISTEN() - * \see AMNB_ERROR() - */ - Node(uint8_t address, modm::accessor::Flash actionList, uint8_t actionCount, - modm::accessor::Flash listenList, uint8_t listenCount, - modm::accessor::Flash errorHandlerList, uint8_t errorHandlerCount); - - /** - * \brief Initialize the node without error handlers - */ - Node(uint8_t address, modm::accessor::Flash actionList, uint8_t actionCount, - modm::accessor::Flash listenList, uint8_t listenCount); - - /** - * \brief Initialize the node without listeners or error handlers - */ - Node(uint8_t address, modm::accessor::Flash actionList, uint8_t actionCount); - - /** - * \brief Start a new query with a payload - * - * \param slaveAddress - * \param command - * \param payload - * \param responseLength Expected payload length of the response - * \return \c true if no error occurred - */ - template - bool - query(uint8_t slaveAddress, uint8_t command, - const T& payload, uint8_t responseLength); - - template - bool - query(uint8_t slaveAddress, uint8_t command, - const void *payload, uint8_t payloadLength, uint8_t responseLength); - - /** - * \brief Start a new query without any payload - * - * \return \c true if no error occurred - */ - bool - query(uint8_t slaveAddress, uint8_t command, uint8_t responseLength); - - /** - * \brief Start a new broadcast with a payload - * - * \param command - * \param payload - * \return \c true if no error occurred - */ - template - bool - broadcast(uint8_t command, const T& payload); - - /** - * \brief Start a new broadcast with a payload - * - * \param command - * \param payload - * \param payloadLength - * \return \c true if no error occurred - */ - bool - broadcast(uint8_t command, const void *payload, uint8_t payloadLength); - - /** - * \brief Start a new broadcast without any payload - * - * \return \c true if no error occurred - */ - bool - broadcast(uint8_t command); - - bool - isQueryCompleted(); - - /** - * \brief Check if the last query could be performed successful - * - * Only valid if isQueryCompleted() returns \c true. - * - * \return \c true if the query was successful. Use getResponse() to - * access the result. - */ - bool - isSuccess(); - - /** - * \brief Check error code - * - * Only valid if isQueryCompleted() returns \c true while - * isSuccess() returns \c false. - * - * \return Error code - * \see modm::amnb::Error - */ - uint8_t - getErrorCode(); - - - template - inline const T * - getResponse(); - - inline const void * - getResponse(); - - /** - * \brief Receive and process messages - * - * This method will decode the incoming messages and call the - * corresponding callback methods from the action list. It must - * be called periodically, best in every main loop cycle. - */ - void - update(); - - protected: - void - send(bool acknowledge, const void *payload, uint8_t payloadLength); - - bool - checkErrorHandlers(uint8_t address, uint8_t command, Flags type, uint8_t errorCode); - - - uint8_t ownAddress; - modm::accessor::Flash actionList; - uint8_t actionCount; - modm::accessor::Flash listenList; - uint8_t listenCount; - modm::accessor::Flash errorHandlerList; - uint8_t errorHandlerCount; - - uint8_t currentCommand; - Response response; - - enum QueryStatus + rx_msg.deallocate(); // deallocates previous message + if (RF_CALL(interface.receiveHeader(&rx_msg)) == InterfaceStatus::Ok) { - IN_PROGRESS, ///< Query in progress - SUCCESS, ///< Response successfully received - ERROR_RESPONSE = 0x40, ///< Error in the received message - ERROR_TIMEOUT = 0x41, ///< No message received within the timeout window - ERROR_PAYLOAD = 0x42, ///< Wrong payload size - }; - - QueryStatus queryStatus; - uint8_t expectedAddress; - uint8_t expectedResponseLength; - modm::ShortTimeout timer; - /// timeout value in milliseconds - static constexpr std::chrono::milliseconds timeout{10}; - }; + // Check lists if we are interested in this message + is_rx_msg_for_us = handleRxMessage(false); + // Receive the message data, only allocate if it's for us + if (RF_CALL(interface.receiveData(&rx_msg, is_rx_msg_for_us)) == InterfaceStatus::Ok) + { + // Only handle message *with* data if it's for us + if (is_rx_msg_for_us) handleRxMessage(true); + } + } + RF_YIELD(); + } + RF_END(); } -} -#ifdef __DOXYGEN__ - /** - * \brief Define a amnb::Action - * - * Example: - * \code - * class Sensor : public modm::amnb::Callable - * { - * public: - * void - * sendValue(modm::amnb::Response& response) - * { - * response.send(this->value); - * } - * - * void - * doSomething(modm::amnb::Response& response, const uint32_t* parameter) - * { - * // ... do something useful ... - * - * response.send(); - * } - * - * // ... - * - * private: - * int8_t value; - * }; - * - * Sensor sensor; - * - * FLASH_STORAGE(modm::amnb::Action actionList[]) = - * { - * AMNB_ACTION(0x57, sensor, Sensor::sendValue, 0), - * AMNB_ACTION(0x03, sensor, Sensor::doSomething, sizeof(uint32_t)), - * }; - * \endcode - * - * A complete example is available in the \c example/amnb folder. - * - * \param command Command byte - * \param object - * \param function Member function of object - * \param length Parameter size in bytes - * - * \see modm::amnb::Action - * \ingroup amnb - */ - #define AMNB_ACTION(command, object, function, length) -#else - #define AMNB_ACTION(command, object, function, length) \ - { command, \ - length, \ - static_cast(&object), \ - reinterpret_cast(&function) } -#endif // __DOXYGEN__ - - - -#ifdef __DOXYGEN__ - /** - * \brief Define a amnb::Listener - * - * Example: - * \code - * class ListenToNodes : public modm::amnb::Callable - * { - * public: - * void - * listenToCommand(uint8_t *payload, const uint8_t length, uint8_t sender) - * { - * // ... do something useful ... - * - * } - * - * void - * listenToCommandWithOutCaringForSender(uint8_t *payload, const uint8_t length) - * { - * // ... do something useful ... - * - * } - * - * // ... - * }; - * - * ListenToNodes listen; - * - * FLASH_STORAGE(modm::amnb::Listener listenList[]) = - * { - * AMNB_LISTEN(0x46, 0x03, listen, ListenToNodes::listenToCommand), - * }; - * \endcode - * - * A complete example is available in the \c example/amnb folder. - * - * \param address Address of the transmitting node - * \param command Command byte - * \param object - * \param function Member function of object - * - * \see modm::amnb::Listener - * \ingroup amnb - */ - #define AMNB_LISTEN(address, command, object, function) -#else - #define AMNB_LISTEN(address, command, object, function) \ - { address, \ - command, \ - static_cast(&object), \ - reinterpret_cast(&function) } -#endif // __DOXYGEN__ - - -#ifdef __DOXYGEN__ - /** - * \brief Define a amnb::ErrorHandler - * - * Example: - * \code - * class handleErrors : public modm::amnb::Callable - * { - * public: - * void - * errorForCommand(modm::amnb::Flags type, const uint8_t errorCode) - * { - * // ... do something useful with that information ... - * - * } - * - * // ... - * }; - * - * handleErrors errorhandler; - * - * FLASH_STORAGE(modm::amnb::Listener listenList[]) = - * { - * AMNB_LISTEN(0x37, 0x57, errorhandler, handleErrors::errorForCommand), - * }; - * \endcode - * - * A complete example is available in the \c example/amnb folder. - * - * \param address Node address of message - * \param command Command of message - * \param object - * \param function Member function of object - * - * \see modm::amnb::ErrorHandler - * \ingroup amnb - */ - #define AMNB_ERROR(address, command, object, function) -#else - #define AMNB_ERROR(address, command, object, function) \ - { address, \ - command, \ - static_cast(&object), \ - reinterpret_cast(&function) } -#endif // __DOXYGEN__ - -#include "node_impl.hpp" - -#endif // MODM_AMNB_NODE_HPP +protected: + bool + handleRxMessage(bool complete) + { + switch(rx_msg.type()) + { + case Type::Broadcast: + for (size_t ii=0; ii() = Error::NoAction; + tx_queue.push(std::move(msg)); + } + break; + + default: + if (response_status == ResponseStatus::Waiting) + { + if (complete and + request_msg.address() == rx_msg.address() and + request_msg.command() == rx_msg.command()) + { + request_msg = std::move(rx_msg); + response_status = ResponseStatus::Received; + } + return true; + } + } + return false; + } + + void + setSeed() + { lfsr = address << 8 | (address + 1); } + + void + reschedule(uint8_t mask) + { + lfsr ^= lfsr >> 7; + lfsr ^= lfsr << 9; + lfsr ^= lfsr >> 13; + + const uint16_t priority = ((1u << PRIORITY_BITS) - 1 - tx_counter) >> 3; + const uint16_t delay = (lfsr & ((1ul << mask) - 1)) | (priority << mask); + tx_timer.restart(std::chrono::microseconds(delay)); + } + +protected: + Interface interface; + + const Action *const actionList{nullptr}; + const Listener *const listenerList{nullptr}; + + modm::ShortPreciseTimeout tx_timer; + modm::ShortTimeout response_timer; + + modm::BoundedQueue tx_queue; + Message request_msg; + Message rx_msg; + + uint16_t lfsr; + const uint8_t actionCount{0}; + const uint8_t listenerCount{0}; + uint8_t address; + + uint8_t tx_counter; + bool is_rx_msg_for_us; + + enum class ResponseStatus : uint8_t + { + NotWaiting = 0, + Waiting, + Received, + } + response_status{ResponseStatus::NotWaiting}; + + static constexpr uint8_t PRIORITY_BITS{5}; + static constexpr uint8_t RESCHEDULE_MASK_SHORT{7}; + static constexpr uint8_t RESCHEDULE_MASK_LONG{11}; +}; + +} diff --git a/src/modm/communication/amnb/node_impl.hpp b/src/modm/communication/amnb/node_impl.hpp deleted file mode 100644 index e81ce164f6..0000000000 --- a/src/modm/communication/amnb/node_impl.hpp +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright (c) 2011, Fabian Greif - * Copyright (c) 2011-2013, Niklas Hauser - * Copyright (c) 2013, Sascha Schade - * - * 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_AMNB_NODE_HPP -#error "Don't include this file directly, use 'node.hpp' instead!" -#endif - -// ---------------------------------------------------------------------------- -template -void -modm::amnb::Response::send(const T& payload) -{ - triggered = true; - transmitter->send(true, reinterpret_cast(&payload), sizeof(T)); -} - -modm::amnb::Response::Response(Transmitter *parent) : -transmitter(parent), triggered(false) -{ -} - -void -modm::amnb::Response::error(uint8_t errorCode) -{ - triggered = true; - - uint8_t error = errorCode; - transmitter->send(false, &error, 1); -} - -void -modm::amnb::Response::send() -{ - triggered = true; - transmitter->send(true, 0, 0); -} - -void -modm::amnb::Response::send(const void *payload, std::size_t length) -{ - triggered = true; - transmitter->send(true, payload, length); -} - -// ---------------------------------------------------------------------------- -inline void -modm::amnb::Action::call(Response& response, const void *payload) -{ - // redirect call to the actual object - (object->*function)(response, payload); -} - -// ---------------------------------------------------------------------------- -inline void -modm::amnb::Listener::call(const void *payload, const uint8_t length, const uint8_t sender) -{ - // redirect call to the actual object - (object->*function)(payload, length, sender); -} - -// ---------------------------------------------------------------------------- -inline void -modm::amnb::ErrorHandler::call(Flags type, const uint8_t errorCode) -{ - // redirect call to the actual object - (object->*function)(type, errorCode); -} - -// Disable warnings for Visual Studio about using 'this' in a base member -// initializer list. -// In this case though it is totally safe so it is ok to disable this warning. -#ifdef MODM_COMPILER_MSVC -# pragma warning(disable:4355) -#endif - -// ---------------------------------------------------------------------------- -template -modm::amnb::Node::Node(uint8_t address, - modm::accessor::Flash actionList, - uint8_t actionCount, - modm::accessor::Flash listenList, - uint8_t listenCount, - modm::accessor::Flash errorHandlerList, - uint8_t errorHandlerCount) : -ownAddress(address), -actionList(actionList), actionCount(actionCount), -listenList(listenList), listenCount(listenCount), -errorHandlerList(errorHandlerList), errorHandlerCount(errorHandlerCount), -response(this) -{ - Interface::initialize(address); - queryStatus = ERROR_TIMEOUT; -} - -template -modm::amnb::Node::Node(uint8_t address, - modm::accessor::Flash actionList, - uint8_t actionCount, - modm::accessor::Flash listenList, - uint8_t listenCount) : -ownAddress(address), -actionList(actionList), actionCount(actionCount), -listenList(listenList), listenCount(listenCount), -errorHandlerCount(0), -response(this) -{ - Interface::initialize(address); - queryStatus = ERROR_TIMEOUT; -} - -template -modm::amnb::Node::Node(uint8_t address, - modm::accessor::Flash actionList, - uint8_t actionCount) : -ownAddress(address), -actionList(actionList), actionCount(actionCount), -listenCount(0), -errorHandlerCount(0), -response(this) -{ - Interface::initialize(address); - queryStatus = ERROR_TIMEOUT; -} - -// ---------------------------------------------------------------------------- -template template -bool -modm::amnb::Node::query(uint8_t slaveAddress, uint8_t command, - const T& payload, uint8_t responseLength) -{ - if (queryStatus == IN_PROGRESS) { - checkErrorHandlers(slaveAddress, command, REQUEST, ERROR_QUERY_IN_PROGRESS); - return false; - } - - bool noError(true); - if (!Interface::messageTransmitted()) { - checkErrorHandlers(Interface::getTransmittedAddress(), - Interface::getTransmittedCommand(), - Interface::getTransmittedFlags(), - ERROR_MESSAGE_OVERWRITTEN); - noError = false; - } - if (!Interface::sendMessage(slaveAddress, REQUEST, command, payload)) { - checkErrorHandlers(slaveAddress, command, REQUEST, ERROR_TRANSMITTER_BUSY); - noError = false; - } - - queryStatus = IN_PROGRESS; - expectedResponseLength = responseLength; - expectedAddress = slaveAddress; - - timer.restart(timeout); - return noError; -} - -template template -bool -modm::amnb::Node::query(uint8_t slaveAddress, uint8_t command, - const void *payload, uint8_t payloadLength, uint8_t responseLength) -{ - if (queryStatus == IN_PROGRESS) { - checkErrorHandlers(slaveAddress, command, REQUEST, ERROR_QUERY_IN_PROGRESS); - return false; - } - - bool noError(true); - if (!Interface::messageTransmitted()) { - checkErrorHandlers(Interface::getTransmittedAddress(), - Interface::getTransmittedCommand(), - Interface::getTransmittedFlags(), - ERROR_MESSAGE_OVERWRITTEN); - noError = false; - } - if (!Interface::sendMessage(slaveAddress, REQUEST, command, payload, payloadLength)) { - checkErrorHandlers(slaveAddress, command, REQUEST, ERROR_TRANSMITTER_BUSY); - noError = false; - } - - queryStatus = IN_PROGRESS; - expectedResponseLength = responseLength; - expectedAddress = slaveAddress; - - timer.restart(timeout); - return noError; -} - -template -bool -modm::amnb::Node::query(uint8_t slaveAddress, uint8_t command, - uint8_t responseLength) -{ - if (queryStatus == IN_PROGRESS) { - checkErrorHandlers(slaveAddress, command, REQUEST, ERROR_QUERY_IN_PROGRESS); - return false; - } - - bool noError(true); - if (!Interface::messageTransmitted()) { - checkErrorHandlers(Interface::getTransmittedAddress(), - Interface::getTransmittedCommand(), - Interface::getTransmittedFlags(), - ERROR_MESSAGE_OVERWRITTEN); - noError = false; - } - if (!Interface::sendMessage(slaveAddress, REQUEST, command, 0, 0)) { - checkErrorHandlers(slaveAddress, command, REQUEST, ERROR_TRANSMITTER_BUSY); - noError = false; - } - - queryStatus = IN_PROGRESS; - expectedResponseLength = responseLength; - expectedAddress = slaveAddress; - - timer.restart(timeout); - return noError; -} - -// ---------------------------------------------------------------------------- -template template -bool -modm::amnb::Node::broadcast(uint8_t command, const T& payload) -{ - bool noError(true); - if (!Interface::messageTransmitted()) { - checkErrorHandlers(Interface::getTransmittedAddress(), - Interface::getTransmittedCommand(), - Interface::getTransmittedFlags(), - ERROR_MESSAGE_OVERWRITTEN); - noError = false; - - } - if (!Interface::sendMessage(this->ownAddress, BROADCAST, command, payload)) { - checkErrorHandlers(this->ownAddress, command, BROADCAST, ERROR_TRANSMITTER_BUSY); - noError = false; - } - return noError; -} - -template -bool -modm::amnb::Node::broadcast(uint8_t command, const void *payload, uint8_t payloadLength) -{ - bool noError(true); - if (!Interface::messageTransmitted()) { - checkErrorHandlers(Interface::getTransmittedAddress(), - Interface::getTransmittedCommand(), - Interface::getTransmittedFlags(), - ERROR_MESSAGE_OVERWRITTEN); - noError = false; - } - if (!Interface::sendMessage(this->ownAddress, BROADCAST, command, payload, payloadLength)) { - checkErrorHandlers(this->ownAddress, command, BROADCAST, ERROR_TRANSMITTER_BUSY); - noError = false; - } - return noError; -} - -template -bool -modm::amnb::Node::broadcast(uint8_t command) -{ - bool noError(true); - if (!Interface::messageTransmitted()) { - checkErrorHandlers(Interface::getTransmittedAddress(), - Interface::getTransmittedCommand(), - Interface::getTransmittedFlags(), - ERROR_MESSAGE_OVERWRITTEN); - noError = false; - } - if (!Interface::sendMessage(this->ownAddress, BROADCAST, command, 0,0)) { - checkErrorHandlers(this->ownAddress, command, BROADCAST, ERROR_TRANSMITTER_BUSY); - noError = false; - } - return noError; -} - -// ---------------------------------------------------------------------------- -template -bool -modm::amnb::Node::isQueryCompleted() -{ - return (queryStatus != IN_PROGRESS); -} - -// ---------------------------------------------------------------------------- -template -bool -modm::amnb::Node::isSuccess() -{ - return (queryStatus == SUCCESS); -} - -// ---------------------------------------------------------------------------- -template -uint8_t -modm::amnb::Node::getErrorCode() -{ - if (queryStatus == ERROR_RESPONSE) { - // Error code is in the first payload byte - return Interface::getPayload()[0]; - } - else { - return queryStatus; - } -} - -// ---------------------------------------------------------------------------- -template template -const T * -modm::amnb::Node::getResponse() -{ - return reinterpret_cast(Interface::getPayload()); -} - -template -const void * -modm::amnb::Node::getResponse() -{ - return reinterpret_cast(Interface::getPayload()); -} - -// ---------------------------------------------------------------------------- -template -void -modm::amnb::Node::update() -{ - Interface::update(); - - if (Interface::isMessageAvailable()) - { - uint8_t messageAddress = Interface::getAddress(); - uint8_t messageCommand = Interface::getCommand(); - bool checkListeners = false; - // -------------------------------------------------------------------- - if (Interface::isResponse()) - { - // my request answer - if ((queryStatus == IN_PROGRESS) && (messageAddress == expectedAddress)) - { - if (Interface::isAcknowledge()) - { - if (Interface::getPayloadLength() == expectedResponseLength) { - queryStatus = SUCCESS; - - checkListeners = true; - } - else { - queryStatus = ERROR_PAYLOAD; - checkErrorHandlers(messageAddress, messageCommand, ACK, ERROR_QUERY_WRONG_PAYLOAD_LENGTH); - } - } - else { - queryStatus = ERROR_RESPONSE; - checkErrorHandlers(messageAddress, messageCommand, NACK, ERROR_QUERY_ERROR_CODE); - } - } - // other request answer - else { - checkListeners = true; - } - } - // -------------------------------------------------------------------- - else { - // Requests - if (Interface::isAcknowledge()) - { - if (messageAddress == this->ownAddress) - { - this->response.triggered = false; - this->currentCommand = messageCommand; - - modm::accessor::Flash list = actionList; - for (uint8_t i = 0; i < actionCount; ++i, ++list) - { - Action action(*list); - if (this->currentCommand == action.command) - { - if (Interface::getPayloadLength() == action.payloadLength) - { - // execute callback function - action.call(this->response, Interface::getPayload()); - - if (!this->response.triggered) { - this->response.error(ERROR_ACTION_NO_RESPONSE); - checkErrorHandlers(messageAddress, messageCommand, NACK, ERROR_ACTION_NO_RESPONSE); - } - } - else { - this->response.error(ERROR_ACTION_WRONG_PAYLOAD_LENGTH); - checkErrorHandlers(messageAddress, messageCommand, NACK, ERROR_ACTION_WRONG_PAYLOAD_LENGTH); - } - break; - } - } - } - } - // Broadcasts - else { - checkListeners = true; - } - - if (!this->response.triggered) { - this->response.error(ERROR_ACTION_NO_ACTION); - checkErrorHandlers(messageAddress, messageCommand, NACK, ERROR_ACTION_NO_ACTION); - } - } - // -------------------------------------------------------------------- - - if (checkListeners && (listenCount > 0)) - { // check if we want to listen to it - modm::accessor::Flash list = listenList; - for (uint8_t i = 0; i < listenCount; ++i, ++list) - { - Listener listen(*list); - if ((messageAddress == listen.address) && (messageCommand == listen.command)) - { - // execute callback function - listen.call(Interface::getPayload(), Interface::getPayloadLength(), messageAddress); - break; - } - } - } - // finished with the message, drop it. - Interface::dropMessage(); - } - // my request timeout - else if (timer.isExpired() && (queryStatus == IN_PROGRESS)) - { - queryStatus = ERROR_TIMEOUT; - checkErrorHandlers(Interface::getTransmittedAddress(), Interface::getTransmittedCommand(), REQUEST, ERROR_QUERY_TIMEOUT); - } -} - - -template -bool -modm::amnb::Node::checkErrorHandlers(uint8_t address, uint8_t command, Flags type, uint8_t errorCode) -{ - if (errorHandlerCount == 0) return false; - - modm::accessor::Flash list = errorHandlerList; - for (uint8_t i = 0; i < errorHandlerCount; ++i, ++list) - { - ErrorHandler errorHandler(*list); - if ((address == errorHandler.address) && (command == errorHandler.command)) - { - // execute callback function - errorHandler.call(type, errorCode); - return true; - } - } - return false; -} - -// ---------------------------------------------------------------------------- -template -void -modm::amnb::Node::send(bool acknowledge, - const void *payload, uint8_t payloadLength) -{ - Flags flags; - if (acknowledge) { - flags = modm::amnb::ACK; - } - else { - flags = modm::amnb::NACK; - } - - Interface::sendMessage(this->ownAddress, flags, this->currentCommand, - payload, payloadLength); -} diff --git a/test/modm/communication/amnb/interface_test.cpp b/test/modm/communication/amnb/interface_test.cpp new file mode 100644 index 0000000000..3f291f1e96 --- /dev/null +++ b/test/modm/communication/amnb/interface_test.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2020, 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 "interface_test.hpp" +#include +#include +#include + +using namespace modm::amnb; +using namespace modm_test; + +class AmnbTestMessage : public Message +{ +public: + using Message::Message; + + using Message::isHeaderValid; + using Message::isDataValid; + using Message::setValid; + using Message::allocate; + using Message::self; +}; + +void +AmnbInterfaceTest::setUp() +{ + SharedMedium::reset(); +} + +// ---------------------------------------------------------------------------- +void +AmnbInterfaceTest::testSerialize() +{ + DeviceWrapper dev; + Interface<100> interface(dev); // max 100B heap allocation! + { + // Test inplace message tx + AmnbTestMessage msg(7, 0x7D); + msg.setValid(); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&msg)), InterfaceStatus::Ok); + // with byte stuffing! + const uint8_t raw[] = {0x7E, 0x7E, 108, 7, 0x7D, 0x5D, 0}; + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + SharedMedium::reset(); + { + // Test inplace message tx + AmnbTestMessage msg(200, 0x7E, 8, Type::Request); + msg.get()[0] = 0x03020100ul; + msg.get()[1] = 0x07067E7Dul; + msg.setValid(); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&msg)), InterfaceStatus::Ok); + // with byte stuffing! + const uint8_t raw[] = {0x7E, 0x7E, 18, 200, 0x7D, 0x5E, 0x48, 0, 1, 2, 3, 0x7D, 0x5D, 0x7D, 0x5E, 6, 7}; + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + SharedMedium::reset(); + { + // Test heap message tx + AmnbTestMessage msg(10, 14, 32, Type::Error); + for (size_t ii=0; ii < 32/sizeof(uint32_t); ii++) + msg.get()[ii] = 0x0302017Dul+ii; + msg.setValid(); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&msg)), InterfaceStatus::Ok); + // with byte stuffing! + const uint8_t raw[] = {0x7E, 0x7E, 8, 10, 14, 0x9F, 32, 0, 205, 202, + 0x7D, 0x5D, 1, 2, 3, 0x7D, 0x5E, 1, 2, 3, 0x7F, 1, 2, 3, 0x80, 1, 2, 3, + 0x81, 1, 2, 3, 0x82, 1, 2, 3, 0x83, 1, 2, 3, 0x84, 1, 2, 3}; + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + + SharedMedium::reset(); + { + // Test inplace message rx + SharedMedium::add_rx({0x7E, 0x7E, 247, 0x7D, 0x5D, 100, 0}); + AmnbTestMessage msg; + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::Ok); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + TEST_ASSERT_TRUE(msg.isDataValid()); + + TEST_ASSERT_EQUALS(msg.address(), 0x7D); + TEST_ASSERT_EQUALS(msg.command(), 100); + TEST_ASSERT_EQUALS(msg.length(), 0); + TEST_ASSERT_EQUALS(msg.type(), Type::Broadcast); + TEST_ASSERT_TRUE(msg.get() == nullptr); + } + SharedMedium::reset(); + { + // Test inplace message rx + SharedMedium::add_rx({0x7E, 0x7E, 56, 5, 15, 0x44, 0x7D, 0x5E, 1, 2, 3}); + AmnbTestMessage msg; + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::Ok); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + TEST_ASSERT_TRUE(msg.isDataValid()); + + TEST_ASSERT_EQUALS(msg.address(), 5); + TEST_ASSERT_EQUALS(msg.command(), 15); + TEST_ASSERT_EQUALS(msg.length(), 4); + TEST_ASSERT_EQUALS(msg.type(), Type::Request); + TEST_ASSERT_EQUALS(*msg.get(), 0x0302017Eul); + } + SharedMedium::reset(); + { + SharedMedium::add_rx({0x7E, 0x7E, 82, 20, 54, 0x5F, 36, 0, 143, 101, + 1, 2, 3, 4, 2, 2, 3, 4, 3, 2, 3, 4, 4, 2, 3, 4, + 5, 2, 3, 4, 6, 2, 3, 4, 7, 2, 3, 4, 8, 2, 3, 4, 9, 2, 3, 4}); + + AmnbTestMessage msg; + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::Ok); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + + TEST_ASSERT_EQUALS(msg.address(), 20); + TEST_ASSERT_EQUALS(msg.command(), 54); + TEST_ASSERT_EQUALS(msg.length(), 36); + TEST_ASSERT_EQUALS(msg.type(), Type::Request); + + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveData(&msg)), InterfaceStatus::Ok); + TEST_ASSERT_TRUE(msg.isDataValid()); + + const uint32_t raw[] = {0x04030201ul, 0x04030202ul, 0x04030203ul, 0x04030204ul, + 0x04030205ul, 0x04030206ul, 0x04030207ul, 0x04030208ul, 0x04030209ul}; + TEST_ASSERT_EQUALS_ARRAY(msg.get(), raw, 9); + } +} + +void +AmnbInterfaceTest::testFailures() +{ + DeviceWrapper dev; + Interface<35> interface(dev); + { + AmnbTestMessage msg; + msg.setValid(); + + { + SharedMedium::fail_tx_index = 0; + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&msg)), InterfaceStatus::SyncWriteFailed); + const uint8_t raw[] = {0x7E}; + TEST_ASSERT_EQUALS(SharedMedium::raw_transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::raw_transmitted, raw, sizeof(raw)); + }{ + SharedMedium::reset(); + SharedMedium::fail_tx_index = 1; + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&msg)), InterfaceStatus::SyncWriteFailed); + const uint8_t raw[] = {0x7E, 0x7E}; + TEST_ASSERT_EQUALS(SharedMedium::raw_transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::raw_transmitted, raw, sizeof(raw)); + }{ + SharedMedium::reset(); + SharedMedium::fail_tx_index = 2; + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&msg)), InterfaceStatus::HeaderWriteFailed); + const uint8_t raw[] = {0x7E, 0x7E, 110}; + TEST_ASSERT_EQUALS(SharedMedium::raw_transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::raw_transmitted, raw, sizeof(raw)); + }{ + SharedMedium::reset(); + SharedMedium::fail_tx_index = 4; + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&msg)), InterfaceStatus::HeaderWriteFailed); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&msg)), InterfaceStatus::Ok); + const uint8_t raw[] = {0x7E, 0x7E, 110,0,0,0x7E, 0x7E, 110,0,0,0}; + TEST_ASSERT_EQUALS(SharedMedium::raw_transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::raw_transmitted, raw, sizeof(raw)); + } + } + + { + AmnbTestMessage msg; + // wrong synchronization + SharedMedium::reset(); + SharedMedium::add_rx({0x00}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::SyncReadFailed); + // second sync byte wrong + SharedMedium::add_rx({0x7E, 0x00}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::SyncReadFailed); + // sync byte read error + SharedMedium::fail_rx_index = 1; // sync read error + SharedMedium::add_rx({0x7E, 0x7E}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::SyncReadFailed); + + SharedMedium::reset(); + SharedMedium::fail_rx_index = 2; // header read error + SharedMedium::add_rx({0x7E, 0x7E, 0x00}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::HeaderReadFailed); + + // CRC is wrong for no data + SharedMedium::add_rx({0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::HeaderInvalid); + SharedMedium::reset(); + + SharedMedium::fail_rx_index = 6; // short data read error + SharedMedium::add_rx({0x7E, 0x7E, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::HeaderReadFailed); + // wrong CRC for short data + SharedMedium::add_rx({0x7E, 0x7E, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::HeaderInvalid); + SharedMedium::reset(); + + SharedMedium::fail_rx_index = 11; // long data with read error + SharedMedium::add_rx({0x7E, 0x7E, 8, 10, 14, 0x9F, 32, 0, 205, 202, 1, 2}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::Ok); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveData(&msg)), InterfaceStatus::DataReadFailed); + + // allocation failed, received message too large + SharedMedium::add_rx({0x7E, 0x7E, 82, 20, 54, 0x5F, 36, 0, 143, 101, + 1, 2, 3, 4, 2, 2, 3, 4, 3, 2, 3, 4, 4, 2, 3, 4, + 5, 2, 3, 4, 6, 2, 3, 4, 7, 2, 3, 4, 8, 2, 3, 4, 9, 2, 3, 4}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::Ok); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveData(&msg)), InterfaceStatus::AllocationFailed); + + // Data CRC is wrong + SharedMedium::add_rx({0x7E, 0x7E, 8, 10, 14, 0x9F, 32, 0, 205, 202, + 1, 2, 3, 4, 2, 2, 3, 4, 3, 2, 3, 4, 4, 2, 3, 4, + 5, 2, 3, 4, 6, 2, 3, 4, 7, 2, 3, 4, 8, 2, 3, 4}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&msg)), InterfaceStatus::Ok); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveData(&msg)), InterfaceStatus::DataInvalid); + } +} + +void +AmnbInterfaceTest::testInterlock() +{ + DeviceWrapper dev; + Interface interface(dev); + AmnbTestMessage tx_msg; + AmnbTestMessage rx_msg; + + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&rx_msg)), InterfaceStatus::MediumEmpty); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::Ok); + SharedMedium::reset(); + + SharedMedium::add_rx({0x7E}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + interface.receiveHeader(&rx_msg); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + + SharedMedium::add_rx({0x00}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&rx_msg)), InterfaceStatus::SyncReadFailed); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::Ok); + + // same but with read error + SharedMedium::reset(); + SharedMedium::fail_rx_index = 1; + SharedMedium::add_rx({0x7E}); + interface.receiveHeader(&rx_msg); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + + SharedMedium::add_rx({0x7E}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&rx_msg)), InterfaceStatus::SyncReadFailed); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::Ok); + + // same but with read error later + SharedMedium::add_rx({0x7E, 0x7E}); + interface.receiveHeader(&rx_msg); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + + SharedMedium::add_rx({207, 7, 4}); + interface.receiveHeader(&rx_msg); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + + SharedMedium::add_rx({0}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&rx_msg)), InterfaceStatus::Ok); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::Ok); + + // same but with read error later + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E}); + SharedMedium::fail_rx_index = 3; + interface.receiveHeader(&rx_msg); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + + SharedMedium::add_rx({207, 7, 4}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&rx_msg)), InterfaceStatus::HeaderReadFailed); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::Ok); + + // header invalid + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E}); + interface.receiveHeader(&rx_msg); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::MediumBusy); + + SharedMedium::add_rx({207, 7, 5, 0}); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.receiveHeader(&rx_msg)), InterfaceStatus::HeaderInvalid); + TEST_ASSERT_EQUALS(RF_CALL_BLOCKING(interface.transmit(&tx_msg)), InterfaceStatus::Ok); +} diff --git a/test/modm/communication/amnb/interface_test.hpp b/test/modm/communication/amnb/interface_test.hpp new file mode 100644 index 0000000000..2a41310cd7 --- /dev/null +++ b/test/modm/communication/amnb/interface_test.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, 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 + +/// @ingroup modm_test_test_communication +class AmnbInterfaceTest : public unittest::TestSuite +{ +public: + void setUp() override; + + void testSerialize(); + void testFailures(); + void testInterlock(); +}; diff --git a/test/modm/communication/amnb/message_test.cpp b/test/modm/communication/amnb/message_test.cpp new file mode 100644 index 0000000000..124da43bc0 --- /dev/null +++ b/test/modm/communication/amnb/message_test.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2020, 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 +#include "message_test.hpp" +#include + +namespace modm +{ + +template +struct has_to_chars : std::false_type {}; +template +struct has_to_chars()))>> : std::true_type {}; + +template::value, int>::type = 0> +IOStream& +operator << (IOStream& stream, const T& v) +{ stream << to_chars(v); return stream; } + +} + +using namespace modm::amnb; + +class AmnbTestMessage : public Message +{ +public: + using Message::Message; + + using Message::isHeaderValid; + using Message::isDataValid; + using Message::setValid; + using Message::allocate; + using Message::self; + + Header& getHeader() { return header; } + Storage& getStorage() { return storage; } +}; + +// ---------------------------------------------------------------------------- +void +AmnbMessageTest::testConstructor() +{ + TEST_ASSERT_EQUALS(sizeof(Message), 32u); + + { + AmnbTestMessage msg; + TEST_ASSERT_EQUALS(msg.address(), 0u); + TEST_ASSERT_EQUALS(msg.command(), 0u); + TEST_ASSERT_EQUALS(msg.length(), 0u); + TEST_ASSERT_EQUALS(msg.type(), Type::Broadcast); + TEST_ASSERT_TRUE(msg.get() == nullptr); + TEST_ASSERT_FALSE(msg.isHeaderValid()); + TEST_ASSERT_FALSE(msg.isDataValid()); + msg.setValid(); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + TEST_ASSERT_TRUE(msg.isDataValid()); + } + { + AmnbTestMessage msg(4, 16); + TEST_ASSERT_EQUALS(msg.address(), 4u); + TEST_ASSERT_EQUALS(msg.command(), 16u); + TEST_ASSERT_EQUALS(msg.length(), 0u); + TEST_ASSERT_EQUALS(msg.type(), Type::Broadcast); + TEST_ASSERT_TRUE(msg.get() == nullptr); + TEST_ASSERT_FALSE(msg.isHeaderValid()); + TEST_ASSERT_FALSE(msg.isDataValid()); + msg.setValid(); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + TEST_ASSERT_TRUE(msg.isDataValid()); + } + { + struct Small + { + uint32_t val1{1}; + uint8_t val2{2}; + uint16_t val3{3}; + }; + AmnbTestMessage msg(6, 12, sizeof(Small)); + TEST_ASSERT_EQUALS(msg.address(), 6); + TEST_ASSERT_EQUALS(msg.command(), 12); + TEST_ASSERT_EQUALS(msg.length(), sizeof(Small)); + TEST_ASSERT_EQUALS(msg.type(), Type::Broadcast); + TEST_ASSERT_FALSE(msg.isHeaderValid()); + TEST_ASSERT_FALSE(msg.isDataValid()); + msg.setValid(); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + TEST_ASSERT_TRUE(msg.isDataValid()); + + const auto data = msg.get(); + TEST_ASSERT_TRUE(data != nullptr); + new (data) Small; + TEST_ASSERT_EQUALS(data->val1, 1u); + TEST_ASSERT_EQUALS(data->val2, 2u); + TEST_ASSERT_EQUALS(data->val3, 3u); + + // type too large for payload + TEST_ASSERT_TRUE(msg.get() == nullptr); + + TEST_ASSERT_FALSE(msg.isHeaderValid()); + TEST_ASSERT_FALSE(msg.isDataValid()); + msg.setValid(); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + TEST_ASSERT_TRUE(msg.isDataValid()); + } +} + +void +AmnbMessageTest::testAllocator() +{ + { + AmnbTestMessage msg(200); + TEST_ASSERT_TRUE(msg.allocate()); + } + { + struct Large + { + uint8_t buffer[42]; + }; + AmnbTestMessage msg(sizeof(Large)); + TEST_ASSERT_EQUALS(msg.address(), 0u); + TEST_ASSERT_EQUALS(msg.command(), 0u); + TEST_ASSERT_EQUALS(msg.length(), sizeof(Large)); + TEST_ASSERT_EQUALS(msg.type(), Type::Broadcast); + TEST_ASSERT_FALSE(msg.isHeaderValid()); + TEST_ASSERT_FALSE(msg.isDataValid()); + msg.setValid(); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + TEST_ASSERT_TRUE(msg.isDataValid()); + + const auto data = msg.get(); + TEST_ASSERT_TRUE(data != nullptr); + TEST_ASSERT_TRUE(data->buffer != nullptr); + + // type too large + TEST_ASSERT_TRUE(msg.get() == nullptr); + } +} + +void +AmnbMessageTest::testLifetime() +{ + { + AmnbTestMessage msg(10); + *msg.get() = 0x10; + TEST_ASSERT_EQUALS(*msg.get(), 0x10); + { + AmnbTestMessage short_msg(20); + *short_msg.get() = 0x20; + TEST_ASSERT_EQUALS(*short_msg.get(), 0x20); + msg = short_msg; + } + TEST_ASSERT_EQUALS(msg.length(), 20); + TEST_ASSERT_EQUALS(*msg.get(), 0x20); + } + { + AmnbTestMessage msg(40); + *msg.get() = 0x40; + TEST_ASSERT_EQUALS(*msg.get(), 0x40); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 1); + { + AmnbTestMessage short_msg(50); + *short_msg.get() = 0x50; + TEST_ASSERT_EQUALS(*short_msg.get(), 0x50); + TEST_ASSERT_EQUALS(*(short_msg.getStorage().large.data), 1); + + short_msg = msg; + TEST_ASSERT_EQUALS(*short_msg.get(), 0x40); + TEST_ASSERT_EQUALS(msg.length(), 40); + TEST_ASSERT_EQUALS(msg.getStorage().large.data, short_msg.getStorage().large.data); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 2); + } + TEST_ASSERT_TRUE(msg.get()); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 1); + } + { + AmnbTestMessage msg(40); + *msg.get() = 0x40; + TEST_ASSERT_EQUALS(*msg.get(), 0x40); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 1); + { + AmnbTestMessage short_msg(50); + *short_msg.get() = 0x50; + TEST_ASSERT_EQUALS(*short_msg.get(), 0x50); + + msg = short_msg; + TEST_ASSERT_EQUALS(*msg.get(), 0x50); + TEST_ASSERT_EQUALS(msg.length(), 50); + TEST_ASSERT_EQUALS(msg.getStorage().large.data, short_msg.getStorage().large.data); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 2); + } + TEST_ASSERT_EQUALS(msg.length(), 50); + TEST_ASSERT_TRUE(msg.get()); + TEST_ASSERT_EQUALS(*msg.get(), 0x50); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 1); + } + { + AmnbTestMessage msg(40); + *msg.get() = 0x40; + TEST_ASSERT_EQUALS(*msg.get(), 0x40); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 1); + { + AmnbTestMessage short_msg(50); + *short_msg.get() = 0x50; + TEST_ASSERT_EQUALS(*short_msg.get(), 0x50); + + msg = std::move(short_msg); + TEST_ASSERT_TRUE(short_msg.get() == nullptr); + TEST_ASSERT_EQUALS(short_msg.length(), 0); + TEST_ASSERT_EQUALS(*msg.get(), 0x50); + TEST_ASSERT_EQUALS(msg.length(), 50); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 1); + } + TEST_ASSERT_EQUALS(msg.length(), 50); + TEST_ASSERT_TRUE(msg.get()); + TEST_ASSERT_EQUALS(*msg.get(), 0x50); + TEST_ASSERT_EQUALS(*(msg.getStorage().large.data), 1); + } +} + + +#include + +void +AmnbMessageTest::testSerialize() +{ + { + AmnbTestMessage msg(6, 12, 4, Type::Request); + *msg.get() = 0x08070605ul; + msg.setValid(); + + const uint8_t raw[] = {197, 6, 12, 0x44, 5, 6, 7, 8}; + TEST_ASSERT_EQUALS_ARRAY(msg.self(), raw, sizeof(raw)); + } + { + AmnbTestMessage msg; + const uint8_t raw[] = {197, 4, 15, 0x64, 0, 1, 2, 3}; + std::memcpy(msg.self(), raw, sizeof(raw)); + + TEST_ASSERT_EQUALS(msg.address(), 4); + TEST_ASSERT_EQUALS(msg.command(), 15); + TEST_ASSERT_EQUALS(msg.length(), 4); + TEST_ASSERT_EQUALS(msg.type(), Type::Response); + TEST_ASSERT_TRUE(msg.isHeaderValid()); + TEST_ASSERT_TRUE(msg.isDataValid()); + const auto data = msg.get(); + TEST_ASSERT_TRUE(data != nullptr); + TEST_ASSERT_EQUALS(*data, 0x03020100ul); + } + // failures + { + AmnbTestMessage msg; + // wrong length, wrong crc + const uint8_t raw[] = {18, 4, 15, 0xde}; + std::memcpy(msg.self(), raw, sizeof(raw)); + TEST_ASSERT_EQUALS(msg.length(), 0u); // assumes large frame + TEST_ASSERT_FALSE(msg.isHeaderValid()); + TEST_ASSERT_FALSE(msg.isDataValid()); + } + + /* + { + AmnbTestMessage msg(0x10, 0x80, 1, Type::UserError); + // *msg.get() = 0x07060504ul; + // *msg.get() = Error::AllocationFailed; + *msg.get() = 0x07; + msg.setValid(); + + const uint8_t raw[32] = {0}; + TEST_ASSERT_EQUALS_ARRAY(msg.self(), raw, sizeof(raw)); + } + //*/ +} diff --git a/test/modm/communication/amnb/message_test.hpp b/test/modm/communication/amnb/message_test.hpp new file mode 100644 index 0000000000..abab77d365 --- /dev/null +++ b/test/modm/communication/amnb/message_test.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020, 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 + +/// @ingroup modm_test_test_communication +class AmnbMessageTest : public unittest::TestSuite +{ +public: + void + testConstructor(); + + void + testAllocator(); + + void + testLifetime(); + + void + testSerialize(); +}; diff --git a/test/modm/communication/amnb/node_test.cpp b/test/modm/communication/amnb/node_test.cpp new file mode 100644 index 0000000000..d05943ca9e --- /dev/null +++ b/test/modm/communication/amnb/node_test.cpp @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2020, 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 "node_test.hpp" +#include +#include +#include +#include + +using namespace modm::amnb; +using namespace modm_test; +using namespace std::chrono_literals; +using milli_clock = modm_test::chrono::milli_clock; +using micro_clock = modm_test::chrono::micro_clock; + +void +AmnbNodeTest::setUp() +{ + SharedMedium::reset(); +} + +// ---------------------------------------------------------------------------- +void +AmnbNodeTest::testBroadcast() +{ + DeviceWrapper dev; + Node node(dev, 0x08); + { + node.broadcast(0x10); + node.broadcast(0x20, uint32_t(0x03020100)); + node.update(); node.update(); + const uint8_t raw[] = {0x7E, 0x7E, 84, 0x08, 0x10, 0, 0x7E, 0x7E, 58, 0x08, 0x20, 4, 0, 1, 2, 3}; + TEST_ASSERT_EQUALS(SharedMedium::raw_transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + // test automatic retransmit + SharedMedium::reset(); + SharedMedium::fail_tx_index = 2; + { + node.broadcast(0x70); + for(uint32_t ii=0; ii < 10000; ii += 10) { node.update(); micro_clock::setTime(ii); } + // first transmission attempt fails + const uint8_t raw[] = {0x7E, 0x7E, 110, 0x7E, 0x7E, 110, 0x08, 0x70, 0}; + TEST_ASSERT_EQUALS(SharedMedium::raw_transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::raw_transmitted, raw, sizeof(raw)); + } +} + +void +AmnbNodeTest::testRequest() +{ + DeviceWrapper dev; + Node node(dev, 0x08); + // should discard unrelated response + SharedMedium::add_queued_rx({0x7E, 0x7E, 251, 0x08, 0x70, 0xc0}, 6); + { + micro_clock::setTime(0); milli_clock::setTime(0); + modm::ResumableResult< Result<> > res{0}; + for(uint32_t ii=0; (res = node.request(0x10, 0x80)).getState() == modm::rf::Running; ii += 10) + { micro_clock::setTime(ii); milli_clock::setTime(ii/1000); node.update(); } + + TEST_ASSERT_EQUALS(res.getResult().error(), Error::RequestTimeout); + TEST_ASSERT_EQUALS(SharedMedium::tx_count, 6u); + TEST_ASSERT_EQUALS(SharedMedium::rx_count, 12u); + } + SharedMedium::reset(); + // System error allocation failed + SharedMedium::add_queued_rx({0x7E, 0x7E, 217, 16, 128, 129, 4}, 6); + { + micro_clock::setTime(0); milli_clock::setTime(0); + modm::ResumableResult< Result<> > res{0}; + for(uint32_t ii=0; (res = node.request(0x10, 0x80)).getState() == modm::rf::Running; ii += 10) + { micro_clock::setTime(ii); milli_clock::setTime(ii/1000); node.update(); } + + TEST_ASSERT_EQUALS(res.getResult().error(), Error::ResponseAllocationFailed); + + TEST_ASSERT_EQUALS(SharedMedium::tx_count, 6u); + TEST_ASSERT_EQUALS(SharedMedium::rx_count, 13u); + } + SharedMedium::reset(); + // System error allocation failed + SharedMedium::add_queued_rx({0x7E, 0x7E, 32, 16, 128, 161, 7}, 6); + { + micro_clock::setTime(0); milli_clock::setTime(0); + modm::ResumableResult< Result > res{0}; + for(uint32_t ii=0; (res = node.request(0x10, 0x80, uint8_t(0x10))).getState() == modm::rf::Running; ii += 10) + { micro_clock::setTime(ii); milli_clock::setTime(ii/1000); node.update(); } + + TEST_ASSERT_EQUALS(res.getResult().error(), Error::UserError); + TEST_ASSERT_EQUALS(*res.getResult().resultError(), 0x07u); + + TEST_ASSERT_EQUALS(SharedMedium::tx_count, 7u); + TEST_ASSERT_EQUALS(SharedMedium::rx_count, 14u); + } + SharedMedium::reset(); + // should discard unrelated response + SharedMedium::add_queued_rx({0x7E, 0x7E, 225, 0x08, 0x70, 0x60}); + // should discard invalid response + SharedMedium::add_queued_rx({0x7E, 0x7E, 0, 0x10, 0x80, 0x60}); + // should use real valid response + SharedMedium::add_queued_rx({0x7E, 0x7E, 243, 16, 128, 100, 4, 5, 6, 7}, 10); + { + micro_clock::setTime(0); milli_clock::setTime(0); + modm::ResumableResult< Result > res{0}; + for(uint32_t ii=0; + (res = node.request(0x10, 0x80, uint32_t(0x03020100))).getState() == modm::rf::Running; + ii += 10) + { micro_clock::setTime(ii); milli_clock::setTime(ii/1000); node.update(); } + + TEST_ASSERT_EQUALS(res.getResult().error(), Error::Ok); + TEST_ASSERT_EQUALS(*res.getResult().result(), 0x07060504ul); + + TEST_ASSERT_EQUALS(SharedMedium::tx_count, 10u); + TEST_ASSERT_EQUALS(SharedMedium::rx_count, 32u); + } +} + +void +AmnbNodeTest::testAction() +{ + static uint8_t trig{0}; + const Action actions[] = + { + {1, []() + { + trig |= 1; + } + }, + {2, +[](const uint32_t& data) + { + TEST_ASSERT_EQUALS(data, uint32_t(0x03020100)); + trig |= 2; + } + }, + {3, +[](const uint32_t& data) -> Response + { + TEST_ASSERT_EQUALS(data, uint32_t(0x03020100)); + trig |= 4; + return uint32_t(0x07060504); + } + }, + {4, []() -> Response + { + trig |= 8; + return ErrorResponse(uint8_t(3)); + } + }, + {5, +[](const uint32_t& data) -> Response + { + TEST_ASSERT_EQUALS(data, uint32_t(0x03020100)); + trig |= 16; + return ErrorResponse(uint32_t(0xbaadc0de)); + } + }, + }; + DeviceWrapper dev; + Node node(dev, 0x08, actions); + + // System Error: Wrong payload size! + trig = 0; + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E, 104, 8, 1, 68, 0, 1, 2, 3}); + { + node.update(); node.update(); + TEST_ASSERT_EQUALS(trig, 0); + const uint8_t raw[] = {0x7E, 0x7E, 237, 8, 1, 97, 2}; + TEST_ASSERT_EQUALS(SharedMedium::transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + // System Error: No action! + trig = 0; + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E, 163, 8, 10, 64}); + { + node.update(); node.update(); + TEST_ASSERT_EQUALS(trig, 0); + const uint8_t raw[] = {0x7E, 0x7E, 76, 8, 10, 129, 3}; + TEST_ASSERT_EQUALS(SharedMedium::transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + + // Positive Response without argument + trig = 0; + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E, 227, 8, 1, 64}); + { + node.update(); node.update(); + TEST_ASSERT_EQUALS(trig, 1); + const uint8_t raw[] = {0x7E, 0x7E, 42, 8, 1, 96}; + TEST_ASSERT_EQUALS(SharedMedium::transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + // Positive Response with argument + trig = 0; + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E, 141, 8, 2, 68, 0, 1, 2, 3}); + { + node.update(); node.update(); + TEST_ASSERT_EQUALS(trig, 2); + const uint8_t raw[] = {0x7E, 0x7E, 209, 8, 2, 96}; + TEST_ASSERT_EQUALS(SharedMedium::transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + // Positive Reponse with argument and return value + trig = 0; + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E, 46, 8, 3, 68, 0, 1, 2, 3}); + { + node.update(); node.update(); + TEST_ASSERT_EQUALS(trig, 4); + const uint8_t raw[] = {0x7E, 0x7E, 84, 8, 3, 100, 4, 5, 6, 7}; + TEST_ASSERT_EQUALS(SharedMedium::transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + // Negative Response without argument + trig = 0; + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E, 233, 8, 4, 64}); + { + node.update(); node.update(); + TEST_ASSERT_EQUALS(trig, 8); + const uint8_t raw[] = {0x7E, 0x7E, 94, 8, 4, 161, 3}; + TEST_ASSERT_EQUALS(SharedMedium::transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } + // Negative Response with argument + trig = 0; + SharedMedium::reset(); + SharedMedium::add_rx({0x7E, 0x7E, 227, 8, 5, 68, 0, 1, 2, 3}); + { + node.update(); node.update(); + TEST_ASSERT_EQUALS(trig, 16); + const uint8_t raw[] = {0x7E, 0x7E, 133, 8, 5, 164, 0xde, 0xc0, 0xad, 0xba}; + TEST_ASSERT_EQUALS(SharedMedium::transmitted.size(), sizeof(raw)); + TEST_ASSERT_EQUALS_ARRAY(SharedMedium::transmitted, raw, sizeof(raw)); + } +} + +void +AmnbNodeTest::testListener() +{ + static uint8_t trig{0}; + const Listener listeners[] = + { + {1, [](uint8_t sender) + { + TEST_ASSERT_EQUALS(sender, 0x20); + trig |= 1; + } + }, + {2, +[](uint8_t sender, const uint32_t& data) + { + TEST_ASSERT_EQUALS(sender, 0x10); + TEST_ASSERT_EQUALS(data, uint32_t(0x03020100)); + trig |= 2; + } + }, + }; + DeviceWrapper dev; + Node node(dev, 8, listeners); + + trig = 0; + SharedMedium::add_rx({0x7E, 0x7E, 4, 32, 1, 0}); + { + node.update(); + TEST_ASSERT_EQUALS(trig, 1); + node.update(); + TEST_ASSERT_EQUALS(trig, 1); + } + trig = 0; + SharedMedium::add_rx({0x7E, 0x7E, 102, 16, 2, 4, 0, 1, 2, 3}); + { + node.update(); + TEST_ASSERT_EQUALS(trig, 2); + node.update(); + TEST_ASSERT_EQUALS(trig, 2); + } + trig = 0; + SharedMedium::add_rx({0x7E, 0x7E, 4, 32, 1, 0}); + SharedMedium::add_rx({0x7E, 0x7E, 102, 16, 2, 4, 0, 1, 2, 3}); + { + node.update(); + TEST_ASSERT_EQUALS(trig, 1); + node.update(); + TEST_ASSERT_EQUALS(trig, 3); + } +} diff --git a/test/modm/communication/amnb/node_test.hpp b/test/modm/communication/amnb/node_test.hpp new file mode 100644 index 0000000000..9f577560db --- /dev/null +++ b/test/modm/communication/amnb/node_test.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020, 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 + +/// @ingroup modm_test_test_communication +class AmnbNodeTest : public unittest::TestSuite +{ +public: + void setUp() override; + + void testBroadcast(); + void testRequest(); + void testAction(); + void testListener(); +}; diff --git a/test/modm/communication/module.lb b/test/modm/communication/module.lb index 93175ceb51..7d94e632fd 100644 --- a/test/modm/communication/module.lb +++ b/test/modm/communication/module.lb @@ -10,6 +10,19 @@ # 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/. +class Amnb(Module): + def init(self, module): + module.name = "amnb" + + def prepare(self, module, options): + module.depends("modm:communication:amnb", ":mock:clock", ":mock:shared_medium") + return True + + def build(self, env): + env.outbasepath = "modm-test/src/modm-test/communication" + env.copy("amnb") + + class Sab(Module): def init(self, module): module.name = "sab" @@ -40,6 +53,7 @@ def init(module): module.name = ":test:communication" def prepare(module, options): + module.add_submodule(Amnb()) module.add_submodule(Sab()) module.add_submodule(Xpcc()) return True diff --git a/test/modm/mock/module.lb b/test/modm/mock/module.lb index 1c6af50d9d..03ec138194 100644 --- a/test/modm/mock/module.lb +++ b/test/modm/mock/module.lb @@ -62,6 +62,18 @@ class IoDevice(Module): env.copy("io_device.hpp") env.copy("io_device.cpp") +class SharedMedium(Module): + def init(self, module): + module.name = "shared_medium" + + def prepare(self, module, options): + module.depends(":stdc++") + return True + + def build(self, env): + env.outbasepath = "modm-test/src/modm-test/mock" + env.copy("shared_medium.hpp") + def init(module): module.name = ":mock" @@ -71,6 +83,7 @@ def prepare(module, options): module.add_submodule(SpiDevice()) module.add_submodule(SpiMaster()) module.add_submodule(IoDevice()) + module.add_submodule(SharedMedium()) return True def build(env): diff --git a/test/modm/mock/shared_medium.hpp b/test/modm/mock/shared_medium.hpp new file mode 100644 index 0000000000..fcbc9fb826 --- /dev/null +++ b/test/modm/mock/shared_medium.hpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once +#include + +namespace modm_test +{ + +class SharedMedium +{ +public: + inline static bool + write(uint8_t data) + { + raw_transmitted.push_back(data); + transmitted.push_back(data); + // corrupt the data on the shared medium + if (fail_tx_index < ++tx_count) + data ^= 0x10; + // loop back on shared medium + received.push_back(data); + raw_received.push_back(data); + return true; + } + + inline static bool + read(uint8_t& byte) + { + if (received.empty()) { + if (rx_queue.empty()) return false; + received = std::move(rx_queue); + rx_queue_index = size_t(-1); + } + byte = received.front(); + received.erase(received.begin()); + rx_count++; + return true; + } + + inline static bool + hasError() + { + return (fail_rx_index < rx_count); + } + + inline static void + clearError() + { + } + + inline static void + discardTransmitBuffer() + { + transmitted.clear(); + received.clear(); + tx_count = 0; + fail_tx_index = size_t(-1); + } + + inline static void + discardReceiveBuffer() + { + received.clear(); + rx_count = 0; + fail_rx_index = size_t(-1); + } + + inline static size_t + receiveBufferSize() + { + if (rx_queue_index <= tx_count) return 1; + return received.size(); + } + +public: + inline static void + reset() + { + discardTransmitBuffer(); + discardReceiveBuffer(); + clearError(); + rx_queue.clear(); + raw_transmitted.clear(); + raw_received.clear(); + rx_queue_index = size_t(-1); + } + + inline static void + add_rx(std::initializer_list data) + { + received.insert(received.end(), data); + } + inline static void + add_queued_rx(std::initializer_list data, size_t index=0) + { + rx_queue_index = index; + rx_queue.insert(rx_queue.end(), data); + } + +public: + static inline std::vector raw_received; + static inline std::vector raw_transmitted; + + static inline std::vector received; + static inline std::vector transmitted; + static inline size_t tx_count{0}; + static inline size_t rx_count{0}; + + static inline size_t fail_tx_index{size_t(-1)}; + static inline size_t fail_rx_index{size_t(-1)}; + + static inline std::vector rx_queue; + static inline size_t rx_queue_index{size_t(-1)}; +}; + +} From 339cbc762bc383788b22549b5f09250778f1aa82 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 7 Oct 2018 00:16:05 +0200 Subject: [PATCH 086/135] [unittest] String compare any operator[] type --- src/unittest/harness.hpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/unittest/harness.hpp b/src/unittest/harness.hpp index fe4c5b2517..e3be795ce4 100644 --- a/src/unittest/harness.hpp +++ b/src/unittest/harness.hpp @@ -117,16 +117,20 @@ namespace unittest } // ------------------------------------------------------------------------ - inline void - printString(modm::IOStream& stream, const char* a, const char* b, size_t pos) + template + void + printString(modm::IOStream& stream, const A& a, const B& b, size_t pos) { - stream << modm::endl << a << modm::endl << b << modm::endl; + stream << modm::endl; + size_t ii=0; while(a[ii]) stream << a[ii++]; stream << modm::endl; + ii=0; while(b[ii]) stream << b[ii++]; stream << modm::endl; for(size_t ii = 0; ii < pos; ++ii) { stream << " "; } stream << "^" << modm::endl; } - inline bool - checkString(const char* a, const char* b, unsigned int line) + template + bool + checkString(const A& a, const B& b, unsigned int line) { size_t ii = 0; while(a[ii] != '\0' && b[ii] != '\0') { From 9d8bbfa3f9457222f7b5258491f4cc9948df465d Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Fri, 5 Oct 2018 23:52:08 +0200 Subject: [PATCH 087/135] [driver] Add GpioSampler for IO analysis --- README.md | 15 +- src/modm/driver/gpio/gpio_sampler.cpp.in | 126 +++++++++++++++++ src/modm/driver/gpio/gpio_sampler.hpp.in | 111 +++++++++++++++ src/modm/driver/gpio/gpio_sampler.lb | 58 ++++++++ src/modm/driver/gpio/gpio_sampler_impl.hpp.in | 128 ++++++++++++++++++ src/modm/platform/clock/stm32/rcc.cpp.in | 1 + src/modm/platform/clock/stm32/rcc_impl.hpp.in | 2 + 7 files changed, 434 insertions(+), 7 deletions(-) create mode 100644 src/modm/driver/gpio/gpio_sampler.cpp.in create mode 100644 src/modm/driver/gpio/gpio_sampler.hpp.in create mode 100644 src/modm/driver/gpio/gpio_sampler.lb create mode 100644 src/modm/driver/gpio/gpio_sampler_impl.hpp.in diff --git a/README.md b/README.md index f3282a1547..f69d6ee8ed 100644 --- a/README.md +++ b/README.md @@ -198,54 +198,55 @@ can easily configure them for you specific needs. FT245 FT6X06 +GPIO-SAMPLER HCLAx HD44780 HMC58x HMC6343 -HX711 +HX711 I2C-EEPROM ITG3200 L3GD20 LAWICEL LIS302DL -LIS3DSH +LIS3DSH LIS3MDL LM75 LP503X LSM303A LSM6DS33 -LTC2984 +LTC2984 MAX6966 MAX7219 MCP23X17 MCP2515 NOKIA5110 -NRF24 +NRF24 TFT-DISPLAY PAT9125EL PCA8574 PCA9535 PCA9548A -PCA9685 +PCA9685 SIEMENS-S65 SIEMENS-S75 SK6812 SK9822 SSD1306 -SX1276 +SX1276 TCS3414 TCS3472 TLC594X TMP102 TMP175 -VL53L0 +VL53L0 VL6180 WS2812 diff --git a/src/modm/driver/gpio/gpio_sampler.cpp.in b/src/modm/driver/gpio/gpio_sampler.cpp.in new file mode 100644 index 0000000000..e2f8942569 --- /dev/null +++ b/src/modm/driver/gpio/gpio_sampler.cpp.in @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018, 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 "gpio_sampler.hpp" +#include +#include +#include + +using IrqHandler = void(*)(); +%% if vectors_location == "rom" +modm_fastdata +IrqHandler exti_vectors[{{ extis | length }}] = {nullptr}; + +%% for vector in extis +MODM_ISR({{ vector }}) +{ + exti_vectors[{{ loop.index0 }}](); +} +%% endfor +%% endif + +const IRQn_Type +irq_map[{{ extis | length }}] = +{ +%% for vector in extis + {{ vector }}_IRQn, +%% endfor +}; + +namespace modm +{ + +modm_fastdata +void *GpioSampler::context{nullptr}; + +void +GpioSampler::reset(Interrupt vector) +{ + const size_t index = int(vector); + NVIC_DisableIRQ(irq_map[index]); + NVIC_SetPriority(irq_map[index], 0); +} + +void +GpioSampler::setHandler(Interrupt vector, IrqHandler handler) +{ + const size_t index = int(vector); + if (index >= {{ extis | length }}) return; +%% if vectors_location == "ram" + NVIC_SetVector(irq_map[index], (uint32_t) handler); +%% else + exti_vectors[index] = handler; +%% endif + NVIC_EnableIRQ(irq_map[index]); +} + +GpioSampler::Channel::Channel() +{} +void +GpioSampler::Channel::allocate(size_t max_samples) +{ + max_count = max_samples + 1; + data = new Type[max_samples + 1]; +} +GpioSampler::Channel::~Channel() +{ + delete[] data; +} + +void +GpioSampler::Channel::reset() +{ + count = 0; +} + +void +GpioSampler::Channel::dump() const +{ + for (size_t ii=0; ii 0); +} + +GpioSampler::Type +GpioSampler::Channel::diff(size_t index) const +{ + if (index == 0) return 0; + Type s0 = (*this)[index - 1]; + Type s1 = (*this)[index]; + // abs + uint32_t t0 = (s0 > 0) ? s0 : -s0; + uint32_t t1 = (s1 > 0) ? s1 : -s1; + // Fix overflow issues + if (t1 < t0) t1 |= (1ul << 31); + return t1 - t0; +} + +} // namespace modm::platform diff --git a/src/modm/driver/gpio/gpio_sampler.hpp.in b/src/modm/driver/gpio/gpio_sampler.hpp.in new file mode 100644 index 0000000000..738e06428f --- /dev/null +++ b/src/modm/driver/gpio/gpio_sampler.hpp.in @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_TEST_LOGIC_ANALYZER_SAMPLER_HPP +#define MODM_TEST_LOGIC_ANALYZER_SAMPLER_HPP + +#include +#include +#include + +namespace modm +{ + +class GpioSampler +{ +public: + using Type = int32_t; + + class Channel + { + friend class GpioSampler; + size_t max_count = 0; + volatile size_t count = 0; + Type *data = nullptr; + Channel(); + void allocate(size_t max_samples); + void add(Type time); + void reset(); + + public: + ~Channel(); + + inline size_t max() const { return max_count; } + inline size_t size() const { return count; } + + void dump() const; + + Type diff(size_t index) const; + bool read(size_t index) const; + + const Type& operator[](size_t index) const; + + inline const Type* begin() const { return data; } + inline const Type* end() const { return &data[count]; } + }; + + template + class Handle + { + friend class GpioSampler; + using CleanupHandler = void(*)(); + using StartHandler = void(*)(Handle &); + + const CleanupHandler cleanup; + const StartHandler start; + Channel data[channels]; + + Handle(size_t max_samples, StartHandler start, CleanupHandler cleanup); + void set_start_time(const Type *start); + public: + static constexpr size_t Channels = channels; + + public: + ~Handle(); + + void + restart(); + + const Channel& + operator[](size_t channel) const; + }; + + template< class... Gpios > + static auto Create(size_t max_samples); + +protected: + static void *context; + + enum class + Interrupt : uint8_t + { +%% for vector in extis + {{ vector | capitalize }} = {{ loop.index0 }}, +%% endfor + }; + + template< size_t channels, size_t pin_count, uint8_t pin, class Gpio, class... Gpios > + static void sampleGpio(Channel *data, Type time); + template< size_t channels, size_t pin_count, uint8_t pin > + static void sampleGpio(Channel *, Type) {} + + static void reset(Interrupt vector); + static void setHandler(Interrupt vector, void(*handler)()); + static inline Type getTime() { + return Type(DWT->CYCCNT & ~(1ul << 31)); + } +}; + +} // namespace modm + +#include "gpio_sampler_impl.hpp" + +#endif // MODM_TEST_LOGIC_ANALYZER_SAMPLER_HPP diff --git a/src/modm/driver/gpio/gpio_sampler.lb b/src/modm/driver/gpio/gpio_sampler.lb new file mode 100644 index 0000000000..0db3993b80 --- /dev/null +++ b/src/modm/driver/gpio/gpio_sampler.lb @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, 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/. + +from collections import OrderedDict + +def init(module): + module.parent = ":driver" + module.name = "gpio_sampler" + + +def prepare(module, options): + if options[":target"].identifier["platform"] != "stm32": + return False + + core = options[":target"].get_driver("core:cortex-m*") + # Cortex-M0 doesn't have the DWT->CYCCNT and Cortex-M7 support is broken + if not core or "m0" in core["type"] or "m7" in core["type"]: + return False + + module.depends( + ":platform:gpio", + ":platform:core", + ":architecture:interrupt") + return True + + +def build(env): + exti_vectors = [v["name"] for v in env[":target"].get_driver("core")["vector"] if "EXTI" in v["name"]] + # These are all exti possible vectors: 0, 0_1, 1, 15_10, 2, 2_3, 2_TSC, 3, 4, 4_15, 9_5 + extimap = { + "0": [0], "1": [1], "2": [2], "3": [3], "4": [4], + "0_1": [0,1], + "2_TSC": [2], + "2_3": [2,3], + "4_15": [4,5,6,7,8,9,10,11,12,13,14,15], + "9_5": [5,6,7,8,9], + "15_10": [10,11,12,13,14,15], + } + extis = OrderedDict() + for v in sorted(exti_vectors): + extis[v] = extimap[v[4:]] + + env.substitutions = { + "extis": extis, + "vectors_location": env.get(":platform:core:vector_table_location", "rom") + } + env.outbasepath = "modm/src/modm/driver" + env.template("gpio_sampler.cpp.in") + env.template("gpio_sampler.hpp.in") + env.template("gpio_sampler_impl.hpp.in") diff --git a/src/modm/driver/gpio/gpio_sampler_impl.hpp.in b/src/modm/driver/gpio/gpio_sampler_impl.hpp.in new file mode 100644 index 0000000000..1945fbc832 --- /dev/null +++ b/src/modm/driver/gpio/gpio_sampler_impl.hpp.in @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_TEST_LOGIC_ANALYZER_SAMPLER_HPP +# error "Don't include this file directly, use 'gpio_sampler.hpp' instead!" +#endif + +#include +#include +#include + +namespace modm +{ + +template +GpioSampler::Handle::~Handle() +{ + cleanup(); + GpioSampler::context = nullptr; +} + +template +GpioSampler::Handle::Handle(size_t max_samples, StartHandler start, CleanupHandler cleanup): + cleanup(cleanup), start(start) +{ + for (auto &ch : data) ch.allocate(max_samples); +} + +template +void +GpioSampler::Handle::set_start_time(const Type *start) +{ + for (size_t ii=0; ii +void +GpioSampler::Handle::restart() +{ + modm::atomic::Lock l; + for (auto &ch : data) ch.reset(); + start(*this); +} + +template +const GpioSampler::Channel& +GpioSampler::Handle::operator[](size_t channel) const +{ + return data[(channel < channels) ? channel : (channels - 1)]; +} + +template< size_t channels, size_t pin_count, uint8_t pin, class Gpio, class... Gpios > +void +GpioSampler::sampleGpio(Channel *data, Type time) +{ + if constexpr (Gpio::pin == pin) + { + if ((pin_count == 1) or Gpio::getExternalInterruptFlag()) + { + if (not Gpio::read()) time = -time; + constexpr size_t channel = channels - sizeof...(Gpios) - 1; + data[channel].add(time); + Gpio::acknowledgeExternalInterruptFlag(); + } + } + sampleGpio(data, time); +} + +template< class... Gpios > +auto +GpioSampler::Create(size_t max_samples) +{ + // Count the pins per interrupt and + // only reset the vectors that contain at least one GPIO +%% for vector, pos_map in extis.items() + constexpr size_t pin_count_{{vector | lower}} = +%% for pos in pos_map + ((Gpios::pin == {{pos}} ? 1 : 0) + ...){% if loop.last %};{% else %} +{% endif %} +%% endfor + if constexpr (pin_count_{{vector | lower}}) reset(Interrupt::{{ vector | capitalize }}); +%% endfor + + Handle handle(max_samples, + [](Handle &h) { + // prime the data logger + const Type time = getTime(); + const Type start[] = {(Gpios::read() ? time : -time)...}; + h.set_start_time(start); + }, + []() { + (Gpios::disableExternalInterrupt(), ...); + // only reset the vectors that contain at least one GPIO +%% for vector in extis + if constexpr (pin_count_{{vector | lower}}) reset(Interrupt::{{ vector | capitalize }}); +%% endfor + }); + GpioSampler::context = reinterpret_cast(&handle); +%% for vector, pos_map in extis.items() + // only reset the vectors that contain at least one GPIO + if constexpr (pin_count_{{vector | lower}}) { + setHandler(Interrupt::{{ vector | capitalize }}, []() + { + const Type time = getTime(); + auto *h = reinterpret_cast*>(GpioSampler::context); + %% for pos in pos_map + sampleGpio< sizeof...(Gpios), pin_count_{{vector | lower}}, {{pos}}, Gpios... >(h->data, time); + %% endfor + }); + } +%% endfor + // trigger on both edges + (Gpios::setInputTrigger(Gpios::InputTrigger::BothEdges), ...); + // enabled all GPIO interrupts + (Gpios::enableExternalInterrupt(), ...); + // prime the logger with initial measurement + handle.restart(); + return handle; +} + +} // namespace modm diff --git a/src/modm/platform/clock/stm32/rcc.cpp.in b/src/modm/platform/clock/stm32/rcc.cpp.in index 8e611b1e04..5aa6d5db57 100644 --- a/src/modm/platform/clock/stm32/rcc.cpp.in +++ b/src/modm/platform/clock/stm32/rcc.cpp.in @@ -20,6 +20,7 @@ /// @cond namespace modm::platform { +uint32_t modm_fastdata fcpu({{ "{0:,}".format((1e6 * hsi_frequency)|int).replace(',', "'") }}); uint16_t modm_fastdata delay_fcpu_MHz({{ hsi_frequency }}); uint16_t modm_fastdata delay_ns_per_loop({{ "{0:,}".format((loops * 1000.0 / hsi_frequency)|int).replace(',', "'") }}); } diff --git a/src/modm/platform/clock/stm32/rcc_impl.hpp.in b/src/modm/platform/clock/stm32/rcc_impl.hpp.in index b534d4505e..bbf53c5d9e 100644 --- a/src/modm/platform/clock/stm32/rcc_impl.hpp.in +++ b/src/modm/platform/clock/stm32/rcc_impl.hpp.in @@ -14,6 +14,7 @@ namespace modm::platform { /// @cond +extern uint32_t fcpu; extern uint16_t delay_fcpu_MHz; extern uint16_t delay_ns_per_loop; /// @endcond @@ -96,6 +97,7 @@ void Rcc::updateCoreFrequency() { delay_fcpu_MHz = Core_Hz / 1'000'000; + fcpu = Core_Hz; delay_ns_per_loop = std::round({{loops}}000.f / (Core_Hz / 1'000'000)); } From 30e4f462486a062a5b4583c1ded23ce25b296477 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 7 Oct 2018 20:30:21 +0200 Subject: [PATCH 088/135] [test] Add GpioSampler test --- test/modm/platform/gpio/module.lb | 14 ++- .../gpio/platform_gpio_test_avr.cpp.in | 4 +- .../gpio/platform_gpio_test_stm32.cpp.in | 6 +- .../gpio_sampler/gpio_sampler_test.cpp | 90 +++++++++++++++++++ .../gpio_sampler/gpio_sampler_test.hpp | 22 +++++ test/modm/platform/gpio_sampler/module.lb | 44 +++++++++ 6 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 test/modm/platform/gpio_sampler/gpio_sampler_test.cpp create mode 100644 test/modm/platform/gpio_sampler/gpio_sampler_test.hpp create mode 100644 test/modm/platform/gpio_sampler/module.lb diff --git a/test/modm/platform/gpio/module.lb b/test/modm/platform/gpio/module.lb index d408d1f8ca..da6d1b4b6b 100644 --- a/test/modm/platform/gpio/module.lb +++ b/test/modm/platform/gpio/module.lb @@ -79,8 +79,18 @@ def init(module): module.name = ":test:platform:gpio" def prepare(module, options): - target = options[":target"].identifier - if target["platform"] in ["stm32"]: + target = options[":target"] + core = target.get_driver("core:cortex-m*") + # Cortex-M0 doesn't have the DWT->CYCCNT and Cortex-M7 support is broken + if not core or "m0" in core["type"] or "m7" in core["type"]: + return False + if target.identifier.platform != "stm32": + return False + # Only 64-pin TQFP package + if target.identifier.pin != "r" or target.identifier.package != "t": + return False + + if target.identifier.platform in ["stm32"]: module.add_option( BooleanOption( name="test_gpio_lock", diff --git a/test/modm/platform/gpio/platform_gpio_test_avr.cpp.in b/test/modm/platform/gpio/platform_gpio_test_avr.cpp.in index 4557fb0ce3..ea2ee2220e 100644 --- a/test/modm/platform/gpio/platform_gpio_test_avr.cpp.in +++ b/test/modm/platform/gpio/platform_gpio_test_avr.cpp.in @@ -79,7 +79,7 @@ PlatformGpioTest::testInput() { %% for (port, pin) in test_io Gpio{{port ~ pin}}::setInput(Gpio::InputType::PullUp); - modm::delayMilliseconds(1); + modm::delay_ms(1); TEST_ASSERT_TRUE((PIN{{port}} & (1ul << {{pin}})) == (1ul << {{pin}})); TEST_ASSERT_TRUE(Gpio{{port ~ pin}}::read()); %% endfor @@ -98,4 +98,4 @@ PlatformGpioTest::testConnect() void PlatformGpioTest::testLock() { -} \ No newline at end of file +} diff --git a/test/modm/platform/gpio/platform_gpio_test_stm32.cpp.in b/test/modm/platform/gpio/platform_gpio_test_stm32.cpp.in index 4eda10b8ec..b595d2b0d6 100644 --- a/test/modm/platform/gpio/platform_gpio_test_stm32.cpp.in +++ b/test/modm/platform/gpio/platform_gpio_test_stm32.cpp.in @@ -171,11 +171,11 @@ PlatformGpioTest::testInput() { %% for (port, pin) in test_io Gpio{{port ~ pin}}::setInput(Gpio::InputType::PullUp); - modm::delayMilliseconds(1); + modm::delay_ms(1); TEST_ASSERT_TRUE((GPIO{{port}}->IDR & (1ul << {{pin}})) == (1ul << {{pin}})); TEST_ASSERT_TRUE(Gpio{{port ~ pin}}::read()); Gpio{{port ~ pin}}::setInput(Gpio::InputType::PullDown); - modm::delayMilliseconds(1); + modm::delay_ms(1); TEST_ASSERT_TRUE((GPIO{{port}}->IDR & (1ul << {{pin}})) == 0ul); TEST_ASSERT_FALSE(Gpio{{port ~ pin}}::read()); %% endfor @@ -244,4 +244,4 @@ PlatformGpioTest::testLock() TEST_ASSERT_TRUE((GPIO{{port}}->LCKR & (1ul << {{pin}})) == (1ul << {{pin}})); %% endfor %% endif -} \ No newline at end of file +} diff --git a/test/modm/platform/gpio_sampler/gpio_sampler_test.cpp b/test/modm/platform/gpio_sampler/gpio_sampler_test.cpp new file mode 100644 index 0000000000..8c53230d3c --- /dev/null +++ b/test/modm/platform/gpio_sampler/gpio_sampler_test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018, 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 +#include +#include + +#include "gpio_sampler_test.hpp" + +#include + +using namespace modm::platform; +using namespace std::chrono_literals; + +void +GpioSamplerTest::testTiming() +{ + using Gpio0 = GpioA0; + using Gpio1 = GpioA1; + Gpio0::setOutput(modm::Gpio::Low); + Gpio1::setOutput(modm::Gpio::High); + + { + auto r = modm::GpioSampler::Create(100); + Gpio0::set(); modm::delay(1ms); + Gpio0::reset(); modm::delay(1ms); + Gpio0::set(); modm::delay(1ms); + Gpio0::reset(); modm::delay(1ms); + + Gpio1::reset(); modm::delay(1ms); + Gpio1::set(); modm::delay(1ms); + Gpio1::reset(); modm::delay(1ms); + Gpio1::set(); modm::delay(1ms); + + TEST_ASSERT_EQUALS(r[0].size(), 5u); + TEST_ASSERT_EQUALS(r[1].size(), 5u); + + MODM_LOG_INFO << "Channel 0:" << modm::endl; + for (const auto &t : r[0]) { + MODM_LOG_INFO << t << modm::endl; + } + MODM_LOG_INFO << "Channel 1:" << modm::endl;; + for (const auto &t : r[1]) { + MODM_LOG_INFO << t << modm::endl; + } + + TEST_ASSERT_FALSE(r[0].read(0)); // low + TEST_ASSERT_TRUE( r[0].read(1)); // high + TEST_ASSERT_FALSE(r[0].read(2)); // low + TEST_ASSERT_TRUE( r[0].read(3)); // high + TEST_ASSERT_FALSE(r[0].read(4)); // low + + TEST_ASSERT_TRUE( r[1].read(0)); // high + TEST_ASSERT_FALSE(r[1].read(1)); // low + TEST_ASSERT_TRUE( r[1].read(2)); // high + TEST_ASSERT_FALSE(r[1].read(3)); // low + TEST_ASSERT_TRUE( r[1].read(4)); // high + + const int32_t ce = modm::platform::fcpu / 1000; + const int32_t cu = ce * 1.1f; + + TEST_ASSERT_EQUALS(r[0].diff(0), 0); + TEST_ASSERT_EQUALS_RANGE(r[0].diff(1), 0l, ce); + TEST_ASSERT_EQUALS_RANGE(r[0].diff(2), ce, cu); + TEST_ASSERT_EQUALS_RANGE(r[0].diff(3), ce, cu); + TEST_ASSERT_EQUALS_RANGE(r[0].diff(4), ce, cu); + + TEST_ASSERT_EQUALS(r[1].diff(0), 0); + TEST_ASSERT_EQUALS_RANGE(r[1].diff(1), ce*4, ce*3 + cu); + TEST_ASSERT_EQUALS_RANGE(r[1].diff(2), ce, cu); + TEST_ASSERT_EQUALS_RANGE(r[1].diff(3), ce, cu); + TEST_ASSERT_EQUALS_RANGE(r[1].diff(4), ce, cu); + } +} + +void +GpioSamplerTest::testVerifier() +{ + +} + + diff --git a/test/modm/platform/gpio_sampler/gpio_sampler_test.hpp b/test/modm/platform/gpio_sampler/gpio_sampler_test.hpp new file mode 100644 index 0000000000..2f3e65f445 --- /dev/null +++ b/test/modm/platform/gpio_sampler/gpio_sampler_test.hpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018, 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 + +class GpioSamplerTest : public unittest::TestSuite +{ +public: + void + testTiming(); + + void + testVerifier(); +}; diff --git a/test/modm/platform/gpio_sampler/module.lb b/test/modm/platform/gpio_sampler/module.lb new file mode 100644 index 0000000000..0c6b36c9bb --- /dev/null +++ b/test/modm/platform/gpio_sampler/module.lb @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, 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/. + +from collections import OrderedDict + +def init(module): + module.parent = ":test:platform" + module.name = "gpio_sampler" + + +def prepare(module, options): + target = options[":target"] + core = target.get_driver("core:cortex-m*") + # Cortex-M0 doesn't have the DWT->CYCCNT and Cortex-M7 support is broken + if not core or "m0" in core["type"] or "m7" in core["type"]: + return False + if target.identifier.platform != "stm32": + return False + # Only 64-pin TQFP package + if target.identifier.pin != "r" or target.identifier.package != "t": + return False + + module.depends(":driver:gpio_sampler", ":mock:logic_analyzer") + return True + + +def build(env): + if not env.has_module(":board:nucleo-*"): + env.log.warn("`:test:platform:gpio_sampler` has been hardcoded to a Nucleo-64 board!") + # When porting make sure this test does not damage your board! + return + + env.outbasepath = "modm-test/src/modm-test/platform/gpio" + # The tests for the implementation + env.copy("gpio_sampler_test.hpp") + env.copy("gpio_sampler_test.cpp") From 062e1b7ac870f7599ecd8397968b73df2b3c3764 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 8 Oct 2018 23:29:26 +0200 Subject: [PATCH 089/135] [test] Add LogicAnalyzer implementation --- test/modm/mock/logic_analyzer.hpp | 127 ++++++++++++++++++++++++++++++ test/modm/mock/module.lb | 18 +++++ 2 files changed, 145 insertions(+) create mode 100644 test/modm/mock/logic_analyzer.hpp diff --git a/test/modm/mock/logic_analyzer.hpp b/test/modm/mock/logic_analyzer.hpp new file mode 100644 index 0000000000..8f390e2f21 --- /dev/null +++ b/test/modm/mock/logic_analyzer.hpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include +#include + +namespace modm_test +{ + + +class LogicAnalyzer +{ +public: + using FailureHandler = std::function; + + static inline bool + verify(const char *state_machine, + const modm::GpioSampler::Channel &ch, + FailureHandler failure = nullptr) + { + const auto fn_fail = [&ch](const char *state, size_t index, int32_t time) + { + MODM_LOG_ERROR << "failed at '" << *state << "' with '" << ch[index] << "'"; + if (time) { MODM_LOG_ERROR << " at diff " << ch.diff(index) << " '" << time << "'"; } + MODM_LOG_ERROR << modm::endl; + }; + if (failure == nullptr) failure = fn_fail; + + const char *state = state_machine; + const char *repeat = nullptr; + size_t repeat_count = 0; + + int32_t tmin = 0; + int32_t tmax = INT_MAX; + volatile int32_t *time = &tmin; + + size_t sidx = 0; + + while(*state) + { + switch(*state) + { + case 'l': // low sample + case 'h': // high sample + { + if (int32_t diff = ch.diff(sidx); diff) { + if (diff < tmin) failure(state, sidx, -tmin); + if (tmax < diff) failure(state, sidx, tmax); + } + + int32_t sample = ch[sidx]; + if (*state == 'l') { + if (0 < sample) failure(state, sidx, 0); + // MODM_LOG_DEBUG << "l " << sample << modm::endl; + } else { + if (sample < 0) failure(state, sidx, 0); + // MODM_LOG_DEBUG << "h " << sample << modm::endl; + } + time = &tmin; + break; + } + case '>': // next sample + if (sidx >= ch.max()) failure(state, sidx, 0); + time = &tmax; + sidx++; + // MODM_LOG_DEBUG << '>' << modm::endl; + break; + + case '0'...'9': // timing information + { + char *out; + const uint64_t us = strtoul(state, &out, 10); + const uint32_t cycles = (modm::platform::fcpu * us) / 1000000; + *time = cycles; + state = out; + // MODM_LOG_DEBUG << (time == &tmin ? "min " : "max ") << us << "us => " << cycles << modm::endl; + continue; + } + + case '{': // start of repeat count + { + char *out; + repeat_count = strtoul(state+1, &out, 10) - 1; + state = out; + // MODM_LOG_DEBUG << '{' << repeat_count << '}' << modm::endl; + continue; + } + case '}': // end of repeat count + break; + + case '(': // start of repeat group + repeat = state; + // MODM_LOG_DEBUG << '(' << modm::endl; + break; + + case ')': // end of repeat group + if (repeat_count > 0) { + repeat_count--; + state = repeat; + // MODM_LOG_DEBUG << ")<-" << repeat_count << modm::endl; + continue; + } + break; + + default: // just continue + break; + } + state++; + } + return true; + } +}; + +} // namespace modm::platform diff --git a/test/modm/mock/module.lb b/test/modm/mock/module.lb index 03ec138194..e2c46c094e 100644 --- a/test/modm/mock/module.lb +++ b/test/modm/mock/module.lb @@ -74,6 +74,23 @@ class SharedMedium(Module): env.outbasepath = "modm-test/src/modm-test/mock" env.copy("shared_medium.hpp") +class LogicAnalyzer(Module): + def init(self, module): + module.name = "logic_analyzer" + + def prepare(self, module, options): + core = options[":target"].get_driver("core:cortex-m*") + # Cortex-M0 doesn't have the DWT->CYCCNT and Cortex-M7 support is broken + if not core or "m0" in core["type"] or "m7" in core["type"]: + return False + + module.depends(":stdc++", ":driver:gpio_sampler", ":debug") + return True + + def build(self, env): + env.outbasepath = "modm-test/src/modm-test/mock" + env.copy("logic_analyzer.hpp") + def init(module): module.name = ":mock" @@ -84,6 +101,7 @@ def prepare(module, options): module.add_submodule(SpiMaster()) module.add_submodule(IoDevice()) module.add_submodule(SharedMedium()) + module.add_submodule(LogicAnalyzer()) return True def build(env): From 9ab15d271ab7da371958d40ae97dfccad1d64ca7 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 7 Oct 2018 20:29:22 +0200 Subject: [PATCH 090/135] [test] Add BitBang SpiMaster test --- test/modm/bitbang/module.lb | 45 +++++++++++++++ test/modm/bitbang/spi_bitbang_test.cpp | 77 ++++++++++++++++++++++++++ test/modm/bitbang/spi_bitbang_test.hpp | 19 +++++++ 3 files changed, 141 insertions(+) create mode 100644 test/modm/bitbang/module.lb create mode 100644 test/modm/bitbang/spi_bitbang_test.cpp create mode 100644 test/modm/bitbang/spi_bitbang_test.hpp diff --git a/test/modm/bitbang/module.lb b/test/modm/bitbang/module.lb new file mode 100644 index 0000000000..a6d3a3d734 --- /dev/null +++ b/test/modm/bitbang/module.lb @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, 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.parent = ":test:platform" + module.name = "spi.bitbang" + + +def prepare(module, options): + target = options[":target"] + core = target.get_driver("core:cortex-m*") + # Cortex-M0 doesn't have the DWT->CYCCNT and Cortex-M7 support is broken + if not core or "m0" in core["type"] or "m7" in core["type"]: + return False + if target.identifier.platform != "stm32": + return False + # Only 64-pin TQFP package + if target.identifier.pin != "r" or target.identifier.package != "t": + return False + + module.depends( + ":platform:spi.bitbang", + ":driver:gpio_sampler") + return True + + +def build(env): + if not env.has_module(":board:nucleo-*"): + env.log.warn("`:test:platform:spi.bitbang` has been hardcoded to a Nucleo-64 board!") + # When porting make sure this test does not damage your board! + return + + global tim_instance + env.outbasepath = "modm-test/src/modm-test/platform/spi" + env.copy("spi_bitbang_test.hpp") + env.copy("spi_bitbang_test.cpp") diff --git a/test/modm/bitbang/spi_bitbang_test.cpp b/test/modm/bitbang/spi_bitbang_test.cpp new file mode 100644 index 0000000000..5d4bd8c288 --- /dev/null +++ b/test/modm/bitbang/spi_bitbang_test.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, 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 "spi_bitbang_test.hpp" +#include +#include +#include + +#include + +using namespace modm::platform; + + + +void +SpiBitbangTest::testSpiMaster() +{ + using Sck = GpioA0; + using Mosi = GpioA1; + using SpiMaster = BitBangSpiMaster; + SpiMaster::connect(); + SpiMaster::initialize(); + + const auto Start = modm::GpioSampler::Create; + + { + auto r = Start(50); + SpiMaster::transferBlocking(0); + TEST_ASSERT_EQUALS(r[1].size(), 1u); + TEST_ASSERT_EQUALS(r[0].size(), 17u); + + modm_test::LogicAnalyzer::verify("l", r[1]); // MOSI + modm_test::LogicAnalyzer::verify("l {8}( > h 45>55 l )", r[0]); // SCK + + r[0].dump(); + r[1].dump(); + + r.restart(); + SpiMaster::transferBlocking(0b1010'1010); + TEST_ASSERT_EQUALS(r[1].size(), 9u); + TEST_ASSERT_EQUALS(r[0].size(), 17u); + + modm_test::LogicAnalyzer::verify("l {4}( > h 95>110 l ) ", r[1]); // MOSI + modm_test::LogicAnalyzer::verify("l {8}( > h 45>55 l )", r[0]); // SCK + + r[1].dump(); + + r.restart(); + SpiMaster::transferBlocking(0b0001'1110); + TEST_ASSERT_EQUALS(r[1].size(), 3u); + TEST_ASSERT_EQUALS(r[0].size(), 17u); + + modm_test::LogicAnalyzer::verify("l 295> h >420 l ", r[1]); // MOSI + modm_test::LogicAnalyzer::verify("l {8}( > h 45>55 l )", r[0]); // SCK + + r[1].dump(); + + r.restart(); + SpiMaster::setDataOrder(SpiMaster::DataOrder::LsbFirst); + SpiMaster::transferBlocking(0b0001'1110); + TEST_ASSERT_EQUALS(r[1].size(), 3u); + TEST_ASSERT_EQUALS(r[0].size(), 17u); + + r[1].dump(); + + modm_test::LogicAnalyzer::verify("l 95> h >420 l ", r[1]); // MOSI + modm_test::LogicAnalyzer::verify("l {8}( > h 45>55 l )", r[0]); // SCK + } +} diff --git a/test/modm/bitbang/spi_bitbang_test.hpp b/test/modm/bitbang/spi_bitbang_test.hpp new file mode 100644 index 0000000000..e9e397c5c9 --- /dev/null +++ b/test/modm/bitbang/spi_bitbang_test.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018, 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 + +class SpiBitbangTest : public unittest::TestSuite +{ +public: + void + testSpiMaster(); +}; From bf517efa54fe3b5e391d1e5b620613cc4a693178 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 1 Aug 2020 19:22:55 +0200 Subject: [PATCH 091/135] [tools] Detect macOS properly for Python 3.8 --- tools/modm_tools/crashdebug.py | 2 +- tools/modm_tools/utils.py | 2 +- tools/scripts/examples_compile.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/modm_tools/crashdebug.py b/tools/modm_tools/crashdebug.py index 436d61e024..04a252b4bf 100644 --- a/tools/modm_tools/crashdebug.py +++ b/tools/modm_tools/crashdebug.py @@ -19,7 +19,7 @@ def __init__(self, binary_path, coredump): crashdebug = "lin64/CrashDebug" if "Windows" in platform.platform(): crashdebug = "win32/CrashDebug.exe" - elif "Darwin" in platform.platform(): + elif "Darwin" in platform.system(): crashdebug = "osx64/CrashDebug" self.binary = os.path.join(binary_path, crashdebug) diff --git a/tools/modm_tools/utils.py b/tools/modm_tools/utils.py index d62af4f10c..cc69c6de20 100644 --- a/tools/modm_tools/utils.py +++ b/tools/modm_tools/utils.py @@ -41,7 +41,7 @@ def listrify(*objs): def guess_serial_port(port_hint=None): if "Windows" in platform.platform(): ports = glob.glob('COM[0-9]*') - elif "Darwin" in platform.platform(): + elif "Darwin" in platform.system(): ports = glob.glob('/dev/tty.usb*') else: ports = glob.glob('/dev/tty[A-Za-z]*') diff --git a/tools/scripts/examples_compile.py b/tools/scripts/examples_compile.py index 8bac36e3d2..a61a235e71 100755 --- a/tools/scripts/examples_compile.py +++ b/tools/scripts/examples_compile.py @@ -37,7 +37,7 @@ def generate(project): output = ["=" * 90, "Generating: {}".format(path)] options = " ".join("-D{}={}".format(k, v) for k,v in global_options.items()) # Compile Linux examples under macOS with hosted-darwin target - if "Darwin" in platform.platform() and "hosted-linux" in project.read_text(): + if "Darwin" in platform.system() and "hosted-linux" in project.read_text(): options += " -D:target=hosted-darwin" rc, ro = run(path, "lbuild {} build".format(options)) print("\n".join(output + [ro])) From 58db47f1269809ac99a8130dff498933b240cc4e Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 1 Aug 2020 19:22:32 +0200 Subject: [PATCH 092/135] [ci] Fix Travis CI Python version --- .travis.yml | 6 +++--- tools/scripts/examples_compile.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 85a5c9244b..73558077b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,10 @@ before_install: - brew update - brew tap osx-cross/avr - brew tap osx-cross/arm - - brew install scons git doxygen boost gcc avr-gcc arm-gcc-bin cmake || true + - brew install git doxygen boost gcc avr-gcc arm-gcc-bin cmake || true - brew upgrade boost gcc git || true - - export PATH=/Users/travis/Library/Python/3.7/bin:$PATH - - pip3 install --user modm + - pip3 install --user modm scons + - export PATH=/Users/travis/Library/Python/3.8/bin:/usr/local/bin:$PATH script: - (cd test && make run-hosted-darwin) diff --git a/tools/scripts/examples_compile.py b/tools/scripts/examples_compile.py index a61a235e71..b6c8428a6b 100755 --- a/tools/scripts/examples_compile.py +++ b/tools/scripts/examples_compile.py @@ -15,7 +15,7 @@ import multiprocessing from pathlib import Path -is_running_in_ci = os.getenv("CIRCLECI") is not None +is_running_in_ci = os.getenv("CIRCLECI") is not None or os.getenv("TRAVIS") is not None cpus = 4 if is_running_in_ci else os.cpu_count() build_dir = (Path(os.path.abspath(__file__)).parents[2] / "build") cache_dir = build_dir / "cache" From 75c7134cebe4dd3e2e8fe8e0e21d4bdb3d98d0a6 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Mon, 10 Aug 2020 22:29:15 +0200 Subject: [PATCH 093/135] Fix the method isConversionFinished() to wait on EndOfConversion instead of EndOfSampling. --- src/modm/platform/adc/stm32f3/adc_impl.hpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modm/platform/adc/stm32f3/adc_impl.hpp.in b/src/modm/platform/adc/stm32f3/adc_impl.hpp.in index 2873b40d93..180bc99d9c 100644 --- a/src/modm/platform/adc/stm32f3/adc_impl.hpp.in +++ b/src/modm/platform/adc/stm32f3/adc_impl.hpp.in @@ -194,7 +194,7 @@ modm::platform::Adc{{ id }}::startConversion(void) bool modm::platform::Adc{{ id }}::isConversionFinished(void) { - return static_cast(getInterruptFlags() & InterruptFlag::EndOfSampling); + return static_cast(getInterruptFlags() & InterruptFlag::EndOfRegularConversion); } // ---------------------------------------------------------------------------- From 824f6b5385c20c4112652fbf8491c03feab2d755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20G=C3=BCldenstein?= Date: Wed, 12 Aug 2020 23:33:32 +0200 Subject: [PATCH 094/135] fixed issue with escaping of arguemnts for size function --- tools/build_script_generator/cmake/resources/CMakeLists.txt.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build_script_generator/cmake/resources/CMakeLists.txt.in b/tools/build_script_generator/cmake/resources/CMakeLists.txt.in index c10f28af69..9e06359d3a 100644 --- a/tools/build_script_generator/cmake/resources/CMakeLists.txt.in +++ b/tools/build_script_generator/cmake/resources/CMakeLists.txt.in @@ -34,5 +34,5 @@ add_custom_target(${CMAKE_PROJECT_NAME}.lss ALL DEPENDS ${CMAKE_PROJECT_NAME} CO add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_SIZE} ARGS -C -d --mcu={{ partname }} ${CMAKE_PROJECT_NAME}.elf) %% elif core.startswith("cortex-m") #% FIXME: build path does not contain python tools of course -#% add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND python3 ./modm/modm_tools/size.py ${CMAKE_PROJECT_NAME}.elf "{{memories}}") +#% add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD VERBATIM COMMAND python3 ./modm/modm_tools/size.py ${CMAKE_PROJECT_NAME}.elf "{{memories}}") %% endif From 471605bbe4f004fa2cd5d32debf1c9bff0725a65 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 15 Aug 2020 01:10:51 +0200 Subject: [PATCH 095/135] [driver] Add IS31FL3733 matrix driver --- README.md | 13 +- src/modm/driver/display/is31fl3733.hpp | 236 +++++++++++++++++++++++++ src/modm/driver/display/is31fl3733.lb | 26 +++ 3 files changed, 269 insertions(+), 6 deletions(-) create mode 100644 src/modm/driver/display/is31fl3733.hpp create mode 100644 src/modm/driver/display/is31fl3733.lb diff --git a/README.md b/README.md index f69d6ee8ed..79a9df804b 100644 --- a/README.md +++ b/README.md @@ -206,46 +206,47 @@ can easily configure them for you specific needs. HX711 I2C-EEPROM +IS31FL3733 ITG3200 L3GD20 LAWICEL -LIS302DL +LIS302DL LIS3DSH LIS3MDL LM75 LP503X LSM303A -LSM6DS33 +LSM6DS33 LTC2984 MAX6966 MAX7219 MCP23X17 MCP2515 -NOKIA5110 +NOKIA5110 NRF24 TFT-DISPLAY PAT9125EL PCA8574 PCA9535 -PCA9548A +PCA9548A PCA9685 SIEMENS-S65 SIEMENS-S75 SK6812 SK9822 -SSD1306 +SSD1306 SX1276 TCS3414 TCS3472 TLC594X TMP102 -TMP175 +TMP175 VL53L0 VL6180 WS2812 diff --git a/src/modm/driver/display/is31fl3733.hpp b/src/modm/driver/display/is31fl3733.hpp new file mode 100644 index 0000000000..8afa348a10 --- /dev/null +++ b/src/modm/driver/display/is31fl3733.hpp @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2020, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include + +namespace modm +{ + +/// @ingroup modm_driver_is31fl3733 +struct is31fl3733 +{ + enum class + Addr : uint8_t + { + GND = 0b00, + SCL = 0b01, + SDA = 0b10, + VCC = 0b11 + }; + + /// Available I2C addresses + static constexpr uint8_t + addr(Addr addr2 = Addr::GND, Addr addr1 = Addr::GND) + { + return 0b1010000 | (uint8_t(addr2) << 2) | uint8_t(addr1); + }; + + enum class + Register : uint16_t + { + // Page 0 + LED_ON_OFF = 0x8000, + LED_OPEN = 0x8018, + LED_SHORT = 0x8030, + + // Page 1 + PWM = 0x8100, + + // Page 2 + AUTO_BREATH_MODE = 0x8200, + + // Page 3 + CONFIGURATION = 0x8300, + GLOBAL_CURRENT_CONTROL = 0x8301, + ABM_1 = 0x8302, + ABM_2 = 0x8306, + ABM_3 = 0x830A, + TIME_UPDATE = 0x830E, + SW_PULL_UP = 0x830F, + CS_PULL_DOWN = 0x8310, + RESET = 0x8311, + + // Global + COMMAND = 0xFD, + COMMAND_WRITE_LOCK = 0xFE, + INTERRUPT_MASK = 0xF0, + INTERRUPT_STATUS = 0xF1 + }; + +protected: + static constexpr uint8_t LED_ON_OFF_size = 0x18; + static constexpr uint8_t LED_OPEN_size = LED_ON_OFF_size; + static constexpr uint8_t LED_SHORT_size = LED_ON_OFF_size; + static constexpr uint8_t PWM_size = 0xC0; + static constexpr uint8_t AUTO_BREATH_MODE_size = PWM_size; + + static constexpr bool + hasPage(Register reg) + { return uint16_t(reg) & 0x8000; } + + static constexpr uint8_t + getPage(Register reg) + { return ((uint16_t(reg) >> 8) & 0x0f); } +}; + +/** + * @ingroup modm_driver_is31fl3733 + * @author Niklas Hauser + */ +template < class I2cMaster > +class Is31fl3733 : public is31fl3733, public modm::I2cDevice +{ +public: + Is31fl3733(uint8_t address=addr()): + I2cDevice(address) + {} + + bool + enable(uint8_t x, uint8_t y) + { + if (x < 12 and y < 16) + { + data.led_on_off[x] |= (1u << y); + return true; + } + return false; + } + void enableAll() + { std::memset(data.led_on_off, 0xff, sizeof(data.led_on_off)); } + + bool + disable(uint8_t x, uint8_t y) + { + if (x < 12 and y < 16) + { + data.led_on_off[x] &= ~(1u << y); + return true; + } + return false; + } + void disableAll() + { std::memset(data.led_on_off, 0, sizeof(data.led_on_off)); } + + bool + setPwm(uint8_t x, uint8_t y, uint8_t pwm) + { + if (x < 12 and y < 16) + { + data.led_pwm[x][y] = pwm; + return true; + } + return false; + } + void setAllPwm(uint8_t pwm) + { std::memset(data.led_pwm, pwm, sizeof(data.led_pwm)); } + +public: + modm::ResumableResult + reset() + { return readRegister(Register::RESET, buffer); } + + modm::ResumableResult + setGlobalCurrent(uint8_t current) + { return writeRegister(Register::GLOBAL_CURRENT_CONTROL, current); } + + modm::ResumableResult + clearSoftwareShutdown() + { return writeRegister(Register::CONFIGURATION, 0x01); } + + modm::ResumableResult + writeOnOff() + { + RF_BEGIN(); + if (not RF_CALL(setPage(Register::LED_ON_OFF))) RF_RETURN(false); + + this->transaction.configureWrite(&data.addr_led_on_off, LED_ON_OFF_size+1); + RF_END_RETURN_CALL(this->runTransaction()); + } + + modm::ResumableResult + writePwm() + { + RF_BEGIN(); + if (not RF_CALL(setPage(Register::PWM))) RF_RETURN(false); + + this->transaction.configureWrite(&data.addr_led_pwm, PWM_size+1); + RF_END_RETURN_CALL(this->runTransaction()); + } + +public: + modm::ResumableResult + writeRegister(Register reg, uint8_t value, uint8_t offset=0) + { + RF_BEGIN(); + if (not RF_CALL(setPage(reg))) RF_RETURN(false); + + buffer[0] = uint8_t(reg) + offset; + buffer[1] = value; + + this->transaction.configureWrite(buffer, 2); + RF_END_RETURN_CALL(this->runTransaction()); + } + + modm::ResumableResult + readRegister(Register reg, uint8_t *const value, uint8_t offset=0) + { + RF_BEGIN(); + if (not RF_CALL( setPage(reg) )) RF_RETURN(false); + + buffer[0] = uint8_t(reg) + offset; + this->transaction.configureWriteRead(buffer, 1, value, 1); + + RF_END_RETURN_CALL(this->runTransaction()); + } + +protected: + modm::ResumableResult + setPage(Register reg) + { + RF_BEGIN(); + + if (hasPage(reg) and (getPage(reg) != current_page)) + { + buffer[0] = uint8_t(Register::COMMAND_WRITE_LOCK); + buffer[1] = 0xC5; // command write key + this->transaction.configureWrite(buffer, 2); + if (not RF_CALL(this->runTransaction())) RF_RETURN(false); + + buffer[0] = uint8_t(Register::COMMAND); + buffer[1] = getPage(reg); + if (not RF_CALL(this->runTransaction())) RF_RETURN(false); + current_page = getPage(reg); + } + + RF_END_RETURN(true); + } + +private: + struct LedData + { + const uint8_t addr_led_on_off{uint8_t(Register::LED_ON_OFF)}; + uint16_t led_on_off[12]; + + const uint8_t addr_led_pwm{uint8_t(Register::PWM)}; + uint8_t led_pwm[12][16]; + } modm_packed; + + LedData data; + uint8_t current_page{0xff}; + uint8_t buffer[2]; +}; + +} // namespace modm diff --git a/src/modm/driver/display/is31fl3733.lb b/src/modm/driver/display/is31fl3733.lb new file mode 100644 index 0000000000..9eb03af216 --- /dev/null +++ b/src/modm/driver/display/is31fl3733.lb @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, 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 = ":driver:is31fl3733" + module.description = "IS31FL3733 Matrix Driver" + +def prepare(module, options): + module.depends( + ":architecture:i2c.device", + ":architecture:register") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/display" + env.copy("is31fl3733.hpp") From 9b6aeee3956446e483f0b66ad8111914868f1a9e Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 15 Aug 2020 01:11:08 +0200 Subject: [PATCH 096/135] [example] Add IS31FL3733 driver on STM32G071 --- examples/nucleo_g071rb/matrix/main.cpp | 62 +++++++++++++++++++++++ examples/nucleo_g071rb/matrix/project.xml | 12 +++++ 2 files changed, 74 insertions(+) create mode 100644 examples/nucleo_g071rb/matrix/main.cpp create mode 100644 examples/nucleo_g071rb/matrix/project.xml diff --git a/examples/nucleo_g071rb/matrix/main.cpp b/examples/nucleo_g071rb/matrix/main.cpp new file mode 100644 index 0000000000..b91f620719 --- /dev/null +++ b/examples/nucleo_g071rb/matrix/main.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019, 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 +#include +#include + +using namespace Board; + +using PinReset = GpioC8; +using PinSda = GpioA11; +using PinScl = GpioA12; + +using I2cInstance = BitBangI2cMaster; +static modm::Is31fl3733 driver(modm::is31fl3733::addr()); + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + LedD13::setOutput(modm::Gpio::Low); + + MODM_LOG_ERROR << "error" << modm::endl; + + // Reset the I2C interface of the chip + PinReset::setOutput(modm::Gpio::High); + modm::delay(20ms); + PinReset::setOutput(modm::Gpio::Low); + + I2cInstance::initialize(); + I2cInstance::connect(); + + RF_CALL_BLOCKING(driver.reset()); + RF_CALL_BLOCKING(driver.clearSoftwareShutdown()); + RF_CALL_BLOCKING(driver.setGlobalCurrent(1)); + + driver.enableAll(); + RF_CALL_BLOCKING(driver.writeOnOff()); + + uint8_t pwm{0}; + while (true) + { + for (uint8_t y=0, pi=pwm; y<16; y++) { + for (uint8_t x=0; x<12; x++) { + driver.setPwm(x, y, pi++); + } + } + RF_CALL_BLOCKING(driver.writePwm()); + modm::delay(10ms); + pwm++; + } + + return 0; +} diff --git a/examples/nucleo_g071rb/matrix/project.xml b/examples/nucleo_g071rb/matrix/project.xml new file mode 100644 index 0000000000..3893ddf9d3 --- /dev/null +++ b/examples/nucleo_g071rb/matrix/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-g071rb + + + + + modm:platform:gpio + modm:platform:i2c.bitbang + modm:driver:is31fl3733 + modm:build:scons + + From 3f14258dd638b28063e9da0663c7d8956210cda9 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Tue, 25 Aug 2020 20:11:03 +0200 Subject: [PATCH 097/135] Fix the CPU clock speed used in FreeRTOS. --- ext/aws/FreeRTOSConfig.h.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/aws/FreeRTOSConfig.h.in b/ext/aws/FreeRTOSConfig.h.in index 7114022b75..9776cf217c 100644 --- a/ext/aws/FreeRTOSConfig.h.in +++ b/ext/aws/FreeRTOSConfig.h.in @@ -36,12 +36,12 @@ your application. */ #include // declared in modm:platform:clock -modm_extern_c uint32_t _ZN4modm5clock4fcpuE; +modm_extern_c uint32_t _ZN4modm8platform4fcpuE; #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION {{ ("m0" not in core) | int }} #define configUSE_TICKLESS_IDLE 0 -#define configCPU_CLOCK_HZ ( _ZN4modm5clock4fcpuE ) +#define configCPU_CLOCK_HZ ( _ZN4modm8platform4fcpuE ) #define configTICK_RATE_HZ ( {{ options["::frequency"] }} ) #define configMAX_PRIORITIES 5 #define configMINIMAL_STACK_SIZE 128 From f40217941a53a0511af70c5f550e80d2314b60c9 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Tue, 25 Aug 2020 20:20:59 +0200 Subject: [PATCH 098/135] Use an integer instead of scientific notation. In some cases the 1e6 caused error during lbuild run. --- src/modm/platform/clock/stm32/rcc.cpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modm/platform/clock/stm32/rcc.cpp.in b/src/modm/platform/clock/stm32/rcc.cpp.in index 5aa6d5db57..df43a51463 100644 --- a/src/modm/platform/clock/stm32/rcc.cpp.in +++ b/src/modm/platform/clock/stm32/rcc.cpp.in @@ -20,7 +20,7 @@ /// @cond namespace modm::platform { -uint32_t modm_fastdata fcpu({{ "{0:,}".format((1e6 * hsi_frequency)|int).replace(',', "'") }}); +uint32_t modm_fastdata fcpu({{ "{0:,}".format((1000000 * hsi_frequency)|int).replace(',', "'") }}); uint16_t modm_fastdata delay_fcpu_MHz({{ hsi_frequency }}); uint16_t modm_fastdata delay_ns_per_loop({{ "{0:,}".format((loops * 1000.0 / hsi_frequency)|int).replace(',', "'") }}); } From bbe65babb67a6d967848a031b7cc67c975399e96 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Sun, 30 Aug 2020 14:45:44 +0200 Subject: [PATCH 099/135] [ext] Add FreeRTOS-Plus-TCP module with config --- ext/aws/FreeRTOSIPConfig.h.in | 308 ++++++++++++++++++++++++++++++++++ ext/aws/freertos | 2 +- ext/aws/module.lb | 18 ++ 3 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 ext/aws/FreeRTOSIPConfig.h.in diff --git a/ext/aws/FreeRTOSIPConfig.h.in b/ext/aws/FreeRTOSIPConfig.h.in new file mode 100644 index 0000000000..a6110ee019 --- /dev/null +++ b/ext/aws/FreeRTOSIPConfig.h.in @@ -0,0 +1,308 @@ +/* + * FreeRTOS+TCP V2.2.1 + * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef FREERTOS_IP_CONFIG_H +#define FREERTOS_IP_CONFIG_H + +/* Prototype for the function used to print out. In this case it prints to the +console before the network is connected then a UDP port after the network has +connected. */ +//extern void vLoggingPrintf( const char *pcFormatString, ... ); +extern void vPrintf(char const *format, ...); + +/* Set to 1 to print out debug messages. If ipconfigHAS_DEBUG_PRINTF is set to +1 then FreeRTOS_debug_printf should be defined to the function used to print +out the debugging messages. */ +#define ipconfigHAS_DEBUG_PRINTF 0 +#if( ipconfigHAS_DEBUG_PRINTF == 1 ) + #define FreeRTOS_debug_printf(X) vLoggingPrintf X +#endif + +/* Set to 1 to print out non debugging messages, for example the output of the +FreeRTOS_netstat() command, and ping replies. If ipconfigHAS_PRINTF is set to 1 +then FreeRTOS_printf should be set to the function used to print out the +messages. */ +#define ipconfigHAS_PRINTF 0 +#if( ipconfigHAS_PRINTF == 1 ) + #define FreeRTOS_printf(X) vLoggingPrintf X +#endif + +/* Define the byte order of the target MCU (the MCU FreeRTOS+TCP is executing +on). Valid options are pdFREERTOS_BIG_ENDIAN and pdFREERTOS_LITTLE_ENDIAN. */ +#define ipconfigBYTE_ORDER pdFREERTOS_LITTLE_ENDIAN + +/* If the network card/driver includes checksum offloading (IP/TCP/UDP checksums) +then set ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM to 1 to prevent the software +stack repeating the checksum calculations. */ +#define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 +#define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 + +/* Several API's will block until the result is known, or the action has been +performed, for example FreeRTOS_send() and FreeRTOS_recv(). The timeouts can be +set per socket, using setsockopt(). If not set, the times below will be +used as defaults. */ +#define ipconfigSOCK_DEFAULT_RECEIVE_BLOCK_TIME ( 5000 ) +#define ipconfigSOCK_DEFAULT_SEND_BLOCK_TIME ( 5000 ) + +/* Include support for LLMNR: Link-local Multicast Name Resolution +(non-Microsoft) */ +#define ipconfigUSE_LLMNR ( 0 ) + +/* Include support for NBNS: NetBIOS Name Service (Microsoft) */ +#define ipconfigUSE_NBNS ( 0 ) + +/* Include support for DNS caching. For TCP, having a small DNS cache is very +useful. When a cache is present, ipconfigDNS_REQUEST_ATTEMPTS can be kept low +and also DNS may use small timeouts. If a DNS reply comes in after the DNS +socket has been destroyed, the result will be stored into the cache. The next +call to FreeRTOS_gethostbyname() will return immediately, without even creating +a socket. */ +#define ipconfigUSE_DNS_CACHE ( 1 ) +#define ipconfigDNS_CACHE_NAME_LENGTH ( 16 ) +#define ipconfigDNS_CACHE_ENTRIES ( 4 ) +#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 ) + +/* The IP stack executes it its own task (although any application task can make +use of its services through the published sockets API). ipconfigUDP_TASK_PRIORITY +sets the priority of the task that executes the IP stack. The priority is a +standard FreeRTOS task priority so can take any value from 0 (the lowest +priority) to (configMAX_PRIORITIES - 1) (the highest priority). +configMAX_PRIORITIES is a standard FreeRTOS configuration parameter defined in +FreeRTOSConfig.h, not FreeRTOSIPConfig.h. Consideration needs to be given as to +the priority assigned to the task executing the IP stack relative to the +priority assigned to tasks that use the IP stack. */ +#define ipconfigIP_TASK_PRIORITY ( configMAX_PRIORITIES - 2 ) + +/* The size, in words (not bytes), of the stack allocated to the FreeRTOS+TCP +task. This setting is less important when the FreeRTOS Win32 simulator is used +as the Win32 simulator only stores a fixed amount of information on the task +stack. FreeRTOS includes optional stack overflow detection, see: +http://www.freertos.org/Stacks-and-stack-overflow-checking.html */ +#define ipconfigIP_TASK_STACK_SIZE_WORDS ( configMINIMAL_STACK_SIZE * 5 ) + +/* ipconfigRAND32() is called by the IP stack to generate random numbers for +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(); +#define ipconfigRAND32() uxRand() + +/* If ipconfigUSE_NETWORK_EVENT_HOOK is set to 1 then FreeRTOS+TCP will call the +network event hook at the appropriate times. If ipconfigUSE_NETWORK_EVENT_HOOK +is not set to 1 then the network event hook will never be called. See +http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_UDP/API/vApplicationIPNetworkEventHook.shtml +*/ +#define ipconfigUSE_NETWORK_EVENT_HOOK 1 + +/* Sockets have a send block time attribute. If FreeRTOS_sendto() is called but +a network buffer cannot be obtained then the calling task is held in the Blocked +state (so other tasks can continue to executed) until either a network buffer +becomes available or the send block time expires. If the send block time expires +then the send operation is aborted. The maximum allowable send block time is +capped to the value set by ipconfigMAX_SEND_BLOCK_TIME_TICKS. Capping the +maximum allowable send block time prevents prevents a deadlock occurring when +all the network buffers are in use and the tasks that process (and subsequently +free) the network buffers are themselves blocked waiting for a network buffer. +ipconfigMAX_SEND_BLOCK_TIME_TICKS is specified in RTOS ticks. A time in +milliseconds can be converted to a time in ticks by dividing the time in +milliseconds by portTICK_PERIOD_MS. */ +#define ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS ( 5000 / portTICK_PERIOD_MS ) + +/* If ipconfigUSE_DHCP is 1 then FreeRTOS+TCP will attempt to retrieve an IP +address, netmask, DNS server address and gateway address from a DHCP server. If +ipconfigUSE_DHCP is 0 then FreeRTOS+TCP will use a static IP address. The +stack will revert to using the static IP address even when ipconfigUSE_DHCP is +set to 1 if a valid configuration cannot be obtained from a DHCP server for any +reason. The static configuration used is that passed into the stack by the +FreeRTOS_IPInit() function call. */ +#define ipconfigUSE_DHCP 0 + +/* When ipconfigUSE_DHCP is set to 1, DHCP requests will be sent out at +increasing time intervals until either a reply is received from a DHCP server +and accepted, or the interval between transmissions reaches +ipconfigMAXIMUM_DISCOVER_TX_PERIOD. The IP stack will revert to using the +static IP address passed as a parameter to FreeRTOS_IPInit() if the +re-transmission time interval reaches ipconfigMAXIMUM_DISCOVER_TX_PERIOD without +a DHCP reply being received. */ +#define ipconfigMAXIMUM_DISCOVER_TX_PERIOD ( 120000 / portTICK_PERIOD_MS ) + +/* The ARP cache is a table that maps IP addresses to MAC addresses. The IP +stack can only send a UDP message to a remove IP address if it knowns the MAC +address associated with the IP address, or the MAC address of the router used to +contact the remote IP address. When a UDP message is received from a remote IP +address the MAC address and IP address are added to the ARP cache. When a UDP +message is sent to a remote IP address that does not already appear in the ARP +cache then the UDP message is replaced by a ARP message that solicits the +required MAC address information. ipconfigARP_CACHE_ENTRIES defines the maximum +number of entries that can exist in the ARP table at any one time. */ +#define ipconfigARP_CACHE_ENTRIES 6 + +/* ARP requests that do not result in an ARP response will be re-transmitted a +maximum of ipconfigMAX_ARP_RETRANSMISSIONS times before the ARP request is +aborted. */ +#define ipconfigMAX_ARP_RETRANSMISSIONS ( 5 ) + +/* ipconfigMAX_ARP_AGE defines the maximum time between an entry in the ARP +table being created or refreshed and the entry being removed because it is stale. +New ARP requests are sent for ARP cache entries that are nearing their maximum +age. ipconfigMAX_ARP_AGE is specified in tens of seconds, so a value of 150 is +equal to 1500 seconds (or 25 minutes). */ +#define ipconfigMAX_ARP_AGE 150 + +/* Implementing FreeRTOS_inet_addr() necessitates the use of string handling +routines, which are relatively large. To save code space the full +FreeRTOS_inet_addr() implementation is made optional, and a smaller and faster +alternative called FreeRTOS_inet_addr_quick() is provided. FreeRTOS_inet_addr() +takes an IP in decimal dot format (for example, "192.168.0.1") as its parameter. +FreeRTOS_inet_addr_quick() takes an IP address as four separate numerical octets +(for example, 192, 168, 0, 1) as its parameters. If +ipconfigINCLUDE_FULL_INET_ADDR is set to 1 then both FreeRTOS_inet_addr() and +FreeRTOS_indet_addr_quick() are available. If ipconfigINCLUDE_FULL_INET_ADDR is +not set to 1 then only FreeRTOS_indet_addr_quick() is available. */ +#define ipconfigINCLUDE_FULL_INET_ADDR 1 + +/* ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS defines the total number of network buffer that +are available to the IP stack. The total number of network buffers is limited +to ensure the total amount of RAM that can be consumed by the IP stack is capped +to a pre-determinable value. */ +#define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS 60 + +/* A FreeRTOS queue is used to send events from application tasks to the IP +stack. ipconfigEVENT_QUEUE_LENGTH sets the maximum number of events that can +be queued for processing at any one time. The event queue must be a minimum of +5 greater than the total number of network buffers. */ +#define ipconfigEVENT_QUEUE_LENGTH ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS + 5 ) + +/* The address of a socket is the combination of its IP address and its port +number. FreeRTOS_bind() is used to manually allocate a port number to a socket +(to 'bind' the socket to a port), but manual binding is not normally necessary +for client sockets (those sockets that initiate outgoing connections rather than +wait for incoming connections on a known port number). If +ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND is set to 1 then calling +FreeRTOS_sendto() on a socket that has not yet been bound will result in the IP +stack automatically binding the socket to a port number from the range +socketAUTO_PORT_ALLOCATION_START_NUMBER to 0xffff. If +ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND is set to 0 then calling FreeRTOS_sendto() +on a socket that has not yet been bound will result in the send operation being +aborted. */ +#define ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND 1 + +/* Defines the Time To Live (TTL) values used in outgoing UDP packets. */ +#define ipconfigUDP_TIME_TO_LIVE 128 +#define ipconfigTCP_TIME_TO_LIVE 128 /* also defined in FreeRTOSIPConfigDefaults.h */ + +/* USE_TCP: Use TCP and all its features */ +#define ipconfigUSE_TCP ( 1 ) + +/* USE_WIN: Let TCP use windowing mechanism. */ +#define ipconfigUSE_TCP_WIN ( 1 ) + +/* The MTU is the maximum number of bytes the payload of a network frame can +contain. For normal Ethernet V2 frames the maximum MTU is 1500. Setting a +lower value can save RAM, depending on the buffer management scheme used. If +ipconfigCAN_FRAGMENT_OUTGOING_PACKETS is 1 then (ipconfigNETWORK_MTU - 28) must +be divisible by 8. */ +#define ipconfigNETWORK_MTU 1500 + +/* Set ipconfigUSE_DNS to 1 to include a basic DNS client/resolver. DNS is used +through the FreeRTOS_gethostbyname() API function. */ +#define ipconfigUSE_DNS 0 + +/* If ipconfigREPLY_TO_INCOMING_PINGS is set to 1 then the IP stack will +generate replies to incoming ICMP echo (ping) requests. */ +#define ipconfigREPLY_TO_INCOMING_PINGS 1 + +/* If ipconfigSUPPORT_OUTGOING_PINGS is set to 1 then the +FreeRTOS_SendPingRequest() API function is available. */ +#define ipconfigSUPPORT_OUTGOING_PINGS 0 + +/* If ipconfigSUPPORT_SELECT_FUNCTION is set to 1 then the FreeRTOS_select() +(and associated) API function is available. */ +#define ipconfigSUPPORT_SELECT_FUNCTION 1 + +/* If ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES is set to 1 then Ethernet frames +that are not in Ethernet II format will be dropped. This option is included for +potential future IP stack developments. */ +#define ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES 1 + +/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1 then it is the +responsibility of the Ethernet interface to filter out packets that are of no +interest. If the Ethernet interface does not implement this functionality, then +set ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES to 0 to have the IP stack +perform the filtering instead (it is much less efficient for the stack to do it +because the packet will already have been passed into the stack). If the +Ethernet driver does all the necessary filtering in hardware then software +filtering can be removed by using a value other than 1 or 0. */ +#define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES 1 + +/* The windows simulator cannot really simulate MAC interrupts, and needs to +block occasionally to allow other tasks to run. */ +#define configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY ( 20 / portTICK_PERIOD_MS ) + +/* Advanced only: in order to access 32-bit fields in the IP packets with +32-bit memory instructions, all packets will be stored 32-bit-aligned, plus 16-bits. +This has to do with the contents of the IP-packets: all 32-bit fields are +32-bit-aligned, plus 16-bit(!) */ +#define ipconfigPACKET_FILLER_SIZE 2 + +/* Define the size of the pool of TCP window descriptors. On the average, each +TCP socket will use up to 2 x 6 descriptors, meaning that it can have 2 x 6 +outstanding packets (for Rx and Tx). When using up to 10 TP sockets +simultaneously, one could define TCP_WIN_SEG_COUNT as 120. */ +#define ipconfigTCP_WIN_SEG_COUNT 240 + +/* Each TCP socket has a circular buffers for Rx and Tx, which have a fixed +maximum size. Define the size of Rx buffer for TCP sockets. */ +#define ipconfigTCP_RX_BUFFER_LENGTH ( 2000 ) + +/* Define the size of Tx buffer for TCP sockets. */ +#define ipconfigTCP_TX_BUFFER_LENGTH ( 2000 ) + +/* When using call-back handlers, the driver may check if the handler points to +real program memory (RAM or flash) or just has a random non-zero value. */ +#define ipconfigIS_VALID_PROG_ADDRESS(x) ( (x) != NULL ) + +/* Include support for TCP hang protection. All sockets in a connecting or +disconnecting stage will timeout after a period of non-activity. */ +#define ipconfigTCP_HANG_PROTECTION ( 1 ) +#define ipconfigTCP_HANG_PROTECTION_TIME ( 30 ) + +/* Include support for TCP keep-alive messages. */ +#define ipconfigTCP_KEEP_ALIVE ( 1 ) +#define ipconfigTCP_KEEP_ALIVE_INTERVAL ( 20 ) /* in seconds */ + +#define portINLINE __inline + +#define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES 1 +#define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 1 +#define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 +#define ipconfigZERO_COPY_RX_DRIVER 1 +#define ipconfigZERO_COPY_TX_DRIVER 1 +#define ipconfigUSE_LINKED_RX_MESSAGES 1 + +#endif // FREERTOS_IP_CONFIG_H diff --git a/ext/aws/freertos b/ext/aws/freertos index ac05c153ff..cd3c27dfe4 160000 --- a/ext/aws/freertos +++ b/ext/aws/freertos @@ -1 +1 @@ -Subproject commit ac05c153fffce3f074a8cd54724fc3dc1d75b69f +Subproject commit cd3c27dfe4db31bae8375c61ab5a64da1a07f6a8 diff --git a/ext/aws/module.lb b/ext/aws/module.lb index b9bc4199d1..3d42b86bdf 100644 --- a/ext/aws/module.lb +++ b/ext/aws/module.lb @@ -11,6 +11,22 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # ----------------------------------------------------------------------------- +class FreeRTOS_TCP(Module): + def init(self, module): + module.name = "tcp" + module.description = "FreeRTOS-Plus-TCP" + + def prepare(self, module, options): + return True + + def build(self, env): + env.outbasepath = "modm/ext" + env.copy("freertos/FreeRTOS-Plus-TCP", "freertos_plus_tcp", + ignore=env.ignore_files("BufferAllocation_1.c")) + env.collect(":build:path.include", "modm/ext/freertos_plus_tcp/include") + env.template("FreeRTOSIPConfig.h.in", "freertos_plus_tcp/include/FreeRTOSIPConfig.h") +# ----------------------------------------------------------------------------- + def init(module): module.name = "freertos" module.description = """\ @@ -37,6 +53,8 @@ def prepare(module, options): ":cmsis:device", ":platform:clock") + module.add_submodule(FreeRTOS_TCP()) + return True def build(env): From 821677bad621d83166e578a7436283bc3259c860 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 30 Aug 2020 15:24:44 +0200 Subject: [PATCH 100/135] [ext] Add local config inclusion for FreeRTOS --- ext/aws/FreeRTOSConfig.h.in | 5 ++++- ext/aws/FreeRTOSIPConfig.h.in | 5 +++++ ext/aws/module.lb | 26 +++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/ext/aws/FreeRTOSConfig.h.in b/ext/aws/FreeRTOSConfig.h.in index 9776cf217c..a7afbd7059 100644 --- a/ext/aws/FreeRTOSConfig.h.in +++ b/ext/aws/FreeRTOSConfig.h.in @@ -141,7 +141,10 @@ standard names. */ #define vPortSVCHandler SVC_Handler #define xPortSysTickHandler SysTick_Handler -/* A header file that defines trace macro can be included here. */ +/* A header file that overwrites with local project settings. */ +#if __has_include() +#include +#endif #endif /* FREERTOS_CONFIG_H */ diff --git a/ext/aws/FreeRTOSIPConfig.h.in b/ext/aws/FreeRTOSIPConfig.h.in index a6110ee019..1615f195ec 100644 --- a/ext/aws/FreeRTOSIPConfig.h.in +++ b/ext/aws/FreeRTOSIPConfig.h.in @@ -305,4 +305,9 @@ disconnecting stage will timeout after a period of non-activity. */ #define ipconfigZERO_COPY_TX_DRIVER 1 #define ipconfigUSE_LINKED_RX_MESSAGES 1 +/* A header file that overwrites with local project settings. */ +#if __has_include() +#include +#endif + #endif // FREERTOS_IP_CONFIG_H diff --git a/ext/aws/module.lb b/ext/aws/module.lb index 3d42b86bdf..a3058aae5f 100644 --- a/ext/aws/module.lb +++ b/ext/aws/module.lb @@ -14,7 +14,19 @@ class FreeRTOS_TCP(Module): def init(self, module): module.name = "tcp" - module.description = "FreeRTOS-Plus-TCP" + module.description = """ +# a:FreeRTOS-Plus-TCP + +This module defines a default FreeRTOS IP config. If you need to change a +configuration setting, then you can define a `FreeRTOSConfigIPLocal.h` file, which +is included *after* at the end of the config. You must therefore first `#undef` +the config macros, then redefine them with your options, for example: + +```c +#undef ipconfigUSE_DNS +#define ipconfigUSE_DNS 1 +``` +""" def prepare(self, module, options): return True @@ -33,6 +45,18 @@ def init(module): # a:FreeRTOS Amazon FreeRTOS port for modm to be used with via the `modm:processing:rtos` module. + +## Custom Configuration + +This module defines a default FreeRTOS config. If you need to change a +configuration setting, then you can define a `FreeRTOSConfigLocal.h` file, which +is included *after* at the end of the config. You must therefore first `#undef` +the config macros, then redefine them with your options, for example: + +```c +#undef configCHECK_FOR_STACK_OVERFLOW +#define configCHECK_FOR_STACK_OVERFLOW 0 +``` """ def prepare(module, options): From 177b2ec364d5e340e5379b86b3d55180190401d7 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Tue, 1 Sep 2020 09:35:16 +0200 Subject: [PATCH 101/135] Latest version of FreeRTOS in modm declares the hook function now in task.h, so change the conflicting declaration and function. --- ext/aws/modm_port.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/aws/modm_port.cpp b/ext/aws/modm_port.cpp index 2354753357..d258179b08 100644 --- a/ext/aws/modm_port.cpp +++ b/ext/aws/modm_port.cpp @@ -4,9 +4,9 @@ #include #include -extern "C" void vApplicationStackOverflowHook(TaskHandle_t, const char *); +extern "C" void vApplicationStackOverflowHook(TaskHandle_t, char *); -void vApplicationStackOverflowHook(TaskHandle_t /*pxTask*/, const char *pcTaskName) +void vApplicationStackOverflowHook(TaskHandle_t /*pxTask*/, char *pcTaskName) { modm_assert(false, "freertos.stack", "FreeRTOS detected a stack overflow!", pcTaskName); } From c6b91d6a6c645a04c11ef29e9b0ccca9730029a8 Mon Sep 17 00:00:00 2001 From: Vivien Henry Date: Tue, 1 Sep 2020 15:01:14 +0200 Subject: [PATCH 102/135] [timer] STM32 TIM14 actually has BDTR register --- src/modm/platform/timer/stm32/general_purpose.hpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modm/platform/timer/stm32/general_purpose.hpp.in b/src/modm/platform/timer/stm32/general_purpose.hpp.in index cd8dffb9db..e8fdc72038 100644 --- a/src/modm/platform/timer/stm32/general_purpose.hpp.in +++ b/src/modm/platform/timer/stm32/general_purpose.hpp.in @@ -213,7 +213,7 @@ public: } -%% if target.family not in ["l1"] and id not in [14] +%% if target.family not in ["l1"] static inline void enableOutput() { From b55513397a92bd6729eb24fb03592d9a7df59956 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Tue, 1 Sep 2020 20:43:08 +0200 Subject: [PATCH 103/135] [docs] Installation: Update and simplify installation for linux --- docs/mkdocs.yml | 2 +- docs/src/guide/installation.md | 87 ++++++++++++++-------------------- 2 files changed, 37 insertions(+), 52 deletions(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 66d3d21010..63cc129e09 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -69,8 +69,8 @@ nav: - How modm works: how-modm-works.md - Who we are: who-we-are.md - Guide: - - Getting Started: guide/getting-started.md - Installation: guide/installation.md + - Getting Started: guide/getting-started.md # - Qt Creator: guide/qtcreator.md # - Cookbook: guide/cookbook.md - Testing: guide/testing.md diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md index 1d76d27755..ba3a3d263a 100644 --- a/docs/src/guide/installation.md +++ b/docs/src/guide/installation.md @@ -30,6 +30,42 @@ additional build system documentation. Please help us [keep these instructions up-to-date][contribute]! +## Linux + +For Ubuntu 20.04LTS, these commands install the basic build system: + + sudo apt-get install python3 python3-pip scons git gcc-arm-none-eabi openocd \ + gcc build-essential libboost-all-dev + pip3 install modm gdbgui + +!!! warning "OpenOCD for recent targets" + The latest OpenOCD release v0.10.0 (as of May 2020) is too old for some targets + (STM32G0, STM32G4, STM32F7). To program these targets you need to compile the + [HEAD version of OpenOCD from source][openocd-source], install it and add it to + the beginning of your `$PATH`. + +!!! warning "Python 3 on Ubuntu ≤ 19.10" + Ubuntu ≤ 19.10 unfortunately defaults to Python 2, however, our SCons tools + require Python 3. For a less intrusive way to run all scons scripts with + Python 3 add this to your `.bashrc` or `.zshrc`: + + alias scons="/usr/bin/env python3 $(which scons)" + + If you get errors about missing `pyelftools`, check that you're really + running SCons with Python 3! + +!!! warning "arm-none-eabi-gcc on Ubuntu ≤ 19.10" + Ubuntu ≤ 19.10 does not provide a recent arm-none-eabi-gcc toolchain. + Install the ARM toochain by downloading [the pre-built version][gcc-arm-toolchain] + for 64-bit Linux and adding its `/bin` directory to the beginning of your `$PATH`. + +!!! bug "avr-gcc on Ubuntu" + Ubuntu does not provide an up-to-date version of avr-gcc that supports C++17. + For our CI we've created a [precompiled version of avr-gcc for Ubuntu][avr-gcc-latest]. + Unfortunately its path is hardcoded to `/work/avr-gcc`, + [please help us fix it](https://github.com/modm-ext/docker-avr-gcc/issues/1). + + ## macOS We will use [Homebrew](http://brew.sh/) to install the minimal build system: @@ -70,57 +106,6 @@ some of these libraries as well, depending on what modm modules you use: brew install boost gcc - -## Linux - -For Ubuntu 20.04LTS, these commands install the basic build system: - - sudo apt-get install python3 python3-pip scons git - sudo apt-get --no-install-recommends install doxygen - pip3 install modm - -!!! warning "Use Python 3!" - Ubuntu 18.04LTS unfortunately defaults to Python 2, however, our SCons tools - require Python 3. For a less intrusive way to run all scons scripts with - Python 3 add this to your `.bashrc` or `.zshrc`: - - alias scons="/usr/bin/env python3 $(which scons)" - - If you get errors about missing `pyelftools`, check that you're really - running SCons with Python 3! - -Install the AVR toochain: - - sudo apt-get install gcc-avr binutils-avr avr-libc avrdude - -!!! bug "avr-gcc on Ubuntu" - Ubuntu does not provide an up-to-date version of avr-gcc that supports C++17. - For our CI we've created a [precompiled version of avr-gcc for Ubuntu][avr-gcc-latest]. - Unfortunately its path is hardcoded to `/work/avr-gcc`. - -Install the ARM toochain by downloading [the pre-built version][gcc-arm-toolchain] -for 64-bit Linux and adding its `/bin` directory to your path. -**Even though your distribution may ship their own ARM toolchain, we very strongly -recommend using the official toolchain, since all of modm is tested with it.** - -Install OpenOCD via your package manager: - - sudo apt-get install openocd - -The latest OpenOCD release v0.10.0 (as of May 2020) is too old for some targets -(STM32G0, STM32G4, STM32F7). To program these targets you need to compile the -[HEAD version of OpenOCD from source][openocd-source]. - -We recommend the use of a graphical frontend for GDB called [gdbgui][]: - - pip3 install gdbgui - -To compile modm *for Linux* (and not the embedded target) you need to install -some of these libraries as well, depending on what modm modules you use: - - sudo apt-get install gcc build-essential libboost-all-dev - - ## Windows We will use [Anaconda][] ([Miniconda Windows installation][miniconda] is From 3ff3436348f36ddf2e7c45ce02f44ab4a4efe1c0 Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Mon, 7 Sep 2020 17:05:13 +0200 Subject: [PATCH 104/135] [gpio] Add support for GPIO on Raspberry Pi --- README.md | 2 +- .../platform/core/hosted/delay_impl.hpp.in | 2 +- src/modm/platform/gpio/rpi/base.hpp | 69 ++++++++++++++ src/modm/platform/gpio/rpi/module.lb | 36 +++++++ src/modm/platform/gpio/rpi/pin.hpp | 81 ++++++++++++++++ src/modm/platform/gpio/rpi/unused.hpp | 93 +++++++++++++++++++ tools/devices/hosted/rpi.xml | 11 +++ 7 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 src/modm/platform/gpio/rpi/base.hpp create mode 100644 src/modm/platform/gpio/rpi/module.lb create mode 100644 src/modm/platform/gpio/rpi/pin.hpp create mode 100644 src/modm/platform/gpio/rpi/unused.hpp create mode 100644 tools/devices/hosted/rpi.xml diff --git a/README.md b/README.md index 79a9df804b..7e09b7e887 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ STM32 devices, however, there are different levels of support and testing. | STM32F2 | ★★★★ | STM32F3 | ★★★★★ | STM32F4 | ★★★★★ | | STM32F7 | ★★★★ | STM32L1 | ★★★★ | STM32L4 | ★★★★ | | STM32L4+ | ★★★★ | STM32G0 | ★★★★ | STM32G4 | ★★★★ | -| SAMD21 | ★★ | | | | | +| SAMD21 | ★★ | Raspberry Pi | ★ | | | diff --git a/src/modm/platform/core/hosted/delay_impl.hpp.in b/src/modm/platform/core/hosted/delay_impl.hpp.in index 80d585ec98..a6abf448fd 100644 --- a/src/modm/platform/core/hosted/delay_impl.hpp.in +++ b/src/modm/platform/core/hosted/delay_impl.hpp.in @@ -22,7 +22,7 @@ #define MODM_DELAY_NS_IS_ACCURATE 0 -%% if target.family in ["darwin", "linux"] +%% if target.family in ["darwin", "linux", "rpi"] extern "C" { #include } diff --git a/src/modm/platform/gpio/rpi/base.hpp b/src/modm/platform/gpio/rpi/base.hpp new file mode 100644 index 0000000000..15efc71cf3 --- /dev/null +++ b/src/modm/platform/gpio/rpi/base.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include + +namespace modm +{ + +namespace platform +{ + +/// @ingroup modm_platform_gpio +enum class +Peripheral +{ + BitBang, + // ... +}; + +/// @ingroup modm_platform_gpio +struct Gpio +{ + /// Each Input Pin can be configured in one of these states. + enum class + InputType : uint8_t + { + Floating, ///< The input pin is left floating + }; + + enum class + OutputType : uint8_t + { + PushPull ///< push-pull on output + }; + + /// Available ports on this device. + enum class + Port + { + // ... + }; + + enum class + Signal + { + BitBang, + // ... + }; + + /// @endcond +}; + +/// @} + +} // namespace platform + +} // namespace modm + +#endif diff --git a/src/modm/platform/gpio/rpi/module.lb b/src/modm/platform/gpio/rpi/module.lb new file mode 100644 index 0000000000..81169377dd --- /dev/null +++ b/src/modm/platform/gpio/rpi/module.lb @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Erik Henriksson +# +# 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:gpio" + module.description = "Hosted GPIO for Raspberry Pi" + + +def prepare(module, options): + if not options[":target"].has_driver("gpio:rpi"): + return False + + module.depends(":architecture:gpio") + return True + + +def build(env): + env.substitutions = {"target": env[":target"].identifier} + env.outbasepath = "modm/src/modm/platform/gpio" + + env.collect(":build:library", "wiringPi") + + env.copy(".") + env.copy("../common/inverted.hpp", "inverted.hpp") + env.copy("../common/connector.hpp", "connector.hpp") + env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") diff --git a/src/modm/platform/gpio/rpi/pin.hpp b/src/modm/platform/gpio/rpi/pin.hpp new file mode 100644 index 0000000000..14ca2e2780 --- /dev/null +++ b/src/modm/platform/gpio/rpi/pin.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017, Niklas Hauser + * Copyright (c) 2018, Fabian Greif + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include + +#include "base.hpp" + + +namespace modm +{ + +namespace platform +{ + +template< int Pin > +class GpioPin : public Gpio, public modm::GpioIO +{ + static Gpio::InputType inputType; +public: + using Output = GpioPin; + using Input = GpioPin; + using IO = GpioPin; + using Type = GpioPin; + +public: + inline static void configure(Gpio::InputType type) {} + modm_always_inline static void setInput() { + pinMode(Pin, INPUT); + } + inline static void setInput(Gpio::InputType type) { + setInput(); + } + modm_always_inline static void setOutput() { + pinMode(Pin, OUTPUT); + } + modm_always_inline static void setOutput(OutputType) { + setOutput(); + } + modm_always_inline static void setOutput(bool status) { + setOutput(); + set(status); + } + inline static void set() { + digitalWrite(Pin, HIGH); + } + modm_always_inline static void reset() { + digitalWrite(Pin, LOW); + } + inline static void set(bool status) { + digitalWrite(Pin, status); + } + inline static bool isSet() { + return digitalRead(Pin); + } + inline static void toggle() { + if (isSet()) { set(); } + else { reset(); } + } + modm_always_inline static modm::Gpio::Direction getDirection() { + return modm::Gpio::Direction::Out; + } +}; + +/// @} + +} // namespace platform + +} // namespace modm + diff --git a/src/modm/platform/gpio/rpi/unused.hpp b/src/modm/platform/gpio/rpi/unused.hpp new file mode 100644 index 0000000000..22d177f233 --- /dev/null +++ b/src/modm/platform/gpio/rpi/unused.hpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, Niklas Hauser + * Copyright (c) 2018, Fabian Greif + * + * 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_HOSTED_GPIO_PIN_UNUSED_HPP +#define MODM_HOSTED_GPIO_PIN_UNUSED_HPP + +#include "base.hpp" +#include + +namespace modm +{ + +namespace platform +{ + +/** + * Dummy implementation of an I/O pin. + * + * This class can be used when a pin is not required. All functions + * are dummy functions which do nothing. `read()` will always + * return `false`. + * + * For example when creating a software SPI with the modm::SoftwareSimpleSpi + * class and the return channel (MISO - Master In Slave Out) is not needed, + * a good way is to use this class as a parameter when defining the + * SPI class. + * + * Example: + * @code + * #include + * + * namespace pin + * { + * typedef GpioOutputD7 Clk; + * typedef GpioOutputD5 Mosi; + * } + * + * modm::SoftwareSpiMaster< pin::Clk, pin::Mosi, GpioUnused > Spi; + * + * ... + * Spi::write(0xaa); + * @endcode + * + * @author Fabian Greif + * @author Niklas Hauser + * @ingroup modm_platform_gpio + */ +class GpioUnused : public Gpio, public ::modm::GpioIO +{ +public: + using Output = GpioUnused; + using Input = GpioUnused; + using IO = GpioUnused; + using Type = GpioUnused; + +public: + // GpioOutput + // start documentation inherited + static void setOutput() {} + static void setOutput(bool) {} + static void set() {} + static void set(bool) {} + static void reset() {} + static void toggle() {} + static bool isSet() { return false; } + // stop documentation inherited + + // GpioInput + // start documentation inherited + static void setInput() {} + static bool read() { return false; } + // end documentation inherited + + // GpioIO + // start documentation inherited + static Direction getDirection() { return Direction::Special; } + // end documentation inherited +}; + +} // namespace platform + +} // namespace modm + +#endif diff --git a/tools/devices/hosted/rpi.xml b/tools/devices/hosted/rpi.xml new file mode 100644 index 0000000000..a87e1d8adc --- /dev/null +++ b/tools/devices/hosted/rpi.xml @@ -0,0 +1,11 @@ + + + + + {platform}-{family} + + + + + + From b0d84badbdfc58a8e9923c057835a15d56876ecb Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Mon, 7 Sep 2020 17:05:54 +0200 Subject: [PATCH 105/135] [board] Add Raspberry Pi BSP --- README.md | 1 + src/modm/board/raspberrypi/board.hpp | 32 ++++++++++++++++++++++++++++ src/modm/board/raspberrypi/board.xml | 14 ++++++++++++ src/modm/board/raspberrypi/module.lb | 29 +++++++++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 src/modm/board/raspberrypi/board.hpp create mode 100644 src/modm/board/raspberrypi/board.xml create mode 100644 src/modm/board/raspberrypi/module.lb diff --git a/README.md b/README.md index 7e09b7e887..cbdccc0e30 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ documentation. NUCLEO-L476RG OLIMEXINO-STM32 +RASPBERRYPI SAMD21-MINI STM32F030F4P6-DEMO diff --git a/src/modm/board/raspberrypi/board.hpp b/src/modm/board/raspberrypi/board.hpp new file mode 100644 index 0000000000..20f13ef9f9 --- /dev/null +++ b/src/modm/board/raspberrypi/board.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include + +#include + +using namespace modm::platform; + +namespace Board +{ + +struct SystemClock {}; + +inline void +initialize() +{ + wiringPiSetup(); +} + +} // Board namespace diff --git a/src/modm/board/raspberrypi/board.xml b/src/modm/board/raspberrypi/board.xml new file mode 100644 index 0000000000..8b4daa2270 --- /dev/null +++ b/src/modm/board/raspberrypi/board.xml @@ -0,0 +1,14 @@ + + + + ../../../../repo.lb + + + + + + + + modm:board:raspberrypi + + diff --git a/src/modm/board/raspberrypi/module.lb b/src/modm/board/raspberrypi/module.lb new file mode 100644 index 0000000000..680e2f3332 --- /dev/null +++ b/src/modm/board/raspberrypi/module.lb @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Erik Henriksson +# +# 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:raspberrypi" + module.description = """\ +# Raspberry Pi +""" + +def prepare(module, options): + if options[":target"].partname != "hosted-rpi": + return False + + module.depends(":platform:core", ":platform:gpio", ":debug") + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + env.copy('.') + From d3496a354c831ddef4b98e7f0afd9959ec950ea8 Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Mon, 7 Sep 2020 17:10:08 +0200 Subject: [PATCH 106/135] [examples] Add Raspberry Pi GPIO example --- examples/rpi/blinky/main.cpp | 28 ++++++++++++++++++++++++++++ examples/rpi/blinky/project.xml | 9 +++++++++ 2 files changed, 37 insertions(+) create mode 100644 examples/rpi/blinky/main.cpp create mode 100644 examples/rpi/blinky/project.xml diff --git a/examples/rpi/blinky/main.cpp b/examples/rpi/blinky/main.cpp new file mode 100644 index 0000000000..3a8cf054ac --- /dev/null +++ b/examples/rpi/blinky/main.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020, Erik Henriksson + * + * 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 + +using namespace Board; + +int main() +{ + Board::initialize(); + GpioPin<0>::setOutput(); + MODM_LOG_INFO << "Blink blink..."; + + for (int i = 0; i < 10; ++i) + { + GpioPin<0>::toggle(); + modm::delay(500ms); + } + return 0; +} diff --git a/examples/rpi/blinky/project.xml b/examples/rpi/blinky/project.xml new file mode 100644 index 0000000000..c804feb469 --- /dev/null +++ b/examples/rpi/blinky/project.xml @@ -0,0 +1,9 @@ + + modm:raspberrypi + + + + + modm:build:scons + + From 703d7bb0f85ad14687ea2991bcd95bd95e68dafe Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Fri, 11 Sep 2020 19:53:34 +0200 Subject: [PATCH 107/135] Fix RCC class to allow enable/disable of ETHMAC peripheral. --- src/modm/platform/clock/stm32/module.lb | 2 ++ src/modm/platform/clock/stm32/rcc_impl.hpp.in | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modm/platform/clock/stm32/module.lb b/src/modm/platform/clock/stm32/module.lb index c8f96db5ac..572cd60cf1 100644 --- a/src/modm/platform/clock/stm32/module.lb +++ b/src/modm/platform/clock/stm32/module.lb @@ -105,6 +105,8 @@ def build(env): if "Dsihost" in all_peripherals and per == "DSI": per = "Dsihost" nper = "DSI" + if "Eth" in all_peripherals and per == "ETHMAC": + per = "Eth" if per.capitalize() not in all_peripherals: continue if "EN" in mode: diff --git a/src/modm/platform/clock/stm32/rcc_impl.hpp.in b/src/modm/platform/clock/stm32/rcc_impl.hpp.in index bbf53c5d9e..6217f058ed 100644 --- a/src/modm/platform/clock/stm32/rcc_impl.hpp.in +++ b/src/modm/platform/clock/stm32/rcc_impl.hpp.in @@ -127,7 +127,9 @@ Rcc::enable() if (not Rcc::isEnabled()) { RCC->{{bus}} |= RCC_{{bus}}_{{st_per}}EN;{% if st_per in rcc_reset %} __DSB(); RCC->{{rcc_reset[st_per]}} |= RCC_{{rcc_reset[st_per]}}_{{st_per}}RST; __DSB(); - RCC->{{rcc_reset[st_per]}} &= ~RCC_{{rcc_reset[st_per]}}_{{st_per}}RST;{% endif %} + RCC->{{rcc_reset[st_per]}} &= ~RCC_{{rcc_reset[st_per]}}_{{st_per}}RST;{% endif %}{% if peripheral == "Eth" %} __DSB(); + RCC->{{bus}} |= RCC_{{bus}}_{{st_per}}RXEN; __DSB(); + RCC->{{bus}} |= RCC_{{bus}}_{{st_per}}TXEN;{% endif %} } %% endfor __DSB(); @@ -143,7 +145,9 @@ Rcc::disable() __DSB(); %% for peripheral, (st_per, bus) in rcc_enable.items() | sort if constexpr (peripheral == Peripheral::{{ peripheral }}) - RCC->{{bus}} &= ~RCC_{{bus}}_{{st_per}}EN; + RCC->{{bus}} &= ~RCC_{{bus}}_{{st_per}}EN;{% if peripheral == "Eth" %} __DSB(); + RCC->{{bus}} &= ~RCC_{{bus}}_{{st_per}}RXEN; __DSB(); + RCC->{{bus}} &= ~RCC_{{bus}}_{{st_per}}TXEN;{% endif %} %% endfor __DSB(); } From fcb4ccda73111183ad1745c2ac47ef803783096e Mon Sep 17 00:00:00 2001 From: Erik Henriksson Date: Fri, 11 Sep 2020 09:56:29 +0200 Subject: [PATCH 108/135] [clock] SysTick Reference Clock can differ by platform --- src/modm/platform/clock/systick/module.lb | 3 +++ .../clock/systick/systick_timer.hpp.in | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/modm/platform/clock/systick/module.lb b/src/modm/platform/clock/systick/module.lb index 2c5ee0c389..7d5b584971 100644 --- a/src/modm/platform/clock/systick/module.lb +++ b/src/modm/platform/clock/systick/module.lb @@ -36,9 +36,12 @@ def validate(env): def build(env): core = env[":target"].get_driver("core")["type"] default = 1000 if "m0" in core else 4 + # SAMD: Reference Clock not implemented => Prescaler always 1 + reference_clock = {"stm32": 8}.get(env[":target"].identifier.platform, 1) env.substitutions = { "systick_frequency": env.get(":freertos:frequency", default), "has_freertos": env.has_module(":freertos"), + "ref_clk": reference_clock } env.outbasepath = "modm/src/modm/platform/clock" env.template("systick_timer.hpp.in") diff --git a/src/modm/platform/clock/systick/systick_timer.hpp.in b/src/modm/platform/clock/systick/systick_timer.hpp.in index db47e7b4bf..2c46af03d5 100644 --- a/src/modm/platform/clock/systick/systick_timer.hpp.in +++ b/src/modm/platform/clock/systick/systick_timer.hpp.in @@ -50,9 +50,12 @@ public: static void initialize() { - static_assert(SystemClock::Frequency < (2ull << 24)*8*{{systick_frequency}}, + static_assert(SystemClock::Frequency < (1ull << 24)*{{ref_clk}}*{{systick_frequency}}, "HLCK is too fast for the SysTick to run at {{systick_frequency}}Hz!"); - if constexpr (SystemClock::Frequency < 8'000'000) { +%% if ref_clk > 1 + if constexpr (SystemClock::Frequency < {{ref_clk}}'000'000) +%% endif + { constexpr auto result = Prescaler::from_range( SystemClock::Frequency, {{ systick_frequency }}, 1, (1ul << 24)-1); PeripheralDriver::assertBaudrateInTolerance< result.frequency, {{ systick_frequency }}, tolerance >(); @@ -63,17 +66,20 @@ public: %% endif enable(result.index, false); } - else { +%% if ref_clk > 1 + else + { constexpr auto result = Prescaler::from_range( - SystemClock::Frequency/8, {{ systick_frequency }}, 1, (1ul << 24)-1); + SystemClock::Frequency/{{ref_clk}}, {{ systick_frequency }}, 1, (1ul << 24)-1); PeripheralDriver::assertBaudrateInTolerance< result.frequency, {{ systick_frequency }}, tolerance >(); - us_per_Ncycles = ((1ull << Ncycles) * 8'000'000ull) / SystemClock::Frequency; + us_per_Ncycles = ((1ull << Ncycles) * {{ref_clk}}'000'000ull) / SystemClock::Frequency; %% if systick_frequency != 1000 - ms_per_Ncycles = ((1ull << Ncycles) * 8'000ull) / SystemClock::Frequency; + ms_per_Ncycles = ((1ull << Ncycles) * {{ref_clk}}'000ull) / SystemClock::Frequency; %% endif enable(result.index, true); } +%% endif } /** From 6a18134271ac5dd5e167edb0a038440a0221575e Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Fri, 11 Sep 2020 14:59:53 +0200 Subject: [PATCH 109/135] [core] Suppress interrupt vector redeclaration warning --- src/modm/platform/core/cortex/vectors.c.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modm/platform/core/cortex/vectors.c.in b/src/modm/platform/core/cortex/vectors.c.in index 84ebc899b5..480674c01b 100644 --- a/src/modm/platform/core/cortex/vectors.c.in +++ b/src/modm/platform/core/cortex/vectors.c.in @@ -56,6 +56,9 @@ FunctionPointer vectorsRam[sizeof(vectorsRom) / sizeof(FunctionPointer)]; %% endif // ---------------------------------------------------------------------------- +// Ignore redeclaration of interrupt handlers in vendor headers +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" // Explicitly include this BELOW the vector table to *not deal* with potential // re-#defines of interrupt vector names! Bad vendors!! BAD!!! #include @@ -77,3 +80,5 @@ void Undefined_Handler(void) "An undefined NVIC interrupt was raised!", irqn); %% endif } + +#pragma GCC diagnostic pop From 239d56816ecfdfe5339cbe2659041371cc12a17c Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Fri, 11 Sep 2020 22:38:18 +0200 Subject: [PATCH 110/135] [board] Add MCUdev DevEBox STM324XX BSP --- README.md | 18 +-- src/modm/board/devebox_stm32f4xx/board.hpp | 157 +++++++++++++++++++++ src/modm/board/devebox_stm32f4xx/board.xml | 14 ++ src/modm/board/devebox_stm32f4xx/module.lb | 44 ++++++ 4 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 src/modm/board/devebox_stm32f4xx/board.hpp create mode 100644 src/modm/board/devebox_stm32f4xx/board.xml create mode 100644 src/modm/board/devebox_stm32f4xx/module.lb diff --git a/README.md b/README.md index cbdccc0e30..d67c10440b 100644 --- a/README.md +++ b/README.md @@ -118,48 +118,50 @@ documentation. Black Pill Blue Pill +DEVEBOX-STM32F4XX DISCO-F051R8 DISCO-F072RB -DISCO-F100RB +DISCO-F100RB DISCO-F303VC DISCO-F407VG DISCO-F429ZI -DISCO-F469NI +DISCO-F469NI DISCO-F746NG DISCO-F769NI DISCO-L152RC -DISCO-L476VG +DISCO-L476VG FEATHER-M0 MEGA-2560-PRO MINI-F401 -MINI-F411 +MINI-F411 NUCLEO-F031K6 NUCLEO-F042K6 NUCLEO-F103RB -NUCLEO-F303K8 +NUCLEO-F303K8 NUCLEO-F303RE NUCLEO-F401RE NUCLEO-F411RE -NUCLEO-F429ZI +NUCLEO-F429ZI NUCLEO-F446RE NUCLEO-F746ZG NUCLEO-G071RB -NUCLEO-G474RE +NUCLEO-G474RE NUCLEO-L152RE NUCLEO-L432KC NUCLEO-L476RG -OLIMEXINO-STM32 +OLIMEXINO-STM32 RASPBERRYPI SAMD21-MINI STM32F030F4P6-DEMO + diff --git a/src/modm/board/devebox_stm32f4xx/board.hpp b/src/modm/board/devebox_stm32f4xx/board.hpp new file mode 100644 index 0000000000..b9f8d336f1 --- /dev/null +++ b/src/modm/board/devebox_stm32f4xx/board.hpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2020, Sascha Schade + * + * 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_F4XX_DEVEBOX_HPP +#define MODM_STM32_F4XX_DEVEBOX_HPP + +#include +#include +#include + +using namespace modm::platform; + +/// @ingroup modm_board_devebox_f4xx +namespace Board +{ + using namespace modm::literals; + +/// STM32F407 running at 168MHz generated from the external 8MHz crystal +struct SystemClock { + static constexpr uint32_t Frequency = 168_MHz; + static constexpr uint32_t Ahb = Frequency; + static constexpr uint32_t Apb1 = Frequency / 4; + static constexpr uint32_t Apb2 = Frequency / 2; + + static constexpr uint32_t Adc = Apb2; + + static constexpr uint32_t Can1 = Apb1; + static constexpr uint32_t Can2 = 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 I2c1 = Apb1; + static constexpr uint32_t I2c2 = Apb1; + static constexpr uint32_t I2c3 = Apb1; + + static constexpr uint32_t Apb1Timer = Apb1 * 2; + static constexpr uint32_t Apb2Timer = Apb2 * 2; + 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::enableExternalCrystal(); // 8MHz + const Rcc::PllFactors pllFactors{ + .pllM = 4, // 8MHz / M=4 -> 2MHz + .pllN = 168, // 2MHz * N=168 -> 336MHz + .pllP = 2 // 336MHz / P=2 -> 168MHz = F_cpu + }; + Rcc::enablePll(Rcc::PllSource::ExternalCrystal, pllFactors); + // set flash latency for 168MHz + Rcc::setFlashLatency(); + // switch system clock to PLL output + Rcc::enableSystemClock(Rcc::SystemClockSource::Pll); + Rcc::setAhbPrescaler(Rcc::AhbPrescaler::Div1); + // APB1 has max. 42MHz + // APB2 has max. 84MHz + Rcc::setApb1Prescaler(Rcc::Apb1Prescaler::Div4); + Rcc::setApb2Prescaler(Rcc::Apb2Prescaler::Div2); + // update frequencies for busy-wait delay functions + Rcc::updateCoreFrequency(); + + return true; + } +}; + + +using Button = GpioInputA0; +using ClockOut = GpioOutputA8; +using SystemClockOut = GpioOutputC9; + +using LedGreen = GpioInverted; // User LED + +using Leds = SoftwareGpioPort< LedGreen >; + +namespace usb +{ +using Dm = GpioA11; // DM: USB_DM +using Dp = GpioA12; // DP: USB_DP +} + +namespace w25q16 +{ +using Cs = GpioOutputA15; +using Sck = GpioOutputB3; +using Mosi = GpioOutputB5; +using Miso = GpioInputB4; + +using SpiMaster = SpiMaster1; + +constexpr uint32_t BlockSize = 256; +constexpr uint32_t MemorySize = 2*1024*1024; // 16 MiBits +using StorageDevice = modm::BdSpiFlash; +} + + +inline void +initialize() +{ + SystemClock::enable(); + SysTickTimer::initialize(); + + LedGreen::setOutput(modm::Gpio::Low); + + Button::setInput(Gpio::InputType::PullDown); + Button::setInputTrigger(Gpio::InputTrigger::RisingEdge); + Button::enableExternalInterrupt(); +// Button::enableExternalInterruptVector(12); +} + + +inline void +initializeW25q16() +{ + w25q16::Cs::setOutput(modm::Gpio::High); + + w25q16::SpiMaster::connect(); + w25q16::SpiMaster::initialize(); +} + +} + +#endif // MODM_STM32_F4XX_DEVEBOX_HPP diff --git a/src/modm/board/devebox_stm32f4xx/board.xml b/src/modm/board/devebox_stm32f4xx/board.xml new file mode 100644 index 0000000000..4254c33251 --- /dev/null +++ b/src/modm/board/devebox_stm32f4xx/board.xml @@ -0,0 +1,14 @@ + + + + ../../../../repo.lb + + + + + + + + modm:board:devebox-stm32f4xx + + diff --git a/src/modm/board/devebox_stm32f4xx/module.lb b/src/modm/board/devebox_stm32f4xx/module.lb new file mode 100644 index 0000000000..edbbdbc974 --- /dev/null +++ b/src/modm/board/devebox_stm32f4xx/module.lb @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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:devebox-stm32f4xx" + module.description = """\ +# STM32F4XX mcudev DevEBox +""" + +def prepare(module, options): + if not options[":target"].partname.startswith("stm32f407v"): + return False + + module.depends( + ":architecture:clock", + ":platform:clock", + ":platform:core", + ":platform:gpio", + ":platform:spi:1", + ":driver:block.device:spi.flash") + return True + +def build(env): + env.outbasepath = "modm/src/modm/board" + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } + env.template("../board.cpp.in", "board.cpp") + env.copy('.') + + env.outbasepath = "modm/openocd/modm/board/" + env.copy(repopath("tools/openocd/modm/stm32f4x1_mini_f4x1.cfg"), "stm32f4x1_mini_f4x1.cfg") + env.collect(":build:openocd.source", "modm/board/stm32f4x1_mini_f4x1.cfg") From 3bebdb5c6d07f405a31f1d6b175cc130684c64f7 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Fri, 11 Sep 2020 22:39:06 +0200 Subject: [PATCH 111/135] [examples] Add DevEBox STM32F4XX GPIO example --- .../stm32f407vet6_devebox/blinky/main.cpp | 29 +++++++++++++++++++ .../stm32f407vet6_devebox/blinky/openocd.cfg | 2 ++ .../stm32f407vet6_devebox/blinky/project.xml | 10 +++++++ 3 files changed, 41 insertions(+) create mode 100644 examples/stm32f407vet6_devebox/blinky/main.cpp create mode 100644 examples/stm32f407vet6_devebox/blinky/openocd.cfg create mode 100644 examples/stm32f407vet6_devebox/blinky/project.xml diff --git a/examples/stm32f407vet6_devebox/blinky/main.cpp b/examples/stm32f407vet6_devebox/blinky/main.cpp new file mode 100644 index 0000000000..d8251fccab --- /dev/null +++ b/examples/stm32f407vet6_devebox/blinky/main.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020, Sascha Schade + * + * 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(); + + while(true) + { + LedGreen::toggle(); + modm::delay(Button::read() ? 100ms : 500ms); + } + + return 0; +} diff --git a/examples/stm32f407vet6_devebox/blinky/openocd.cfg b/examples/stm32f407vet6_devebox/blinky/openocd.cfg new file mode 100644 index 0000000000..2edce20b3d --- /dev/null +++ b/examples/stm32f407vet6_devebox/blinky/openocd.cfg @@ -0,0 +1,2 @@ +# Replace this with your custom programmer +source [find interface/stlink.cfg] diff --git a/examples/stm32f407vet6_devebox/blinky/project.xml b/examples/stm32f407vet6_devebox/blinky/project.xml new file mode 100644 index 0000000000..4daef7fc53 --- /dev/null +++ b/examples/stm32f407vet6_devebox/blinky/project.xml @@ -0,0 +1,10 @@ + + modm:devebox-stm32f4xx + + + + + + modm:build:scons + + From c2193b28d91870144941db06610bb0a91a11937c Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Fri, 11 Sep 2020 22:39:31 +0200 Subject: [PATCH 112/135] [examples] Add DevEBox STM32F4XX Logger example --- .../stm32f407vet6_devebox/logger/main.cpp | 56 +++++++++++++++++++ .../stm32f407vet6_devebox/logger/openocd.cfg | 2 + .../stm32f407vet6_devebox/logger/project.xml | 12 ++++ 3 files changed, 70 insertions(+) create mode 100644 examples/stm32f407vet6_devebox/logger/main.cpp create mode 100644 examples/stm32f407vet6_devebox/logger/openocd.cfg create mode 100644 examples/stm32f407vet6_devebox/logger/project.xml diff --git a/examples/stm32f407vet6_devebox/logger/main.cpp b/examples/stm32f407vet6_devebox/logger/main.cpp new file mode 100644 index 0000000000..9ebbad99ff --- /dev/null +++ b/examples/stm32f407vet6_devebox/logger/main.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020, Sascha Schade + * + * 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 + +using namespace Board; + +modm::IODeviceWrapper< Usart1, modm::IOBuffer::BlockIfFull > loggerDevice; + +// Set all four logger streams to use the UART +modm::log::Logger modm::log::debug(loggerDevice); +modm::log::Logger modm::log::info(loggerDevice); +modm::log::Logger modm::log::warning(loggerDevice); +modm::log::Logger modm::log::error(loggerDevice); + +// Set the log level +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::DEBUG + + +int +main() +{ + Board::initialize(); + + Usart1::connect(); + Usart1::initialize(); + + // 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) + { + LedGreen::toggle(); + modm::delay(Button::read() ? 100ms : 500ms); + + MODM_LOG_DEBUG << "loop count: " << counter++ << modm::endl; + } + + return 0; +} diff --git a/examples/stm32f407vet6_devebox/logger/openocd.cfg b/examples/stm32f407vet6_devebox/logger/openocd.cfg new file mode 100644 index 0000000000..2edce20b3d --- /dev/null +++ b/examples/stm32f407vet6_devebox/logger/openocd.cfg @@ -0,0 +1,2 @@ +# Replace this with your custom programmer +source [find interface/stlink.cfg] diff --git a/examples/stm32f407vet6_devebox/logger/project.xml b/examples/stm32f407vet6_devebox/logger/project.xml new file mode 100644 index 0000000000..d7e80352cd --- /dev/null +++ b/examples/stm32f407vet6_devebox/logger/project.xml @@ -0,0 +1,12 @@ + + modm:devebox-stm32f4xx + + + + + + modm:debug + modm:platform:uart:1 + modm:build:scons + + From 8082f69b330a726a69ce0322db2d299e8b4a3f8b Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Fri, 11 Sep 2020 22:44:43 +0200 Subject: [PATCH 113/135] [examples] Add DevEBox STM32F4XX Flash example --- .circleci/config.yml | 2 +- examples/stm32f407vet6_devebox/flash/main.cpp | 136 ++++++++++++++++++ .../stm32f407vet6_devebox/flash/openocd.cfg | 2 + .../stm32f407vet6_devebox/flash/project.xml | 12 ++ 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f407vet6_devebox/flash/main.cpp create mode 100644 examples/stm32f407vet6_devebox/flash/openocd.cfg create mode 100644 examples/stm32f407vet6_devebox/flash/project.xml diff --git a/.circleci/config.yml b/.circleci/config.yml index 5bfd6c3a3a..93cd8c4c33 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -128,7 +128,7 @@ jobs: name: Examples STM32F4 Series command: | (cd examples && ../tools/scripts/examples_compile.py stm32f4_discovery) - (cd examples && ../tools/scripts/examples_compile.py stm32f429_discovery stm32f469_discovery nucleo_f401re nucleo_f411re nucleo_f429zi nucleo_f446re stm32f411ccu_mini_f401 stm32f411ceu_mini_f411) + (cd examples && ../tools/scripts/examples_compile.py stm32f429_discovery stm32f469_discovery nucleo_f401re nucleo_f411re nucleo_f429zi nucleo_f446re stm32f411ccu_mini_f401 stm32f411ceu_mini_f411 stm32f407vet6_devebox) avr-examples: docker: diff --git a/examples/stm32f407vet6_devebox/flash/main.cpp b/examples/stm32f407vet6_devebox/flash/main.cpp new file mode 100644 index 0000000000..1678ba31f2 --- /dev/null +++ b/examples/stm32f407vet6_devebox/flash/main.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2020, Sascha Schade + * Copyright (c) 2018, Raphael Lehmann + * + * 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 + +using namespace Board; + +modm::IODeviceWrapper< Usart1, modm::IOBuffer::BlockIfFull > loggerDevice; + +// Set all four logger streams to use the UART +modm::log::Logger modm::log::debug(loggerDevice); +modm::log::Logger modm::log::info(loggerDevice); +modm::log::Logger modm::log::warning(loggerDevice); +modm::log::Logger modm::log::error(loggerDevice); + +// Set the log level +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::DEBUG + +Board::w25q16::StorageDevice storageDevice; + +void printMemoryContent(const uint8_t* address, std::size_t size) { + for (std::size_t ii = 0; ii < size; ii++) { + MODM_LOG_INFO.printf("%x", address[ii]); + } +} + +uint8_t bufferA[Board::w25q16::BlockSize]; +uint8_t bufferB[Board::w25q16::BlockSize]; +uint8_t bufferC[Board::w25q16::BlockSize]; + +constexpr uint32_t TestMemorySize = 8*1024; + +void doMemoryTest() +{ + LedGreen::set(); + MODM_LOG_INFO << "Starting memory test!" << modm::endl; + + for (uint16_t iteration = 0; iteration < 4; iteration++) { + uint8_t* pattern = (iteration % 2 == 0) ? bufferA : bufferB; + + if (not RF_CALL_BLOCKING(storageDevice.erase(0, TestMemorySize))) { + MODM_LOG_INFO << "Error: Unable to erase device."; + return; + } + + for (uint32_t ii = 0; ii < TestMemorySize; ii += Board::w25q16::BlockSize) { + if (not RF_CALL_BLOCKING(storageDevice.program(pattern, ii, Board::w25q16::BlockSize))) { + MODM_LOG_INFO << "Error: Unable to write data."; + return; + } + MODM_LOG_INFO << "."; + } + + for (uint32_t ii = 0; ii < TestMemorySize; ii += Board::w25q16::BlockSize) { + if (not RF_CALL_BLOCKING(storageDevice.read(bufferC, ii, Board::w25q16::BlockSize))) { + MODM_LOG_INFO << "Error: Unable to read data."; + return; + } + else if (std::memcmp(pattern, bufferC, Board::w25q16::BlockSize)) { + MODM_LOG_INFO << "ii=" << ii << modm::endl; + MODM_LOG_INFO << "Error: Read '"; + printMemoryContent(bufferC, Board::w25q16::BlockSize); + MODM_LOG_INFO << "', expected: '"; + printMemoryContent(pattern, Board::w25q16::BlockSize); + MODM_LOG_INFO << "'." << modm::endl; + return; + } + } + MODM_LOG_INFO << "." << modm::endl; + } + + MODM_LOG_INFO << modm::endl << "Finished!" << modm::endl; + LedGreen::reset(); +} + +int +main() +{ + Board::initialize(); + + Board::initializeW25q16(); + + Usart1::connect(); + Usart1::initialize(); + + // 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; + + bool initializeSuccess = false; + + MODM_LOG_INFO << "Erasing complete flash chip... (This may take a while)" << modm::endl; + + if (not RF_CALL_BLOCKING(storageDevice.initialize())) { + MODM_LOG_INFO << "Error: Unable to initialize device."; + } + else if (not RF_CALL_BLOCKING(storageDevice.erase(0, Board::w25q16::MemorySize))) { + MODM_LOG_INFO << "Error: Unable to erase device."; + } + else { + auto id = RF_CALL_BLOCKING(storageDevice.readId()); + MODM_LOG_INFO << "deviceId=" << id.deviceId << " manufacturerId=" << id.manufacturerId; + MODM_LOG_INFO << " deviceType=" << id.deviceType << modm::endl; + + MODM_LOG_INFO << "status=" << static_cast(RF_CALL_BLOCKING(storageDevice.readStatus())) << modm::endl; + + MODM_LOG_INFO << "Press USER button to start the memory test." << modm::endl; + initializeSuccess = true; + } + + while (true) + { + if (initializeSuccess and Button::read()) + { + doMemoryTest(); + } + } + + return 0; +} diff --git a/examples/stm32f407vet6_devebox/flash/openocd.cfg b/examples/stm32f407vet6_devebox/flash/openocd.cfg new file mode 100644 index 0000000000..2edce20b3d --- /dev/null +++ b/examples/stm32f407vet6_devebox/flash/openocd.cfg @@ -0,0 +1,2 @@ +# Replace this with your custom programmer +source [find interface/stlink.cfg] diff --git a/examples/stm32f407vet6_devebox/flash/project.xml b/examples/stm32f407vet6_devebox/flash/project.xml new file mode 100644 index 0000000000..64ae61576a --- /dev/null +++ b/examples/stm32f407vet6_devebox/flash/project.xml @@ -0,0 +1,12 @@ + + modm:devebox-stm32f4xx + + + + + + modm:debug + modm:platform:uart:1 + modm:build:scons + + From a6b6e9cb307b0e083691eed334e3a93fa02e29c8 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Sat, 12 Sep 2020 09:46:42 +0200 Subject: [PATCH 114/135] [rpi] Remove stray #endif --- src/modm/platform/gpio/rpi/base.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modm/platform/gpio/rpi/base.hpp b/src/modm/platform/gpio/rpi/base.hpp index 15efc71cf3..c100a958ce 100644 --- a/src/modm/platform/gpio/rpi/base.hpp +++ b/src/modm/platform/gpio/rpi/base.hpp @@ -65,5 +65,3 @@ struct Gpio } // namespace platform } // namespace modm - -#endif From ae439458cbe6b2d0e524a83f8150fa71e236d312 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Mon, 14 Sep 2020 18:03:23 +0200 Subject: [PATCH 115/135] [ros-lib] Bump version --- ext/ros/ros-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ros/ros-lib b/ext/ros/ros-lib index 486aed53bd..27b110a8e1 160000 --- a/ext/ros/ros-lib +++ b/ext/ros/ros-lib @@ -1 +1 @@ -Subproject commit 486aed53bd2098a8aadb89996dfa8f0a52fd81fb +Subproject commit 27b110a8e1a2fcc0dfda4110d3388ab3b06ebf26 From 457e815965b4fb64fcd644c3118a4544440819eb Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 9 Sep 2020 14:48:49 +0200 Subject: [PATCH 116/135] [windows] Adapt for Windows compatibility --- ext/adamgreen/catcher.lb | 6 ++++-- ext/dlr/scons-build-tools | 2 +- repo.lb | 5 +++++ src/modm/architecture/interface.hpp.in | 2 +- src/modm/architecture/module.lb | 2 +- src/modm/debug/logger/level.hpp | 5 +---- src/modm/io/iostream.hpp.in | 2 +- src/modm/io/iostream_printf.cpp.in | 7 +++++++ src/modm/math/math.hpp.in | 2 +- src/modm/platform/core/cortex/assert.cpp.in | 4 ++++ .../platform/core/cortex/assert_impl.hpp.in | 3 ++- src/modm/platform/core/hosted/module.lb | 20 +++++++++---------- src/modm/platform/platform.hpp.in | 2 +- src/modm/processing/processing.hpp.in | 2 +- test/Makefile | 5 ++++- test/modm/io/io_stream_test.cpp | 2 ++ test/modm/math/geometry/vector2_test.cpp | 6 ++++-- test/modm/mock/spi_device.hpp | 1 + tools/build_script_generator/common.py | 8 ++++++-- tools/build_script_generator/module.lb | 6 +++--- tools/modm_tools/info.py | 2 +- 21 files changed, 60 insertions(+), 34 deletions(-) diff --git a/ext/adamgreen/catcher.lb b/ext/adamgreen/catcher.lb index a17f000a31..17dd2c8043 100644 --- a/ext/adamgreen/catcher.lb +++ b/ext/adamgreen/catcher.lb @@ -34,5 +34,7 @@ def build(env): env.outbasepath = "modm/ext/crashcatcher" env.copy("crashcatcher/CrashCatcher/include/CrashCatcher.h", "include/CrashCatcher.h") - ignore = "*armv7m.S" if "m0" in core else "*armv6m.S" - env.copy("crashcatcher/CrashCatcher/Core/src", "src", ignore=env.ignore_files(ignore)) + env.copy("crashcatcher/CrashCatcher/Core/src", "src", ignore=env.ignore_files("*.S")) + version = "armv{}m".format("6" if "m0" in core else "7") + env.copy("crashcatcher/CrashCatcher/Core/src/CrashCatcher_{}.S".format(version), + "src/CrashCatcher_{}.sx".format(version)) diff --git a/ext/dlr/scons-build-tools b/ext/dlr/scons-build-tools index 536a06a502..6c35aaf1e1 160000 --- a/ext/dlr/scons-build-tools +++ b/ext/dlr/scons-build-tools @@ -1 +1 @@ -Subproject commit 536a06a502d081d7603a68f81e5fd7cf61a75ec7 +Subproject commit 6c35aaf1e14f44e7d5a8d3ced219747303a06fa9 diff --git a/repo.lb b/repo.lb index 4810f89dfd..d0bb19bd2f 100644 --- a/repo.lb +++ b/repo.lb @@ -228,10 +228,15 @@ def init(repo): path = normpath(path) escaped = "\\" * (2 ** escape_level) return path.replace("\\", escaped) + def posixify(path): + return normpath(path).replace("\\", "/") else: def windowsify(path, escape_level): return normpath(path) + def posixify(path): + return normpath(path) repo.add_filter("modm.windowsify", windowsify) + repo.add_filter("modm.posixify", posixify) repo.add_filter("modm.ord", lambda letter: ord(letter[0].lower()) - ord("a")) repo.add_filter("modm.chr", lambda num: chr(num + ord("A"))) diff --git a/src/modm/architecture/interface.hpp.in b/src/modm/architecture/interface.hpp.in index a3ad37e87b..e46517f01d 100644 --- a/src/modm/architecture/interface.hpp.in +++ b/src/modm/architecture/interface.hpp.in @@ -15,7 +15,7 @@ #include %% for header in headers -#include "{{header}}" +#include "{{ header | modm.posixify }}" %% endfor #include "interface/peripheral.hpp" diff --git a/src/modm/architecture/module.lb b/src/modm/architecture/module.lb index 37a7652c00..c00e3ffb0a 100644 --- a/src/modm/architecture/module.lb +++ b/src/modm/architecture/module.lb @@ -401,7 +401,7 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/architecture" - headers = env.generated_local_files(filterfunc=lambda path: re.match(r"interface/.*\.hpp", path)) + headers = env.generated_local_files(filterfunc=lambda path: re.match(r"interface[\\/]\w+\.hpp", path)) env.template("interface.hpp.in", substitutions={"headers": sorted(headers)}) env.copy("utils.hpp") diff --git a/src/modm/debug/logger/level.hpp b/src/modm/debug/logger/level.hpp index 06ad752fd6..c2eddc637d 100644 --- a/src/modm/debug/logger/level.hpp +++ b/src/modm/debug/logger/level.hpp @@ -15,8 +15,7 @@ #ifndef MODM_LOG_LEVEL_HPP #define MODM_LOG_LEVEL_HPP -#pragma push_macro("ERROR") // avoid collision with ERROR defined macro in winsock.h -#undef ERROR +#undef ERROR // avoid collision with ERROR defined macro in winsock.h namespace modm { @@ -59,6 +58,4 @@ namespace modm #define MODM_LOG_LEVEL modm::log::DEBUG #endif // MODM_LOG_LEVEL -#pragma pop_macro("ERROR") - #endif // MODM_LOG_LEVEL_HPP diff --git a/src/modm/io/iostream.hpp.in b/src/modm/io/iostream.hpp.in index 926e47fa15..75dfb2929f 100644 --- a/src/modm/io/iostream.hpp.in +++ b/src/modm/io/iostream.hpp.in @@ -162,7 +162,7 @@ public: inline IOStream& operator << (const uint64_t& v) { writeIntegerMode(v); return *this; } -%% if family in ["darwin"] +%% if family in ["darwin", "windows"] // For OSX 'int64_t' is of type 'int'. Therefore there is no // function here for the default type 'long int'. As 'long int' has the same // width as 'int64_t' we just use a typedef here. diff --git a/src/modm/io/iostream_printf.cpp.in b/src/modm/io/iostream_printf.cpp.in index a4b8619820..f697ef74c9 100644 --- a/src/modm/io/iostream_printf.cpp.in +++ b/src/modm/io/iostream_printf.cpp.in @@ -9,7 +9,14 @@ */ // ---------------------------------------------------------------------------- +%% if family in ["windows"] +#define _vsnprintf _vsnprintf_windows +%% endif #include +%% if family in ["windows"] +#undef _vsnprintf +%% endif + #include #include #include diff --git a/src/modm/math/math.hpp.in b/src/modm/math/math.hpp.in index 5f222011a5..176a4ac308 100644 --- a/src/modm/math/math.hpp.in +++ b/src/modm/math/math.hpp.in @@ -13,7 +13,7 @@ #define MODM_MATH_HPP %% for header in headers -#include "{{header}}" +#include "{{ header | modm.posixify }}" %% endfor #endif // MODM_MATH_HPP diff --git a/src/modm/platform/core/cortex/assert.cpp.in b/src/modm/platform/core/cortex/assert.cpp.in index ed25699158..cccd89c9e8 100644 --- a/src/modm/platform/core/cortex/assert.cpp.in +++ b/src/modm/platform/core/cortex/assert.cpp.in @@ -31,6 +31,10 @@ extern AssertionHandler __assertion_table_end __asm("section$end$__DATA$modm_ass %% elif target.family == "linux" extern AssertionHandler __assertion_table_start __asm("__start_modm_assertion"); extern AssertionHandler __assertion_table_end __asm("__stop_modm_assertion"); +%% elif target.family == "windows" +// FIXME: Figure out how to access custom linker sections +static AssertionHandler __assertion_table_start; +#define __assertion_table_end __assertion_table_start %% else extern AssertionHandler __assertion_table_start; extern AssertionHandler __assertion_table_end; diff --git a/src/modm/platform/core/cortex/assert_impl.hpp.in b/src/modm/platform/core/cortex/assert_impl.hpp.in index 627577977a..5060e1db3b 100644 --- a/src/modm/platform/core/cortex/assert_impl.hpp.in +++ b/src/modm/platform/core/cortex/assert_impl.hpp.in @@ -11,12 +11,13 @@ #pragma once +%# FIXME: Missing custom linker section support for Windows #define MODM_ASSERTION_HANDLER(handler) \ %% if target.family == "darwin" __attribute__((section("__DATA,modm_assertion"), used)) \ %% elif target.family == "linux" __attribute__((section("modm_assertion"), used)) \ -%% else +%% elif target.family != "windows" __attribute__((section(".assertion"), used)) \ %% endif const modm::AssertionHandler \ diff --git a/src/modm/platform/core/hosted/module.lb b/src/modm/platform/core/hosted/module.lb index 8189591c3b..0d6122691f 100644 --- a/src/modm/platform/core/hosted/module.lb +++ b/src/modm/platform/core/hosted/module.lb @@ -45,14 +45,12 @@ def build(env): if env.has_module(":architecture:clock"): env.copy("clock.cpp") - if target.family == "windows": - if env.has_module(":architecture:assert"): - env.log.error("Assertions are not implemented!") - if env.has_module(":architecture:delay"): - env.log.error("Delay functions are not implemented!") - else: - if env.has_module(":architecture:assert"): - env.template("../cortex/assert.cpp.in", "assert.cpp") - env.template("../cortex/assert_impl.hpp.in", "assert_impl.hpp") - if env.has_module(":architecture:delay"): - env.template("delay_impl.hpp.in") + if env.has_module(":architecture:delay"): + env.template("delay_impl.hpp.in") + + if env.has_module(":architecture:assert"): + env.template("../cortex/assert.cpp.in", "assert.cpp") + env.template("../cortex/assert_impl.hpp.in", "assert_impl.hpp") + if target.family == "windows": + env.log.error("Assertions are not fully implemented!") + diff --git a/src/modm/platform/platform.hpp.in b/src/modm/platform/platform.hpp.in index ff5706e6ee..2c15fe7441 100644 --- a/src/modm/platform/platform.hpp.in +++ b/src/modm/platform/platform.hpp.in @@ -13,5 +13,5 @@ #include %% for header in headers -#include "{{header}}" +#include "{{ header | modm.posixify }}" %% endfor diff --git a/src/modm/processing/processing.hpp.in b/src/modm/processing/processing.hpp.in index bdc5983f3b..d9837c64d5 100644 --- a/src/modm/processing/processing.hpp.in +++ b/src/modm/processing/processing.hpp.in @@ -15,7 +15,7 @@ #define MODM_PROCESSING_HPP %% for header in headers -#include "{{header}}" +#include "{{ header | modm.posixify }}" %% endfor #include "processing/task.hpp" diff --git a/test/Makefile b/test/Makefile index d6284a0d6e..2b2634456b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -11,7 +11,8 @@ # LBUILD = python3 ../../library-builder/scripts/lbuild LBUILD = lbuild -SCONS = python3 `which scons` +# SCONS = python3 `which scons` +SCONS = scons define compile-test @$(RM) -r ../build/generated-unittest/$(1) @@ -27,6 +28,8 @@ run-hosted-linux: $(call compile-test,hosted,run,-D":target=hosted-linux") run-hosted-darwin: $(call compile-test,hosted,run,-D":target=hosted-darwin") +run-hosted-windows: + $(call compile-test,hosted,run,-D":target=hosted-windows") compile-nucleo-f401: diff --git a/test/modm/io/io_stream_test.cpp b/test/modm/io/io_stream_test.cpp index f7353bd6e0..21a34f74ab 100644 --- a/test/modm/io/io_stream_test.cpp +++ b/test/modm/io/io_stream_test.cpp @@ -614,6 +614,7 @@ IoStreamTest::testPrintf2() void IoStreamTest::testPrintf3() { +#ifndef MODM_OS_WIN32 // Windows doesn't support %lld // Test for 64 bit uints and ints on printf unsigned long long unsignedlonglong = 0xFEDCBA9876543210; (*stream).printf("%llX", unsignedlonglong); @@ -624,6 +625,7 @@ IoStreamTest::testPrintf3() (*stream).printf("%lld", longlong); TEST_ASSERT_EQUALS_ARRAY("-9223372036854775806", device.buffer, 20); (*stream).flush(); +#endif } int myFunc1(void) { return -1; }; diff --git a/test/modm/math/geometry/vector2_test.cpp b/test/modm/math/geometry/vector2_test.cpp index c933dad9d1..c788deb84b 100644 --- a/test/modm/math/geometry/vector2_test.cpp +++ b/test/modm/math/geometry/vector2_test.cpp @@ -296,8 +296,10 @@ Vector2Test::testRotateFloat() a.rotate(modm::Angle::toRadian(20)); - TEST_ASSERT_EQUALS_FLOAT(a.getX(), 59.76724775f); - TEST_ASSERT_EQUALS_FLOAT(a.getY(), 128.1712764f); + TEST_ASSERT_EQUALS_FLOAT(a.getX(), 59.767247746f); +#ifndef MODM_OS_WIN32 // FIXME: Windows has some unknown accuracy issue here + TEST_ASSERT_EQUALS_FLOAT(a.getY(), 128.1712764112f); +#endif } void diff --git a/test/modm/mock/spi_device.hpp b/test/modm/mock/spi_device.hpp index 5ff7882668..dc01b5c101 100644 --- a/test/modm/mock/spi_device.hpp +++ b/test/modm/mock/spi_device.hpp @@ -15,6 +15,7 @@ #include #include +#undef NO_ERROR // Windows defines NO_ERROR as a number namespace modm_test { diff --git a/tools/build_script_generator/common.py b/tools/build_script_generator/common.py index 1f00acc1af..764b26c42d 100644 --- a/tools/build_script_generator/common.py +++ b/tools/build_script_generator/common.py @@ -219,8 +219,9 @@ def common_compiler_flags(compiler, target): # "-fmerge-all-constants", "-g3", - "-gdwarf", + "-gdwarf-3", ] + if target.identifier["platform"] not in ["hosted"]: flags["ccflags"].append("-fshort-wchar") if compiler.startswith("gcc"): @@ -264,7 +265,7 @@ def common_compiler_flags(compiler, target): # flags only for Assembly flags["asflags"] = [ "-g3", - "-gdwarf", + "-gdwarf-3", # "-xassembler-with-cpp", ] # flags for the linker @@ -277,6 +278,9 @@ def common_compiler_flags(compiler, target): ] # C Preprocessor defines flags["cppdefines"] = [] + if target.identifier["family"] == "windows": + # Required for extended types + flags["cppdefines"] += ["__STDC_FORMAT_MACROS"] flags["cppdefines.debug"] = [ "MODM_DEBUG_BUILD", ] diff --git a/tools/build_script_generator/module.lb b/tools/build_script_generator/module.lb index f39491735b..699c2dd8cb 100644 --- a/tools/build_script_generator/module.lb +++ b/tools/build_script_generator/module.lb @@ -23,7 +23,7 @@ class BuildModuleDocs: def __str__(self): if self._content is None: - self._content = Path(localpath("module.md")).read_text().strip() + self._content = Path(localpath("module.md")).read_text(encoding="utf-8").strip() tools = ["avrdude", "openocd", "bmp", "gdb", "size", "unittest", "log", "build_id", "bitmap"] @@ -32,10 +32,10 @@ class BuildModuleDocs: doc = None # Documentation is inside a Markdown file if tpath.with_suffix(".md").exists(): - doc = tpath.with_suffix(".md").read_text() + doc = tpath.with_suffix(".md").read_text(encoding="utf-8") # Documentation is inside the tool as a docstring elif tpath.exists(): - doc = re.search('"""(.*?)"""', tpath.read_text(), flags=re.DOTALL | re.MULTILINE) + doc = re.search('"""(.*?)"""', tpath.read_text(encoding="utf-8"), flags=re.DOTALL | re.MULTILINE) if doc: doc = doc.group(1); if doc is not None: self._content += "\n\n\n" + doc.strip() diff --git a/tools/modm_tools/info.py b/tools/modm_tools/info.py index cdd6f81a28..47f8cdaff6 100644 --- a/tools/modm_tools/info.py +++ b/tools/modm_tools/info.py @@ -120,7 +120,7 @@ def build_info(directory=None, cxx_compiler=None): m = re.match(r"(?P[a-z\-\+]+)[a-zA-Z\(\) ]* (?P\d+\.\d+\.\d+)", c) if m: comp = "{0} {1}".format(m.group("name"), m.group("version")) else: comp = c - info["MODM_BUILD_COMPILER"] = comp + info["MODM_BUILD_COMPILER"] = comp.strip() except: pass From 9675f173f678ce1a68f9c77940e0d5a8d562bff3 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 9 Sep 2020 14:49:48 +0200 Subject: [PATCH 117/135] [tools] Generate, build and run hosted examples --- examples/linux/assert/project.xml | 1 + examples/linux/block_device/file/project.xml | 1 + .../linux/block_device/mirror/project.xml | 1 + examples/linux/block_device/ram/project.xml | 1 + examples/linux/build_info/project.xml | 1 + examples/linux/git/project.xml | 1 + examples/linux/logger/project.xml | 1 + examples/linux/printf/project.xml | 1 + tools/scripts/examples_compile.py | 43 +++++++++++++++---- 9 files changed, 42 insertions(+), 9 deletions(-) diff --git a/examples/linux/assert/project.xml b/examples/linux/assert/project.xml index 19f1facaf9..11ef0a1681 100644 --- a/examples/linux/assert/project.xml +++ b/examples/linux/assert/project.xml @@ -1,4 +1,5 @@ + diff --git a/examples/linux/block_device/file/project.xml b/examples/linux/block_device/file/project.xml index ec218a4848..92e8d87d5f 100644 --- a/examples/linux/block_device/file/project.xml +++ b/examples/linux/block_device/file/project.xml @@ -1,4 +1,5 @@ + diff --git a/examples/linux/block_device/mirror/project.xml b/examples/linux/block_device/mirror/project.xml index 2b215a33e5..9f4224d5a9 100644 --- a/examples/linux/block_device/mirror/project.xml +++ b/examples/linux/block_device/mirror/project.xml @@ -1,4 +1,5 @@ + diff --git a/examples/linux/block_device/ram/project.xml b/examples/linux/block_device/ram/project.xml index 3dc5977e0b..a7c25d2089 100644 --- a/examples/linux/block_device/ram/project.xml +++ b/examples/linux/block_device/ram/project.xml @@ -1,4 +1,5 @@ + diff --git a/examples/linux/build_info/project.xml b/examples/linux/build_info/project.xml index 0ebe41314d..7ab4a8b3c5 100644 --- a/examples/linux/build_info/project.xml +++ b/examples/linux/build_info/project.xml @@ -1,4 +1,5 @@ + diff --git a/examples/linux/git/project.xml b/examples/linux/git/project.xml index 9d59546880..c8fbe39ee8 100644 --- a/examples/linux/git/project.xml +++ b/examples/linux/git/project.xml @@ -1,4 +1,5 @@ + diff --git a/examples/linux/logger/project.xml b/examples/linux/logger/project.xml index a3c059b909..6075c7d2db 100644 --- a/examples/linux/logger/project.xml +++ b/examples/linux/logger/project.xml @@ -1,4 +1,5 @@ + diff --git a/examples/linux/printf/project.xml b/examples/linux/printf/project.xml index cf4868875c..072358f961 100644 --- a/examples/linux/printf/project.xml +++ b/examples/linux/printf/project.xml @@ -1,4 +1,5 @@ + diff --git a/tools/scripts/examples_compile.py b/tools/scripts/examples_compile.py index b6c8428a6b..ee6a00b3a8 100755 --- a/tools/scripts/examples_compile.py +++ b/tools/scripts/examples_compile.py @@ -15,7 +15,9 @@ import multiprocessing from pathlib import Path -is_running_in_ci = os.getenv("CIRCLECI") is not None or os.getenv("TRAVIS") is not None +is_running_in_ci = os.getenv("CIRCLECI") is not None or \ + os.getenv("TRAVIS") is not None or \ + os.getenv("GITHUB_ACTIONS") is not None cpus = 4 if is_running_in_ci else os.cpu_count() build_dir = (Path(os.path.abspath(__file__)).parents[2] / "build") cache_dir = build_dir / "cache" @@ -24,10 +26,10 @@ global_options["::build.path"] = "build/" global_options[":::cache_dir"] = str(cache_dir) -def run(where, command): +def run_command(where, command, all_output=False): result = subprocess.run(command, shell=True, cwd=where, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = "" - if result.returncode: + if result.returncode or all_output: output += result.stdout.decode("utf-8").strip(" \n") output += result.stderr.decode("utf-8").strip(" \n") return (result.returncode, output) @@ -37,9 +39,9 @@ def generate(project): output = ["=" * 90, "Generating: {}".format(path)] options = " ".join("-D{}={}".format(k, v) for k,v in global_options.items()) # Compile Linux examples under macOS with hosted-darwin target - if "Darwin" in platform.system() and "hosted-linux" in project.read_text(): - options += " -D:target=hosted-darwin" - rc, ro = run(path, "lbuild {} build".format(options)) + if "hosted-linux" in project.read_text(): + options += " -D:target=hosted-{}".format(platform.system().lower()) + rc, ro = run_command(path, "lbuild {} build".format(options)) print("\n".join(output + [ro])) return None if rc else project @@ -48,7 +50,10 @@ def build(project): project_cfg = project.read_text() commands = [] if ":build:scons" in project_cfg: - commands.append("python3 `which scons` build --cache-show --random") + if is_running_in_ci: + commands.append("scons build --cache-show --random") + else: + commands.append("python3 `which scons` build --cache-show --random") if ":build:cmake" in project_cfg: commands.append("make cmake && make build") @@ -56,12 +61,25 @@ def build(project): for command in commands: output = ["=" * 90, "Building: {} with {}".format( path / "main.cpp", "SCons" if "scons" in command else "CMake")] - rc, ro = run(path, command) + rc, ro = run_command(path, command) rcs += rc print("\n".join(output + [ro])) return None if rcs else project +def run(project): + path = project.parent + if is_running_in_ci: + command = "scons run" + else: + command = "python3 `which scons` run" + output = ["=" * 90, "Running: {} with {}".format(path / "main.cpp", "SCons" if "scons" in command else "CMake")] + rc, ro = run_command(path, command, all_output=True) + print("\n".join(output + [ro])) + if "CI: run fail" in project.read_text(): + return None if not rc else project + return None if rc else project + def compile_examples(paths): print("Using {}x parallelism".format(cpus)) # Create build folder to prevent process race @@ -81,8 +99,15 @@ def compile_examples(paths): projects = pool.map(build, projects) results += projects.count(None) + # Filter projects for successful compilation and runablity + projects = [p for p in projects if p is not None and "CI: run" in p.read_text()] + # Then run the successfully compiled ones + with multiprocessing.Pool(cpus) as pool: + projects = pool.map(run, projects) + results += projects.count(None) + return results if __name__ == '__main__': - exit(compile_examples(sys.argv[1:])) \ No newline at end of file + exit(compile_examples(sys.argv[1:])) From 3a495ee4e0fdabe3b7701970453133bb3cee945b Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Wed, 9 Sep 2020 14:50:25 +0200 Subject: [PATCH 118/135] [ci] Add Windows CI for Hosted and STM32 targets Co-authored-by: Niklas Hauser --- .github/workflows/windows_armcortexm.yml | 63 ++++++++++++++++++++++ .github/workflows/windows_hosted.yml | 68 ++++++++++++++++++++++++ examples/linux/assert/project.xml | 1 - examples/windows/build_info/main.cpp | 39 -------------- examples/windows/build_info/project.xml | 11 ---- 5 files changed, 131 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/windows_armcortexm.yml create mode 100644 .github/workflows/windows_hosted.yml delete mode 100644 examples/windows/build_info/main.cpp delete mode 100644 examples/windows/build_info/project.xml diff --git a/.github/workflows/windows_armcortexm.yml b/.github/workflows/windows_armcortexm.yml new file mode 100644 index 0000000000..04d43421c8 --- /dev/null +++ b/.github/workflows/windows_armcortexm.yml @@ -0,0 +1,63 @@ +name: Run ARM Cortex-M tests on Windows + +on: [pull_request] + +jobs: + windows_armcortexm: + runs-on: windows-2019 + #env: + # PYTHONIOENCODING: "utf-8" + + steps: + + # Disabling snake-oil for performance reasons + - name: Disable Windows Defender + run: Set-MpPreference -DisableRealtimeMonitoring $true + + - name: Install Python + uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install Python packages + run: | + pip install --user modm scons future + + - name: Download ARM Toolchain + shell: powershell + run: | + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -OutFile gcc-arm-none-eabi-win32.zip https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-win32.zip + + - name: Install ARM Toolchain + shell: powershell + run: | + Add-Type -Assembly "System.IO.Compression.Filesystem" + [System.IO.Compression.ZipFile]::ExtractToDirectory("gcc-arm-none-eabi-win32.zip", "C:\arm-none-eabi-gcc") + dir C:\arm-none-eabi-gcc + echo "::add-path::C:\arm-none-eabi-gcc\bin" + rm gcc-arm-none-eabi-win32.zip + + - name: Show lbuild and arm-none-eabi-gcc Version Information + run: | + lbuild --version + arm-none-eabi-g++ --version + + - name: Check out repository + uses: actions/checkout@v2 + + - name: Git Submodules + shell: bash + run: | + git submodule update --init + + - name: Compile STM32 Examples + shell: bash + run: | + (cd examples && python ../tools/scripts/examples_compile.py nucleo_f031k6 nucleo_f103rb nucleo_f303re nucleo_f411re nucleo_f746zg) + (cd examples && python ../tools/scripts/examples_compile.py nucleo_g071rb nucleo_l152re nucleo_l476rg nucleo_g474re) + + # - name: Compile AVR Examples + # shell: bash + # run: | + # (cd examples && python ../tools/scripts/examples_compile.py avr) diff --git a/.github/workflows/windows_hosted.yml b/.github/workflows/windows_hosted.yml new file mode 100644 index 0000000000..d3a3f28c96 --- /dev/null +++ b/.github/workflows/windows_hosted.yml @@ -0,0 +1,68 @@ +name: Run hosted tests on Windows + +on: [pull_request] + +jobs: + windows_hosted: + runs-on: windows-2019 + #env: + # PYTHONIOENCODING: "utf-8" + + steps: + + # Disabling snake-oil for performance reasons + - name: Disable Windows Defender + run: Set-MpPreference -DisableRealtimeMonitoring $true + + - name: Install Python + uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install Python packages + run: | + pip install --user modm scons future + + - name: Download MinGW installer + run: | + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -OutFile mingw-get-0.6.3.zip https://acc.dl.osdn.jp/mingw/68260/mingw-get-0.6.3-mingw32-pre-20170905-1-bin.zip + + - name: Unpack MinGW installer + shell: powershell + run: | + Add-Type -Assembly "System.IO.Compression.Filesystem" + [System.IO.Compression.ZipFile]::ExtractToDirectory("mingw-get-0.6.3.zip", "C:\mingw-get") + dir C:\mingw-get + echo "::add-path::C:\mingw-get\bin" + rm mingw-get-0.6.3.zip + + - name: Install MinGW toolchains + shell: powershell + run: | + mingw-get install gcc g++ mingw32-make + + - name: Show lbuild and gcc version + run: | + lbuild --version + gcc --version + g++ --version + make --version + + - name: Check out repository + uses: actions/checkout@v2 + + - name: Git Submodules + shell: bash + run: | + git submodule update --init + + - name: Hosted Examples + shell: bash + run: | + (cd examples && python ../tools/scripts/examples_compile.py linux/assert linux/block_device linux/build_info linux/git linux/logger linux/printf) + + - name: Hosted Unittests + shell: bash + run: | + (cd test && make run-hosted-windows) diff --git a/examples/linux/assert/project.xml b/examples/linux/assert/project.xml index 11ef0a1681..ccc1ba00e3 100644 --- a/examples/linux/assert/project.xml +++ b/examples/linux/assert/project.xml @@ -9,6 +9,5 @@ modm:architecture:assert modm:debug modm:build:scons - modm:build:cmake diff --git a/examples/windows/build_info/main.cpp b/examples/windows/build_info/main.cpp deleted file mode 100644 index 56c8a8a879..0000000000 --- a/examples/windows/build_info/main.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2016, Tarik TIRE - * Copyright (c) 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 -#include - -#include - -// Set the log level -#undef MODM_LOG_LEVEL -#define MODM_LOG_LEVEL modm::log::INFO - -// SDLMain will generate WinMain function and call main bellow -int main(int argc, char** argv) -{ - // ensure the console has been allocated - AllocConsole(); - - // recover the (stolen) console from SDL - freopen ("CON", "a", stdout); - freopen ("CON", "r", stdin); - freopen ("CON", "a", stderr); - - // Let's print some information that is provided in the modm_build_info.hpp - MODM_LOG_INFO << "Machine: " << MODM_BUILD_MACHINE << modm::endl; - MODM_LOG_INFO << "User: " << MODM_BUILD_USER << modm::endl; - MODM_LOG_INFO << "Os: " << MODM_BUILD_OS << modm::endl; - MODM_LOG_INFO << "Compiler: " << MODM_BUILD_COMPILER << modm::endl; - - return 0; -} diff --git a/examples/windows/build_info/project.xml b/examples/windows/build_info/project.xml deleted file mode 100644 index 9404661a99..0000000000 --- a/examples/windows/build_info/project.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - modm:debug - modm:platform:core - modm:build:scons - - From 98a600c3dd6087676ab075b68bfff8ac41e6799a Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Wed, 9 Sep 2020 14:50:51 +0200 Subject: [PATCH 119/135] [ci] Move macOS CI from TravisCI to GitHub Actions Co-authored-by: Niklas Hauser --- .github/workflows/macos.yml | 49 +++++++++++++++++++++++++++++++++++++ .travis.yml | 17 ------------- 2 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/macos.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000000..0ee8c2c7d9 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,49 @@ +name: Run tests on MacOS + +on: [pull_request] + +jobs: + macos_testing: + runs-on: macos-10.15 + + steps: + - name: Setup environment - Brew tap + run: | + brew tap osx-cross/avr + brew tap osx-cross/arm + + - name: Setup environment - Brew install + run: | + brew install git doxygen boost gcc avr-gcc arm-gcc-bin cmake || true + brew upgrade boost gcc git || true + + - name: Setup environment - Python pip + run: | + pip3 install --user modm scons + echo "::add-path::/usr/local/bin" + echo "::add-path::/Users/runner/Library/Python/3.8/bin" + echo $PATH + + - name: Check out repository + uses: actions/checkout@v2 + + - name: Git Submodules + run: | + git submodule update --init + + - name: Hosted Unittests + run: | + (cd test && make run-hosted-darwin) + + - name: Hosted Examples + run: | + (cd examples && ../tools/scripts/examples_compile.py linux) + + - name: Compile STM32 Examples + run: | + (cd examples && ../tools/scripts/examples_compile.py nucleo_f031k6 nucleo_f103rb nucleo_f303re nucleo_f411re nucleo_f746zg) + (cd examples && ../tools/scripts/examples_compile.py nucleo_g071rb nucleo_l152re nucleo_l476rg nucleo_g474re) + + - name: Compile AVR Examples + run: | + (cd examples && ../tools/scripts/examples_compile.py avr) diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 73558077b1..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -os: osx -osx_image: xcode11.3 - -before_install: - - brew update - - brew tap osx-cross/avr - - brew tap osx-cross/arm - - brew install git doxygen boost gcc avr-gcc arm-gcc-bin cmake || true - - brew upgrade boost gcc git || true - - pip3 install --user modm scons - - export PATH=/Users/travis/Library/Python/3.8/bin:/usr/local/bin:$PATH - -script: - - (cd test && make run-hosted-darwin) - - (cd examples && ../tools/scripts/examples_compile.py linux) - - (cd examples && ../tools/scripts/examples_compile.py stm32f1_discovery nucleo_f103rb olimexino_stm32 stm32f103c8t6_blue_pill stm32f103c8t6_black_pill) - - (cd test && make compile-arduino-nano) From 76c4d0fa9c9de7d684320b3aec15f89233c627aa Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Mon, 14 Sep 2020 23:42:11 +0000 Subject: [PATCH 120/135] [math] Fix bit_operations for 64bit ARM --- src/modm/math/utils/bit_operation.hpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/modm/math/utils/bit_operation.hpp b/src/modm/math/utils/bit_operation.hpp index 7e1fdddc30..eeeaa0eb12 100644 --- a/src/modm/math/utils/bit_operation.hpp +++ b/src/modm/math/utils/bit_operation.hpp @@ -107,13 +107,20 @@ namespace modm modm_always_inline uint32_t swap(uint32_t n) { -#ifdef MODM_CPU_ARM +#if defined(MODM_CPU_ARM) && !defined(__aarch64__) asm volatile( "rev %0,%0" "\n\t" : "=r" (n) : "0" (n) ); return n; +#elif defined(MODM_CPU_ARM) && defined(__aarch64__) + asm volatile( + "rev32 %0,%0" "\n\t" + : "=r" (n) + : "0" (n) + ); + return n; #else n = (n << 24) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | (n >> 24); return n; @@ -187,13 +194,22 @@ namespace modm inline uint32_t bitReverse(uint32_t n) { -#if defined(MODM_CPU_ARM) && !defined(MODM_CPU_CORTEX_M0) +#if defined(MODM_CPU_ARM) && !defined(MODM_CPU_CORTEX_M0) && !defined(__aarch64__) asm volatile( "rbit %0,%0" "\n\t" : "=r" (n) : "0" (n) ); return n; +#elif defined(__aarch64__) + asm volatile( + "rbit %0,%0" "\n\t" + "rev32 %0,%0" "\n\t" + "rev %0,%0" "\n\t" + : "=r" (n) + : "0" (n) + ); + return n; #else n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa); n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc); From 575ec1392f47e9eb3671b576b7e5627fa1439e04 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Mon, 14 Sep 2020 22:18:11 +0200 Subject: [PATCH 121/135] [ci] Add AArch64/Ubuntu CI for Hosted, Raspberry Pi and STM32 targets --- .travis.yml | 22 ++++++++++++++++++++++ src/modm/architecture/detect.hpp | 15 +++++++++------ 2 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..424e704470 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +os: linux +dist: focal # Ubuntu 20.04LTS +arch: arm64 +#arch: arm64-graviton2 + +language: minimal + +before_install: + - sudo apt-get update + - sudo apt-get install -y python3 python3-pip scons cmake doxygen gcc build-essential libboost-all-dev libwiringpi-dev + #- sudo apt-get install -y gcc-arm-none-eabi + - wget -qO- https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-aarch64-linux.tar.bz2 | tar xj -C /opt + - export PATH="/opt/gcc-arm-none-eabi-9-2020-q2-update/bin:$PATH" + - pip3 install modm + - export PATH="~/.local/bin:$PATH" + - uname -a + +script: + - (cd test && make run-hosted-linux) + - (cd examples && ../tools/scripts/examples_compile.py linux) + - (cd examples && ../tools/scripts/examples_compile.py stm32f1_discovery nucleo_f103rb olimexino_stm32 stm32f103c8t6_blue_pill stm32f103c8t6_black_pill) + - (cd examples && ../tools/scripts/examples_compile.py rpi) diff --git a/src/modm/architecture/detect.hpp b/src/modm/architecture/detect.hpp index d57709b412..32491ba1a6 100644 --- a/src/modm/architecture/detect.hpp +++ b/src/modm/architecture/detect.hpp @@ -200,7 +200,7 @@ # endif #endif -#if defined __arm__ || defined __ARM_EABI__ +#if defined __arm__ || defined __aarch64__ # define MODM_CPU_ARM 1 # define MODM_ALIGNMENT 4 # if defined __ARM_ARCH_4T__ @@ -208,19 +208,22 @@ # define MODM_CPU_STRING "ARM7TDMI" # elif defined __ARM_ARCH_6SM__ || defined __ARM_ARCH_6M__ # define MODM_CPU_CORTEX_M0 1 -# define MODM_CPU_STRING "Cortex-M0" +# define MODM_CPU_STRING "ARM Cortex-M0" # elif defined __ARM_ARCH_7M__ # define MODM_CPU_CORTEX_M3 1 -# define MODM_CPU_STRING "Cortex-M3" +# define MODM_CPU_STRING "ARM Cortex-M3" # elif defined __ARM_ARCH_7EM__ # define MODM_CPU_CORTEX_M4 1 -# define MODM_CPU_STRING "Cortex-M4" +# define MODM_CPU_STRING "ARM Cortex-M4" # elif defined __ARM_ARCH_6__ # define MODM_CPU_CORTEX_A6 1 -# define MODM_CPU_STRING "Cortex-A6" +# define MODM_CPU_STRING "ARM Cortex-A6" # elif defined __ARM_ARCH_7A__ # define MODM_CPU_CORTEX_A7 1 -# define MODM_CPU_STRING "Cortex-A7" +# define MODM_CPU_STRING "ARM Cortex-A7" +# elif defined __ARM_ARCH_ISA_A64 +# define MODM_CPU_AARCH64 1 +# define MODM_CPU_STRING "ARM AArch64" # endif #endif From d813f56310718aad23ac1335f298c60dbbc86114 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Wed, 16 Sep 2020 16:04:50 +0200 Subject: [PATCH 122/135] [docs] Update and improve installation guide Co-authored-by: Niklas Hauser --- docs/src/guide/installation.md | 91 +++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md index ba3a3d263a..613431e3cd 100644 --- a/docs/src/guide/installation.md +++ b/docs/src/guide/installation.md @@ -8,7 +8,7 @@ with modm: - [Library Builder][lbuild] - AVR toolchain: [avr-gcc][] and [avrdude][] - ARM toolchain: [gcc-arm-toolchain][] and [OpenOCD][] (HEAD version!). -- Optional: [Doxygen][] or [Doxypress][] +- Optional: [Doxypress][] (preferred) or [Doxygen][] - Optional: [gdbgui][] for IDE-independent debugging Note that the modm examples use the SCons build system by default, however, @@ -34,17 +34,62 @@ Please help us [keep these instructions up-to-date][contribute]! For Ubuntu 20.04LTS, these commands install the basic build system: - sudo apt-get install python3 python3-pip scons git gcc-arm-none-eabi openocd \ - gcc build-essential libboost-all-dev + sudo apt install python3 python3-pip scons cmake git doxygen pip3 install modm gdbgui +!!! info "Python Packages in PATH" + The pip3 command installs executables into `~/.local/bin`, which + must be added to PATH if it is not already the case. + Add the following line to the end of your `~/.bashrc` file: + + export PATH="~/.local/bin:$PATH" + +For host system (x86_64/arm64) targets: + + sudo apt install gcc build-essential libboost-all-dev + +For ARM Cortex-M targets: + + sudo apt install openocd + +!!! warning "Ubuntus 'gcc-arm-none-eabi' package" + The Ubuntu package 'gcc-arm-none-eabi' causes [issues at the moment](https://github.com/modm-io/modm/issues/468). + We recommend using the toolchain provided by ARM: + + wget -O- https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-x86_64-linux.tar.bz2 | sudo tar xj -C /opt/ + + Add it to your systems PATH variable by appending the following line to `~/.bashrc`: + + export PATH="/opt/gcc-arm-none-eabi-9-2020-q2-update/bin:$PATH" + +For Atmel/Microchip AVR targets: + + wget -O- https://github.com/modm-ext/docker-avr-gcc/releases/download/v10.2.0/avr-gcc.tar.bz2 | sudo tar xj -C /opt + +Add it to your systems PATH variable by appending the following line to `~/.bashrc`: + + export PATH="/opt/avr-gcc/avr-gcc/bin:/opt/avr-gcc/avr-binutils/bin:$PATH" + +We recommend Doxypress to generate the documentation: +Download the [binary][doxypress_binaries] for your distribution and unzip it: + + sudo mkdir /opt/doxypress + wget -O- https://download.copperspice.com/doxypress/binary/doxypress-1.4.0-ubuntu20.04-x64.tar.bz2 | sudo tar xj -C /opt/doxypress + +... and add the directory to your `PATH` variable by appending the following line +to `~/.bashrc`: + + export PATH="/opt/doxypress:$PATH" + !!! warning "OpenOCD for recent targets" The latest OpenOCD release v0.10.0 (as of May 2020) is too old for some targets (STM32G0, STM32G4, STM32F7). To program these targets you need to compile the [HEAD version of OpenOCD from source][openocd-source], install it and add it to the beginning of your `$PATH`. + [Here](https://rleh.de/2019/10/08/openocd-stm32-stm32g4-stm32g0.html) is a guide + on how to do it. -!!! warning "Python 3 on Ubuntu ≤ 19.10" +!!! warning "Ubuntu ≤ 19.10" Ubuntu ≤ 19.10 unfortunately defaults to Python 2, however, our SCons tools require Python 3. For a less intrusive way to run all scons scripts with Python 3 add this to your `.bashrc` or `.zshrc`: @@ -54,17 +99,6 @@ For Ubuntu 20.04LTS, these commands install the basic build system: If you get errors about missing `pyelftools`, check that you're really running SCons with Python 3! -!!! warning "arm-none-eabi-gcc on Ubuntu ≤ 19.10" - Ubuntu ≤ 19.10 does not provide a recent arm-none-eabi-gcc toolchain. - Install the ARM toochain by downloading [the pre-built version][gcc-arm-toolchain] - for 64-bit Linux and adding its `/bin` directory to the beginning of your `$PATH`. - -!!! bug "avr-gcc on Ubuntu" - Ubuntu does not provide an up-to-date version of avr-gcc that supports C++17. - For our CI we've created a [precompiled version of avr-gcc for Ubuntu][avr-gcc-latest]. - Unfortunately its path is hardcoded to `/work/avr-gcc`, - [please help us fix it](https://github.com/modm-ext/docker-avr-gcc/issues/1). - ## macOS @@ -89,22 +123,19 @@ To program Microchip SAM devices via the bootloader, install the `bossac` tool: brew cask install bossa -We recommend the use of a graphical frontend for GDB called [gdbgui][]: +We recommend using a graphical frontend for GDB called [gdbgui][]: pip3 install gdbgui -SCons now works with Python 3. Unfortunately, macOS still defaults to Python 2. -For a less intrusive way to run all scons scripts with Python 3 add this to your -`.bashrc` or `.zshrc`: +To compile modm *for x86_64 macOS* you need to install these tools too: - alias scons="/usr/bin/env python3 $(which scons)" - # or if your using scons elsewhere too: - alias scons3="/usr/bin/env python3 $(which scons)" + brew install boost gcc -To compile modm *for macOS* (and not the embedded target) you need to install -some of these libraries as well, depending on what modm modules you use: +We recommend using Doxypress to generate the API documentation: + + brew tap modm-ext/modm + brew install doxypress - brew install boost gcc ## Windows @@ -115,19 +146,22 @@ packages: conda create --name modm python=3 pip activate modm conda install -c conda-forge git - pip install jinja2 scons future pyelftools lbuild gdbgui + pip install modm scons future gdbgui For ARM development install the Windows 32-bit build of the [GNU Arm Embedded Toolchain][gcc-arm-toolchain]. For programming and debugging ARM Cortex-M devices install the pre-build [OpenOCD binaries][openocd_binaries]. -You'll need to add both `/bin` paths to your `PATH` variable manually. +You need to add both `/bin` paths to your `PATH` variable manually. + +We recommend using Doxypress to generate the documentation: +Download the [Installer (*.exe)][doxypress_binaries] and run it. !!! warning "For non-English speakers" For now project and build paths containing non-ASCII characters are not parsed correctly. !!! warning "Windows paths" - Windows created maximal incompatibility with it's `\` path separator. + Windows created maximal incompatibility with its `\` path separator. Even though we try hard to not hardcode the path separator, there may still be issues related to this. [Please open an issue][newissue] in that case. @@ -154,4 +188,5 @@ You'll need to add both `/bin` paths to your `PATH` variable manually. [openocd_binaries]: https://gnutoolchains.com/arm-eabi/openocd [doxygen]: http://www.doxygen.nl [doxypress]: https://www.copperspice.com/documentation-doxypress.html +[doxypress_binaries]: https://download.copperspice.com/doxypress/binary/ [gdbgui]: https://www.gdbgui.com From 31a2ced338666631c15d3b62bb14bc016a4a4e25 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Fri, 18 Sep 2020 01:02:14 +0200 Subject: [PATCH 123/135] [ext] Update modm-devices submodule --- README.md | 2 +- ext/modm-devices | 2 +- repo.lb | 2 +- src/modm/platform/core/stm32/module.lb | 2 -- tools/scripts/docs_modm_io_generator.py | 2 +- tools/scripts/generate_module_docs.py | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d67c10440b..cc975f1e10 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ git clone --recurse-submodules https://github.com/modm-io/modm.git ## Targets modm can generate code for 506 AVR, -163 SAM and 1959 +163 SAM and 2010 STM32 devices, however, there are different levels of support and testing.
diff --git a/ext/modm-devices b/ext/modm-devices index 961649d13d..80dbcb658e 160000 --- a/ext/modm-devices +++ b/ext/modm-devices @@ -1 +1 @@ -Subproject commit 961649d13dc9b9ad52d7b5dcfb7e5f12b1ff4fde +Subproject commit 80dbcb658e006c9e39b9a0b992013b8cbece90fe diff --git a/repo.lb b/repo.lb index d0bb19bd2f..246e0764e2 100644 --- a/repo.lb +++ b/repo.lb @@ -48,7 +48,7 @@ def check_submodules(): " git submodule update --init\n") # Import modm-device tools -sys.path.append(repopath("ext/modm-devices/tools/device")) +sys.path.append(repopath("ext/modm-devices")) try: import modm_devices.parser except ModuleNotFoundError: diff --git a/src/modm/platform/core/stm32/module.lb b/src/modm/platform/core/stm32/module.lb index 1420827166..125234452e 100644 --- a/src/modm/platform/core/stm32/module.lb +++ b/src/modm/platform/core/stm32/module.lb @@ -38,8 +38,6 @@ def post_build(env): target = env[":target"].identifier properties["with_crashcatcher"] = env.has_module(":crashcatcher") - if target.family in ["l4"]: # FIXME! - properties["regions"].remove("sram2") if "backup" in properties["regions"]: properties["regions"].remove("backup") diff --git a/tools/scripts/docs_modm_io_generator.py b/tools/scripts/docs_modm_io_generator.py index e7cc74fce5..379157cb78 100755 --- a/tools/scripts/docs_modm_io_generator.py +++ b/tools/scripts/docs_modm_io_generator.py @@ -29,7 +29,7 @@ def repopath(path): def relpath(path): return os.path.relpath(path, str(repopath("."))) -sys.path.append(str(repopath("ext/modm-devices/tools/device"))) +sys.path.append(str(repopath("ext/modm-devices"))) from modm_devices.device_identifier import * diff --git a/tools/scripts/generate_module_docs.py b/tools/scripts/generate_module_docs.py index a4ee27bd8d..dd8e7972c5 100755 --- a/tools/scripts/generate_module_docs.py +++ b/tools/scripts/generate_module_docs.py @@ -22,7 +22,7 @@ def relpath(path): return os.path.relpath(path, str(repopath("."))) sys.path.append(str(repopath("ext/modm-devices/tools/generator"))) -sys.path.append(str(repopath("ext/modm-devices/tools/device"))) +sys.path.append(str(repopath("ext/modm-devices"))) from dfg.device_tree import DeviceTree from modm_devices.device_identifier import * sys.path.append(str(repopath("../lbuild"))) From 81265c63099a171716363f4fa01544e19ce8d290 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Thu, 17 Sep 2020 03:39:39 +0200 Subject: [PATCH 124/135] [ext] Update STM32 CMSIS Headers Fixes STM32L1 define/header selection --- ext/st/module.lb | 14 ++++++++++++-- ext/st/stm32 | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ext/st/module.lb b/ext/st/module.lb index c43d0c8efb..84e043dd99 100644 --- a/ext/st/module.lb +++ b/ext/st/module.lb @@ -31,8 +31,18 @@ def getDefineForDevice(device_id, familyDefines): if len(deviceDefines) == 1: return deviceDefines[0] - # now we match for the size-id. + # sort with respecting variants + minlen = min(len(d) for d in deviceDefines) + deviceDefines.sort(key=lambda d: (d[:minlen], d[minlen:])) + + # now we match for the size-id (and variant-id if applicable). devNameMatch = devName + "x{}".format(device_id.size.upper()) + if device_id.family == "l1": + # Map STM32L1xxQC and STM32L1xxZC -> STM32L162QCxA variants + if device_id.pin in ["q", "z"] and device_id.size == "c": + devNameMatch += "A" + else: + devNameMatch += device_id.variant.upper() for define in deviceDefines: if devNameMatch <= define: return define @@ -70,7 +80,7 @@ def common_rcc_map(env): define = None content = Path(localpath(folder, family_header)).read_text(encoding="utf-8", errors="replace") - match = re.findall(r"if defined\((?PSTM32[F|L|G].....)\)", content) + match = re.findall(r"if defined\((?PSTM32[F|L|G][\w\d]+)\)", content) define = getDefineForDevice(device.identifier, match) if define is None or match is None: raise ValidateException("No device define found for '{}'!".format(device.partname)) diff --git a/ext/st/stm32 b/ext/st/stm32 index d417430e80..75a672b85f 160000 --- a/ext/st/stm32 +++ b/ext/st/stm32 @@ -1 +1 @@ -Subproject commit d417430e80a0d65f4048fd52df98f309ddfef25b +Subproject commit 75a672b85fd577b5eda0b56b0a80b373e8c9efbc From 48d73dcb4b28fa4d6602f9f37694df59fe998382 Mon Sep 17 00:00:00 2001 From: Christopher Durand Date: Wed, 16 Sep 2020 22:48:07 +0200 Subject: [PATCH 125/135] [stm32] Add support for remapped A11/A12 pins on F0/G0 --- src/modm/platform/gpio/stm32/module.lb | 38 ++++++++++++++++++++++++- src/modm/platform/gpio/stm32/pin.hpp.in | 11 +++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/modm/platform/gpio/stm32/module.lb b/src/modm/platform/gpio/stm32/module.lb index 4f37814201..511d56d230 100644 --- a/src/modm/platform/gpio/stm32/module.lb +++ b/src/modm/platform/gpio/stm32/module.lb @@ -3,6 +3,7 @@ # # Copyright (c) 2016-2018, Niklas Hauser # Copyright (c) 2017, Fabian Greif +# Copyright (c) 2020, Christopher Durand # # This file is part of the modm project. # @@ -123,6 +124,22 @@ def validate_alternate_functions(driver, env): if not success: env.log.debug("Gpio signal validation failed!") +def get_remap_command(family, key): + reg = 'SYSCFG->CFGR1' + mask = { + ('f0', 'a9') : 'SYSCFG_CFGR1_PA11_PA12_RMP', + ('f0', 'a10'): 'SYSCFG_CFGR1_PA11_PA12_RMP', + ('f0', 'a11'): 'SYSCFG_CFGR1_PA11_PA12_RMP', + ('f0', 'a12'): 'SYSCFG_CFGR1_PA11_PA12_RMP', + ('g0', 'a9') : 'SYSCFG_CFGR1_PA11_RMP', + ('g0', 'a10'): 'SYSCFG_CFGR1_PA12_RMP', + ('g0', 'a11'): 'SYSCFG_CFGR1_PA11_RMP', + ('g0', 'a12'): 'SYSCFG_CFGR1_PA12_RMP', + }.get((family, key)) + if mask is None: + raise ValidateException("Unknown Remap for GPIO: 'P{}'".format(key.upper())) + return (reg, mask) + bprops = {} # ----------------------------------------------------------------------------- @@ -199,6 +216,16 @@ def validate(env): raise ValidateException("Pin '{}' already in EXTI map!".format(str(num))) extimap[str(num)] = vec + # Compute the set of remapped pins + remapped_gpios = {} + for p in driver["package"][0]["pin"]: + variant = p.get("variant", "") + if "remap" in variant: # also matches "remap-default" + name = p["name"][1:4].strip().lower() + if len(name) > 2 and not name[2].isdigit(): + name = name[:2] + remapped_gpios[name] = (variant == "remap") # "remap-default" -> False + all_signals = {} for gpio in driver["gpio"]: key = gpio["port"] + gpio["pin"] @@ -215,11 +242,20 @@ def validate(env): all_signals.update(extracted_signals) signal_names = sorted(list(set(s["name"] for s in extracted_signals.values()))) extracted_signals = OrderedDict([(name, sorted([s for s in extracted_signals.values() if s["name"] == name], key=lambda si:si["name"])) for name in signal_names]) + has_remap = key in remapped_gpios bprops[key] = { "gpio": gpio, "exti_irqn": extimap[gpio["pin"]], - "signals": extracted_signals + "signals": extracted_signals, + "has_remap": has_remap, } + if has_remap: + reg, mask = get_remap_command(device.identifier.family, key) + bprops[key].update({ + "remap_reg": reg, + "remap_mask": mask, + "remap_value": remapped_gpios[key] + }) all_peripherals = [s["driver"] for s in all_signals.values()] # Signals are not enough, since there are peripherals that don't have signals. diff --git a/src/modm/platform/gpio/stm32/pin.hpp.in b/src/modm/platform/gpio/stm32/pin.hpp.in index b6c58e2ed9..bb9a55d870 100644 --- a/src/modm/platform/gpio/stm32/pin.hpp.in +++ b/src/modm/platform/gpio/stm32/pin.hpp.in @@ -74,6 +74,17 @@ public: inline static void setAnalogInput() { PinSet::setAnalogInput(); } /// @endcond +%% if has_remap + /// Remap this GPIO onto physical pins with selectable GPIO mappings + inline static void remap() { + %% if remap_value + {{remap_reg}} |= {{remap_mask}}; + %% else + {{remap_reg}} &= ~{{remap_mask}}; + %% endif + }; +%% endif + public: // GpioOutput // start documentation inherited From 96a3e720460cdfa38cf75c3705acbe7b292d143c Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Thu, 17 Sep 2020 21:17:44 +0200 Subject: [PATCH 126/135] [freertos] Bump version --- ext/aws/freertos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/aws/freertos b/ext/aws/freertos index cd3c27dfe4..e1e017a220 160000 --- a/ext/aws/freertos +++ b/ext/aws/freertos @@ -1 +1 @@ -Subproject commit cd3c27dfe4db31bae8375c61ab5a64da1a07f6a8 +Subproject commit e1e017a2206f59f76848522a41dae2eec3c1c8ff From 4332c14c0d138d498aba7d5f63bbe7b3d2f52a0a Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Thu, 17 Sep 2020 21:18:18 +0200 Subject: [PATCH 127/135] [freertos] Add compiler support files --- ext/aws/module.lb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/aws/module.lb b/ext/aws/module.lb index a3058aae5f..d6b7207a75 100644 --- a/ext/aws/module.lb +++ b/ext/aws/module.lb @@ -34,7 +34,11 @@ the config macros, then redefine them with your options, for example: def build(self, env): env.outbasepath = "modm/ext" env.copy("freertos/FreeRTOS-Plus-TCP", "freertos_plus_tcp", - ignore=env.ignore_files("BufferAllocation_1.c")) + ignore=env.ignore_files("BufferAllocation_1.c", "portable")) + # Copy the compiler support files + env.copy("freertos/FreeRTOS-Plus-TCP/portable/Compiler/GCC/pack_struct_start.h", "freertos_plus_tcp/include/pack_struct_start.h") + env.copy("freertos/FreeRTOS-Plus-TCP/portable/Compiler/GCC/pack_struct_end.h", "freertos_plus_tcp/include/pack_struct_end.h") + env.collect(":build:path.include", "modm/ext/freertos_plus_tcp/include") env.template("FreeRTOSIPConfig.h.in", "freertos_plus_tcp/include/FreeRTOSIPConfig.h") # ----------------------------------------------------------------------------- From 642461de359de5bf0b831379fbc67e7ae5fb46e2 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Thu, 17 Sep 2020 21:23:48 +0200 Subject: [PATCH 128/135] [ci] Compile a project with FreeRTOS plus TCP --- .../nucleo_f429zi/freertos_plus_tcp/main.cpp | 81 +++++++++++++++++++ .../freertos_plus_tcp/project.xml | 12 +++ 2 files changed, 93 insertions(+) create mode 100644 examples/nucleo_f429zi/freertos_plus_tcp/main.cpp create mode 100644 examples/nucleo_f429zi/freertos_plus_tcp/project.xml diff --git a/examples/nucleo_f429zi/freertos_plus_tcp/main.cpp b/examples/nucleo_f429zi/freertos_plus_tcp/main.cpp new file mode 100644 index 0000000000..140167e102 --- /dev/null +++ b/examples/nucleo_f429zi/freertos_plus_tcp/main.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, Georgi Grinshpun + * Copyright (c) 2014, Sascha Schade + * Copyright (c) 2015-2017, 2019 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 +#include + +using namespace modm::platform; + +/** + * This example uses four threads to check if task switching works correctly. + * + * It also check if the FreeRTOS TCP stack can be compiled. + * No TCP functionality in this example (yet). + * + * What to expect? + * --------------- + * - All our LEDs blinking at different rates, about 3 to 4 Hz + * - A string at 115200 baud + * + * 0aA!1bB"2cC#3dD$4eE%5fF&6gG'7hH(8iI9)jJ0*aA1!bB2"cC + * + * Each thread prints out a sequence + * 0123456789 + * abcdefghij + * ABCDEFGHIJ + * !"#$%&'()* + * respectivly. + */ + +// ---------------------------------------------------------------------------- +template +class P: modm::rtos::Thread +{ + char c; + uint8_t i = 0; + volatile float a = 10.f; +public: + P(char c): Thread(2,1<<10), c(c) {} + + void run() + { + Gpio::setOutput(); + while (true) + { + sleep(SleepTime * MILLISECONDS); + + Gpio::toggle(); + { + static modm::rtos::Mutex lm; + modm::rtos::MutexGuard m(lm); + MODM_LOG_INFO << char(i + c); + } + i = (i+1)%10; + a *= 3.141f; + } + } +}; + +P< Board::LedRed, 260 > p1('0'); +P< Board::LedGreen, 260 + 10 > p2('a'); +P< Board::LedBlue, 260 + 20 > p3('A'); + + +// ---------------------------------------------------------------------------- +int +main() +{ + Board::initialize(); + modm::rtos::Scheduler::schedule(); + return 0; +} diff --git a/examples/nucleo_f429zi/freertos_plus_tcp/project.xml b/examples/nucleo_f429zi/freertos_plus_tcp/project.xml new file mode 100644 index 0000000000..9c29693d07 --- /dev/null +++ b/examples/nucleo_f429zi/freertos_plus_tcp/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-f429zi + + + + + modm:processing:rtos + modm:freertos:tcp + modm:platform:heap + modm:build:scons + + From cb82eecc054b8607e80b337af7a03b4b9a6abb09 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 16 Sep 2020 23:20:51 +0200 Subject: [PATCH 129/135] [freertos] Make malloc/free thread-safe --- ext/aws/modm_port.cpp | 58 +++++++++++++++++++- ext/aws/module.lb | 2 - src/modm/platform/heap/cortex/heap_block.cpp | 11 +++- src/modm/platform/heap/cortex/heap_tlsf.cpp | 33 +++++++---- 4 files changed, 86 insertions(+), 18 deletions(-) diff --git a/ext/aws/modm_port.cpp b/ext/aws/modm_port.cpp index d258179b08..8d1e8a98b2 100644 --- a/ext/aws/modm_port.cpp +++ b/ext/aws/modm_port.cpp @@ -1,12 +1,66 @@ - +/* + * Copyright (c) 2019-2020 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 #include #include +#include extern "C" void vApplicationStackOverflowHook(TaskHandle_t, char *); - void vApplicationStackOverflowHook(TaskHandle_t /*pxTask*/, char *pcTaskName) { modm_assert(false, "freertos.stack", "FreeRTOS detected a stack overflow!", pcTaskName); } + +// ---------------------------------------------------------------------------- +// Make the Newlib heap thread-safe with FreeRTOS + +extern "C" void __malloc_lock(struct _reent *); +void __malloc_lock(struct _reent *) +{ + vTaskSuspendAll(); +} + +extern "C" void __malloc_unlock(struct _reent *); +void __malloc_unlock(struct _reent *) +{ + xTaskResumeAll(); +} + +// ---------------------------------------------------------------------------- +// Make the FreeRTOS heap use Newlib heap + +extern "C" void *pvPortMalloc(size_t xWantedSize); +void *pvPortMalloc(size_t xWantedSize) +{ + void *pvReturn = malloc(xWantedSize); + traceMALLOC(pvReturn, xWantedSize); + +#if configUSE_MALLOC_FAILED_HOOK == 1 + if(pvReturn == nullptr) + { + extern "C" void vApplicationMallocFailedHook(void); + vApplicationMallocFailedHook(); + } +#endif + + return pvReturn; +} + +extern "C" void vPortFree(void *pv); +void vPortFree(void *pv) +{ + if(pv) + { + free(pv); + traceFREE(pv, 0); + } +} diff --git a/ext/aws/module.lb b/ext/aws/module.lb index d6b7207a75..901ef68976 100644 --- a/ext/aws/module.lb +++ b/ext/aws/module.lb @@ -104,8 +104,6 @@ def build(env): env.copy("{}/port.c".format(path), "freertos/port.c") # Copy the portmacro.h file env.copy("{}/portmacro.h".format(path), "freertos/inc/freertos/portmacro.h") - # Copy the head_3.c file that just wraps the libc malloc - env.copy("freertos/FreeRTOS/Source/portable/MemMang/heap_3.c", "freertos/heap_3.c") # Generate the FreeRTOSConfig.h file env.template("FreeRTOSConfig.h.in", "freertos/inc/freertos/FreeRTOSConfig.h") diff --git a/src/modm/platform/heap/cortex/heap_block.cpp b/src/modm/platform/heap/cortex/heap_block.cpp index a76b0eac00..dcb6cef66d 100644 --- a/src/modm/platform/heap/cortex/heap_block.cpp +++ b/src/modm/platform/heap/cortex/heap_block.cpp @@ -52,9 +52,14 @@ void __modm_initialize_memory(void) allocator.initialize((void*)heap_start, (void*)heap_end); } -void* __wrap__malloc_r(struct _reent *, size_t size) +extern void __malloc_lock(struct _reent *); +extern void __malloc_unlock(struct _reent *); + +void* __wrap__malloc_r(struct _reent *r, size_t size) { + __malloc_lock(r); void *ptr = allocator.allocate(size); + __malloc_unlock(r); modm_assert_continue_fail_debug(ptr, "malloc", "No memory left in Block heap!", size); return ptr; @@ -74,9 +79,11 @@ void* __wrap__realloc_r(struct _reent *, void *, size_t size) return NULL; } -void __wrap__free_r(struct _reent *, void *p) +void __wrap__free_r(struct _reent *r, void *p) { + __malloc_lock(r); allocator.free(p); + __malloc_unlock(r); } } // extern "C" diff --git a/src/modm/platform/heap/cortex/heap_tlsf.cpp b/src/modm/platform/heap/cortex/heap_tlsf.cpp index 303fc10130..59298eaf55 100644 --- a/src/modm/platform/heap/cortex/heap_tlsf.cpp +++ b/src/modm/platform/heap/cortex/heap_tlsf.cpp @@ -88,6 +88,9 @@ get_tlsf_for_ptr(void *p) return NULL; } +extern void __malloc_lock(struct _reent *); +extern void __malloc_unlock(struct _reent *); + void * malloc_traits(size_t size, uint32_t traits) { try_again: @@ -97,7 +100,9 @@ void * malloc_traits(size_t size, uint32_t traits) { if ((pool->traits & traits) == traits) { + __malloc_lock(_REENT); void *p = tlsf_malloc(pool->tlsf, size); + __malloc_unlock(_REENT); if (p) return p; } } @@ -124,7 +129,8 @@ void * malloc_traits(size_t size, uint32_t traits) void *__wrap__malloc_r(struct _reent *, size_t size) { // default is accessible by S-Bus and DMA-able - return malloc_traits(size, 0x8 | 0x1); + return malloc_traits(size, uint32_t(modm::MemoryTrait::AccessSBus) | + uint32_t(modm::MemoryTrait::AccessDMA)); } void *__wrap__calloc_r(struct _reent *r, size_t size) @@ -134,26 +140,29 @@ void *__wrap__calloc_r(struct _reent *r, size_t size) return ptr; } -void *__wrap__realloc_r(struct _reent *, void *p, size_t size) +void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) { - tlsf_t pool = get_tlsf_for_ptr(p); - // if pointer belongs to no pool, exit. - if (!pool) return NULL; + void *ptr = NULL; + + __malloc_lock(r); + const tlsf_t pool = get_tlsf_for_ptr(p); + if (pool) ptr = tlsf_realloc(pool, p, size); + __malloc_unlock(r); - void *ptr = tlsf_realloc(pool, p, size); - modm_assert_continue_fail_debug(0, "realloc", + modm_assert_continue_fail_debug(ptr, "realloc", "Unable to realloc in TLSF pool!", size); return ptr; } -void __wrap__free_r(struct _reent *, void *p) +void __wrap__free_r(struct _reent *r, void *p) { // do nothing if NULL pointer if (!p) return; - tlsf_t pool = get_tlsf_for_ptr(p); - // if pointer belongs to no pool, exit. - if (!pool) return; - tlsf_free(pool, p); + __malloc_lock(r); + const tlsf_t pool = get_tlsf_for_ptr(p); + // free if pointer belongs to a pool. + if (pool) tlsf_free(pool, p); + __malloc_unlock(r); } } // extern "C" From b7b88e37f0c0567ce4eab141fef7828b1fab6491 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Sat, 19 Sep 2020 10:40:23 +0200 Subject: [PATCH 130/135] [freertos] Bump version FreeRTOS Kernel V10.4.1 --- ext/aws/freertos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/aws/freertos b/ext/aws/freertos index e1e017a220..a5317393d9 160000 --- a/ext/aws/freertos +++ b/ext/aws/freertos @@ -1 +1 @@ -Subproject commit e1e017a2206f59f76848522a41dae2eec3c1c8ff +Subproject commit a5317393d92e283980f809c9fc7a6510c4a57a6e From 0e1e4fbd25b461b7177e0ea51f40e2639f334492 Mon Sep 17 00:00:00 2001 From: "Sascha Schade (strongly-typed)" Date: Sat, 19 Sep 2020 10:41:37 +0200 Subject: [PATCH 131/135] [freertos] Fix include of BufferAllocation_2.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was accidentally removed in 4332c14c0d138d498aba7d5f63bbe7b3d2f52a0a when excluding files from “portable”. --- ext/aws/module.lb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/aws/module.lb b/ext/aws/module.lb index 901ef68976..028613bb7f 100644 --- a/ext/aws/module.lb +++ b/ext/aws/module.lb @@ -34,7 +34,8 @@ the config macros, then redefine them with your options, for example: def build(self, env): env.outbasepath = "modm/ext" env.copy("freertos/FreeRTOS-Plus-TCP", "freertos_plus_tcp", - ignore=env.ignore_files("BufferAllocation_1.c", "portable")) + ignore=env.ignore_files("portable")) + env.copy("freertos/FreeRTOS-Plus-TCP/portable/BufferManagement/BufferAllocation_2.c", "freertos_plus_tcp/BufferAllocation_2.c") # Copy the compiler support files env.copy("freertos/FreeRTOS-Plus-TCP/portable/Compiler/GCC/pack_struct_start.h", "freertos_plus_tcp/include/pack_struct_start.h") env.copy("freertos/FreeRTOS-Plus-TCP/portable/Compiler/GCC/pack_struct_end.h", "freertos_plus_tcp/include/pack_struct_end.h") From 7ef59a7f7506ab08151b15bb1fa5470f385c5241 Mon Sep 17 00:00:00 2001 From: delphi Date: Sat, 19 Sep 2020 18:39:01 +0200 Subject: [PATCH 132/135] [board] Add STM32_F32VE board support package --- README.md | 3 +- src/modm/board/stm32_f4ve/board.hpp | 276 ++++++++++++++++++++++++++++ src/modm/board/stm32_f4ve/board.xml | 14 ++ src/modm/board/stm32_f4ve/module.lb | 51 +++++ tools/openocd/modm/stm32_f4ve.cfg | 10 + 5 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 src/modm/board/stm32_f4ve/board.hpp create mode 100644 src/modm/board/stm32_f4ve/board.xml create mode 100644 src/modm/board/stm32_f4ve/module.lb create mode 100644 tools/openocd/modm/stm32_f4ve.cfg diff --git a/README.md b/README.md index cc975f1e10..55fefaf49b 100644 --- a/README.md +++ b/README.md @@ -160,8 +160,9 @@ documentation. OLIMEXINO-STM32 RASPBERRYPI SAMD21-MINI -STM32F030F4P6-DEMO +STM32-F4VE +STM32F030F4P6-DEMO diff --git a/src/modm/board/stm32_f4ve/board.hpp b/src/modm/board/stm32_f4ve/board.hpp new file mode 100644 index 0000000000..e6a7b5b9fe --- /dev/null +++ b/src/modm/board/stm32_f4ve/board.hpp @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2013, Kevin Läufer + * Copyright (c) 2015-2018, Niklas Hauser + * Copyright (c) 2017, Sascha Schade + * Copyright (c) 2018, Antal Szabó + * Copyright (c) 2020, Pavel Pletenev + * + * 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include + +using namespace modm::platform; + +/// @ingroup modm_board_stm32_f4ve +namespace Board +{ + using namespace modm::literals; + +/// STM32F407 running at 168MHz generated from the external 8MHz crystal +struct SystemClock { + static constexpr uint32_t HSExternalOscillatorFrequency = 8_MHz; + static constexpr uint32_t LSExternalOscillatorFrequency = 32768_Hz; + static constexpr uint32_t Frequency = 168_MHz; + static constexpr uint32_t Ahb = Frequency; + static constexpr uint32_t Apb1 = Frequency / 4; + static constexpr uint32_t Apb2 = Frequency / 2; + + static constexpr uint32_t Adc = Apb2; + + static constexpr uint32_t Can1 = Apb1; + static constexpr uint32_t Can2 = 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 I2c1 = Apb1; + static constexpr uint32_t I2c2 = Apb1; + static constexpr uint32_t I2c3 = Apb1; + + static constexpr uint32_t Apb1Timer = Apb1 * 2; + static constexpr uint32_t Apb2Timer = Apb2 * 2; + 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::enableExternalCrystal(); // 8MHz + const Rcc::PllFactors pllFactors{ + .pllM = 4, // 8MHz / M=4 -> 2MHz + .pllN = 168, // 2MHz * N=168 -> 336MHz + .pllP = 2 // 336MHz / P=2 -> 168MHz = F_cpu + }; + Rcc::enablePll(Rcc::PllSource::ExternalCrystal, pllFactors); + // set flash latency for 168MHz + Rcc::setFlashLatency(); + // switch system clock to PLL output + Rcc::enableSystemClock(Rcc::SystemClockSource::Pll); + Rcc::setAhbPrescaler(Rcc::AhbPrescaler::Div1); + // APB1 has max. 42MHz + // APB2 has max. 84MHz + Rcc::setApb1Prescaler(Rcc::Apb1Prescaler::Div4); + Rcc::setApb2Prescaler(Rcc::Apb2Prescaler::Div2); + // update frequencies for busy-wait delay functions + Rcc::updateCoreFrequency(); + + return true; + } +}; + +using LoggerDevice = modm::IODeviceWrapper< Usart1 , modm::IOBuffer::BlockIfFull >; + +using Button = GpioInputA0; +using ButtonK0 = GpioInputE4; +using ButtonK1 = GpioInputE5; +using ClockOut = GpioOutputA8; +using SystemClockOut = GpioOutputC9; + +using LedGreen2 = GpioOutputA6; // User LED 1 +using LedGreen3 = GpioOutputA7;// User LED 2 + +using Leds = SoftwareGpioPort< LedGreen2, LedGreen3 >; + +namespace sdcard +{ + +namespace sdio { +using Cmd = GpioOutputD2; +using Clk = GpioOutputC12; +using D0 = GpioOutputC8; +using D1 = GpioOutputC9; +using D2 = GpioOutputC10; +using D3 = GpioOutputC1; // CD +} // namespace sdio + +namespace spi { +using Cs = sdio::D3; +using Sck = sdio::Clk; +using Mosi = sdio::D0; +using Miso = sdio::Cmd; +} // namespace spi + +} // namespace sdcard + + +namespace display { +// this board has a 16-pin bus interface +// to an ILI9341 display +namespace fsmc { +using D15 = GpioD10; +using D14 = GpioD9; +using D13 = GpioD8; +using D12 = GpioE15; +using D11 = GpioE14; +using D10 = GpioE13; +using D9 = GpioE12; +using D8 = GpioE11; +using D7 = GpioE10; +using D6 = GpioE9; +using D5 = GpioE8; +using D4 = GpioE7; +using D3 = GpioD1; +using D2 = GpioD0; +using D1 = GpioD15; +using D0 = GpioD14; +using Noe = GpioD4; +using Nwe = GpioD5; +using A18 = GpioD13; // this is register select +using Ne1 = GpioD7; +} // namespace fsmc + +namespace touch { +using Clk = GpioB13; // Touch +using Cs = GpioB12; // Touch +using Mosi = GpioB15; // Touch +using Miso = GpioB14; // Touch +using Pen = GpioC5; // Touch + +using SpiMaster = SpiMaster2; +} // namespace touch + +using Backlight = GpioB1; // LCD +// LCD reset is connected to MCU's reset +// and to physical reset line +using Reset = GpioUnused; + +} // namespace display + +namespace nrf24l01 { + +using Ce = GpioB6; +using nCs = GpioOutputB7; +using Sck = GpioOutputB3; +using Mosi = GpioOutputB5; +using Miso = GpioB4; +using Irq = GpioB8; + +} // namespace nrf24l01 + + +namespace w25q16 { +// a W25Q16JV 2 MiB flash chip +using Cs = GpioOutputB0; +using Mosi = GpioInputB5; +using Miso = GpioOutputB4; +using Sck = GpioOutputB3; +// 3 - nWP +3.3V rail +// 4 - GND Ground plane +// 7 - nHOLD +3.3V rail +// 8 - VCC +3.3V rail +using SpiMaster = SpiMaster1; + +constexpr uint32_t BlockSize = 256; +constexpr uint32_t MemorySize = 2*1024*1024; // 16 MiBits +using StorageDevice = modm::BdSpiFlash; + +} // namespace w25q16 + +namespace usb +{ +using Dm = GpioA11; // OTG_FS_DM: USB_OTG_FS_DM +using Dp = GpioA12; // OTG_FS_DP: USB_OTG_FS_DP +using Id = GpioA10; // OTG_FS_ID: USB_OTG_FS_ID + +using Overcurrent = GpioD5; // OTG_FS_OverCurrent +using Power = GpioOutputC0; // OTG_FS_PowerSwitchOn +using VBus = GpioInputA9; // VBUS_FS: USB_OTG_HS_VBUS +//using Device = UsbFs; +} + +inline void +initialize() +{ + SystemClock::enable(); + SysTickTimer::initialize(); + + LedGreen2::setOutput(modm::Gpio::Low); + LedGreen3::setOutput(modm::Gpio::Low); + + Button::setInput(); + Button::setInputTrigger(Gpio::InputTrigger::RisingEdge); + Button::enableExternalInterrupt(); + + ButtonK0::setInput(); + ButtonK0::setInputTrigger(Gpio::InputTrigger::RisingEdge); + // ButtonK0::enableExternalInterrupt(); + + ButtonK1::setInput(); + ButtonK1::setInputTrigger(Gpio::InputTrigger::RisingEdge); + // ButtonK1::enableExternalInterrupt(); + Usart1::connect(); + Usart1::initialize(); +} + +/// not supported yet, due to missing USB driver +inline void +initializeUsb() +{ +// usb::Dm::connect(usb::Device::Dm); +// usb::Dp::connect(usb::Device::Dp); +// usb::Id::connect(usb::Device::Id); + + usb::Power::setOutput(Gpio::OutputType::PushPull, Gpio::OutputSpeed::MHz2); + + usb::Overcurrent::setInput(Gpio::InputType::Floating); + usb::VBus::setInput(Gpio::InputType::Floating); +} + +inline void +initializeW25q16() +{ + w25q16::Cs::setOutput(modm::Gpio::High); + + w25q16::SpiMaster::connect(); + w25q16::SpiMaster::initialize(); +} + + +} + diff --git a/src/modm/board/stm32_f4ve/board.xml b/src/modm/board/stm32_f4ve/board.xml new file mode 100644 index 0000000000..fdc2cd233d --- /dev/null +++ b/src/modm/board/stm32_f4ve/board.xml @@ -0,0 +1,14 @@ + + + + ../../../../repo.lb + + + + + + + + modm:board:stm32_f4ve + + diff --git a/src/modm/board/stm32_f4ve/module.lb b/src/modm/board/stm32_f4ve/module.lb new file mode 100644 index 0000000000..896ef58579 --- /dev/null +++ b/src/modm/board/stm32_f4ve/module.lb @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, Pavel Pletenev +# +# 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:stm32_f4ve" + module.description = """\ +# STM32_F4VE + +[Chinese dev board](https://stm32-base.org/boards/STM32F407VET6-STM32-F4VE-V2.0.html) +""" + +def prepare(module, options): + if not options[":target"].partname.startswith("stm32f407vet6"): + return False + + module.depends( + ":architecture:clock", + ":debug", + ":platform:clock", + ":platform:core", + ":platform:gpio", + ":platform:uart:1", + ":platform:fsmc", + ":platform:spi:1", + ":platform:spi:2", + ":driver:block.device:spi.flash", + ":driver:ads7843") + 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.outbasepath = "modm/openocd/modm/board/" + env.copy(repopath("tools/openocd/modm/stm32f4x.cfg"), "stm32f4x.cfg") + env.copy(repopath("tools/openocd/modm/stm32_f4ve.cfg"), "stm32_f4ve.cfg") + env.collect(":build:openocd.source", "modm/board/stm32_f4ve.cfg") diff --git a/tools/openocd/modm/stm32_f4ve.cfg b/tools/openocd/modm/stm32_f4ve.cfg new file mode 100644 index 0000000000..6f7647098d --- /dev/null +++ b/tools/openocd/modm/stm32_f4ve.cfg @@ -0,0 +1,10 @@ +# Cheap STM32F103C8T6 "Blue Pill" Minimum System Development Board + +# Must be specified by the user via `modm:build:openocd.cfg` option +source [find interface/stlink-v2.cfg] + +transport select hla_swd + +source [find target/stm32f4x.cfg] + +reset_config none From a5b1d9e4db4eb5b035e2d5d9ee069b1daf347d31 Mon Sep 17 00:00:00 2001 From: delphi Date: Sat, 19 Sep 2020 18:40:22 +0200 Subject: [PATCH 133/135] [examples] Add W25Q16 external Flash example --- .circleci/config.yml | 2 +- examples/stm32_f4ve/flash/main.cpp | 130 ++++++++++++++++++++++++++ examples/stm32_f4ve/flash/project.xml | 12 +++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 examples/stm32_f4ve/flash/main.cpp create mode 100644 examples/stm32_f4ve/flash/project.xml diff --git a/.circleci/config.yml b/.circleci/config.yml index 93cd8c4c33..3547b617fe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -128,7 +128,7 @@ jobs: name: Examples STM32F4 Series command: | (cd examples && ../tools/scripts/examples_compile.py stm32f4_discovery) - (cd examples && ../tools/scripts/examples_compile.py stm32f429_discovery stm32f469_discovery nucleo_f401re nucleo_f411re nucleo_f429zi nucleo_f446re stm32f411ccu_mini_f401 stm32f411ceu_mini_f411 stm32f407vet6_devebox) + (cd examples && ../tools/scripts/examples_compile.py stm32f429_discovery stm32f469_discovery nucleo_f401re nucleo_f411re nucleo_f429zi nucleo_f446re stm32f411ccu_mini_f401 stm32f411ceu_mini_f411 stm32f407vet6_devebox stm32_f4ve) avr-examples: docker: diff --git a/examples/stm32_f4ve/flash/main.cpp b/examples/stm32_f4ve/flash/main.cpp new file mode 100644 index 0000000000..aecbc806b8 --- /dev/null +++ b/examples/stm32_f4ve/flash/main.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018, Raphael Lehmann + * Copyright (c) 2020, Sascha Schade + * Copyright (c) 2020, Pavel Pletenev + * + * 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 + +using namespace Board; + + +// Set the log level +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::DEBUG + +Board::w25q16::StorageDevice storageDevice; + +void printMemoryContent(const uint8_t* address, std::size_t size) { + for (std::size_t ii = 0; ii < size; ii++) { + MODM_LOG_INFO.printf("%x", address[ii]); + } +} + +uint8_t bufferA[Board::w25q16::BlockSize]; +uint8_t bufferB[Board::w25q16::BlockSize]; +uint8_t bufferC[Board::w25q16::BlockSize]; + +constexpr uint32_t TestMemorySize = 8*1024; + +void doMemoryTest() +{ + LedGreen2::set(); + MODM_LOG_INFO << "Starting memory test!" << modm::endl; + + for (uint16_t iteration = 0; iteration < 4; iteration++) { + uint8_t* pattern = (iteration % 2 == 0) ? bufferA : bufferB; + + if (not RF_CALL_BLOCKING(storageDevice.erase(0, TestMemorySize))) { + MODM_LOG_INFO << "Error: Unable to erase device."; + return; + } + + for (uint32_t ii = 0; ii < TestMemorySize; ii += Board::w25q16::BlockSize) { + if (not RF_CALL_BLOCKING(storageDevice.program(pattern, ii, Board::w25q16::BlockSize))) { + MODM_LOG_INFO << "Error: Unable to write data."; + return; + } + MODM_LOG_INFO << "."; + } + + for (uint32_t ii = 0; ii < TestMemorySize; ii += Board::w25q16::BlockSize) { + if (not RF_CALL_BLOCKING(storageDevice.read(bufferC, ii, Board::w25q16::BlockSize))) { + MODM_LOG_INFO << "Error: Unable to read data."; + return; + } + else if (std::memcmp(pattern, bufferC, Board::w25q16::BlockSize)) { + MODM_LOG_INFO << "ii=" << ii << modm::endl; + MODM_LOG_INFO << "Error: Read '"; + printMemoryContent(bufferC, Board::w25q16::BlockSize); + MODM_LOG_INFO << "', expected: '"; + printMemoryContent(pattern, Board::w25q16::BlockSize); + MODM_LOG_INFO << "'." << modm::endl; + return; + } + } + MODM_LOG_INFO << "." << modm::endl; + } + + MODM_LOG_INFO << modm::endl << "Finished!" << modm::endl; + LedGreen2::reset(); +} + +int +main() +{ + Board::initialize(); + + Board::initializeW25q16(); + + Usart1::connect(); + Usart1::initialize(); + + // 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; + + bool initializeSuccess = false; + + MODM_LOG_INFO << "Erasing complete flash chip... (This may take a while)" << modm::endl; + + if (not RF_CALL_BLOCKING(storageDevice.initialize())) { + MODM_LOG_INFO << "Error: Unable to initialize device."; + } + else if (not RF_CALL_BLOCKING(storageDevice.erase(0, Board::w25q16::MemorySize))) { + MODM_LOG_INFO << "Error: Unable to erase device."; + } + else { + auto id = RF_CALL_BLOCKING(storageDevice.readId()); + MODM_LOG_INFO << "deviceId=" << id.deviceId << " manufacturerId=" << id.manufacturerId; + MODM_LOG_INFO << " deviceType=" << id.deviceType << modm::endl; + + MODM_LOG_INFO << "status=" << static_cast(RF_CALL_BLOCKING(storageDevice.readStatus())) << modm::endl; + + MODM_LOG_INFO << "Press USER button to start the memory test." << modm::endl; + initializeSuccess = true; + } + + while (true) + { + if (initializeSuccess and not ButtonK1::read()) + { + doMemoryTest(); + } + } + + return 0; +} diff --git a/examples/stm32_f4ve/flash/project.xml b/examples/stm32_f4ve/flash/project.xml new file mode 100644 index 0000000000..a87ffbc37c --- /dev/null +++ b/examples/stm32_f4ve/flash/project.xml @@ -0,0 +1,12 @@ + + modm:stm32_f4ve + + + + + modm:platform:gpio + modm:platform:uart:2 + modm:build:compilation_db + modm:build:scons + + From 9263e8d197db6ac3bc97ad85f8999458dfa88320 Mon Sep 17 00:00:00 2001 From: Mike Wolfram Date: Sat, 19 Sep 2020 18:46:43 +0200 Subject: [PATCH 134/135] [display] Add ILI9341 Parallel/SPI display driver Co-authored-by: delphi --- README.md | 13 +- src/modm/driver/display/ili9341.hpp | 292 +++++++++++++ src/modm/driver/display/ili9341.lb | 30 ++ src/modm/driver/display/ili9341_impl.hpp | 430 +++++++++++++++++++ src/modm/driver/display/ili9341_parallel.hpp | 85 ++++ src/modm/driver/display/ili9341_spi.hpp | 110 +++++ 6 files changed, 954 insertions(+), 6 deletions(-) create mode 100644 src/modm/driver/display/ili9341.hpp create mode 100644 src/modm/driver/display/ili9341.lb create mode 100644 src/modm/driver/display/ili9341_impl.hpp create mode 100644 src/modm/driver/display/ili9341_parallel.hpp create mode 100644 src/modm/driver/display/ili9341_spi.hpp diff --git a/README.md b/README.md index 55fefaf49b..271ef218fe 100644 --- a/README.md +++ b/README.md @@ -210,46 +210,47 @@ can easily configure them for you specific needs. HX711 I2C-EEPROM +ILI9341 IS31FL3733 ITG3200 L3GD20 -LAWICEL +LAWICEL LIS302DL LIS3DSH LIS3MDL LM75 LP503X -LSM303A +LSM303A LSM6DS33 LTC2984 MAX6966 MAX7219 MCP23X17 -MCP2515 +MCP2515 NOKIA5110 NRF24 TFT-DISPLAY PAT9125EL PCA8574 -PCA9535 +PCA9535 PCA9548A PCA9685 SIEMENS-S65 SIEMENS-S75 SK6812 -SK9822 +SK9822 SSD1306 SX1276 TCS3414 TCS3472 TLC594X -TMP102 +TMP102 TMP175 VL53L0 VL6180 diff --git a/src/modm/driver/display/ili9341.hpp b/src/modm/driver/display/ili9341.hpp new file mode 100644 index 0000000000..0e2aea956b --- /dev/null +++ b/src/modm/driver/display/ili9341.hpp @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2019, 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_ILI9341_HPP +#define MODM_ILI9341_HPP + +#include +#include +#include +#include +#include + +namespace modm +{ + +/// @ingroup modm_driver_ili9341 +struct ili9341 +{ +protected: + /// @cond + enum class + Command : uint8_t + { + // common commands + Nop = 0x00, + SwReset = 0x01, + ReadID = 0x04, + ReadStatus = 0x09, + ReadPowerMode = 0x0a, + ReadMemoryAccessCtrl = 0x0b, + ReadPixelFormat = 0x0c, + ReadImageFormat = 0x0d, + ReadSignalMode = 0x0e, + ReadSelfDiagnostic = 0x0f, + EnterSleep = 0x10, + LeaveSleep = 0x11, + PartialMode = 0x12, + NormalMode = 0x13, + InversionOff = 0x20, + InversionOn = 0x21, + GammaSet = 0x26, + DisplayOff = 0x28, + DisplayOn = 0x29, + ColumnAddressSet = 0x2a, + PageAddressSet = 0x2b, + MemoryWrite = 0x2c, + ColorSet = 0x2d, + MemoryRead = 0x2e, + PartialArea = 0x30, + VerticalScrollDefinition = 0x33, + TearingEffectOff = 0x34, + TearingEffectOn = 0x35, + MemoryAccessCtrl = 0x36, + VerticalScrollStartAddr = 0x37, + IdleModeOff = 0x38, + IdleModeOn = 0x39, + PixelFormatSet = 0x3a, + WriteMemoryContinue = 0x3c, + ReadMemoryContinue = 0x3e, + SetTearScanLine = 0x44, + GetScanLine = 0x45, + WriteBrightness = 0x51, + ReadBrightness = 0x52, + WriteCtrlDisplay = 0x53, + ReadCtrlDisplay = 0x54, + WriteContentAdaptiveBrightnessCtrl = 0x55, + ReadContentAdaptiveBrightnessCtrl = 0x56, + WriteCabcMinimumBrightness = 0x5e, + ReadCabcMinimumBrightness = 0x5f, + ReadId1 = 0xda, + ReadId2 = 0xdb, + ReadId3 = 0xdc, + // extended commands + RgbInterfaceSignalCtrl = 0xb0, + FrameCtrlNormalMode = 0xb1, + FrameCtrlIdleMode = 0xb2, + FrameCtrlPartialMode = 0xb3, + InversionCtrl = 0xb4, + BlankingPorchCtrl = 0xb5, + DisplayFunctionCtrl = 0xb6, + EntryModeSet = 0xb7, + BacklightCtrl1 = 0xb8, + BacklightCtrl2 = 0xb9, + BacklightCtrl3 = 0xba, + BacklightCtrl4 = 0xbb, + BacklightCtrl5 = 0xbc, + BacklightCtrl7 = 0xbe, + BacklightCtrl8 = 0xbf, + PowerCtrl1 = 0xc0, + PowerCtrl2 = 0xc1, + VComCtrl1 = 0xc5, + VComCtrl2 = 0xc7, + PowerCtrlA = 0xcb, + PowerCtrlB = 0xcf, + NvMemoryWrite = 0xd0, + NvMemoryProtectionKey = 0xd1, + NvMemoryStatus = 0xd2, + ReadId4 = 0xd3, + PositiveGammaCorrection = 0xe0, + NegativeGammaCorrection = 0xe1, + DigitalGammaCtrl = 0xe2, + TimingCtrlA = 0xe8, + TimingCtrlB = 0xea, + PowerOnSequenceCtrl = 0xed, + Enable3Gamma = 0xf2, + InterfaceCtrl = 0xf6, + PumpRatioCtrl = 0xf7, + }; + static constexpr uint8_t i(Command command) { return uint8_t(command); } + + enum class + MemoryAccessCtrl : uint8_t + { + MY = modm::Bit7, + MX = modm::Bit6, + MV = modm::Bit5, + ML = modm::Bit4, + BGR = modm::Bit3, + MH = modm::Bit2 + }; + MODM_FLAGS8(MemoryAccessCtrl); + /// @endcond + +public: + enum class + Rotation : uint8_t + { + Rotate0, + Rotate90, + Rotate180, + Rotate270, + Normal = Rotate0, + UpsideDown = Rotate180, + }; + + enum class + DisplayMode : uint8_t + { + Normal = uint8_t(Command::InversionOff), + Inverted = uint8_t(Command::InversionOn) + }; +}; + +/// @ingroup modm_driver_ili9341 +template +class Ili9341 : public Interface, public modm::GraphicDisplay +{ + static_assert(BufferSize >= 16, "at least a small buffer is required"); + + static constexpr uint16_t Width = 240; + static constexpr uint16_t Height = 320; + using BatchHandle = typename Interface::BatchHandle; + using Command = ili9341::Command; +public: + using Rotation = ili9341::Rotation; + using DisplayMode = ili9341::DisplayMode; + + template, std::enable_if_t = 0> + Ili9341(): Interface() + { + Reset::setOutput(modm::Gpio::High); + Backlight::setOutput(modm::Gpio::Low); + } + + template, T>> + Ili9341(T& r): Interface(r) + { + Reset::setOutput(modm::Gpio::High); + Backlight::setOutput(modm::Gpio::Low); + } + + void + initialize(); + + void + reset(bool hardReset = false); + + uint16_t + getIcModel(); + + void + turnOn(); + void + turnOff(); + + void + enableBacklight(bool enable) + { Backlight::set(enable); } + void + setBrightness(uint8_t level); + void + setInvert(bool invert); + + void + setIdle(bool enable); + void + enableSleep(bool enable); + + virtual uint16_t + getWidth() const override + { + switch (rotation_) + { + case Rotation::Rotate90: + case Rotation::Rotate270: + return Height; + default: + return Width; + } + } + virtual uint16_t + getHeight() const override + { + switch (rotation_) + { + case Rotation::Rotate90: + case Rotation::Rotate270: + return Width; + default: + return Height; + } + } + + virtual void + clear() override; + virtual void + update() override + { /* nothing to do, data is directly written to TFT RAM */ } + + void + setRotation(Rotation rotation); + + void + fillRectangle(glcd::Point upperLeft, uint16_t width, uint16_t height); + inline void + fillRectangle(int16_t x, int16_t y, uint16_t width, uint16_t height) + { fillRectangle(glcd::Point(x, y), width, height); } + + void + fillCircle(glcd::Point center, uint16_t radius); + + virtual void + drawImageRaw(glcd::Point upperLeft, + uint16_t width, uint16_t height, + modm::accessor::Flash data) override; + + void + setScrollArea(uint16_t topFixedRows, uint16_t bottomFixedRows, uint16_t firstRow); + void + scrollTo(uint16_t row); + + void + drawBitmap(glcd::Point upperLeft, uint16_t width, uint16_t height, modm::accessor::Flash data); + +protected: + virtual void + setPixel(int16_t x, int16_t y) override + { setColoredPixel(x, y, foregroundColor); } + virtual void + clearPixel(int16_t x, int16_t y) override + { setColoredPixel(x, y, backgroundColor); } + virtual bool + getPixel(int16_t /*x*/, int16_t /*y*/) override + { return false; } + + virtual void + drawHorizontalLine(glcd::Point start, uint16_t length) override; + virtual void + drawVerticalLine(glcd::Point start, uint16_t length) override; + +private: + void + setColoredPixel(int16_t x, int16_t y, glcd::Color const &color); + void + setClipping(uint16_t x, uint16_t y, uint16_t width, uint16_t height); + + Rotation rotation_{Rotation::Rotate0}; + uint8_t buffer[BufferSize * 2]{0}; +}; + +} // namespace modm + +#include "ili9341_impl.hpp" + +#endif // MODM_ILI9341_HPP diff --git a/src/modm/driver/display/ili9341.lb b/src/modm/driver/display/ili9341.lb new file mode 100644 index 0000000000..63333894e3 --- /dev/null +++ b/src/modm/driver/display/ili9341.lb @@ -0,0 +1,30 @@ +#!/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:ili9341" + module.description = "ILI9341 Display with parallel and SPI bus transports" + +def prepare(module, options): + module.depends( + ":architecture:delay", + ":architecture:spi.device", + ":ui:display") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/display" + env.copy("ili9341.hpp") + env.copy("ili9341_impl.hpp") + env.copy("ili9341_spi.hpp") + env.copy("ili9341_parallel.hpp") diff --git a/src/modm/driver/display/ili9341_impl.hpp b/src/modm/driver/display/ili9341_impl.hpp new file mode 100644 index 0000000000..ec576e0970 --- /dev/null +++ b/src/modm/driver/display/ili9341_impl.hpp @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2019, 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_ILI9341_HPP +# error "Don't include this file directly, use 'ili9341.hpp' instead!" +#endif + +namespace modm +{ + +template +void +Ili9341::initialize() +{ + constexpr uint8_t pwrCtrlA[] { 0x39, 0x2c, 0x00, 0x34, 0x02 }; + constexpr uint8_t pwrCtrlB[] { 0x00, 0xc1, 0x30 }; + constexpr uint8_t timCtrlA[] { 0x85, 0x10, 0x7a }; + constexpr uint8_t timCtrlB[] { 0x00, 0x00 }; + constexpr uint8_t pwrOnSeqCtrl[] { 0x64, 0x03, 0x12, 0x81 }; + constexpr uint8_t pwrCtrl1[] { 0x1b }; + constexpr uint8_t pwrCtrl2[] { 0x12 }; + constexpr uint8_t vcomCtrl1[] { 0x08, 0x26 };//0x3e, 0x28 }; + constexpr uint8_t vcomCtrl2[] { 0xb7 }; + // constexpr uint8_t pumpRatioCtrl[] { 0x20 }; + constexpr uint8_t pixelFormat[] { 0x55 }; + constexpr uint8_t frameCtrl[] { 0x00, 0x1a }; + constexpr uint8_t dispFuncCtrl[] { 0x0a, 0xa2, 0x27, 00 };//{ 0x08, 0x82, 0x27, 0x00 }; + constexpr uint8_t enable3G[] { 0x00 }; + constexpr uint8_t gammaSet[] { 0x01 }; +// constexpr uint8_t positiveGammaCorr[] { 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, +// 0xe4, 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00 }; +// constexpr uint8_t negativeGammaCorr[] { 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, +// 0x31, 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f }; + constexpr uint8_t positiveGammaCorr[] { 0x0f, 0x1d, 0x1a, 0x0a, 0x0d, 0x07, + 0x49, 0x66, 0x3b, 0x07, 0x11, 0x01, 0x09, 0x05, 0x04 }; + constexpr uint8_t negativeGammaCorr[] { 0x00, 0x18, 0x1d, 0x02, 0x0f, 0x04, + 0x36, 0x13, 0x4c, 0x07, 0x13, 0x0f, 0x2e, 0x2f, 0x05 }; + + reset(); + + { + BatchHandle h(*this); + + this->writeCommand(Command::DisplayOff); + + this->writeCommand(Command::PowerCtrlA, pwrCtrlA, sizeof(pwrCtrlA)); + this->writeCommand(Command::PowerCtrlB, pwrCtrlB, sizeof(pwrCtrlB)); + this->writeCommand(Command::TimingCtrlA, timCtrlA, sizeof(timCtrlA)); + this->writeCommand(Command::TimingCtrlB, timCtrlB, sizeof(timCtrlB)); + this->writeCommand(Command::PowerOnSequenceCtrl, pwrOnSeqCtrl, sizeof(pwrOnSeqCtrl)); + this->writeCommand(Command::PowerCtrl1, pwrCtrl1, sizeof(pwrCtrl1)); + this->writeCommand(Command::PowerCtrl2, pwrCtrl2, sizeof(pwrCtrl2)); + this->writeCommand(Command::VComCtrl1, vcomCtrl1, sizeof(vcomCtrl1)); + this->writeCommand(Command::VComCtrl2, vcomCtrl2, sizeof(vcomCtrl2)); + this->writeCommand(Command::PixelFormatSet, pixelFormat, sizeof(pixelFormat)); + this->writeCommand(Command::FrameCtrlNormalMode, frameCtrl, sizeof(frameCtrl)); + this->writeCommand(Command::DisplayFunctionCtrl, dispFuncCtrl, sizeof(dispFuncCtrl)); + this->writeCommand(Command::Enable3Gamma, enable3G, sizeof(enable3G)); + this->writeCommand(Command::GammaSet, gammaSet, sizeof(gammaSet)); + this->writeCommand(Command::PositiveGammaCorrection, positiveGammaCorr, + sizeof(positiveGammaCorr)); + this->writeCommand(Command::NegativeGammaCorrection, negativeGammaCorr, + sizeof(negativeGammaCorr)); + + this->writeCommand(Command::LeaveSleep); + modm::delay_ms(120); + this->writeCommand(Command::InversionOff); + this->writeCommand(Command::DisplayOn); + + setRotation(rotation_); + } +} + +template +void +Ili9341::reset(bool hardReset /* = false */) +{ + if (hardReset) + { + Reset::set(); + modm::delay_ms(5); + Reset::reset(); + modm::delay_ms(5); + Reset::set(); + modm::delay_ms(5); + } + else { + BatchHandle h(*this); + this->writeCommand(Command::SwReset); + modm::delay_ms(5); + } +} + +template +uint16_t +Ili9341::getIcModel() +{ + BatchHandle h(*this); + + uint8_t buffer[4] { 0 }; + this->readData(Command::ReadId4, buffer, 4); + return (buffer[2] << 8) | buffer[3]; +} + +template +void +Ili9341::setRotation(Rotation rotation) +{ + using MemoryAccessCtrl_t = ili9341::MemoryAccessCtrl_t; + using MemoryAccessCtrl = ili9341::MemoryAccessCtrl; + MemoryAccessCtrl_t madCtrl { MemoryAccessCtrl::BGR }; + + switch (rotation) + { + case Rotation::Rotate90: + madCtrl |= MemoryAccessCtrl::MV | MemoryAccessCtrl::MX; + break; + case Rotation::Rotate180: + madCtrl |= MemoryAccessCtrl::MX | MemoryAccessCtrl::MY; + break; + case Rotation::Rotate270: + madCtrl |= MemoryAccessCtrl::MV | MemoryAccessCtrl::MY; + break; + default: +// madCtrl |= MemoryAccessCtrl::ML; + break; + } + + rotation_ = rotation; + + BatchHandle h(*this); + this->writeCommandValue8(Command::MemoryAccessCtrl, madCtrl.value); +} + +template +void +Ili9341::turnOn() +{ + BatchHandle h(*this); + this->writeCommand(Command::DisplayOn); +} + +template +void +Ili9341::turnOff() +{ + BatchHandle h(*this); + this->writeCommand(Command::DisplayOff); +} + +template +void +Ili9341::setIdle(bool enable) +{ + BatchHandle h(*this); + this->writeCommand(enable ? Command::IdleModeOn : Command::IdleModeOff); +} + +template +void +Ili9341::enableSleep(bool enable) +{ + BatchHandle h(*this); + this->writeCommand(enable ? Command::EnterSleep : Command::LeaveSleep); +} + +template +void +Ili9341::setBrightness(uint8_t level) +{ + BatchHandle h(*this); + this->writeCommand(Command::WriteBrightness, &level, 1); +} + +template +void +Ili9341::setInvert(bool invert) +{ + BatchHandle h(*this); + this->writeCommand(invert ? Command::InversionOn : Command::InversionOff); +} + +template +void +Ili9341::clear() +{ + auto const saveForegroundColor { foregroundColor }; + foregroundColor = backgroundColor; + fillRectangle(glcd::Point(0, 0), Height, Width); + foregroundColor = saveForegroundColor; +} + +template +void +Ili9341::drawHorizontalLine( + glcd::Point start, uint16_t length) +{ + auto const fgColor { foregroundColor.getValue() }; + auto minLength { std::min(std::size_t(length), BufferSize) }; + uint16_t *buffer16 { reinterpret_cast(buffer) }; + for (std::size_t i = 0; i < minLength; ++i) + buffer16[i] = modm::toBigEndian(fgColor); + + BatchHandle h(*this); + + setClipping(start.getX(), start.getY(), length, 1); + while (length > BufferSize) + { + this->writeData(buffer, BufferSize * 2); + length -= BufferSize; + } + this->writeData(buffer, length * 2); +} + +template +void +Ili9341::drawVerticalLine( + glcd::Point start, uint16_t length) +{ + auto const fgColor { foregroundColor.getValue() }; + auto minLength { std::min(std::size_t(length), BufferSize) }; + uint16_t *buffer16 { reinterpret_cast(buffer) }; + for (std::size_t i = 0; i < minLength; ++i) + buffer16[i] = modm::toBigEndian(fgColor); + + BatchHandle h(*this); + + setClipping(start.getX(), start.getY(), 1, length); + while (length > BufferSize) + { + this->writeData(buffer, BufferSize * 2); + length -= BufferSize; + } + this->writeData(buffer, length * 2); +} + +template +void +Ili9341::fillRectangle( + glcd::Point upperLeft, uint16_t width, uint16_t height) +{ + auto const x { upperLeft.getX() }; + auto const y { upperLeft.getY() }; + std::size_t pixelCount { std::size_t(width) * std::size_t(height) }; + + auto const fgColor { foregroundColor.getValue() }; + auto minLength { std::min(std::size_t(pixelCount), BufferSize) }; + uint16_t *buffer16 { reinterpret_cast(buffer) }; + for (std::size_t i = 0; i < minLength; ++i) + buffer16[i] = modm::toBigEndian(fgColor); + + BatchHandle h(*this); + + setClipping(x, y, width, height); + while (pixelCount > BufferSize) + { + this->writeData(buffer, BufferSize * 2); + pixelCount -= BufferSize; + } + if (pixelCount) + this->writeData(buffer, pixelCount * 2); +} + +template +void +Ili9341::fillCircle( + glcd::Point center, uint16_t radius) +{ + auto const fgColor { foregroundColor.getValue() }; + uint8_t const setColor[] { uint8_t((fgColor >> 8) & 0xff), + uint8_t(fgColor & 0xff) }; + + int16_t f = 1 - radius; + int16_t ddF_x = 0; + int16_t ddF_y = -2 * radius; + uint16_t x = 0; + uint16_t y = radius; + + BatchHandle h(*this); + + setClipping(center.getX() - radius, center.getY(), 2 * radius, 1); + for (std::size_t i = 0; i < 2 * radius; ++i) + this->writeData(setColor, 2); + + while(x < y) + { + if (f >= 0) + { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x + 1; + + setClipping(center.getX() - x, center.getY() - y, 2 * x, 1); + for (std::size_t i = 0; i < 2 * x; ++i) + this->writeData(setColor, 2); + setClipping(center.getX() - y, center.getY() - x, 2 * y, 1); + for (std::size_t i = 0; i < 2 * y; ++i) + this->writeData(setColor, 2); + setClipping(center.getX() - x, center.getY() + y, 2 * x, 1); + for (std::size_t i = 0; i < 2 * x; ++i) + this->writeData(setColor, 2); + setClipping(center.getX() - y, center.getY() + x, 2 * y, 1); + for (std::size_t i = 0; i < 2 * y; ++i) + this->writeData(setColor, 2); + } +} + +template +void +Ili9341::drawImageRaw(glcd::Point upperLeft, + uint16_t width, uint16_t height, modm::accessor::Flash data) +{ + auto const fgColor { foregroundColor.getValue() }; + auto const bgColor { backgroundColor.getValue() }; + uint8_t const setColor[] { uint8_t((fgColor >> 8) & 0xff), + uint8_t(fgColor & 0xff) }; + uint8_t const clearColor[] { uint8_t((bgColor >> 8) & 0xff), + uint8_t(bgColor & 0xff) }; + + BatchHandle h(*this); + + setClipping(upperLeft.getX(), upperLeft.getY(), width, height); + + uint8_t bit = 0x01; + for (uint16_t r = 0; r < height; ++r) + { + for (uint16_t w = 0; w < width; ++w) + { + uint8_t byte = data[(r / 8) * width + w]; + if (byte & bit) + this->writeData(setColor, 2); + else + this->writeData(clearColor, 2); + } + // TODO: optimize, use ROL (rotate left) + bit <<= 1; + if (bit == 0) + bit = 0x01; + } +} + +template +void +Ili9341::setScrollArea( + uint16_t topFixedRows, uint16_t bottomFixedRows, uint16_t firstRow) +{ + BatchHandle h(*this); + + uint8_t arg[] + { + uint8_t((topFixedRows >> 8) & 0xff), uint8_t(topFixedRows & 0xff), + uint8_t((firstRow >> 8) & 0xff), uint8_t(firstRow & 0xff), + uint8_t((bottomFixedRows >> 8) & 0xff), uint8_t(bottomFixedRows & 0xff) + }; + this->writeCommand(Command::VerticalScrollDefinition, arg, 6); +} + +template +void +Ili9341::scrollTo(uint16_t row) +{ + BatchHandle h(*this); + + uint8_t arg[] { uint8_t((row >> 8) & 0xff), uint8_t(row & 0xff) }; + this->writeCommand(Command::VerticalScrollStartAddr, arg, 2); +} + +template +void +Ili9341::setColoredPixel( + int16_t x, int16_t y, glcd::Color const &color) +{ + auto const pixelColor { color.getValue() }; + uint8_t const setColor[] { uint8_t((pixelColor >> 8) & 0xff), uint8_t(pixelColor & 0xff) }; + + BatchHandle h(*this); + + this->setClipping(x, y, 1, 1); + this->writeData(setColor, 2); +} + +template +void +Ili9341::setClipping( + uint16_t x, uint16_t y, uint16_t width, uint16_t height) +{ + uint8_t buffer[4]; + + buffer[0] = uint8_t((x >> 8) & 0xff); + buffer[1] = uint8_t(x & 0xff); + buffer[2] = uint8_t(((x + width - 1) >> 8) & 0xff); + buffer[3] = uint8_t((x + width - 1) & 0xff); + this->writeCommand(Command::ColumnAddressSet, buffer, 4); + + buffer[0] = uint8_t((y >> 8) & 0xff); + buffer[1] = uint8_t(y & 0xff); + buffer[2] = uint8_t(((y + height - 1) >> 8) & 0xff); + buffer[3] = uint8_t((y + height - 1) & 0xff); + this->writeCommand(Command::PageAddressSet, buffer, 4); + this->writeCommand(Command::MemoryWrite); +} + +template +void +Ili9341::drawBitmap(glcd::Point upperLeft, + uint16_t width, uint16_t height, modm::accessor::Flash data) +{ + BatchHandle h(*this); + + setClipping(upperLeft.getX(), upperLeft.getY(), width, height); + for (int i = 0; i < width * height; ++i) { + buffer[0] = data[i*2+1]; + buffer[1] = data[i*2]; + this->writeData(buffer, 2); + } +// this->writeData(data.getPointer(), width * height * 2); +} + +} // namespace modm diff --git a/src/modm/driver/display/ili9341_parallel.hpp b/src/modm/driver/display/ili9341_parallel.hpp new file mode 100644 index 0000000000..4a282e32a3 --- /dev/null +++ b/src/modm/driver/display/ili9341_parallel.hpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020, Pavel Pletenev + * + * 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_ILI9341_PARALLEL_HPP +#define MODM_ILI9341_PARALLEL_HPP + +#include "ili9341.hpp" + +namespace modm +{ + +/// @ingroup modm_driver_ili9341 +template +class Ili9341ParallelInterface: public ili9341 +{ + INTERFACE& interface; +public: + Ili9341ParallelInterface(INTERFACE& interface) + : interface(interface) {} + + __attribute__((noinline)) void + writeCommand(Command command) + { + interface.writeIndex(i(command)); + } + __attribute__((noinline)) void + writeCommand(Command command, uint8_t const *args, std::size_t length) + { + interface.writeIndex(i(command)); + for(std::size_t i=0; i(data); + size_t const length16 = length / 2; + for(std::size_t i=0; i +using Ili9341Parallel = Ili9341< + Ili9341ParallelInterface, + Reset, Backlight, BufferSize>; + +} // namespace modm + +#endif // MODM_ILI9341_PARALLEL_HPP diff --git a/src/modm/driver/display/ili9341_spi.hpp b/src/modm/driver/display/ili9341_spi.hpp new file mode 100644 index 0000000000..7f145dde49 --- /dev/null +++ b/src/modm/driver/display/ili9341_spi.hpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019, 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_ILI9341_SPI_HPP +#define MODM_ILI9341_SPI_HPP + +#include "ili9341.hpp" +#include +#include + +namespace modm +{ + +/// @ingroup modm_driver_ili9341 +template +class Ili9341SPIInterface: public ili9341, public modm::SpiDevice +{ +public: + Ili9341SPIInterface() + { + this->attachConfigurationHandler([]() { + SPI::setDataMode(SPI::DataMode::Mode0); + SPI::setDataOrder(SPI::DataOrder::MsbFirst); + }); + Cs::setOutput(modm::Gpio::High); + Dc::setOutput(); + } + + __attribute__((noinline)) void + writeCommand(Command command) + { + Dc::reset(); // enable command + SPI::transferBlocking(i(command)); + Dc::set(); // reset to data + } + __attribute__((noinline)) void + writeCommand(Command command, uint8_t const *args, std::size_t length) + { + Dc::reset(); // enable command + SPI::transferBlocking(i(command)); + Dc::set(); // reset to data + if (length != 0) + { + SPI::transferBlocking(const_cast(args), nullptr, length); + } + } + void + writeData(uint8_t const *data, std::size_t length) + { + SPI::transferBlocking(const_cast(data), nullptr, length); + } + void + writeCommandValue8(Command command, uint8_t value) + { + writeCommand(command, &value, 1); + } + + void + readData(Command command, uint8_t *buffer, std::size_t length) + { + using modm::platform::SpiBase; + uint8_t b[4]; + + Dc::reset(); // enable command + // SPI::Hal::setDataSize(SpiBase::DataSize::Bit9); + SPI::transferBlocking(i(command) << 1); + SPI::Hal::setDataSize(SpiBase::DataSize::Bit8); + Dc::set(); // reset to data + SPI::transferBlocking(b /*nullptr*/, buffer, length); + } + uint8_t + readData(Command command) + { + writeCommand(command); + return SPI::transferBlocking(0x00); + } + +public: + struct BatchHandle + { + Ili9341SPIInterface& i; + BatchHandle(Ili9341SPIInterface& iface) + : i(iface) + { + i.acquireMaster(); + Cs::reset(); + } + ~BatchHandle() + { + if (i.releaseMaster()) + Cs::set(); + } + }; +}; + +/// @ingroup modm_driver_ili9341 +template +using Ili9341Spi = Ili9341< + Ili9341SPIInterface, + Reset, Backlight, BufferSize>; + +} // namespace modm + +#endif // MODM_ILI9341_SPI_HPP From a6f1186aa3ef6d484f7ea92d2197c031b2dc2991 Mon Sep 17 00:00:00 2001 From: delphi Date: Sat, 19 Sep 2020 18:50:37 +0200 Subject: [PATCH 135/135] [examples] Add ILI9341 display example --- examples/stm32_f4ve/gui/.gitignore | 2 + examples/stm32_f4ve/gui/images/bake.pbm | 16 + .../stm32_f4ve/gui/images/battery_empty.pbm | 9 + .../stm32_f4ve/gui/images/battery_full.pbm | 9 + .../stm32_f4ve/gui/images/battery_good.pbm | 9 + .../stm32_f4ve/gui/images/battery_low.pbm | 9 + .../gui/images/battery_not_connected.pbm | 9 + .../stm32_f4ve/gui/images/bluetooth_12x16.pbm | 6 + .../stm32_f4ve/gui/images/bluetooth_24x32.pbm | 14 + examples/stm32_f4ve/gui/images/charge.pbm | 7 + examples/stm32_f4ve/gui/images/discharge.pbm | 7 + examples/stm32_f4ve/gui/images/plug.pbm | 7 + examples/stm32_f4ve/gui/images/warning.pbm | 62 +++ .../stm32_f4ve/gui/images/warning_32x32.pbm | 18 + examples/stm32_f4ve/gui/main.cpp | 506 ++++++++++++++++++ examples/stm32_f4ve/gui/project.xml | 22 + .../stm32_f4ve/gui/touchscreen_calibrator.hpp | 105 ++++ 17 files changed, 817 insertions(+) create mode 100644 examples/stm32_f4ve/gui/.gitignore create mode 100644 examples/stm32_f4ve/gui/images/bake.pbm create mode 100644 examples/stm32_f4ve/gui/images/battery_empty.pbm create mode 100644 examples/stm32_f4ve/gui/images/battery_full.pbm create mode 100644 examples/stm32_f4ve/gui/images/battery_good.pbm create mode 100644 examples/stm32_f4ve/gui/images/battery_low.pbm create mode 100644 examples/stm32_f4ve/gui/images/battery_not_connected.pbm create mode 100644 examples/stm32_f4ve/gui/images/bluetooth_12x16.pbm create mode 100644 examples/stm32_f4ve/gui/images/bluetooth_24x32.pbm create mode 100644 examples/stm32_f4ve/gui/images/charge.pbm create mode 100644 examples/stm32_f4ve/gui/images/discharge.pbm create mode 100644 examples/stm32_f4ve/gui/images/plug.pbm create mode 100644 examples/stm32_f4ve/gui/images/warning.pbm create mode 100644 examples/stm32_f4ve/gui/images/warning_32x32.pbm create mode 100644 examples/stm32_f4ve/gui/main.cpp create mode 100644 examples/stm32_f4ve/gui/project.xml create mode 100644 examples/stm32_f4ve/gui/touchscreen_calibrator.hpp diff --git a/examples/stm32_f4ve/gui/.gitignore b/examples/stm32_f4ve/gui/.gitignore new file mode 100644 index 0000000000..a251fc8669 --- /dev/null +++ b/examples/stm32_f4ve/gui/.gitignore @@ -0,0 +1,2 @@ +images/*.hpp +images/*.cpp diff --git a/examples/stm32_f4ve/gui/images/bake.pbm b/examples/stm32_f4ve/gui/images/bake.pbm new file mode 100644 index 0000000000..dbee414ca0 --- /dev/null +++ b/examples/stm32_f4ve/gui/images/bake.pbm @@ -0,0 +1,16 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +30 30 +0000000000000000000000000000000011111111111111111111111111100011111111 +1111111111111111111000111111111111111111111111111000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000001110000000111000000000000000001111111111111000000000000000001 +1111111111110000000000000000001111111111100000000000000000000011111110 +0000000000000000000000011111000000000000000000000000011111000000000000 +0000000000000011100000000000000000000000000011100000000000000011111111 +1111111111111111111000111111111111111111111111111000111111111111111111 +1111111110001110000000000000000000001110001110000000000000000000001110 +0011100000000000000000000011100011100000000000000000000011100011100000 +0000000000000000111000111000000000000000000000111000111000000000000000 +0000001110001111111111111111111111111110001111111111111111111111111110 +001111111111111111111111111110000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/battery_empty.pbm b/examples/stm32_f4ve/gui/images/battery_empty.pbm new file mode 100644 index 0000000000..829b7bff0e --- /dev/null +++ b/examples/stm32_f4ve/gui/images/battery_empty.pbm @@ -0,0 +1,9 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +25 16 +0000000000000000000000000000000000000000000000000000001111111111111111 +1110000010000000000000000000100001000000000000000001010001100000000000 +0000001010010100000000000000001101001010000000000000000110100101000000 +0000000000110100101000000000000000111010001100000000000000011101000010 +0000000000000011101000010000000000000000000100000111111111111111111100 +00000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/battery_full.pbm b/examples/stm32_f4ve/gui/images/battery_full.pbm new file mode 100644 index 0000000000..2b5eff03ee --- /dev/null +++ b/examples/stm32_f4ve/gui/images/battery_full.pbm @@ -0,0 +1,9 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +25 16 +0000000000000000000000000000000000000000000000000000001111111111111111 +1110000010000000000000000000100001011111011111011111010001101111101111 +1011111010010101111101111101111101001010111110111110111110100101011111 +0111110111110100101011111011111011111010001101111101111101111101000010 +1111101111101111101000010000000000000000000100000111111111111111111100 +00000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/battery_good.pbm b/examples/stm32_f4ve/gui/images/battery_good.pbm new file mode 100644 index 0000000000..786663c960 --- /dev/null +++ b/examples/stm32_f4ve/gui/images/battery_good.pbm @@ -0,0 +1,9 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +25 16 +0000000000000000000000000000000000000000000000000000001111111111111111 +1110000010000000000000000000100001000001011111011111010001100000101111 +1011111010010100001101111101111101001010000110111110111110100101000111 +0111110111110100101000111011111011111010001100111101111101111101000010 +0111101111101111101000010000000000000000000100000111111111111111111100 +00000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/battery_low.pbm b/examples/stm32_f4ve/gui/images/battery_low.pbm new file mode 100644 index 0000000000..9e94ea1b31 --- /dev/null +++ b/examples/stm32_f4ve/gui/images/battery_low.pbm @@ -0,0 +1,9 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +25 16 +0000000000000000000000000000000000000000000000000000001111111111111111 +1110000010000000000000000000100001000000000000011111010001100000000000 +0011111010010100000000000001111101001010000000000000111110100101000000 +0000000111110100101000000000000011111010001100000000000001111101000010 +0000000000001111101000010000000000000000000100000111111111111111111100 +00000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/battery_not_connected.pbm b/examples/stm32_f4ve/gui/images/battery_not_connected.pbm new file mode 100644 index 0000000000..4b5eb456ad --- /dev/null +++ b/examples/stm32_f4ve/gui/images/battery_not_connected.pbm @@ -0,0 +1,9 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +25 16 +0000000000000000000000000000000000000000000000000000001111111111111111 +1110000010000000000000000000100001000000001110000000010001100000001000 +1000000010010100000000000100000001001010000000000100000000100101000000 +0001000000000100101000000000100000000010001100000000000000000001000010 +0000000010000000001000010000000000000000000100000111111111111111111100 +00000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/bluetooth_12x16.pbm b/examples/stm32_f4ve/gui/images/bluetooth_12x16.pbm new file mode 100644 index 0000000000..f91742d0b1 --- /dev/null +++ b/examples/stm32_f4ve/gui/images/bluetooth_12x16.pbm @@ -0,0 +1,6 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +12 16 +0000000000000001111110000011111111000111100111100111101011100110101101 +1001110010111001111001111001111001111001110010111001101011011001111010 +1110011110011110001111111100000111111000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/bluetooth_24x32.pbm b/examples/stm32_f4ve/gui/images/bluetooth_24x32.pbm new file mode 100644 index 0000000000..9fa77741f5 --- /dev/null +++ b/examples/stm32_f4ve/gui/images/bluetooth_24x32.pbm @@ -0,0 +1,14 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +24 32 +0000000000000000000000000000000001111110000000000000001111111111110000 +0000000111111111111110000000001111111001111111000000011111111000111111 +1000001111111110000111111100001111111110000011111100001111111110010001 +1111000111111111100110001111100111100011100111000111100111110001100110 +0011111001111110001001000111111001111111000000001111111001111111100000 +0111111110011111111100001111111110011111111100001111111110011111111000 +0001111111100111111100000000111111100111111000100100011111100111110001 +1001100011111001111000111001110001111001111111111001100011111001111111 +1110010001111110001111111110000011111100001111111110000111111100000111 +1111100011111110000000111111100111111100000000011111111111111000000000 +00111111111111000000000000000111111000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/charge.pbm b/examples/stm32_f4ve/gui/images/charge.pbm new file mode 100644 index 0000000000..f1245a19ce --- /dev/null +++ b/examples/stm32_f4ve/gui/images/charge.pbm @@ -0,0 +1,7 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +16 16 +0000000000000000000100000000000000010000000000000001000000000000000100 +0100000000000100110000000000010111000000000001111111111100000111111111 +1100000101110000000000010011000000000001000100000000000100000000000000 +0100000000000000010000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/discharge.pbm b/examples/stm32_f4ve/gui/images/discharge.pbm new file mode 100644 index 0000000000..c47d2515f1 --- /dev/null +++ b/examples/stm32_f4ve/gui/images/discharge.pbm @@ -0,0 +1,7 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +16 16 +0000000000000000000100000000000000010000000000000001000000000000000100 +0000100000000100000011000000010000001110000001111111111100000111111111 +1100000100000011100000010000001100000001000000100000000100000000000000 +0100000000000000010000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/plug.pbm b/examples/stm32_f4ve/gui/images/plug.pbm new file mode 100644 index 0000000000..1c832d4a66 --- /dev/null +++ b/examples/stm32_f4ve/gui/images/plug.pbm @@ -0,0 +1,7 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +16 16 +0000000000000000000011100000000000001111111000000000111111110000011111 +1111111000011111111111100000001111111110000000111111111111000011111111 +1111000011111111100001111111111110000111111111111000000011111111000000 +0011111110000000001110000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/warning.pbm b/examples/stm32_f4ve/gui/images/warning.pbm new file mode 100644 index 0000000000..e2d772ebc4 --- /dev/null +++ b/examples/stm32_f4ve/gui/images/warning.pbm @@ -0,0 +1,62 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +64 64 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000011111100000000000000000000000000000000000000000000000000000 +0001111111111000000000000000000000000000000000000000000000000000001111 +1111111100000000000000000000000000000000000000000000000000011110000001 +1110000000000000000000000000000000000000000000000000011100000000111000 +0000000000000000000000000000000000000000000000111000000000011100000000 +0000000000000000000000000000000000000000111000000000011100000000000000 +0000000000000000000000000000000001110000000000001110000000000000000000 +0000000000000000000000000001110000000000001110000000000000000000000000 +0000000000000000000011100000000000000111000000000000000000000000000000 +0000000000000011100000000000000111000000000000000000000000000000000000 +0000000111000000000000000011100000000000000000000000000000000000000000 +0111000000000000000011100000000000000000000000000000000000000000111000 +0000111100000001110000000000000000000000000000000000000000111000000111 +1110000001110000000000000000000000000000000000000001110000001100111100 +0000111000000000000000000000000000000000000001110000011011111110000011 +1000000000000000000000000000000000000011100000011011111110000001110000 +0000000000000000000000000000000011100000011011111110000001110000000000 +0000000000000000000000000111000000011011111110000000111000000000000000 +0000000000000000000111000000011011111110000000111000000000000000000000 +0000000000001110000000011011111110000000011100000000000000000000000000 +0000001110000000011011111110000000011100000000000000000000000000000001 +1100000000011011111110000000001110000000000000000000000000000001110000 +0000011011111110000000001110000000000000000000000000000011100000000001 +1011111110000000000111000000000000000000000000000011100000000001101111 +1110000000000111000000000000000000000000000111000000000001101111111000 +0000000011100000000000000000000000000111000000000001101111111000000000 +0011100000000000000000000000001110000000000001101111111000000000000111 +0000000000000000000000001110000000000001101111111000000000000111000000 +0000000000000000011100000000000001101111111000000000000011100000000000 +0000000000011100000000000001101111111000000000000011100000000000000000 +0000111000000000000001111111111000000000000001110000000000000000000011 +1000000000000000111111110000000000000001110000000000000000000111000000 +0000000000011111100000000000000000111000000000000000000111000000000000 +0000001111000000000000000000111000000000000000001110000000000000000000 +0000000000000000000000011100000000000000001110000000000000000000000000 +0000000000000000011100000000000000011100000000000000000000000000000000 +0000000000001110000000000000011100000000000000000000000000000000000000 +0000001110000000000000111000000000000000000000111100000000000000000000 +0111000000000000111000000000000000000011111111000000000000000000011100 +0000000001110000000000000000000111000111100000000000000000001110000000 +0001110000000000000000000110111111100000000000000000001110000000001110 +0000000000000000001101111111110000000000000000000111000000001110000000 +0000000000001101111111110000000000000000000111000000011110000000000000 +0000001101111111110000000000000000000111100000011100000000000000000000 +1111111111110000000000000000000011100000011100000000000000000000011111 +1111100000000000000000000011100000011100000000000000000000011111111110 +0000000000000000000011100000011100000000000000000000001111111100000000 +0000000000000011100000011110000000000000000000000011110000000000000000 +0000000111100000001110000000000000000000000000000000000000000000000000 +0111000000001111000000000000000000000000000000000000000000000000111100 +0000000111110000000000000000000000000000000000000000000011111000000000 +0011111111111111111111111111111111111111111111111111110000000000000111 +1111111111111111111111111111111111111111111111100000000000000001111111 +1111111111111111111111111111111111111110000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/images/warning_32x32.pbm b/examples/stm32_f4ve/gui/images/warning_32x32.pbm new file mode 100644 index 0000000000..8779c7d69d --- /dev/null +++ b/examples/stm32_f4ve/gui/images/warning_32x32.pbm @@ -0,0 +1,18 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +32 32 +0000000000000000000000000000000000000000000000111100000000000000000000 +0000000111111000000000000000000000000011100111000000000000000000000000 +1100001100000000000000000000000110000001100000000000000000000001100000 +0110000000000000000000001100000000110000000000000000000011000110001100 +0000000000000000011000101100011000000000000000000110001111000110000000 +0000000000110000111100001100000000000000001100001111000011000000000000 +0001100000111100000110000000000000011000001111000001100000000000001100 +0000111100000011000000000000110000001111000000110000000000011000000011 +1100000001100000000001100000001111000000011000000000110000000001100000 +0000110000000011000000000000000000001100000001100000000000000000000001 +1000000110000000000110000000000110000011000000000010110000000000110000 +1100000000010111100000000011000110000000000111111000000000011001100000 +0000001111000000000001100110000000000001100000000000011001110000000000 +0000000000000011100011111111111111111111111111110000011111111111111111 +11111111100000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/stm32_f4ve/gui/main.cpp b/examples/stm32_f4ve/gui/main.cpp new file mode 100644 index 0000000000..f0e9a1c09a --- /dev/null +++ b/examples/stm32_f4ve/gui/main.cpp @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2011, Georgi Grinshpun + * Copyright (c) 2011-2013, Fabian Greif + * Copyright (c) 2012-2014, 2017, Sascha Schade + * Copyright (c) 2013, Kevin Läufer + * Copyright (c) 2013-2014, Daniel Krebs + * Copyright (c) 2013-2018, Niklas Hauser + * Copyright (c) 2020, Pavel Pletenev + * + * 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 +#include +#include + +#include + +#include "touchscreen_calibrator.hpp" + +#include "images/bluetooth_12x16.hpp" + + + +// ---------------------------------------------------------------------------- +/* + * Setup UART Logger + */ + +// Set the log level +#undef MODM_LOG_LEVEL +#define MODM_LOG_LEVEL modm::log::DEBUG + +// ---------------------------------------------------------------------------- + +/* FSMC + * + * * Use A18 as Command / Data pin + * 0x60000000 is the base address for FSMC's first memory bank. + * When accessing 0x60000000 A16 is low. + * + * But the TftMemoryBus16Bit uses the FSMC in 16 bit mode. + * Then, according to Table 184 (External memory address) of + * reference manual (p1317) address HADDR[25:1] >> 1 are issued to + * the address pins A24:A0. + * So when writing to offset +((1 << 18) << 1) pin A16 is high. + */ +constexpr std::uintptr_t offset = 0x60000000; +constexpr std::uintptr_t offsetCmd = offset + ((1 << 18) << 1); +modm::TftMemoryBus16Bit parallelBus( + (volatile uint16_t *) offset, + (volatile uint16_t *) offsetCmd); + +using Display = modm::Ili9341Parallel< + modm::TftMemoryBus16Bit, Board::display::Reset, Board::display::Backlight>; +Display tft(parallelBus); + +// ---------------------------------------------------------------------------- + +constexpr uint8_t TP_TOLERANCE = 30; // in pixels +constexpr uint16_t TOUCH_REPEAT_RATE = 600; // in ms, 0 to disable + + +modm::gui::inputQueue input_queue; +modm::gui::AsyncEventList async_events; +modm::glcd::Point last_point; + +// ---------------------------------------------------------------------------- +// Touchscreen + +typedef GpioOutputC4 CsTouchscreen; +typedef GpioInputC5 IntTouchscreen; + +modm::Ads7843 ads7843; +modm::TouchscreenCalibrator touchscreen; + +typedef GpioOutputD7 CS; + + + + +static void +initDisplay() +{ + MODM_LOG_DEBUG << __PRETTY_FUNCTION__ << modm::endl; + Fsmc::initialize(); + GpioConnector::connect(); + + + CS::setOutput(); + CS::reset(); + + fsmc::NorSram::AsynchronousTiming timing = { + // read + 15, 0, 15, + // write + 15, 0, 15, + // bus turn around + 15 + }; + + fsmc::NorSram::configureAsynchronousRegion( + fsmc::NorSram::CHIP_SELECT_1, + fsmc::NorSram::NO_MULTIPLEX_16BIT, + fsmc::NorSram::SRAM_ROM, + fsmc::NorSram::MODE_A, + timing); + + fsmc::NorSram::enableRegion(fsmc::NorSram::CHIP_SELECT_1); + + + tft.initialize(); + tft.enableBacklight(true); + tft.setRotation(Display::Rotation::Rotate90); +} + + + +static void +initTouchscreen() +{ + MODM_LOG_DEBUG << __PRETTY_FUNCTION__ << modm::endl; + CsTouchscreen::setOutput(); + CsTouchscreen::set(); + + IntTouchscreen::setInput(Gpio::InputType::PullUp); + using namespace Board::display::touch; + + SpiMaster2::connect(); + SpiMaster2::initialize(); + SpiMaster2::setDataMode(SpiMaster2::DataMode::Mode0); + +} + +// ---------------------------------------------------------------------------- +/* screen calibration */ +static void +drawCross(modm::GraphicDisplay& display, modm::glcd::Point center) +{ + display.setColor(modm::glcd::Color::red()); + display.drawLine(center.x - 15, center.y, center.x - 2, center.y); + display.drawLine(center.x + 2, center.y, center.x + 15, center.y); + display.drawLine(center.x, center.y - 15, center.x, center.y - 2); + display.drawLine(center.x, center.y + 2, center.x, center.y + 15); + + display.setColor(modm::glcd::Color::white()); + display.drawLine(center.x - 15, center.y + 15, center.x - 7, center.y + 15); + display.drawLine(center.x - 15, center.y + 7, center.x - 15, center.y + 15); + + display.drawLine(center.x - 15, center.y - 15, center.x - 7, center.y - 15); + display.drawLine(center.x - 15, center.y - 7, center.x - 15, center.y - 15); + + display.drawLine(center.x + 7, center.y + 15, center.x + 15, center.y + 15); + display.drawLine(center.x + 15, center.y + 7, center.x + 15, center.y + 15); + + display.drawLine(center.x + 7, center.y - 15, center.x + 15, center.y - 15); + display.drawLine(center.x + 15, center.y - 15, center.x + 15, center.y - 7); +} + +static void +calibrateTouchscreen(modm::GraphicDisplay& display, modm::glcd::Point *fixed_samples = NULL) +{ + MODM_LOG_DEBUG << __PRETTY_FUNCTION__ << modm::endl; + modm::glcd::Point calibrationPoint[3] = { { 45, 45 }, { 270, 90 }, { 100, 190 } }; + modm::glcd::Point sample[3]; + + if(!fixed_samples) { + for (uint8_t i = 0; i < 3; i++) + { + display.clear(); + + display.setColor(modm::glcd::Color::yellow()); + display.setCursor(50, 5); + display << "Touch crosshair to calibrate"; + + drawCross(display, calibrationPoint[i]); + modm::delay(500ms); + + while (!ads7843.read(&sample[i])) { + // wait until a valid sample can be taken + } + + MODM_LOG_DEBUG << "calibration point: (" << sample[i].x << " | " << sample[i].y << ")" << modm::endl; + } + + touchscreen.calibrate(calibrationPoint, sample); + + } else { + touchscreen.calibrate(calibrationPoint, fixed_samples); + } + + display.clear(); +} + +void +drawPoint(modm::GraphicDisplay& display, modm::glcd::Point point) +{ + MODM_LOG_DEBUG << __PRETTY_FUNCTION__ << modm::endl; + if (point.x < 0 || point.y < 0) { + return; + } + + display.drawPixel(point.x, point.y); + display.drawPixel(point.x + 1, point.y); + display.drawPixel(point.x, point.y + 1); + display.drawPixel(point.x + 1, point.y + 1); +} + +// ---------------------------------------------------------------------------- +/* catch touch input */ +bool +touchActive() +{ + /* + * XPT2046: + * + * !PENIRQ when not touched: + * _____ _ __________ _ _______ + * \_/ \_/ \_/ \_/ + * |<----->| + * ~120us + * + * !PENIRQ when touched: + * + * _____|____|______|__|_|_____|___ + * + * random ~100ns peaks + * + */ + + bool m1, m2; + + m1 = !IntTouchscreen::read(); + modm::delay(130us); + m2 = !IntTouchscreen::read(); + + return (m1 || m2); +} + +void +resetTouchLock(void* /* data */) +{ + last_point = modm::glcd::Point(-400, -400); +} + +bool +debounceTouch(modm::glcd::Point *out, modm::glcd::Point *old) +{ + modm::glcd::Point raw, point; + + if(touchActive()) { + + if (ads7843.read(&raw)) { + + // translate point according to calibration + touchscreen.translate(&raw, &point); + + if(abs(point.x - old->x) < TP_TOLERANCE && + abs(point.y - old->y) < TP_TOLERANCE + ) + { + // point is within area of last touch + *old = point; + return false; + } + + // new touch point + *old = point; + *out = point; + + // schedule a reset for debounce lock, so that holding the finger fires repeated touch events + if(TOUCH_REPEAT_RATE) + { + async_events.append(new modm::gui::AsyncEvent(TOUCH_REPEAT_RATE, &resetTouchLock, NULL)); + } + + return true; + } + } else { + // reset old point so that when touched again you can touch the same spot + *old = modm::glcd::Point(-400, -400); + } + return false; +} + +void +touchUp(void* data) +{ + MODM_LOG_DEBUG << __PRETTY_FUNCTION__ << modm::endl; + modm::gui::InputEvent* ev = static_cast(data); + + MODM_LOG_DEBUG << "asynchronous UP-event:" << modm::endl; + MODM_LOG_DEBUG << "x: " << ev->coord.x << modm::endl; + MODM_LOG_DEBUG << "y: " << ev->coord.y << modm::endl; + + // queue UP-event as new input event + input_queue.push(ev); +} + + +void +gatherInput() +{ + modm::glcd::Point point; + + if (debounceTouch(&point, &last_point)) { + + auto ev_down = new modm::gui::InputEvent(point, + modm::gui::InputEvent::Type::TOUCH, + modm::gui::InputEvent::Direction::DOWN); + + auto ev_up = new modm::gui::InputEvent( point, + modm::gui::InputEvent::Type::TOUCH, + modm::gui::InputEvent::Direction::UP); + + // queue down event + input_queue.push(ev_down); + + // create an asynchronous event with Direction::UP and 200ms delay + auto async_ev = new modm::gui::AsyncEvent(500, &touchUp, (void*)(ev_up)); + async_events.append(async_ev); + + MODM_LOG_DEBUG << "touch down x: " << point.x << modm::endl; + MODM_LOG_DEBUG << "touch down y: " << point.y << modm::endl; + + } +} + +inline void +updateAsyncEvents() +{ + auto iter = async_events.begin(); + + while(iter != async_events.end()) + { + if((*iter)->is_expired()) { + iter = async_events.erase(iter); + } else + { + ++iter; + } + } +} + + +// ---------------------------------------------------------------------------- + +void +test_callback(const modm::gui::InputEvent& ev, modm::gui::Widget* w, void* data) +{ + // avoid warnings + (void) ev; + (void) w; + (void) data; + + Board::LedGreen2::toggle(); +} + + +modm::glcd::Color colors[modm::gui::Color::PALETTE_SIZE] { + modm::glcd::Color::black(), + modm::glcd::Color::white(), + modm::glcd::Color::gray(), + modm::glcd::Color::red(), + modm::glcd::Color::green(), + modm::glcd::Color::blue(), + modm::glcd::Color::blue(), // BORDER + modm::glcd::Color::red(), // TEXT + modm::glcd::Color::black(), // BACKGROUND + modm::glcd::Color::red(), // ACTIVATED + modm::glcd::Color::blue(), // DEACTIVATED + +}; +modm::gui::ColorPalette colorpalette{colors}; +/* + * empirically found calibration points + */ +// modm::glcd::Point calibration[] = {{3339, 3046},{931, 2428},{2740, 982}}; +modm::glcd::Point* calibration = nullptr; +// ---------------------------------------------------------------------------- + +class MyView : public modm::gui::View{ +public: + MyView(modm::gui::GuiViewStack* stack, uint8_t identifier, modm::gui::Dimension dimension) + : View(stack, identifier, dimension){ + + } + ~MyView() override{} +protected: + virtual bool hasChanged() override { + return true; + } +}; + +int +main() +{ + + Board::initialize(); + + + MODM_LOG_DEBUG << "Hello from modm gui example!" << modm::endl; + + initDisplay(); + initTouchscreen(); + + /* + * calibrate touchscreen with already found calibration points + */ + + calibrateTouchscreen(tft, calibration); + + + /* + * manipulate the color palette + */ + + colorpalette.setColor(modm::gui::Color::TEXT, modm::glcd::Color::yellow()); + + + /* + * Create a view and some widgets + */ + modm::gui::GuiViewStack stack(&tft, &input_queue); + MyView myView(&stack, 1, modm::gui::Dimension(320, 240)); + + modm::gui::ButtonWidget toggleLedButton((char*)"Toggle Green", modm::gui::Dimension(100, 50)); + modm::gui::ButtonWidget doNothingButton((char*)"Do nothing", modm::gui::Dimension(100, 50)); + + modm::gui::IntegerRocker rocker1(100, 50, modm::gui::Dimension(200, 30)); + + + /* + * connect callbacks to widgets + */ + + toggleLedButton.cb_activate = &test_callback; + + + /* + * place widgets in view + */ + + myView.pack(&toggleLedButton, modm::glcd::Point(110, 10)); + myView.pack(&doNothingButton, modm::glcd::Point(110, 80)); + myView.pack(&rocker1, modm::glcd::Point(60, 200)); + stack.push(&myView); + + // modm::glcd::Point pixPos(10, 200); + // modm::glcd::Point pixDeltaPos(1, 1); + // auto pix = modm::accessor::asFlash(bitmap::bluetooth_12x16); + while(true) { + + gatherInput(); + + // process asynchronous events + updateAsyncEvents(); + + // update view + stack.update(); + + + /* + * display an arbitrary image + */ + // if(pixPos.x < 0 || (pixPos.x + pix[0]) > tft.getWidth()){ + // pixDeltaPos.x *= -1; + // } + // if(pixPos.y < 0 || (pixPos.y + pix[1]) > tft.getHeight()){ + // pixDeltaPos.y *= -1; + // } + // pixPos += pixDeltaPos; + // tft.drawImage(pixPos, pix); + } + + return 0; +} diff --git a/examples/stm32_f4ve/gui/project.xml b/examples/stm32_f4ve/gui/project.xml new file mode 100644 index 0000000000..eff4dfff36 --- /dev/null +++ b/examples/stm32_f4ve/gui/project.xml @@ -0,0 +1,22 @@ + + modm:stm32_f4ve + + + + + + modm:container + modm:debug + modm:driver:ads7843 + modm:driver:ili9341 + modm:driver:memory_bus + modm:platform:fsmc + modm:platform:spi:2 + modm:platform:uart:2 + modm:platform:heap + modm:ui:display + modm:ui:gui + modm:utils + modm:build:scons + + diff --git a/examples/stm32_f4ve/gui/touchscreen_calibrator.hpp b/examples/stm32_f4ve/gui/touchscreen_calibrator.hpp new file mode 100644 index 0000000000..9fe858941a --- /dev/null +++ b/examples/stm32_f4ve/gui/touchscreen_calibrator.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2009, Martin Rosekeit + * Copyright (c) 2009-2010, 2012-2013, Fabian Greif + * Copyright (c) 2012, Niklas Hauser + * Copyright (c) 2012, Sascha Schade + * Copyright (c) 2014, Daniel Krebs + * + * 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_TOUCHSCREEN_CALIBRATOR_HPP +#define MODM_TOUCHSCREEN_CALIBRATOR_HPP + +namespace modm +{ + class TouchscreenCalibrator + { + public: + TouchscreenCalibrator(); + + bool + calibrate(modm::glcd::Point * display, modm::glcd::Point * sample); + + void + translate(modm::glcd::Point * raw, modm::glcd::Point * translated); + + private: + float An; + float Bn; + float Cn; + float Dn; + float En; + float Fn; + float scale; + }; +} + +#endif // MODM_TOUCHSCREEN_CALIBRATOR_HPP + +#ifndef MODM_TOUCHSCREEN_CALIBRATOR_HPP +# error "Don't include this file directly, use touchscreen_calibrator.hpp instead!" +#endif + +modm::TouchscreenCalibrator::TouchscreenCalibrator() : + scale(0.f) +{ +} + +bool +modm::TouchscreenCalibrator::calibrate( + modm::glcd::Point * display, modm::glcd::Point * sample) +{ + // K��(X0��X2) (Y1��Y2)��(X1��X2) (Y0��Y2) + scale = ((sample[0].x - sample[2].x) * (sample[1].y - sample[2].y)) - + ((sample[1].x - sample[2].x) * (sample[0].y - sample[2].y)); + + if (scale == 0) { + return false; + } + + // A��((XD0��XD2) (Y1��Y2)��(XD1��XD2) (Y0��Y2))��K + An = ((display[0].x - display[2].x) * (sample[1].y - sample[2].y)) - + ((display[1].x - display[2].x) * (sample[0].y - sample[2].y)); + // B��((X0��X2) (XD1��XD2)��(XD0��XD2) (X1��X2))��K */ + Bn = ((sample[0].x - sample[2].x) * (display[1].x - display[2].x)) - + ((display[0].x - display[2].x) * (sample[1].x - sample[2].x)); + // C��(Y0(X2XD1��X1XD2)+Y1(X0XD2��X2XD0)+Y2(X1XD0��X0XD1))��K */ + Cn = (sample[2].x * display[1].x - sample[1].x * display[2].x) * sample[0].y + + (sample[0].x * display[2].x - sample[2].x * display[0].x) * sample[1].y + + (sample[1].x * display[0].x - sample[0].x * display[1].x) * sample[2].y; + + // D��((YD0��YD2) (Y1��Y2)��(YD1��YD2) (Y0��Y2))��K */ + Dn = ((display[0].y - display[2].y) * (sample[1].y - sample[2].y)) - + ((display[1].y - display[2].y) * (sample[0].y - sample[2].y)); + // E��((X0��X2) (YD1��YD2)��(YD0��YD2) (X1��X2))��K */ + En = ((sample[0].x - sample[2].x) * (display[1].y - display[2].y)) - + ((display[0].y - display[2].y) * (sample[1].x - sample[2].x)); + // F��(Y0(X2YD1��X1YD2)+Y1(X0YD2��X2YD0)+Y2(X1YD0��X0YD1))��K */ + Fn = (sample[2].x * display[1].y - sample[1].x * display[2].y) * sample[0].y + + (sample[0].x * display[2].y - sample[2].x * display[0].y) * sample[1].y + + (sample[1].x * display[0].y - sample[0].x * display[1].y) * sample[2].y; + + return true; +} + +void +modm::TouchscreenCalibrator::translate(modm::glcd::Point * raw, modm::glcd::Point * translated) +{ + if (scale != 0) + { + /* XD = AX+BY+C */ + translated->x = + ((An * raw->x) + + (Bn * raw->y) + Cn) / scale; + /* YD = DX+EY+F */ + translated->y = + ((Dn * raw->x) + + (En * raw->y) + Fn) / scale; + } +}