diff --git a/src/modm/architecture/interface/spi.hpp b/src/modm/architecture/interface/spi.hpp index 80e2dc85fe..decc22da46 100644 --- a/src/modm/architecture/interface/spi.hpp +++ b/src/modm/architecture/interface/spi.hpp @@ -12,13 +12,24 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_INTERFACE_SPI_HPP -#define MODM_INTERFACE_SPI_HPP +#pragma once #include namespace modm { +namespace spi +{ + +template +concept Support_DataSize_Bit16 = requires +{ Spi::DataSize::Bit16; }; + +template +concept Support_DataSize_Bit32 = requires +{ Spi::DataSize::Bit32; }; + +} // namespace spi /// @ingroup modm_architecture_spi struct Spi @@ -43,8 +54,7 @@ struct Spi MsbFirst = 0b0, LsbFirst = 0b1, }; + }; } // namespace modm - -#endif // MODM_INTERFACE_SPI_HPP diff --git a/src/modm/architecture/interface/spi_master.hpp b/src/modm/architecture/interface/spi_master.hpp index 0bcbe52ccc..8a64dfac97 100644 --- a/src/modm/architecture/interface/spi_master.hpp +++ b/src/modm/architecture/interface/spi_master.hpp @@ -12,8 +12,7 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_INTERFACE_SPI_MASTER_HPP -#define MODM_INTERFACE_SPI_MASTER_HPP +#pragma once #include #include "spi.hpp" @@ -156,8 +155,22 @@ class SpiMaster : public ::modm::PeripheralDriver, public Spi static modm::ResumableResult transfer(const uint8_t *tx, uint8_t *rx, std::size_t length); #endif + +public: + enum State : uint8_t { + Idle = Bit6, // Transaction is running + Repeat = Bit7, // Send same tx multiple times + }; + MODM_FLAGS8(State); + + enum DataType : uint8_t { + Byte = 0, // 1 byte + HalfWord = 1, // 2 bytes + Word = 2, // 4 bytes + // WordWord = 3 // 8 bytes + }; + typedef Value DataType_t; + }; } // namespace modm - -#endif // MODM_INTERFACE_SPI_MASTER_HPP diff --git a/src/modm/driver/display/ili9341.hpp b/src/modm/driver/display/ili9341.hpp index 76ac5b18c0..b6b05bcd28 100644 --- a/src/modm/driver/display/ili9341.hpp +++ b/src/modm/driver/display/ili9341.hpp @@ -139,11 +139,9 @@ struct ili9341 }; /// @ingroup modm_driver_ili9341 -template +template class Ili9341 : public Interface, public modm::ColorGraphicDisplay { - 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; @@ -298,7 +296,7 @@ class Ili9341 : public Interface, public modm::ColorGraphicDisplay Orientation orientation{Orientation::Landscape0}; - uint8_t buffer[BufferSize * 2]{0}; + uint8_t buffer[4]; }; } // namespace modm diff --git a/src/modm/driver/display/ili9341_impl.hpp b/src/modm/driver/display/ili9341_impl.hpp index d44ce85c69..6903937bd1 100644 --- a/src/modm/driver/display/ili9341_impl.hpp +++ b/src/modm/driver/display/ili9341_impl.hpp @@ -15,9 +15,9 @@ namespace modm { -template +template void -Ili9341::initialize() +Ili9341::initialize() { constexpr uint8_t pwrCtrlA[] { 0x39, 0x2c, 0x00, 0x34, 0x02 }; constexpr uint8_t pwrCtrlB[] { 0x00, 0xc1, 0x30 }; @@ -78,9 +78,9 @@ Ili9341::initialize() } } -template +template void -Ili9341::reset(bool hardReset /* = false */) +Ili9341::reset(bool hardReset /* = false */) { if (hardReset) { @@ -98,9 +98,9 @@ Ili9341::reset(bool hardReset /* = fals } } -template +template uint16_t -Ili9341::getIcModel() +Ili9341::getIcModel() { BatchHandle h(*this); @@ -109,9 +109,9 @@ Ili9341::getIcModel() return (buffer[2] << 8) | buffer[3]; } -template +template inline void -Ili9341::setOrientation(glcd::Orientation orientation) +Ili9341::setOrientation(glcd::Orientation orientation) { using MemoryAccessCtrl_t = ili9341::MemoryAccessCtrl_t; using MemoryAccessCtrl = ili9341::MemoryAccessCtrl; @@ -139,57 +139,57 @@ Ili9341::setOrientation(glcd::Orientati this->writeCommandValue8(Command::MemoryAccessCtrl, madCtrl.value); } -template +template void -Ili9341::turnOn() +Ili9341::turnOn() { BatchHandle h(*this); this->writeCommand(Command::DisplayOn); } -template +template void -Ili9341::turnOff() +Ili9341::turnOff() { BatchHandle h(*this); this->writeCommand(Command::DisplayOff); } -template +template void -Ili9341::setIdle(bool enable) +Ili9341::setIdle(bool enable) { BatchHandle h(*this); this->writeCommand(enable ? Command::IdleModeOn : Command::IdleModeOff); } -template +template void -Ili9341::enableSleep(bool enable) +Ili9341::enableSleep(bool enable) { BatchHandle h(*this); this->writeCommand(enable ? Command::EnterSleep : Command::LeaveSleep); } -template +template void -Ili9341::setBrightness(uint8_t level) +Ili9341::setBrightness(uint8_t level) { BatchHandle h(*this); this->writeCommand(Command::WriteBrightness, &level, 1); } -template +template void -Ili9341::setInvert(bool invert) +Ili9341::setInvert(bool invert) { BatchHandle h(*this); this->writeCommand(invert ? Command::InversionOn : Command::InversionOff); } -template +template void -Ili9341::clear() +Ili9341::clear() { auto const saveForegroundColor { foregroundColor }; foregroundColor = backgroundColor; @@ -197,82 +197,45 @@ Ili9341::clear() foregroundColor = saveForegroundColor; } -template +template void -Ili9341::drawHorizontalLine( +Ili9341::drawHorizontalLine( glcd::Point start, uint16_t length) { - uint16_t const pixelValue { modm::toBigEndian(foregroundColor.color) }; - auto minLength { std::min(std::size_t(length), BufferSize) }; - uint16_t *buffer16 { reinterpret_cast(buffer) }; - std::fill(buffer16, buffer16+minLength, pixelValue); - - 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); + fillRectangle(start, length, 1); } -template +template void -Ili9341::drawVerticalLine( +Ili9341::drawVerticalLine( glcd::Point start, uint16_t length) { - uint16_t const pixelValue { modm::toBigEndian(foregroundColor.color) }; - auto minLength { std::min(std::size_t(length), BufferSize) }; - uint16_t *buffer16 { reinterpret_cast(buffer) }; - std::fill(buffer16, buffer16+minLength, pixelValue); - - 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); + fillRectangle(start, 1, length); } -template +template void -Ili9341::fillRectangle( +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) }; - - uint16_t const pixelValue { modm::toBigEndian(foregroundColor.color) }; - auto minLength { std::min(std::size_t(pixelCount), BufferSize) }; - uint16_t *buffer16 { reinterpret_cast(buffer) }; - std::fill(buffer16, buffer16+minLength, pixelValue); + uint64_t pixelCount = uint64_t(width) * height; BatchHandle h(*this); - setClipping(x, y, width, height); - while (pixelCount > BufferSize) + setClipping(upperLeft.getX(), upperLeft.getY(), width, height); + while (pixelCount > std::numeric_limits::max()) { - this->writeData(buffer, BufferSize * 2); - pixelCount -= BufferSize; + this->writeDataRepeat(foregroundColor, std::numeric_limits::max()); + pixelCount -= std::numeric_limits::max(); } - if (pixelCount) - this->writeData(buffer, pixelCount * 2); + this->writeDataRepeat(foregroundColor, pixelCount); } -template +template void -Ili9341::fillCircle( +Ili9341::fillCircle( glcd::Point center, uint16_t radius) { - uint8_t const setColor[] { uint8_t((foregroundColor.color >> 8) & 0xff), - uint8_t(foregroundColor.color & 0xff) }; - int16_t f = 1 - radius; int16_t ddF_x = 0; int16_t ddF_y = -2 * radius; @@ -283,7 +246,7 @@ Ili9341::fillCircle( setClipping(center.getX() - radius, center.getY(), 2 * radius, 1); for (std::size_t i = 0; i < 2 * radius; ++i) - this->writeData(setColor, 2); + this->writeData(foregroundColor.color); while(x < y) { @@ -299,29 +262,24 @@ Ili9341::fillCircle( setClipping(center.getX() - x, center.getY() - y, 2 * x, 1); for (std::size_t i = 0; i < 2 * x; ++i) - this->writeData(setColor, 2); + this->writeData(foregroundColor.color); setClipping(center.getX() - y, center.getY() - x, 2 * y, 1); for (std::size_t i = 0; i < 2 * y; ++i) - this->writeData(setColor, 2); + this->writeData(foregroundColor.color); setClipping(center.getX() - x, center.getY() + y, 2 * x, 1); for (std::size_t i = 0; i < 2 * x; ++i) - this->writeData(setColor, 2); + this->writeData(foregroundColor.color); setClipping(center.getX() - y, center.getY() + x, 2 * y, 1); for (std::size_t i = 0; i < 2 * y; ++i) - this->writeData(setColor, 2); + this->writeData(foregroundColor.color); } } -template +template void -Ili9341::drawImageRaw(glcd::Point upperLeft, +Ili9341::drawImageRaw(glcd::Point upperLeft, uint16_t width, uint16_t height, modm::accessor::Flash data) { - uint8_t const setColor[] { uint8_t((foregroundColor.color >> 8) & 0xff), - uint8_t(foregroundColor.color & 0xff) }; - uint8_t const clearColor[] { uint8_t((backgroundColor.color >> 8) & 0xff), - uint8_t(backgroundColor.color & 0xff) }; - BatchHandle h(*this); setClipping(upperLeft.getX(), upperLeft.getY(), width, height); @@ -333,9 +291,9 @@ Ili9341::drawImageRaw(glcd::Point upper { uint8_t byte = data[(r / 8) * width + w]; if (byte & bit) - this->writeData(setColor, 2); + this->writeData(foregroundColor.color); else - this->writeData(clearColor, 2); + this->writeData(backgroundColor.color); } // TODO: optimize, use ROL (rotate left) bit <<= 1; @@ -344,25 +302,20 @@ Ili9341::drawImageRaw(glcd::Point upper } } -template +template void -Ili9341::drawRaw(glcd::Point upperLeft, +Ili9341::drawRaw(glcd::Point upperLeft, uint16_t width, uint16_t height, color::Rgb565* data) { BatchHandle h(*this); - uint16_t* buffer = (uint16_t*)data; - for(size_t i = 0; i < size_t(width*height); i++) { - buffer[i] = modm::fromBigEndian(buffer[i]); - } - setClipping(upperLeft.getX(), upperLeft.getY(), width, height); - this->writeData((uint8_t*)buffer, width * height * 2); + this->writeData(data, width * height); } -template +template void -Ili9341::setScrollArea( +Ili9341::setScrollArea( uint16_t topFixedRows, uint16_t bottomFixedRows, uint16_t firstRow) { BatchHandle h(*this); @@ -376,9 +329,9 @@ Ili9341::setScrollArea( this->writeCommand(Command::VerticalScrollDefinition, arg, 6); } -template +template void -Ili9341::scrollTo(uint16_t row) +Ili9341::scrollTo(uint16_t row) { BatchHandle h(*this); @@ -386,23 +339,20 @@ Ili9341::scrollTo(uint16_t row) this->writeCommand(Command::VerticalScrollStartAddr, arg, 2); } -template +template void -Ili9341::setColoredPixel( +Ili9341::setColoredPixel( int16_t x, int16_t y, color::Rgb565 const &color) { - auto const pixelColor { color }; - uint8_t const setColor[] { uint8_t((pixelColor.color >> 8) & 0xff), uint8_t(pixelColor.color & 0xff) }; - BatchHandle h(*this); this->setClipping(x, y, 1, 1); - this->writeData(setColor, 2); + this->writeData(color); } -template +template void -Ili9341::setClipping( +Ili9341::setClipping( uint16_t x, uint16_t y, uint16_t width, uint16_t height) { uint8_t buffer[4]; @@ -421,9 +371,9 @@ Ili9341::setClipping( this->writeCommand(Command::MemoryWrite); } -template +template void -Ili9341::drawBitmap(glcd::Point upperLeft, +Ili9341::drawBitmap(glcd::Point upperLeft, uint16_t width, uint16_t height, modm::accessor::Flash data) { BatchHandle h(*this); diff --git a/src/modm/driver/display/ili9341_parallel.hpp b/src/modm/driver/display/ili9341_parallel.hpp index 986573c8a5..41aff2e3c9 100644 --- a/src/modm/driver/display/ili9341_parallel.hpp +++ b/src/modm/driver/display/ili9341_parallel.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Pavel Pletenev + * Copyright (c) 2020, Thomas Sommer * * This file is part of the modm project. * @@ -7,9 +8,7 @@ * 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 - +#pragma once #include "ili9341.hpp" namespace modm @@ -36,14 +35,20 @@ class Ili9341ParallelInterface: public ili9341 for(std::size_t i=0; i(data); - size_t const length16 = length / 2; - for(std::size_t i=0; i, Reset, Backlight, BufferSize>; -} // namespace modm - -#endif // MODM_ILI9341_PARALLEL_HPP +} // namespace modm \ No newline at end of file diff --git a/src/modm/driver/display/ili9341_spi.hpp b/src/modm/driver/display/ili9341_spi.hpp index 59fe7a5856..cdb74966af 100644 --- a/src/modm/driver/display/ili9341_spi.hpp +++ b/src/modm/driver/display/ili9341_spi.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Mike Wolfram + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -7,12 +8,10 @@ * 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 - +#pragma once #include "ili9341.hpp" + #include -#include namespace modm { @@ -28,6 +27,7 @@ class Ili9341SPIInterface: public ili9341, public modm::SpiDevice SPI::setDataMode(SPI::DataMode::Mode0); SPI::setDataOrder(SPI::DataOrder::MsbFirst); }); + Cs::setOutput(modm::Gpio::High); Dc::setOutput(); } @@ -35,6 +35,8 @@ class Ili9341SPIInterface: public ili9341, public modm::SpiDevice modm_noinline void writeCommand(Command command) { + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit8); Dc::reset(); // enable command SPI::transferBlocking(i(command)); Dc::set(); // reset to data @@ -42,43 +44,75 @@ class Ili9341SPIInterface: public ili9341, public modm::SpiDevice modm_noinline void writeCommand(Command command, uint8_t const *args, std::size_t length) { + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit8); Dc::reset(); // enable command SPI::transferBlocking(i(command)); Dc::set(); // reset to data if (length != 0) { - SPI::transferBlocking(const_cast(args), nullptr, length); + SPI::transferBlocking(args, nullptr, length); } } void writeData(uint8_t const *data, std::size_t length) { - SPI::transferBlocking(const_cast(data), nullptr, length); + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit8); + SPI::transferBlocking(data, nullptr, length); + } + + void + writeData(color::Rgb565 rgb565) + { + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit16); + SPI::transferBlocking(rgb565.color); } + + void + writeDataRepeat(color::Rgb565 rgb565, std::size_t repeat) + { + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit16); + SPI::transferBlocking16(rgb565.color, repeat); + } + + void + writeData(color::Rgb565 const *data, std::size_t length) + { + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit16); + SPI::transferBlocking16(reinterpret_cast(data), nullptr, length); + } + void writeCommandValue8(Command command, uint8_t value) { + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit8); 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); + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit8); + Dc::reset(); SPI::transferBlocking(i(command) << 1); - SPI::Hal::setDataSize(SpiBase::DataSize::Bit8); - Dc::set(); // reset to data + Dc::set(); SPI::transferBlocking(b /*nullptr*/, buffer, length); } uint8_t readData(Command command) { + if constexpr ( spi::Support_DataSize_Bit16 ) + SPI::setDataSize(SPI::DataSize::Bit8); writeCommand(command); - return SPI::transferBlocking(0x00); + return SPI::transferBlocking(0); } public: @@ -100,11 +134,9 @@ class Ili9341SPIInterface: public ili9341, public modm::SpiDevice }; /// @ingroup modm_driver_ili9341 -template +template using Ili9341Spi = Ili9341< Ili9341SPIInterface, - Reset, Backlight, BufferSize>; - -} // namespace modm + Reset, Backlight>; -#endif // MODM_ILI9341_SPI_HPP +} // namespace modm \ No newline at end of file diff --git a/src/modm/driver/touch/touch2046.hpp b/src/modm/driver/touch/touch2046.hpp index 97f75ce12b..b750c55499 100644 --- a/src/modm/driver/touch/touch2046.hpp +++ b/src/modm/driver/touch/touch2046.hpp @@ -1,6 +1,7 @@ // coding: utf-8 /* * Copyright (c) 2021, Raphael Lehmann + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -10,67 +11,122 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_TOUCH2046_HPP -#define MODM_TOUCH2046_HPP +#pragma once + +#include +#include #include #include #include -#include -#include +#include namespace modm { /// \ingroup modm_driver_touch2046 struct touch2046 { - enum class Orientation { - Normal, - //... + enum class Control : uint8_t + { + START = Bit7, // 1: Marks the control byte + + A2 = Bit6, // see enum class ChDiff / ChSingleEnd + A1 = Bit5, + A0 = Bit4, + + MODE = Bit3, // see enum class Mode + REF = Bit2, // see enum class Reference + + PD1 = Bit1, // see enum class PowerDown + PD0 = Bit0, + }; + MODM_FLAGS8(Control); + + // Valid when Control::MODE is 0 + enum class ChDiff : uint8_t + { + Z1 = int(Control::A0) | int(Control::A1), + Z2 = int(Control::A2), + X = int(Control::A0) | int(Control::A2), + Y = int(Control::A0) + }; + typedef Configuration ChDiff_t; + + // Valid when Control::MODE is 1 + enum class ChSingleEnd : uint8_t + { + TEMP0 = 0, + Y = int(Control::A0), + VBAT = int(Control::A1), + Z1 = int(Control::A0) | int(Control::A1), + Z2 = int(Control::A2), + X = int(Control::A0) | int(Control::A2), + AUX = int(Control::A0) | int(Control::A1), + TEMP1 = int(Control::A0) | int(Control::A1) | int(Control::A2) + }; + typedef Configuration ChSingleEnd_t; + + enum class Mode : uint8_t + { + Res_12Bit = 0, + Res_8Bit = int(Control::MODE) + }; + typedef Configuration Mode_t; + + enum class Reference : uint8_t + { + Differential = 0, + SingleEnded = int(Control::REF) + }; + typedef Configuration Reference_t; + + enum class PowerDown : uint8_t + { + Auto = 0, + RefOff_AdcOn = int(Control::PD0), + RefOn_AdcOff = int(Control::PD1), + AlwaysOn = int(Control::PD0) | int(Control::PD1) }; + typedef Configuration PowerDown_t; + /** * Calibration values are used to calculate touch point * from raw values. * - * \ref FactorX and \ref FactorY scaled by 1000000 to avoid float - * arithmetic. E.g. to get a factor of 0.75 \ref FactorX has to be - * set to 750'000. - * * isTouched() = bool(Z > ThresholdZ) * * X = (rawX * FactorX / 1000000) + OffsetX * limited to [0, MaxX] * Y = (rawY * FactorY / 1000000) + OffsetY * limited to [0, MaxY] - * - * Orientation (rotation, mirror) are applied after the - * above operations. */ + struct Calibration { + int32_t FactorX = 24; int16_t OffsetX = 0; + int32_t FactorY = 24; int16_t OffsetY = 0; - int32_t FactorX = 1'000'000; - int32_t FactorY = 1'000'000; - uint16_t MaxX = 240; - uint16_t MaxY = 320; - uint16_t ThresholdZ = 1500; - Orientation orientation = Orientation::Normal; + uint16_t ThresholdZ = 1000; }; }; /** * \ingroup modm_driver_touch2046 - * \author Raphael Lehmann + * \author Raphael Lehmann, Thomas Sommer * * Datasheet TSC2046: https://www.ti.com/lit/ds/symlink/tsc2046.pdf */ -template < class SpiMaster, class Cs> +template < class SpiMaster, class Cs, int16_t Width, int16_t Height> class Touch2046 : public touch2046, public modm::SpiDevice< SpiMaster >, protected modm::NestedResumable<3> { public: + Touch2046(); + + using Orientation = modm::glcd::Orientation; + /** * Set calibration data * @@ -81,14 +137,6 @@ class Touch2046 : public touch2046, public modm::SpiDevice< SpiMaster >, protect cal = calibration; } - /** - * Get raw X, Y and Z values - * - * \return Position and intensity of touch point. Full int16_t range. - */ - modm::ResumableResult> - getRawValues(); - /** * Is screen touched? * @@ -98,40 +146,54 @@ class Touch2046 : public touch2046, public modm::SpiDevice< SpiMaster >, protect isTouched(); /** - * Get touch position + * Get touch position as tuple * * \return Position (X, Y) of touch point. */ modm::ResumableResult> getTouchPosition(); + /** + * Get touch position as modm::glcd::Point + * + * \return Point of touch point. + */ + modm::ResumableResult + getTouchPoint(); + + void setOrientation(Orientation orientation) + { this->orientation = orientation; } + + Orientation getOrientation() const + { return orientation; } + private: - static constexpr uint8_t MeasureZ1 = 0xB1; - static constexpr uint8_t MeasureZ2 = 0xC1; - static constexpr uint8_t MeasureX = 0xD1; - static constexpr uint8_t MeasureY = 0x91; - static constexpr uint8_t Powerdown = 0b1111'1100; - static constexpr std::array bufferWrite = { - MeasureZ1, 0x00, - MeasureZ2, 0x00, - MeasureY, 0x00, - MeasureX, 0x00, - MeasureY, 0x00, - MeasureX, 0x00, - MeasureY, 0x00, - (MeasureX & Powerdown), 0x00, - 0x00}; - std::array bufferRead = {}; - - uint16_t x = 0; - uint16_t y = 0; - uint16_t z = 0; + modm::ResumableResult + updateZ(); + + modm::ResumableResult + updateXY(); + + static constexpr Control_t Measure = Control::START | Mode_t(Mode::Res_12Bit) + | Reference_t(Reference::Differential) | PowerDown_t(PowerDown::RefOff_AdcOn); + + static constexpr std::array bufferWrite = { + (Measure | ChDiff_t(ChDiff::Z1)).value, + ((Measure | ChDiff_t(ChDiff::Z2)) & ~PowerDown_t::mask()).value, + (Measure | ChDiff_t(ChDiff::X)).value, + (Measure | ChDiff_t(ChDiff::Y)).value, + (Measure | ChDiff_t(ChDiff::X)).value, + (Measure | ChDiff_t(ChDiff::Y)).value, + (Measure | ChDiff_t(ChDiff::X)).value, + ((Measure | ChDiff_t(ChDiff::Y)) & ~PowerDown_t::mask()).value + }; + std::array bufferRead = {}; +public: + uint16_t x{0}, y{0}, z{0}; Calibration cal; + Orientation orientation = Orientation::Portrait90; }; +} // namespace modm -} // modm namespace - -#include "touch2046_impl.hpp" - -#endif // MODM_TOUCH2046_HPP +#include "touch2046_impl.hpp" \ No newline at end of file diff --git a/src/modm/driver/touch/touch2046.lb b/src/modm/driver/touch/touch2046.lb index 56d09b40d6..d419f1dd63 100644 --- a/src/modm/driver/touch/touch2046.lb +++ b/src/modm/driver/touch/touch2046.lb @@ -35,4 +35,6 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/driver/touch" env.copy("touch2046.hpp") - env.copy("touch2046_impl.hpp") + + env.substitutions = {'spi_16bit_hardware': env[":target"].has_driver("spi:stm32")} + env.template("touch2046_impl.hpp.in") diff --git a/src/modm/driver/touch/touch2046_impl.hpp b/src/modm/driver/touch/touch2046_impl.hpp index c5f9718363..6ee35bcc1f 100644 --- a/src/modm/driver/touch/touch2046_impl.hpp +++ b/src/modm/driver/touch/touch2046_impl.hpp @@ -1,6 +1,7 @@ // coding: utf-8 /* * Copyright (c) 2021, Raphael Lehmann + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -10,70 +11,121 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_TOUCH2046_HPP - #error "Don't include this file directly, use 'touch2046.hpp' instead!" -#endif +#pragma once +#include "touch2046.hpp" +#include -template < class SpiMaster, class Cs > -modm::ResumableResult> -modm::Touch2046::getRawValues() +template +modm::Touch2046::Touch2046() +{ + if constexpr ( spi::Support_DataSize_Bit16 ) { + this->attachConfigurationHandler([]() { + SpiMaster::setDataMode(SpiMaster::DataMode::Mode0); + SpiMaster::setDataOrder(SpiMaster::DataOrder::MsbFirst); + SpiMaster::setDataSize(SpiMaster::DataSize::Bit16); + }); + } else { + this->attachConfigurationHandler([]() { + SpiMaster::setDataMode(SpiMaster::DataMode::Mode0); + SpiMaster::setDataOrder(SpiMaster::DataOrder::MsbFirst); + }); + } + + Cs::setOutput(true); +} + +template +modm::ResumableResult +modm::Touch2046::updateZ() { RF_BEGIN(); RF_WAIT_UNTIL(this->acquireMaster()); Cs::reset(); - RF_CALL(SpiMaster::transfer( - bufferWrite.data(), - reinterpret_cast(bufferRead.data()) + 1, - 17)); + RF_CALL(SpiMaster::transfer16(bufferWrite.begin(), bufferWrite.begin() + 3, bufferRead.begin())); + + z = 4095 + (bufferRead[1] >> 3) - (bufferRead[2] >> 3); - z = 4095 + (modm::fromBigEndian(bufferRead[1]) >> 3) - - (modm::fromBigEndian(bufferRead[2]) >> 3); + if (this->releaseMaster()) + Cs::set(); - y = (modm::fromBigEndian(bufferRead[3]) >> 3) - + (modm::fromBigEndian(bufferRead[5]) >> 3) - + (modm::fromBigEndian(bufferRead[7]) >> 3); + RF_END_RETURN(); +} - x = (modm::fromBigEndian(bufferRead[4]) >> 3) - + (modm::fromBigEndian(bufferRead[6]) >> 3) - + (modm::fromBigEndian(bufferRead[8]) >> 3); +template +modm::ResumableResult +modm::Touch2046::updateXY() +{ + RF_BEGIN(); - if (this->releaseMaster()) { + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + RF_CALL(SpiMaster::transfer16(bufferWrite.begin() + 2, bufferWrite.end(), bufferRead.begin())); + + if (this->releaseMaster()) Cs::set(); - } - RF_END_RETURN(std::make_tuple(x, y, z)); + // TODO rewrite with algorithms + x = (bufferRead[1] >> 3) + (bufferRead[3] >> 3) + (bufferRead[5] >> 3); + y = (bufferRead[2] >> 3) + (bufferRead[4] >> 3) + (bufferRead[6] >> 3); + + static constexpr int scale_shift = 10; + x = std::clamp((((uint32_t)(x * cal.FactorX) >> scale_shift) + cal.OffsetX), 0, Width); + y = std::clamp((((uint32_t)(y * cal.FactorY) >> scale_shift) + cal.OffsetY), 0, Height); + + RF_END_RETURN(); } -template < class SpiMaster, class Cs > +template modm::ResumableResult -modm::Touch2046::isTouched() +modm::Touch2046::isTouched() { RF_BEGIN(); - std::tie(std::ignore, std::ignore, z) = RF_CALL(getRawValues()); + RF_CALL(updateZ()); RF_END_RETURN(z > cal.ThresholdZ); } -template < class SpiMaster, class Cs > -modm::ResumableResult> -modm::Touch2046::getTouchPosition() +template +modm::ResumableResult +modm::Touch2046::getTouchPoint() { RF_BEGIN(); + RF_CALL(updateXY()); + + switch(orientation) { + case Orientation::Landscape0: + RF_RETURN(modm::glcd::Point(Height - y, Width - x)); + case Orientation::Portrait90: + RF_RETURN(modm::glcd::Point(x, Height - y)); + case Orientation::Landscape180: + RF_RETURN(modm::glcd::Point(y, x)); + case Orientation::Portrait270: + RF_RETURN(modm::glcd::Point(Width - x, y)); + } - std::tie(x, y, std::ignore) = RF_CALL(getRawValues()); - - x = std::min( - ((static_cast(x * cal.FactorX) / 1'000'000) - + cal.OffsetX), - cal.MaxX); - y = std::min( - ((static_cast(y * cal.FactorY) / 1'000'000) - + cal.OffsetY), - cal.MaxY); + RF_END(); +} - // todo: orientation processing +template +modm::ResumableResult> +modm::Touch2046::getTouchPosition() +{ + RF_BEGIN(); + RF_CALL(updateXY()); + + switch(orientation) { + case Orientation::Landscape0: + RF_RETURN(std::make_tuple(Height - y, Width - x)); + case Orientation::Portrait90: + RF_RETURN(std::make_tuple(x, Height - y)); + case Orientation::Landscape180: + RF_RETURN(std::make_tuple(y, x)); + case Orientation::Portrait270: + RF_RETURN(std::make_tuple(Width - x, y)); + } - RF_END_RETURN(std::make_tuple(x, y)); + RF_END(); } diff --git a/src/modm/math/utils/binary.hpp b/src/modm/math/utils/binary.hpp new file mode 100644 index 0000000000..2d759724db --- /dev/null +++ b/src/modm/math/utils/binary.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, Thomas Sommer + * + * 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 + +// TODO I'm not sure about this :/ + +namespace modm { + template + concept unsigned_integral_max8 = std::unsigned_integral and std::numeric_limits::digits <= 8; + + template + concept unsigned_integral_max16 = std::unsigned_integral and std::numeric_limits::digits <= 16; + + template + concept unsigned_integral_max32 = std::unsigned_integral and std::numeric_limits::digits <= 32; + + template + concept unsigned_integral_max64 = std::unsigned_integral and std::numeric_limits::digits <= 64; +} \ No newline at end of file diff --git a/src/modm/platform/dma/stm32/dma.hpp.in b/src/modm/platform/dma/stm32/dma.hpp.in index 5f56237bbf..96d55f33ae 100644 --- a/src/modm/platform/dma/stm32/dma.hpp.in +++ b/src/modm/platform/dma/stm32/dma.hpp.in @@ -196,6 +196,18 @@ public: ChannelHal::setPeripheralAddress(address); } + static void + setMemoryDataSize(MemoryDataSize memoryDataSize) + { + ChannelHal::setMemoryDataSize(memoryDataSize); + } + + static void + setPeripheralDataSize(PeripheralDataSize peripheralDataSize) + { + ChannelHal::setPeripheralDataSize(peripheralDataSize); + } + /** * Enable/disable memory increment * diff --git a/src/modm/platform/dma/stm32/dma_base.hpp.in b/src/modm/platform/dma/stm32/dma_base.hpp.in index 0b1f12a7d8..ec22d427f8 100644 --- a/src/modm/platform/dma/stm32/dma_base.hpp.in +++ b/src/modm/platform/dma/stm32/dma_base.hpp.in @@ -162,6 +162,7 @@ public: Bit16 = HalfWord, Word = {{ reg_prefix }}_MSIZE_1, Bit32 = Word, + All = DMA_CCR_MSIZE_0 | DMA_CCR_MSIZE_1 }; enum class @@ -173,6 +174,7 @@ public: Bit16 = HalfWord, Word = {{ reg_prefix }}_PSIZE_1, Bit32 = Word, + All = DMA_CCR_PSIZE_0 | DMA_CCR_PSIZE_1 }; enum class diff --git a/src/modm/platform/dma/stm32/dma_hal.hpp.in b/src/modm/platform/dma/stm32/dma_hal.hpp.in index 7d123cbd05..dafc4b6556 100644 --- a/src/modm/platform/dma/stm32/dma_hal.hpp.in +++ b/src/modm/platform/dma/stm32/dma_hal.hpp.in @@ -254,6 +254,22 @@ public: %% endif } + static void + setMemoryDataSize(MemoryDataSize memoryDataSize) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CCR &= ~uint32_t(MemoryDataSize::All); + Base->CCR |= uint32_t(memoryDataSize); + } + + static void + setPeripheralDataSize(PeripheralDataSize peripheralDataSize) + { + DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; + Base->CCR &= ~uint32_t(PeripheralDataSize::All); + Base->CCR |= uint32_t(peripheralDataSize); + } + /** * Enable/disable memory increment * @@ -294,9 +310,11 @@ public: /** * Set length of data to transfer + * + * @param lenght range: 0-65535 */ static void - setDataLength(std::size_t length) + setDataLength(uint16_t length) { DMA_Channel_TypeDef *Base = (DMA_Channel_TypeDef *) CHANNEL_BASE; %% if dmaType in ["stm32-channel-request", "stm32-channel", "stm32-mux"] diff --git a/src/modm/platform/spi/at90_tiny_mega/module.lb b/src/modm/platform/spi/at90_tiny_mega/module.lb index 46fa67ee62..ec0f5457fc 100644 --- a/src/modm/platform/spi/at90_tiny_mega/module.lb +++ b/src/modm/platform/spi/at90_tiny_mega/module.lb @@ -48,8 +48,9 @@ class Instance(Module): env.substitutions = properties env.outbasepath = "modm/src/modm/platform/spi" - env.template("spi.hpp.in", "spi{}.hpp".format(self.instance)) + env.template("spi_base.hpp.in", "spi{}.hpp".format(self.instance)) env.template("spi_master.hpp.in", "spi_master{}.hpp".format(self.instance)) + env.template("spi_master_impl.hpp.in", "spi_master{}_impl.hpp".format(self.instance)) env.template("spi_master.cpp.in", "spi_master{}.cpp".format(self.instance)) def init(module): @@ -87,6 +88,7 @@ def build(env): env.outbasepath = "modm/src/modm/platform/spi" if "instance" not in driver: + env.template("spi_base.hpp.in") env.template("spi_master.hpp.in") + env.template("spi_master_impl.hpp.in") env.template("spi_master.cpp.in") - env.template("spi.hpp.in") diff --git a/src/modm/platform/spi/at90_tiny_mega/spi.hpp.in b/src/modm/platform/spi/at90_tiny_mega/spi.hpp.in index 4eec7ada92..287c9645d3 100644 --- a/src/modm/platform/spi/at90_tiny_mega/spi.hpp.in +++ b/src/modm/platform/spi/at90_tiny_mega/spi.hpp.in @@ -9,16 +9,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // ---------------------------------------------------------------------------- - -#ifndef MODM_AVR_SPI_HPP -#define MODM_AVR_SPI_HPP +#pragma once #include /// @cond %% if partname in ["attiny40"] // The avr-libc Header files for the ATtiny40 is missing the following -// defines although they are meantioned in the datasheet. +// defines although they are mentioned in the datasheet. #define SPR0 0 #define SPR1 1 #define CPHA 2 @@ -51,10 +49,7 @@ %% endif /// @endcond -namespace modm -{ - -namespace platform +namespace modm::platform { /// @ingroup modm_platform_spi @@ -71,10 +66,29 @@ struct Spi Div64 = (1 << SPR1{{ id }}), Div128 = (1 << SPR1{{ id }}) | (1 << SPR0{{ id }}), }; -}; -} // namespace platform + enum State : uint8_t { + Idle = Bit6, // Transaction is running + Repeat = Bit7, // Send same tx multiple times + }; + MODM_FLAGS8(State); + + enum DataType : uint8_t { + Byte = 0, // 1 byte + HalfWord = 1, // 2 bytes + Word = 2, // 4 bytes + // WordWord = 3 // 8 bytes + }; + typedef Value DataType_t; +}; -} // namespace modm + enum Type : uint8_t { + Byte = 0, // 1 byte + HalfWord = 1, // 2 bytes + Word = 2, // 4 bytes + // WordWord = 3 // 8 bytes + }; + typedef Value Type_t; +}; -#endif // MODM_AVR_SPI_HPP +} // namespace modm::platform \ No newline at end of file diff --git a/src/modm/platform/spi/at90_tiny_mega/spi_base.hpp.in b/src/modm/platform/spi/at90_tiny_mega/spi_base.hpp.in new file mode 100644 index 0000000000..153dfdbd76 --- /dev/null +++ b/src/modm/platform/spi/at90_tiny_mega/spi_base.hpp.in @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013-2015, 2017, 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/. + */ +// ---------------------------------------------------------------------------- +#pragma once + +#include + +/// @cond +%% if partname in ["attiny40"] +// The avr-libc Header files for the ATtiny40 is missing the following +// defines although they are mentioned in the datasheet. +#define SPR0 0 +#define SPR1 1 +#define CPHA 2 +#define CPOL 3 +#define MSTR 4 +#define DORD 5 +#define SPE 6 +#define SPIE 7 + +#define SPI2X 0 +#define WCOL 6 +#define SPIF 7 +%% elif partname in ["atmega164pa", "atmega324pa"] +// The avr-libc Header files for the ATmega{16,32}4pa are declaring the +// following registers with a trailing zero. Why, Atmel, whyyyy? +#define SPCR SPCR0 +#define SPR0 SPR00 +#define SPR1 SPR10 +#define CPHA CPHA0 +#define CPOL CPOL0 +#define MSTR MSTR0 +#define DORD DORD0 +#define SPE SPE0 +#define SPIE SPIE0 +#define SPSR SPSR0 +#define SPI2X SPI2X0 +#define WCOL WCOL0 +#define SPIF SPIF0 +#define SPDR SPDR0 +%% endif +/// @endcond + +namespace modm::platform +{ + +/// @ingroup modm_platform_spi +struct SpiBase +{ + enum class + Prescaler : uint8_t + { + Div2 = 0x80 | 0, + Div4 = 0, + Div8 = 0x80 | (1 << SPR0{{ id }}), + Div16 = (1 << SPR0{{ id }}), + Div32 = 0x80 | (1 << SPR1{{ id }}), + Div64 = (1 << SPR1{{ id }}), + Div128 = (1 << SPR1{{ id }}) | (1 << SPR0{{ id }}), + }; +}; + +} // namespace modm::platform \ No newline at end of file diff --git a/src/modm/platform/spi/at90_tiny_mega/spi_master.cpp.in b/src/modm/platform/spi/at90_tiny_mega/spi_master.cpp.in index 685ad7afc5..78be20bf14 100644 --- a/src/modm/platform/spi/at90_tiny_mega/spi_master.cpp.in +++ b/src/modm/platform/spi/at90_tiny_mega/spi_master.cpp.in @@ -2,6 +2,7 @@ * Copyright (c) 2013-2018, Niklas Hauser * Copyright (c) 2014, Sascha Schade * Copyright (c) 2017, Fabian Greif + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -11,37 +12,172 @@ */ // ---------------------------------------------------------------------------- -#include "spi_master{{ id }}.hpp" +#include "spi_master.hpp" #include #include #include #include +#include -// bit 7 (0x80) is used for transfer 1 byte -// bit 6 (0x40) is used for transfer multiple byte -// bit 5-0 (0x3f) are used to store the acquire count -uint8_t -modm::platform::SpiMaster{{ id }}::state(0); +// ---------------------------------------------------------------------------- + +uint8_t modm::platform::SpiMaster{{ id }}::count(0); -void * -modm::platform::SpiMaster{{ id }}::context(nullptr); +void *modm::platform::SpiMaster{{ id }}::context(nullptr); modm::Spi::ConfigurationHandler modm::platform::SpiMaster{{ id }}::configuration(nullptr); + +// ---------------------------------------------------------------------------- +using State = modm::platform::SpiMaster::State; +using DataType = modm::platform::SpiMaster::DataType; + +modm::platform::SpiMaster::State_t +modm::platform::SpiMaster{{ id }}::state(0); + +uint8_t modm::platform::SpiMaster{{ id }}::shift(0); + +uint32_t modm::platform::SpiMaster{{ id }}::temp(0); + +modm::platform::SpiMaster{{ id }}::unsigned_types_const + modm::platform::SpiMaster{{ id }}::tx{nullptr}; + +modm::platform::SpiMaster{{ id }}::unsigned_types_const + modm::platform::SpiMaster{{ id }}::tx_end{nullptr}; + +modm::platform::SpiMaster{{ id }}::unsigned_types + modm::platform::SpiMaster{{ id }}::rx{nullptr}; + +modm::platform::SpiMaster{{ id }}::unsigned_types + modm::platform::SpiMaster{{ id }}::rx_end{nullptr}; + // ---------------------------------------------------------------------------- -void -modm::platform::SpiMaster{{ id }}::initialize(Prescaler prescaler) +MODM_ISR(SPI_STC) { - modm::atomic::Lock lock; + modm::platform::SpiMaster{{ id }}::isr_handler(); +} - SPCR{{ id }} = (1 << SPE{{ id }}) | (1 << MSTR{{ id }}) | (static_cast(prescaler) & ~0x80); - SPSR{{ id }} = (static_cast(prescaler) & 0x80) ? (1 << SPI2X{{ id }}) : 0; - state &= 0x3f; +void modm::platform::SpiMaster{{ id }}::isr_handler() +{ + constexpr State_t state_normal = State::Idle; + constexpr State_t state_repeat = State::Idle | State::Repeat; + + switch (state.value) { + case state_normal.value | DataType::Byte: + // Store received byte + if (rx.u8) + *rx.u8++ = SPDR{{ id }}; + if (++tx.u8 == tx_end.u8) + { + // Job done, disable Interrupt. + SPCR{{ id }} &= ~(1 << SPIE); + return; + } + // Transmit next byte + SPDR{{ id }} = *tx.u8; + return; + case state_repeat.value | DataType::Byte: + if(++tx.repeat == tx_end.repeat) + { + // Job done, disable Interrupt. + SPCR{{ id }} &= ~(1 << SPIE); + return; + } + // Transmit byte again + SPDR{{ id }} = uint8_t(temp); + return; + case state_normal.value | DataType::HalfWord: + if (shift) + { + // Store received byte + if (rx.u8) + *rx.u16 = uint16_t(SPDR{{ id }}) << 8; // << shift TODO ?? OK? + // Transmit next byte + shift = 0; + SPDR{{ id }} = *tx.u16; + return; + } + // Store received byte + if (rx.u16) + *rx.u16++ |= SPDR{{ id }}; + if (++tx.u16 == tx_end.u16) + { + // Job done, disable Interrupt. + SPCR{{ id }} &= ~(1 << SPIE); + return; + } + // Transmit next halfword + shift = 1 * 8; + SPDR{{ id }} = *tx.u16 >> shift; + return; + case state_repeat.value | DataType::HalfWord: + if (shift) + { + // Transmit next sub-byte + shift = 0; + SPDR{{ id }} = uint16_t(temp); + return; + } + if(++tx.repeat == tx_end.repeat) + { + // Job done, disable Interrupt. + SPCR{{ id }} &= ~(1 << SPIE); + return; + } + // Transmit halfword again + shift = 1 * 8; + SPDR{{ id }} = uint16_t(temp) >> shift; + return; + case state_normal.value | DataType::Word: + if (shift) + { + // Store received sub-byte + if (rx.u32) + *rx.u32 |= uint32_t(SPDR{{ id }}) << shift; + // Transmit next sub-byte + shift -= 8; + SPDR{{ id }} = *tx.u32 >> shift; + return; + } + // Store received sub-byte + if (rx.u32) { + *rx.u32++ |= SPDR{{ id }}; + *rx.u32 = 0; + } + if (++tx.u32 == tx_end.u32) + { + // Job done, disable Interrupt. + SPCR{{ id }} &= ~(1 << SPIE); + return; + } + // Transmit next word + shift = 3 * 8; + SPDR{{ id }} = *tx.u32 >> shift; + return; + case state_repeat.value | DataType::Word: + if (shift) + { + // Transmit next sub-byte + shift -= 8; + SPDR{{ id }} = uint32_t(temp) >> shift; + return; + } + if(++tx.repeat == tx_end.repeat) + { + // Job done, disable Interrupt. + SPCR{{ id }} &= ~(1 << SPIE); + return; + } + // Transmit word again + shift = 3 * 8; + SPDR{{ id }} = uint32_t(temp) >> shift; + return; + } } -// ---------------------------------------------------------------------------- +// --------------------------------------------------------------------------- uint8_t modm::platform::SpiMaster{{ id }}::acquire(void *ctx, ConfigurationHandler handler) @@ -49,7 +185,7 @@ modm::platform::SpiMaster{{ id }}::acquire(void *ctx, ConfigurationHandler handl if (context == nullptr) { context = ctx; - state = (state & ~0x3f) | 1; + count = 1; // if handler is not nullptr and is different from previous configuration if (handler and configuration != handler) { configuration = handler; @@ -59,111 +195,25 @@ modm::platform::SpiMaster{{ id }}::acquire(void *ctx, ConfigurationHandler handl } if (ctx == context) - return (++state & 0x3f); + return ++count; return 0; } uint8_t -modm::platform::SpiMaster{{ id }}::release(void *ctx) +modm::platform::SpiMaster::release(void *ctx) { - if (ctx == context) - { - if ((--state & 0x3f) == 0) - context = nullptr; - } - return (state & 0x3f); -} -// ---------------------------------------------------------------------------- + if (ctx == context and --count == 0) + context = nullptr; -modm::ResumableResult -modm::platform::SpiMaster{{ id }}::transfer(uint8_t data) -{ -%% if options["busywait"] - SPDR{{ id }} = data; - - // wait for transfer to finish - while (!(SPSR{{ id }} & (1 << SPIF{{ id }}))) - ; - - data = SPDR{{ id }}; - return {modm::rf::Stop, data}; -%% else - // 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. - - // MSB != Bit7 ? - if ( !(state & Bit7) ) - { - // start transfer by copying data into register - SPDR{{ id }} = data; - - // set MSB = Bit7 - state |= Bit7; - } - - // wait for transfer to finish - if (!(SPSR{{ id }} & (1 << SPIF{{ id }}))) - return {modm::rf::Running}; - - data = SPDR{{ id }}; - state &= ~Bit7; - return {modm::rf::Stop, data}; -%% endif + return count; } -modm::ResumableResult -modm::platform::SpiMaster{{ id }}::transfer(const uint8_t *tx, uint8_t *rx, std::size_t length) +void modm::platform::SpiMaster{{ id }}::initialize(Prescaler prescaler) { -%% if options["busywait"] - for (std::size_t index = 0; index < length; index++) - { - modm::ResumableResult result = transfer(tx ? tx[index] : 0); - if (rx) rx[index] = result.getResult(); - } - return {modm::rf::Stop}; -%% else - // 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 (Bit6 of state): - // 1. initialize index, and - // 2. wait for 1-byte transfer to finish. - - // we need to globally remember which byte we are currently transferring - static std::size_t index = 0; - - // we are only interested in Bit6 - switch(state & Bit6) - { - case 0: - // we will only visit this state once - state |= Bit6; - - // initialize index and check range - index = 0; - while (index < length) - { - default: - { - // call the resumable function - modm::ResumableResult result = transfer(tx ? tx[index] : 0); - - // if the resumable function is still running, so are we - if (result.getState() > modm::rf::NestingError) - return {modm::rf::Running}; - - // if rx != 0, we copy the result into the array - if (rx) rx[index] = result.getResult(); - } - index++; - } + modm::atomic::Lock lock; - // clear the state - state &= ~Bit6; - return {modm::rf::Stop}; - } -%% endif -} + SPCR{{ id }} = (1 << SPE) | (1 << MSTR) | (static_cast(prescaler) & ~0x80); + SPSR{{ id }} = (static_cast(prescaler) & 0x80) ? (1 << SPI2X) : 0; + state = State(0); +} \ No newline at end of file diff --git a/src/modm/platform/spi/at90_tiny_mega/spi_master.hpp.in b/src/modm/platform/spi/at90_tiny_mega/spi_master.hpp.in index c7339d22be..a4b63d7368 100644 --- a/src/modm/platform/spi/at90_tiny_mega/spi_master.hpp.in +++ b/src/modm/platform/spi/at90_tiny_mega/spi_master.hpp.in @@ -2,6 +2,7 @@ * Copyright (c) 2013-2017, Niklas Hauser * Copyright (c) 2014, Sascha Schade * Copyright (c) 2017, Fabian Greif + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -10,15 +11,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // ---------------------------------------------------------------------------- - -#ifndef MODM_AVR_SPI_MASTER{{ id }}_HPP -#define MODM_AVR_SPI_MASTER{{ id }}_HPP +#pragma once #include #include -#include #include +#include +#include %% set sck = "Sck" %% if target["type"] in ["u2"] @@ -26,35 +26,45 @@ %% set sck = "Sclk" %% endif -#include "spi{{ id }}.hpp" - -namespace modm -{ +#include "spi{{ id }}_base.hpp" -namespace platform +namespace modm::platform { /** - * Implementation of the SpiMaster. - * - * The fast SPI clock speeds make it unreasonable to use an interrupt - * based approach to shifting out each byte of the data, since the interrupt - * handling might decrease performance over busy waiting especially for - * targets operating at low CPU frequencies. - * Therefore the asynchronous methods are implemented synchronously. + * Implementation of the SpiMaster{{ id }}. * * @warning When the !SS pin is configured in input mode and pulled low, the * hardware will switch into SPI slave mode. It is therefore necessary * to configure the pin either in output mode or to pull it high. * - * @ingroup modm_platform_spi modm_platform_spi_{{id}} * @author Niklas Hauser + * @ingroup modm_platform_spi modm_platform_spi_{{id}} */ -class SpiMaster{{ id }} : public ::modm::SpiMaster, private Spi +class SpiMaster{{ id }} : public modm::SpiMaster, private SpiBase { - static uint8_t state; +protected: + static uint8_t count; static void *context; static ConfigurationHandler configuration; + + static State_t state; + static uint8_t shift; + static uint32_t temp; + + union unsigned_types_const { + uint8_t const *u8; + uint16_t const *u16; + uint32_t const *u32; + uint16_t repeat; + }; + static unsigned_types_const tx, tx_end; + union unsigned_types { + uint8_t *u8; + uint16_t *u16; + uint32_t *u32; + }; + static unsigned_types rx, rx_end; public: /// Spi Data Mode, Mode0 is the most common mode enum class @@ -73,7 +83,7 @@ public: connect() { using Connector = GpioConnector; - using Sck = typename Connector::template GetSignal; + using Sck = typename Connector::template GetSignal; using Mosi = typename Connector::template GetSignal; using Miso = typename Connector::template GetSignal; @@ -85,7 +95,7 @@ public: } template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(5) > - static inline void + static void initialize() { constexpr auto result = modm::Prescaler::from_power(SystemClock::Spi, baudrate, 2, 128); @@ -114,56 +124,181 @@ public: static void setDataOrder(DataOrder order) { - if (order == DataOrder::LsbFirst) { + if (order == DataOrder::LsbFirst) SPCR{{ id }} |= (1 << DORD{{ id }}); - } else { + else SPCR{{ id }} &= ~(1 << DORD{{ id }}); - } } - static uint8_t acquire(void *ctx, ConfigurationHandler handler = nullptr); static uint8_t release(void *ctx); + // TODO Complete the docs + + /** + * @brief + * + * @param data + * @return modm::ResumableResult + */ + template + static modm::ResumableResult + transmit(const T data); + + // OPTIMIZE specialize transmit(byte) without ISR overhead + // static modm::ResumableResult + // transmit(const uint8_t data); + + /** + * @brief + * + * @param data + * @return modm::ResumableResult + */ + template + static modm::ResumableResult + transmit(const T data, std::size_t repeat); + + /** + * @brief Send and optional receive data in range begin()->end(). + * Works with C-array or std::array for tx / rx + * + * @param tx_first Pointer to first element to send. f.e. tx.begin() OR tx.begin() + 1 + * @param tx_first Pointer to one after last element to send: f.e. tx.end() OR tx.end() - 4 + * @param rx_first Pointer to first element to receive: f.e. rx_data.begin() OR rx_data.begin() + 1 + */ + template + static modm::ResumableResult + transmit(const T *tx_first, const T *tx_last, T *rx_first = nullptr); + /** + * @brief Send a std::array + * + * @param tx_array std::array with data to send + */ + template + static modm::ResumableResult + transmit(const C &tx_arr) + { return transmit(tx_arr.begin(), tx_arr.end()); }; + + /** + * @brief Transmit and receive a std::array + * + * @param tx_arr std::array with data to send + * @param rx_arr std::array for received data + */ + template + static modm::ResumableResult + transmit(const C &tx_arr, C &rx_arr) + { return transmit(tx_arr.begin(), tx_arr.end(), rx_arr.begin()); }; + +// -- Backwards compatible API -------------------------------------------------------- + + // Backwards compatible API static uint8_t - transferBlocking(uint8_t data) - { -%% if options["busywait"] - return transfer(data).getResult(); -%% else - return RF_CALL_BLOCKING(transfer(data)); -%% endif - } + transferBlocking(const uint8_t data) + { return RF_CALL_BLOCKING(transmit(data)); } + + static uint16_t + transferBlocking16(const uint16_t data) + { return RF_CALL_BLOCKING(transmit(data)); } + + static uint32_t + transferBlocking32(const uint32_t data) + { return RF_CALL_BLOCKING(transmit(data)); } + + static void + transferBlocking(const uint8_t data, std::size_t repeat) + { RF_CALL_BLOCKING(transmit(data, repeat)); } + + static void + transferBlocking16(const uint16_t data, std::size_t repeat) + { RF_CALL_BLOCKING(transmit(data, repeat)); } + + static void + transferBlocking32(const uint32_t data, std::size_t repeat) + { RF_CALL_BLOCKING(transmit(data, repeat)); } static void transferBlocking(const uint8_t *tx, uint8_t *rx, std::size_t length) - { -%% if options["busywait"] - transfer(tx, rx, length); -%% else - RF_CALL_BLOCKING(transfer(tx, rx, length)); -%% endif - } + { RF_CALL_BLOCKING(transmit(tx, tx + length, rx)); } + static void + transferBlocking16(const uint16_t *tx, uint16_t *rx, std::size_t length) + { RF_CALL_BLOCKING(transmit(tx, tx + length, rx)); } + static void + transferBlocking32(const uint32_t *tx, uint32_t *rx, std::size_t length) + { RF_CALL_BLOCKING(transmit(tx, tx + length, rx)); } static modm::ResumableResult - transfer(uint8_t data); + transfer(const uint8_t data) { + return transmit(data); + } + + static modm::ResumableResult + transfer16(const uint16_t data) { + return transmit(data); + } + + static modm::ResumableResult + transfer32(const uint32_t data) { + return transmit(data); + } static modm::ResumableResult - transfer(const uint8_t *tx, uint8_t *rx, std::size_t length); + transfer(const uint8_t tx, const std::size_t repeat) { + return transmit(tx, repeat); + } + + static modm::ResumableResult + transfer16(const uint16_t tx, const std::size_t repeat) { + return transmit(tx, repeat); + } + + static modm::ResumableResult + transfer32(const uint32_t tx, const std::size_t repeat) { + return transmit(tx, repeat); + } + + static modm::ResumableResult + transfer(const uint8_t *tx, uint8_t *rx, const std::size_t length) { + return transmit(tx, tx + length, rx); + } + + static modm::ResumableResult + transfer16(const uint16_t *tx, uint16_t *rx, const std::size_t length) { + return transmit(tx, tx + length, rx); + } + + static modm::ResumableResult + transfer32(const uint32_t *tx, uint32_t *rx, const std::size_t length) { + return transmit(tx, tx + length, rx); + } // end documentation inherited -protected: + // TODO friend with MODM_ISR(SPI_STC) possible? + static void isr_handler(); + +private: + template + static void + setup_isr(); + + template + static void + setup_isr(const T* tx_first, const T* tx_last); + + static bool isRunning() { + return SPCR{{ id }} & (1 << SPIE); + } + static void initialize(Prescaler prescaler); }; -} // namespace platform - -} // namespace modm +} // namespace modm::platform -#endif // MODM_AVR_SPI_MASTER_HPP +#include "spi_master_impl.hpp" \ No newline at end of file diff --git a/src/modm/platform/spi/at90_tiny_mega/spi_master_impl.hpp.in b/src/modm/platform/spi/at90_tiny_mega/spi_master_impl.hpp.in new file mode 100644 index 0000000000..8b6f5a522d --- /dev/null +++ b/src/modm/platform/spi/at90_tiny_mega/spi_master_impl.hpp.in @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021, Thomas Sommer + * + * 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 "spi_master.hpp" + +#include + +template +modm::ResumableResult +modm::platform::SpiMaster{{ id }}::transmit(const T data) +{ + // Interrupt enabled? transmission in progress! + if(SPCR{{ id }} & (1 << SPIE)) { + return {modm::rf::Running}; + } else if(!state.all(State::Idle)) { + state.set(State::Idle); + state.reset(State::Repeat); + + temp = data; + tx.repeat = 0; + tx_end.repeat = 1; + + setup_isr(); + return {modm::rf::Running}; + } + + state.reset(State::Idle); + + return {modm::rf::Stop, T(temp | SPDR{{ id }})}; +} + +template +modm::ResumableResult +modm::platform::SpiMaster{{ id }}::transmit(const T data, std::size_t repeat) +{ + // Interrupt enabled? transmission in progress! + if(SPCR{{ id }} & (1 << SPIE)) { + return {modm::rf::Running}; + } else if(!state.all(State::Idle)) { + state.set(State::Idle); + state.set(State::Repeat); + + temp = data; + tx.repeat = 0; + tx_end.repeat = repeat; + + setup_isr(); + return {modm::rf::Running}; + } + + state.reset(State::Idle); + return {modm::rf::Stop}; +} + +template +modm::ResumableResult +modm::platform::SpiMaster{{ id }}::transmit(const T *tx_first, const T *tx_last, T *rx_first) +{ + modm_assert_continue_fail_debug(tx_first < tx_last, "SpiMaster::transmit", "tx_first > tx_last"); + modm_assert_continue_fail_debug( + tx_last < rx_first + || rx_first + (tx_last - tx_first) <= tx_first, + "SpiMaster::transmit", "rx overlaps tx" + ); + + // Interrupt enabled? transmission in progress! + if(SPCR{{ id }} & (1 << SPIE)) { + return {modm::rf::Running}; + } else if(!state.all(State::Idle)) { + state.set(State::Idle); + state.reset(State::Repeat); + + setup_isr(tx_first, tx_last); + return {modm::rf::Running}; + } + + state.reset(State::Idle); + return {modm::rf::Stop}; +} + +template +void modm::platform::SpiMaster{{ id }}::setup_isr() +{ + if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::Byte); + // Transmit first byte + SPDR{{ id }} = uint8_t(temp); + } + else if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::HalfWord); + shift = 1 * 8; + // Transmit first byte + SPDR{{ id }} = uint16_t(temp) >> shift; + } + else if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::Word); + shift = 3 * 8; + // Transmit first byte + SPDR{{ id }} = uint32_t(temp) >> shift; + } + // enable Interrupt + SPCR{{ id }} |= (1 << SPIE); +} + +template +void modm::platform::SpiMaster{{ id }}::setup_isr(const T *tx_first, const T *tx_last) +{ + if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::Byte); + tx.u8 = tx_first; + tx_end.u8 = tx_last; + // Transmit first byte + SPDR{{ id }} = *tx.u8; + } + else if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::HalfWord); + tx.u16 = tx_first; + tx_end.u16 = tx_last; + shift = 1 * 8; + // Transmit first byte + SPDR{{ id }} = *tx.u16 >> shift; + } + else if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::Word); + tx.u32 = tx_first; + tx_end.u32 = tx_last; + if (rx.u32) + *rx.u32 = 0; + shift = 3 * 8; + // Transmit first byte + SPDR{{ id }} = *tx.u32 >> shift; + } + // enable Interrupt + SPCR{{ id }} |= (1 << SPIE); +} \ No newline at end of file diff --git a/src/modm/platform/spi/stm32/module.lb b/src/modm/platform/spi/stm32/module.lb index 783b3eb8a3..00965553f3 100644 --- a/src/modm/platform/spi/stm32/module.lb +++ b/src/modm/platform/spi/stm32/module.lb @@ -42,6 +42,7 @@ class Instance(Module): env.template("spi_hal.hpp.in", "spi_hal_{}.hpp".format(self.instance)) 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_impl.hpp.in", "spi_master_{}_impl.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)) diff --git a/src/modm/platform/spi/stm32/spi_hal.hpp.in b/src/modm/platform/spi/stm32/spi_hal.hpp.in index cac0022289..15e3143ad6 100644 --- a/src/modm/platform/spi/stm32/spi_hal.hpp.in +++ b/src/modm/platform/spi/stm32/spi_hal.hpp.in @@ -82,24 +82,24 @@ public: isTransmitRegisterEmpty(); /** - * Write up to 16 Bit to the data register + * Write up to 8 Bit to the data register * * @warning This method does NOT do any sanity checks!! * It is your responsibility to check if the register * is empty! */ static void - write(uint16_t data); + write(uint8_t data); /** - * Write 8 Bit to the data register + * Write up to 16 Bit to the data register * * @warning This method does NOT do any sanity checks!! * It is your responsibility to check if the register * is empty! */ static void - write(uint8_t data); + write(uint16_t data); /** * Returns the value of the data register @@ -109,7 +109,7 @@ public: * contains something useful! */ static void - read(uint8_t &data); + read(uint8_t *data); /** * Returns the value of the data register @@ -119,7 +119,7 @@ public: * contains something useful! */ static void - read(uint16_t &data); + read(uint16_t *data); static void enableInterruptVector(bool enable, uint32_t priority); 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 c75a7667c7..90f3a78140 100644 --- a/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in +++ b/src/modm/platform/spi/stm32/spi_hal_impl.hpp.in @@ -3,6 +3,7 @@ * Copyright (c) 2013-2017, Niklas Hauser * Copyright (c) 2014, Daniel Krebs * Copyright (c) 2020, Mike Wolfram + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -137,15 +138,15 @@ modm::platform::SpiHal{{ id }}::write(uint8_t data) } void inline -modm::platform::SpiHal{{ id }}::read(uint8_t &data) +modm::platform::SpiHal{{ id }}::read(uint8_t *data) { - data = static_cast(SPI{{ id }}->DR); + *data = static_cast(SPI{{ id }}->DR); } void inline -modm::platform::SpiHal{{ id }}::read(uint16_t &data) +modm::platform::SpiHal{{ id }}::read(uint16_t *data) { - data = static_cast(SPI{{ id }}->DR); + *data = static_cast(SPI{{ id }}->DR); } void inline diff --git a/src/modm/platform/spi/stm32/spi_master.cpp.in b/src/modm/platform/spi/stm32/spi_master.cpp.in index b31a39c0de..428c571d45 100644 --- a/src/modm/platform/spi/stm32/spi_master.cpp.in +++ b/src/modm/platform/spi/stm32/spi_master.cpp.in @@ -5,6 +5,7 @@ * Copyright (c) 2012-2017, Niklas Hauser * Copyright (c) 2013, Kevin Läufer * Copyright (c) 2014, Sascha Schade + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -16,11 +17,6 @@ #include "spi_master_{{id}}.hpp" -// Bit0: single transfer state -// Bit1: block transfer state -uint8_t -modm::platform::SpiMaster{{ id }}::state(0); - uint8_t modm::platform::SpiMaster{{ id }}::count(0); @@ -29,6 +25,28 @@ modm::platform::SpiMaster{{ id }}::context(nullptr); modm::Spi::ConfigurationHandler modm::platform::SpiMaster{{ id }}::configuration(nullptr); + +// ---------------------------------------------------------------------------- + +modm::platform::SpiMaster{{ id }}::State_t +modm::platform::SpiMaster{{ id }}::state(0); + +uint8_t modm::platform::SpiMaster{{ id }}::shift(0); + +uint32_t modm::platform::SpiMaster{{ id }}::temp(0); + +modm::platform::SpiMaster{{ id }}::unsigned_types_const + modm::platform::SpiMaster{{ id }}::tx{nullptr}; + +modm::platform::SpiMaster{{ id }}::unsigned_types_const + modm::platform::SpiMaster{{ id }}::tx_end{nullptr}; + +modm::platform::SpiMaster{{ id }}::unsigned_types + modm::platform::SpiMaster{{ id }}::rx{nullptr}; + +modm::platform::SpiMaster{{ id }}::unsigned_types + modm::platform::SpiMaster{{ id }}::rx_end{nullptr}; + // ---------------------------------------------------------------------------- uint8_t @@ -62,84 +80,107 @@ modm::platform::SpiMaster{{ id }}::release(void *ctx) } return count; } -// ---------------------------------------------------------------------------- -modm::ResumableResult -modm::platform::SpiMaster{{ id }}::transfer(uint8_t data) +MODM_ISR(SPI{{ id }}_EV) { - // 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) ) - { - // 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}; + // TODO Required? + // Spi::acknowledgeInterruptFlags(); + modm::platform::SpiMaster{{ id }}::isr_handler(); } -modm::ResumableResult -modm::platform::SpiMaster{{ id }}::transfer( - const uint8_t * tx, uint8_t * rx, std::size_t length) +void modm::platform::SpiMaster{{ id }}::isr_handler() { - // 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 need to globally remember which byte we are currently transferring - static std::size_t index = 0; - - // we are only interested in Bit1 - switch(state & Bit1) - { - case 0: - // we will only visit this state once - state |= Bit1; - - // initialize index and check range - index = 0; - while (index < length) - { - default: + constexpr State_t state_normal = State::Idle; + constexpr State_t state_repeat = State::Idle | State::Repeat; + + switch (state.value) { + case state_normal.value | DataType::Byte: + // Store received byte + if (rx.u8) + SpiHal{{ id }}::read(rx.u8++); + if (++tx.u8 == tx_end.u8) { - // if tx == 0, we use a dummy byte 0x00 - // else we copy it from the array - // call the resumable function - modm::ResumableResult result = transfer(tx ? tx[index] : 0); - - // if the resumable function is still running, so are we - if (result.getState() > modm::rf::NestingError) - return {modm::rf::Running}; - - // if rx != 0, we copy the result into the array - if (rx) rx[index] = result.getResult(); + // Job done, disable Interrupt. + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxBufferEmpty); + return; } - index++; - } - - // clear the state - state &= ~Bit1; - return {modm::rf::Stop}; + // Transmit next byte + SpiHal{{ id }}::write(*tx.u8); + return; + case state_repeat.value | DataType::Byte: + if(++tx.repeat == tx_end.repeat) + { + // Job done, disable Interrupt. + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxBufferEmpty); + return; + } + // Transmit byte again + SpiHal{{ id }}::write(uint8_t(temp)); + return; + case state_normal.value | DataType::HalfWord: + // Store received halfword + if (rx.u16) + SpiHal{{ id }}::read(rx.u16++); + if (++tx.u16 == tx_end.u16) + { + // Job done, disable Interrupt. + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxBufferEmpty); + return; + } + // Transmit next halfword + SpiHal{{ id }}::write(*tx.u16); + return; + case state_repeat.value | DataType::HalfWord: + if(++tx.repeat == tx_end.repeat) + { + // Job done, disable Interrupt. + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxBufferEmpty); + return; + } + // Transmit halfword again + SpiHal{{ id }}::write(uint16_t(temp)); + return; + case state_normal.value | DataType::Word: + if (shift) + { + // Store received sub-halfword + if (rx.u32) + SpiHal{{ id }}::read((rx.u16 - 1)); // TODO confirm this targets the right address + // Transmit next sub-byte + shift = 0; + SpiHal{{ id }}::write(uint16_t(*tx.u32)); + return; + } + // Store received sub-byte + if (rx.u32) + SpiHal{{ id }}::read((uint16_t*)(rx.u32++)); + if (++tx.u32 == tx_end.u32) + { + // Job done, disable Interrupt. + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxBufferEmpty); + return; + } + // Transmit next word + shift = 16; + SpiHal{{ id }}::write(uint16_t(*tx.u32 >> shift)); + return; + case state_repeat.value | DataType::Word: + if (shift) + { + // Transmit next sub-byte + shift = 0; + SpiHal{{ id }}::write(uint16_t(*tx.u32)); + return; + } + if(++tx.repeat == tx_end.repeat) + { + // Job done, disable Interrupt. + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxBufferEmpty); + return; + } + // Transmit word again + shift = 16; + SpiHal{{ id }}::write(uint16_t(*tx.u32 >> shift)); + return; } -} +} \ No newline at end of file diff --git a/src/modm/platform/spi/stm32/spi_master.hpp.in b/src/modm/platform/spi/stm32/spi_master.hpp.in index 323bf7f853..13e2f3e4d5 100644 --- a/src/modm/platform/spi/stm32/spi_master.hpp.in +++ b/src/modm/platform/spi/stm32/spi_master.hpp.in @@ -5,6 +5,7 @@ * Copyright (c) 2012, Georgi Grinshpun * Copyright (c) 2013, Kevin Läufer * Copyright (c) 2014, Sascha Schade + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -14,18 +15,15 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_STM32_SPI_MASTER{{ id }}_HPP -#define MODM_STM32_SPI_MASTER{{ id }}_HPP +#pragma once #include #include #include +#include #include "spi_hal_{{ id }}.hpp" -namespace modm -{ - -namespace platform +namespace modm::platform { /** @@ -38,10 +36,27 @@ namespace platform */ class SpiMaster{{ id }} : public modm::SpiMaster { - static uint8_t state; static uint8_t count; static void *context; static ConfigurationHandler configuration; + + static State_t state; + static uint8_t shift; + static uint32_t temp; + + union unsigned_types_const { + uint8_t const *u8; + uint16_t const *u16; + uint32_t const *u32; + uint16_t repeat; + }; + static unsigned_types_const tx, tx_end; + union unsigned_types { + uint8_t *u8; + uint16_t *u16; + uint32_t *u32; + }; + static unsigned_types rx, rx_end; public: using Hal = SpiHal{{ id }}; @@ -96,7 +111,7 @@ public: // initialize the Spi SpiHal{{ id }}::initialize(prescaler); - state = 0; + state = modm::SpiMaster::State(0); } static void @@ -110,6 +125,7 @@ public: { SpiHal{{ id }}::setDataOrder(static_cast(order)); } + static void setDataSize(DataSize size) { @@ -123,30 +139,162 @@ public: static uint8_t release(void *ctx); + // TODO Complete the docs + + /** + * @brief + * + * @param data + * @return modm::ResumableResult + */ + template + static modm::ResumableResult + transmit(const T data); + + // OPTIMIZE specialize transmit(byte) without ISR overhead + // static modm::ResumableResult + // transmit(const uint8_t data); + + /** + * @brief + * + * @param data + * @return modm::ResumableResult + */ + template + static modm::ResumableResult + transmit(const T data, std::size_t repeat); + + /** + * @brief Send and optional receive data in range begin()->end(). + * Works with C-array or std::array for tx / rx + * + * @param tx_first Pointer to first element to send. f.e. tx.begin() OR tx.begin() + 1 + * @param tx_first Pointer to one after last element to send: f.e. tx.end() OR tx.end() - 4 + * @param rx_first Pointer to first element to receive: f.e. rx_data.begin() OR rx_data.begin() + 1 + */ + template + static modm::ResumableResult + transmit(const T *tx_first, const T *tx_last, T *rx_first = nullptr); + + /** + * @brief Send a std::array + * + * @param tx_array std::array with data to send + */ + template + static modm::ResumableResult + transmit(const C &tx_arr) + { return transmit(tx_arr.begin(), tx_arr.end()); }; + + /** + * @brief Transmit and receive a std::array + * + * @param tx_arr std::array with data to send + * @param rx_arr std::array for received data + */ + template + static modm::ResumableResult + transmit(const C &tx_arr, C &rx_arr) + { return transmit(tx_arr.begin(), tx_arr.end(), rx_arr.begin()); }; + +// -- Backwards compatible API -------------------------------------------------------- + // Backwards compatible API static uint8_t - transferBlocking(uint8_t data) - { - return RF_CALL_BLOCKING(transfer(data)); - } + transferBlocking(const uint8_t data) + { return RF_CALL_BLOCKING(transmit(data)); } + + static uint16_t + transferBlocking16(const uint16_t data) + { return RF_CALL_BLOCKING(transmit(data)); } + + static uint32_t + transferBlocking32(const uint32_t data) + { return RF_CALL_BLOCKING(transmit(data)); } + + static void + transferBlocking(const uint8_t data, std::size_t repeat) + { RF_CALL_BLOCKING(transmit(data, repeat)); } + + static void + transferBlocking16(const uint16_t data, std::size_t repeat) + { RF_CALL_BLOCKING(transmit(data, repeat)); } + + static void + transferBlocking32(const uint32_t data, std::size_t repeat) + { RF_CALL_BLOCKING(transmit(data, repeat)); } static void transferBlocking(const uint8_t *tx, uint8_t *rx, std::size_t length) - { - RF_CALL_BLOCKING(transfer(tx, rx, length)); - } + { RF_CALL_BLOCKING(transmit(tx, tx + length, rx)); } + static void + transferBlocking16(const uint16_t *tx, uint16_t *rx, std::size_t length) + { RF_CALL_BLOCKING(transmit(tx, tx + length, rx)); } + static void + transferBlocking32(const uint32_t *tx, uint32_t *rx, std::size_t length) + { RF_CALL_BLOCKING(transmit(tx, tx + length, rx)); } static modm::ResumableResult - transfer(uint8_t data); + transfer(const uint8_t data) { + return transmit(data); + } + + static modm::ResumableResult + transfer16(const uint16_t data) { + return transmit(data); + } + + static modm::ResumableResult + transfer32(const uint32_t data) { + return transmit(data); + } + + static modm::ResumableResult + transfer(const uint8_t tx, const std::size_t repeat) { + return transmit(tx, repeat); + } + + static modm::ResumableResult + transfer16(const uint16_t tx, const std::size_t repeat) { + return transmit(tx, repeat); + } + + static modm::ResumableResult + transfer32(const uint32_t tx, const std::size_t repeat) { + return transmit(tx, repeat); + } + + static modm::ResumableResult + transfer(const uint8_t *tx, uint8_t *rx, const std::size_t length) { + return transmit(tx, tx + length, rx); + } + + static modm::ResumableResult + transfer16(const uint16_t *tx, uint16_t *rx, const std::size_t length) { + return transmit(tx, tx + length, rx); + } static modm::ResumableResult - transfer(const uint8_t *tx, uint8_t *rx, std::size_t length); + transfer32(const uint32_t *tx, uint32_t *rx, const std::size_t length) { + return transmit(tx, tx + length, rx); + } // end documentation inherited -}; -} // namespace platform + // TODO friend with MODM_ISR(SPI_STC) possible? + static void isr_handler(); + +private: + template + static void + setup_isr(); + + template + static void + setup_isr(const T* tx_first, const T* tx_last); +}; -} // namespace modm +} // namespace modm::platform -#endif // MODM_STM32_SPI_MASTER{{ id }}_HPP +#include "spi_master_{{ id }}_impl.hpp" diff --git a/src/modm/platform/spi/stm32/spi_master_dma.hpp.in b/src/modm/platform/spi/stm32/spi_master_dma.hpp.in index 1f53b2eb1c..61bfc1c921 100644 --- a/src/modm/platform/spi/stm32/spi_master_dma.hpp.in +++ b/src/modm/platform/spi/stm32/spi_master_dma.hpp.in @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Mike Wolfram + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -56,23 +57,99 @@ public: static void initialize(); + static void + setDataSize(DataSize size) + { + SpiMaster{{ id }}::setDataSize(size); + + if(size < DataSize::Bit9) { + Dma::TxChannel::setPeripheralDataSize(DmaBase::PeripheralDataSize::Byte); + Dma::RxChannel::setMemoryDataSize(DmaBase::MemoryDataSize::Byte); + } + else { + Dma::TxChannel::setPeripheralDataSize(DmaBase::PeripheralDataSize::HalfWord); + Dma::RxChannel::setMemoryDataSize(DmaBase::MemoryDataSize::HalfWord); + } + } + static uint8_t - transferBlocking(uint8_t data) + transferBlocking(const uint8_t data) + { + return RF_CALL_BLOCKING(transmit(data)); + } + + static uint16_t + transferBlocking16(const uint16_t data) + { + return RF_CALL_BLOCKING(transmit(data)); + } + + static void + transferBlocking(const uint8_t *tx, uint16_t repeat) + { + RF_CALL_BLOCKING(transmit(tx, repeat)); + } + + static void + transferBlocking16(const uint16_t *tx, uint16_t repeat) + { + RF_CALL_BLOCKING(transmit(tx, repeat)); + } + + static void + transferBlocking(const uint8_t *tx, uint8_t *rx, uint16_t length) { - return RF_CALL_BLOCKING(transfer(data)); + RF_CALL_BLOCKING(transmit(tx, rx, length)); } static void - transferBlocking(const uint8_t *tx, uint8_t *rx, std::size_t length) + transferBlocking16(const uint16_t *tx, uint16_t *rx, uint16_t length) { - RF_CALL_BLOCKING(transfer(tx, rx, length)); + RF_CALL_BLOCKING(transmit(tx, rx, length)); } static modm::ResumableResult - transfer(uint8_t data); + transfer(const uint8_t data) { + return transmit(data); + } + + static modm::ResumableResult + transfer16(const uint16_t data) { + return transmit(data); + } + + static modm::ResumableResult + transfer(const uint8_t *tx, const uint16_t repeat) { + return transmit(tx, repeat); + } + + static modm::ResumableResult + transfer16(const uint16_t *tx, const uint16_t repeat) { + return transmit(tx, repeat); + } + + static modm::ResumableResult + transfer(const uint8_t *tx, uint8_t *rx, const uint16_t length) { + return transmit(tx, rx, length); + } + + static modm::ResumableResult + transfer16(const uint16_t *tx, uint16_t *rx, const uint16_t length) { + return transmit(tx, rx, length); + } + + // TODO "transmit" == good? + template + static modm::ResumableResult + transmit(const T data); + + template + static modm::ResumableResult + transmit(const T *tx, const uint16_t repeat); + template static modm::ResumableResult - transfer(const uint8_t *tx, uint8_t *rx, std::size_t length); + transmit(const T *tx, T *rx, const uint16_t length); private: static void 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 index aa89e933bf..cc125e65b0 100644 --- a/src/modm/platform/spi/stm32/spi_master_dma_impl.hpp.in +++ b/src/modm/platform/spi/stm32/spi_master_dma_impl.hpp.in @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Mike Wolfram + * Copyright (c) 2021, Thomas Sommer * * This file is part of the modm project. * @@ -51,18 +52,19 @@ modm::platform::SpiMaster{{ id }}_Dma::initialize() } template -modm::ResumableResult -modm::platform::SpiMaster{{ id }}_Dma::transfer(uint8_t data) +template +modm::ResumableResult +modm::platform::SpiMaster{{ id }}_Dma::transmit(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) ) + + if (!state.all(SpiBase::LowByte)) { - // disable DMA for single byte transfer + // disable DMA for single transfer SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxDmaEnable | SpiBase::Interrupt::RxDmaEnable); @@ -74,7 +76,7 @@ modm::platform::SpiMaster{{ id }}_Dma::transfer(uint SpiHal{{ id }}::write(data); // set LSB = Bit0 - state |= Bit0; + state.set(SpiBase::LowByte); } if (!SpiHal{{ id }}::isReceiveRegisterNotEmpty()) @@ -83,28 +85,91 @@ modm::platform::SpiMaster{{ id }}_Dma::transfer(uint SpiHal{{ id }}::read(data); // transfer finished - state &= ~Bit0; + state.reset(SpiBase::LowByte); return {modm::rf::Stop, data}; } template +template modm::ResumableResult -modm::platform::SpiMaster{{ id }}_Dma::transfer(const uint8_t *tx, - uint8_t *rx, std::size_t length) +modm::platform::SpiMaster{{ id }}_Dma::transmit(const T *tx, + const uint16_t repeat) { // 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. + // 2. wait for transfer to finish. + + switch(int(state.all(SpiBase::Idle))) + { + case 0: + // we will only visit this state once + state.set(SpiBase::Idle); + dmaError = false; + + SpiHal{{ id }}::enableInterrupt(SpiBase::Interrupt::TxDmaEnable | + SpiBase::Interrupt::RxDmaEnable); + + Dma::TxChannel::setMemoryAddress(uint32_t(tx)); + Dma::TxChannel::setMemoryIncrementMode(false); + + // Althought not used, RxChannel must be treated to keep the (current) Spi-Dma logic alive + Dma::RxChannel::setMemoryAddress(uint32_t(&dmaDummy)); + Dma::RxChannel::setMemoryIncrementMode(false); + + Dma::TxChannel::setDataLength(repeat); + dmaTransmitComplete = false; + Dma::TxChannel::start(); + + Dma::RxChannel::setDataLength(repeat); + Dma::RxChannel::start(); + + [[fallthrough]]; + + default: + while (true) { + if (dmaError) + break; + if (not dmaTransmitComplete) + return { modm::rf::Running }; + if (SpiHal{{ id }}::getInterruptFlags() & SpiBase::InterruptFlag::Busy) + return { modm::rf::Running }; +%% if "fifo" in features + if (SpiHal{{ id }}::getInterruptFlags() & SpiBase::InterruptFlag::FifoTxLevel) + return { modm::rf::Running }; +%% endif + break; + } + + SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxDmaEnable | + SpiBase::Interrupt::RxDmaEnable); + + state.reset(SpiBase::Idle); + return {modm::rf::Stop}; + } +} + +template +template +modm::ResumableResult +modm::platform::SpiMaster{{ id }}_Dma::transmit(const T *tx, + T *rx, const uint16_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 transfer to finish. // we are only interested in Bit1 - switch(state & Bit1) + switch(int(state.all(SpiBase::Idle))) { case 0: // we will only visit this state once - state |= Bit1; + state.set(SpiBase::Idle); dmaError = false; + SpiHal{{ id }}::enableInterrupt(SpiBase::Interrupt::TxDmaEnable | SpiBase::Interrupt::RxDmaEnable); @@ -115,6 +180,7 @@ modm::platform::SpiMaster{{ id }}_Dma::transfer(cons Dma::TxChannel::setMemoryAddress(uint32_t(&dmaDummy)); Dma::TxChannel::setMemoryIncrementMode(false); } + if (rx) { Dma::RxChannel::setMemoryAddress(uint32_t(rx)); Dma::RxChannel::setMemoryIncrementMode(true); @@ -123,14 +189,14 @@ modm::platform::SpiMaster{{ id }}_Dma::transfer(cons Dma::RxChannel::setMemoryIncrementMode(false); } - Dma::RxChannel::setDataLength(length); - dmaReceiveComplete = false; - Dma::RxChannel::start(); - Dma::TxChannel::setDataLength(length); dmaTransmitComplete = false; Dma::TxChannel::start(); + Dma::RxChannel::setDataLength(length); + dmaReceiveComplete = false; + Dma::RxChannel::start(); + [[fallthrough]]; default: @@ -152,8 +218,8 @@ modm::platform::SpiMaster{{ id }}_Dma::transfer(cons SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxDmaEnable | SpiBase::Interrupt::RxDmaEnable); - // clear the state - state &= ~Bit1; + + state.reset(SpiBase::Idle); return {modm::rf::Stop}; } } @@ -164,23 +230,23 @@ modm::platform::SpiMaster{{ id }}_Dma::handleDmaTran { SpiHal{{ id }}::disableInterrupt(SpiBase::Interrupt::TxDmaEnable | SpiBase::Interrupt::RxDmaEnable); - Dma::RxChannel::stop(); Dma::TxChannel::stop(); + Dma::RxChannel::stop(); dmaError = true; } template void -modm::platform::SpiMaster{{ id }}_Dma::handleDmaReceiveComplete() +modm::platform::SpiMaster{{ id }}_Dma::handleDmaTransmitComplete() { - Dma::RxChannel::stop(); - dmaReceiveComplete = true; + Dma::TxChannel::stop(); + dmaTransmitComplete = true; } template void -modm::platform::SpiMaster{{ id }}_Dma::handleDmaTransmitComplete() +modm::platform::SpiMaster{{ id }}_Dma::handleDmaReceiveComplete() { - Dma::TxChannel::stop(); - dmaTransmitComplete = true; -} + Dma::RxChannel::stop(); + dmaReceiveComplete = true; +} \ No newline at end of file diff --git a/src/modm/platform/spi/stm32/spi_master_impl.hpp.in b/src/modm/platform/spi/stm32/spi_master_impl.hpp.in new file mode 100644 index 0000000000..75220eac17 --- /dev/null +++ b/src/modm/platform/spi/stm32/spi_master_impl.hpp.in @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2009-2011, Fabian Greif + * Copyright (c) 2010, Martin Rosekeit + * Copyright (c) 2011-2017, Niklas Hauser + * Copyright (c) 2012, Georgi Grinshpun + * Copyright (c) 2013, Kevin Läufer + * Copyright (c) 2014, Sascha Schade + * Copyright (c) 2021, Thomas Sommer + * + * 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 "spi_master_{{ id }}.hpp" + +template +void modm::platform::SpiMaster{{ id }}::setup_isr() +{ + if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::Byte); + // Transmit first byte + SpiHal{{ id }}::write(uint8_t(temp)); + } + else if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::HalfWord); + // Transmit first byte + SpiHal{{ id }}::write(uint16_t(temp)); + } + else if constexpr (std::is_same::value) + { + DataType_t::set(state, DataType::Word); + shift = 1 * 16; + // Transmit first byte + SpiHal{{ id }}::write(uint16_t(uint32_t(temp) >> shift)); + } + + SpiHal{{ id }}::enableInterrupt(SpiBase::Interrupt::TxBufferEmpty); +} + +template +void modm::platform::SpiMaster{{ id }}::setup_isr(const T *tx_first, const T *tx_last) +{ + if constexpr (std::is_same::value) + { + // TODO move this upwards into SpiApi + DataType_t::set(state, DataType::Byte); + tx.u8 = tx_first; + tx_end.u8 = tx_last; + // Transmit first byte + SpiHal{{ id }}::write(*tx.u8); + } + else if constexpr (std::is_same::value) + { + // TODO move this upwards into SpiApi + DataType_t::set(state, DataType::HalfWord); + tx.u16 = tx_first; + tx_end.u16 = tx_last; + // Transmit first byte + SpiHal{{ id }}::write(*tx.u16); + } + else if constexpr (std::is_same::value) + { + // TODO move this upwards into SpiApi + DataType_t::set(state, DataType::Word); + tx.u32 = tx_first; + tx_end.u32 = tx_last; + shift = 1 * 16; + // Transmit first byte + SpiHal{{ id }}::write(uint16_t(*tx.u32 >> shift)); + } + + SpiHal{{ id }}::enableInterrupt(SpiBase::Interrupt::TxBufferEmpty); +} \ No newline at end of file