From 9d8bbfa3f9457222f7b5258491f4cc9948df465d Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Fri, 5 Oct 2018 23:52:08 +0200 Subject: [PATCH] [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)); }