From def628e33aa4b34a7a331c0a63be58b980409120 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Tue, 10 Oct 2023 01:32:04 +0200 Subject: [PATCH 1/5] [driver] Rename SSD1306 driver to ssd1306.i2c --- README.md | 2 +- .../display/{sh1106.hpp => sh1106_i2c.hpp} | 19 +- .../display/{sh1106.lb => sh1106_i2c.lb} | 8 +- src/modm/driver/display/ssd1306.hpp | 185 +------------- src/modm/driver/display/ssd1306_common.hpp | 230 ++++++++++++++++++ src/modm/driver/display/ssd1306_common.lb | 23 ++ src/modm/driver/display/ssd1306_i2c.hpp | 143 +++++++++++ .../display/{ssd1306.lb => ssd1306_i2c.lb} | 11 +- src/modm/driver/display/ssd1306_i2c_impl.hpp | 138 +++++++++++ .../display/ssd1306_i2c_transaction_impl.cpp | 17 +- src/modm/driver/display/ssd1306_impl.hpp | 186 -------------- src/modm/driver/display/ssd1306_register.hpp | 100 -------- src/modm/driver/module.lb | 5 + 13 files changed, 571 insertions(+), 496 deletions(-) rename src/modm/driver/display/{sh1106.hpp => sh1106_i2c.hpp} (76%) rename src/modm/driver/display/{sh1106.lb => sh1106_i2c.lb} (76%) create mode 100644 src/modm/driver/display/ssd1306_common.hpp create mode 100644 src/modm/driver/display/ssd1306_common.lb create mode 100644 src/modm/driver/display/ssd1306_i2c.hpp rename src/modm/driver/display/{ssd1306.lb => ssd1306_i2c.lb} (74%) create mode 100644 src/modm/driver/display/ssd1306_i2c_impl.hpp delete mode 100644 src/modm/driver/display/ssd1306_impl.hpp delete mode 100644 src/modm/driver/display/ssd1306_register.hpp diff --git a/README.md b/README.md index a2ad41c854..2f6843f035 100644 --- a/README.md +++ b/README.md @@ -792,7 +792,7 @@ your specific needs. PCA9685 QMC5883L -SH1106 +SH1106-I2C SIEMENS-S65 SIEMENS-S75 SK6812 diff --git a/src/modm/driver/display/sh1106.hpp b/src/modm/driver/display/sh1106_i2c.hpp similarity index 76% rename from src/modm/driver/display/sh1106.hpp rename to src/modm/driver/display/sh1106_i2c.hpp index 92120d7c80..3874fd6e8f 100644 --- a/src/modm/driver/display/sh1106.hpp +++ b/src/modm/driver/display/sh1106_i2c.hpp @@ -11,7 +11,7 @@ #pragma once -#include "ssd1306.hpp" +#include "ssd1306_i2c.hpp" namespace modm { @@ -26,10 +26,10 @@ namespace modm * @ingroup modm_driver_sh1106 */ template -class Sh1106 : public Ssd1306 +class Sh1106I2c : public Ssd1306I2c { public: - Sh1106(uint8_t address = 0x3C) : Ssd1306(address) {} + Sh1106I2c(uint8_t address = 0x3C) : Ssd1306I2c(address) {} protected: modm::ResumableResult @@ -39,13 +39,10 @@ class Sh1106 : public Ssd1306 this->transaction_success = true; - this->commandBuffer[0] = ssd1306::AdressingCommands::HigherColumnStartAddress; - this->commandBuffer[1] = 0x02; - for (page = 0; page < Height / 8; page++) { - this->commandBuffer[2] = 0xB0 | page; - this->transaction_success &= RF_CALL(this->writeCommands(3)); + this->commandBuffer[0] = std::to_underlying(ssd1306::AdressingCommands::PageStartAddress) | page; + this->transaction_success &= RF_CALL(this->writeCommands(1)); RF_WAIT_UNTIL( this->transaction.configureDisplayWrite((uint8_t*)&this->buffer[page], 128)); @@ -62,14 +59,14 @@ class Sh1106 : public Ssd1306 { RF_BEGIN(); // Default on Power-up - can be omitted - this->commandBuffer[0] = ssd1306::AdressingCommands::MemoryMode; - this->commandBuffer[1] = ssd1306::MemoryMode::PAGE; + this->commandBuffer[0] = std::to_underlying(ssd1306::AdressingCommands::MemoryMode); + this->commandBuffer[1] = std::to_underlying(ssd1306::MemoryMode::PAGE); this->transaction_success &= RF_CALL(this->writeCommands(2)); RF_END(); } private: - size_t page; + uint8_t page; }; } // namespace modm diff --git a/src/modm/driver/display/sh1106.lb b/src/modm/driver/display/sh1106_i2c.lb similarity index 76% rename from src/modm/driver/display/sh1106.lb rename to src/modm/driver/display/sh1106_i2c.lb index 4222f45849..486524a2e9 100644 --- a/src/modm/driver/display/sh1106.lb +++ b/src/modm/driver/display/sh1106_i2c.lb @@ -12,13 +12,13 @@ def init(module): - module.name = ":driver:sh1106" - module.description = "SH1106 Display" + module.name = ":driver:sh1106.i2c" + module.description = "SH1106 Display in I2C mode" def prepare(module, options): - module.depends(":driver:ssd1306") + module.depends(":driver:ssd1306.i2c") return True def build(env): env.outbasepath = "modm/src/modm/driver/display" - env.copy("sh1106.hpp") + env.copy("sh1106_i2c.hpp") diff --git a/src/modm/driver/display/ssd1306.hpp b/src/modm/driver/display/ssd1306.hpp index e62881d154..ac9d958668 100644 --- a/src/modm/driver/display/ssd1306.hpp +++ b/src/modm/driver/display/ssd1306.hpp @@ -1,7 +1,5 @@ /* - * Copyright (c) 2014, 2016-2017, Sascha Schade - * Copyright (c) 2014-2016, 2018, Niklas Hauser - * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2023, Raphael Lehmann * * This file is part of the modm project. * @@ -11,181 +9,6 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_SSD1306_HPP -#define MODM_SSD1306_HPP - -#include -#include -#include -#include - -#include "ssd1306_register.hpp" - -namespace modm -{ - -/// @ingroup modm_driver_ssd1306 -struct ssd1306 : public ssd1306_register -{ -public: - enum class - ScrollStep : uint8_t - { - Frames2 = 0b111, - Frames3 = 0b100, - Frames4 = 0b101, - Frames5 = 0b000, - Frames25 = 0b110, - Frames64 = 0b001, - Frames128 = 0b010, - Frames256 = 0b011 - }; - - enum class - ScrollDirection : uint8_t - { - Right = HorizontalScrollRight, - Left = HorizontalScrollLeft, - // RightBottom = VerticalAndHorizontalScrollRight, - // LeftBottom = VerticalAndHorizontalScrollLeft, - }; - - enum class - DisplayMode : uint8_t - { - Normal = NormalDisplay, - Inverted = InvertedDisplay - }; - -public: - /// @cond - class Ssd1306_I2cWriteTransaction : public modm::I2cWriteTransaction - { - public: - Ssd1306_I2cWriteTransaction(uint8_t address); - - bool - configureDisplayWrite(const uint8_t *buffer, std::size_t size); - - protected: - virtual Writing - writing() override; - - virtual void - detaching(modm::I2c::DetachCause cause) override; - - inline bool - isWritable() - { return !transfer_active; } - - private: - uint8_t transfer_type; - bool transfer_active; - }; - /// @endcond -}; // struct ssd1306 - -/** - * Driver for SSD1306 based OLED-displays using I2C. - * This display is only rated to be driven with 400kHz, which limits - * the frame rate to about 40Hz. - * - * @author Niklas Hauser - * @author Thomas Sommer - * @ingroup modm_driver_ssd1306 - */ -template -class Ssd1306 : public ssd1306, - public MonochromeGraphicDisplayVertical<128, Height>, - public I2cDevice -{ - static_assert((Height == 64) or (Height == 32), "Display height must be either 32 or 64 pixel!"); - -public: - Ssd1306(uint8_t address = 0x3C); - - /// Pings the display - bool inline pingBlocking() - { return RF_CALL_BLOCKING(this->ping()); } - - /// initializes for 3V3 with charge-pump - bool inline initializeBlocking() - { return RF_CALL_BLOCKING(initialize()); } - - /// Update the display with the content of the RAM buffer. - void - update() override - { RF_CALL_BLOCKING(startWriteDisplay()); } - - /// Use this method to synchronize writing to the displays buffer - /// to avoid tearing. - /// @return `true` if the frame buffer is not being copied to the display - bool isWritable() - { return this->transaction.isWriteable(); } - - // MARK: - TASKS - /// initializes for 3V3 with charge-pump asynchronously - modm::ResumableResult - initialize(); - - // starts a frame transfer and waits for completion - virtual modm::ResumableResult - writeDisplay(); - - modm::ResumableResult - setDisplayMode(DisplayMode mode = DisplayMode::Normal) - { - commandBuffer[0] = mode; - return writeCommands(1); - } - - modm::ResumableResult - setContrast(uint8_t contrast = 0xCE) - { - commandBuffer[0] = FundamentalCommands::ContrastControl; - commandBuffer[1] = contrast; - return writeCommands(2); - } - - /** - * \param orientation glcd::Orientation::Landscape0 or glcd::Orientation::Landscape180 - */ - modm::ResumableResult - setOrientation(glcd::Orientation orientation); - - modm::ResumableResult - configureScroll(uint8_t origin, uint8_t size, ScrollDirection direction, ScrollStep steps); - - modm::ResumableResult - enableScroll() - { - commandBuffer[0] = ScrollingCommands::EnableScroll; - return writeCommands(1); - } - - modm::ResumableResult - disableScroll() - { - commandBuffer[0] = ScrollingCommands::DisableScroll; - return writeCommands(1); - } - -protected: - modm::ResumableResult - writeCommands(std::size_t length); - - virtual modm::ResumableResult - initializeMemoryMode(); - - virtual modm::ResumableResult - startWriteDisplay(); - - uint8_t commandBuffer[7]; - bool transaction_success; -}; - -} // namespace modm - -#include "ssd1306_impl.hpp" - -#endif // MODM_SSD1306_HPP +// DEPRECATE 2024q4 +#warning "Use 'ssd1306_i2c.hpp' instead!" +#include "ssd1306_i2c.hpp" diff --git a/src/modm/driver/display/ssd1306_common.hpp b/src/modm/driver/display/ssd1306_common.hpp new file mode 100644 index 0000000000..4818234219 --- /dev/null +++ b/src/modm/driver/display/ssd1306_common.hpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2023, Raphael Lehmann + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_SSD1306_COMMON_HPP +#define MODM_SSD1306_COMMON_HPP + +#include +#include +#include + +namespace modm +{ + +/// @ingroup modm_driver_ssd1306 +struct ssd1306 +{ +protected: + enum class + FundamentalCommands : uint8_t + { + ContrastControl = 0x81, // Range 1-255 + EntireDisplayResumeToRam = 0xA4, + EntireDisplayIgnoreRam = 0xA5, + NormalDisplay = 0xA6, + InvertedDisplay = 0xA7, + DisplayOff = 0xAE, + DisplayOn = 0xAF, + }; + + enum class + AdressingCommands : uint8_t + { + MemoryMode = 0x20, // enum MemoryMode + // HORIZONTAL and VERTICAL addressing only + ColumnAddress = 0x21, // Range 0-127 + PageAddress = 0x22, // Range 0-7 + // PAGE addressing only + PageStartAddress = 0xB0, // Range 0-7 + LowerColumnStartAddress = 0x00, + HigherColumnStartAddress = 0x10, + }; + + enum class + HardwareConfigCommands : uint8_t + { + DisplayStartLine = 0x40, + SegmentRemap0 = 0xA0, + SegmentRemap127 = 0xA1, + MultiplexRatio = 0xA8, // Range 16-64 + ComOutputScanDirectionIncrement = 0xC0, + ComOutputScanDirectionDecrement = 0xC8, + DisplayOffset = 0xD3, // Range 0-63 + ComPinsOrder = 0xDA, // enum ComPinsOrder + }; + + enum class + ScrollingCommands : uint8_t + { + HorizontalScrollRight = 0x26, + HorizontalScrollLeft = 0x27, + VerticalAndHorizontalScrollRight = 0x29, + VerticalAndHorizontalScrollLeft = 0x2A, + VerticalScrollArea = 0xA3, + DisableScroll = 0x2E, + EnableScroll = 0x2F, + }; + + enum class + MemoryMode : uint8_t + { + HORIZONTAL = 0, + VERTICAL = 1, + PAGE = 2 + }; + + enum class + TimingAndDrivingCommands : uint8_t + { + ChargePump = 0x8D, // enum ChargePump + DisplayClockDivideRatio = 0xD5, // [7:4] Frequency [3:0] Prescaler + PreChargePeriod = 0xD9, + V_DeselectLevel = 0xDB, // 0: ~0.65 x VCC, 1: 0.71 x VCC, 2: 0.77 x VCC, 3: 0.83 x VCC + Nop = 0xE3 + }; + + enum class + ChargePump : uint8_t + { + DISABLE = 0x10, + V7_5 = 0x14, + V8_5 = 0x94, + V9 = 0x95, + }; + +public: + enum class + ScrollStep : uint8_t + { + Frames2 = 0b111, + Frames3 = 0b100, + Frames4 = 0b101, + Frames5 = 0b000, + Frames25 = 0b110, + Frames64 = 0b001, + Frames128 = 0b010, + Frames256 = 0b011 + }; + + enum class + ScrollDirection : uint8_t + { + Right = std::to_underlying(ScrollingCommands::HorizontalScrollRight), + Left = std::to_underlying(ScrollingCommands::HorizontalScrollLeft), + // RightBottom = ScrollingCommands::VerticalAndHorizontalScrollRight, + // LeftBottom = ScrollingCommands::VerticalAndHorizontalScrollLeft, + }; + + enum class + DisplayMode : uint8_t + { + Normal = std::to_underlying(FundamentalCommands::NormalDisplay), + Inverted = std::to_underlying(FundamentalCommands::InvertedDisplay), + }; + +}; + +/** + * Driver for SSD1306 based OLED-displays using SPI 4-wire mode. + * + * @author Raphael Lehmann + * @ingroup modm_driver_ssd1306 + */ +template +class Ssd1306Common : public ssd1306, + public MonochromeGraphicDisplayVertical<128, Height> +{ + static_assert((Height == 64) or (Height == 32), "Display height must be either 32 or 64 pixel!"); + +public: + virtual modm::ResumableResult + setDisplayMode(DisplayMode mode = DisplayMode::Normal) + { + commandBuffer[0] = std::to_underlying(mode); + return writeCommands(1); + } + + virtual modm::ResumableResult + setContrast(uint8_t contrast = 0xCE) + { + commandBuffer[0] = std::to_underlying(FundamentalCommands::ContrastControl); + commandBuffer[1] = contrast; + return writeCommands(2); + } + + /** + * \param orientation glcd::Orientation::Landscape0 or glcd::Orientation::Landscape180 + */ + virtual modm::ResumableResult + setOrientation(glcd::Orientation orientation) + { + if (orientation == glcd::Orientation::Landscape0) + { + commandBuffer[0] = std::to_underlying(HardwareConfigCommands::SegmentRemap127); + commandBuffer[1] = std::to_underlying(HardwareConfigCommands::ComOutputScanDirectionDecrement); + } + else if (orientation == glcd::Orientation::Landscape180) + { + commandBuffer[0] = std::to_underlying(HardwareConfigCommands::SegmentRemap0); + commandBuffer[1] = std::to_underlying(HardwareConfigCommands::ComOutputScanDirectionIncrement); + } + + return writeCommands(2); + } + + virtual modm::ResumableResult + configureScroll(uint8_t origin, uint8_t size, ScrollDirection direction, ScrollStep steps) + { + + commandBuffer[0] = std::to_underlying(ScrollingCommands::DisableScroll); // disableScroll() + + uint8_t beginY = (origin > 7) ? 7 : origin; + + uint8_t endY = ((origin + size) > 7) ? 7 : (origin + size); + if (endY < beginY) endY = beginY; + + commandBuffer[1] = std::to_underlying(direction); + commandBuffer[2] = 0x00; + commandBuffer[3] = beginY; + commandBuffer[4] = std::to_underlying(steps); + commandBuffer[5] = endY; + commandBuffer[6] = 0x00; + commandBuffer[7] = 0xFF; + + return writeCommands(8); + } + + virtual modm::ResumableResult + enableScroll() + { + commandBuffer[0] = std::to_underlying(ScrollingCommands::EnableScroll); + return writeCommands(1); + } + + virtual modm::ResumableResult + disableScroll() + { + commandBuffer[0] = std::to_underlying(ScrollingCommands::DisableScroll); + return writeCommands(1); + } + +protected: + virtual modm::ResumableResult + writeCommands(std::size_t length) = 0; + + //uint8_t commandBuffer[31]; + std::array commandBuffer; +}; + +} // namespace modm + +#endif // MODM_SSD1306_COMMON_HPP diff --git a/src/modm/driver/display/ssd1306_common.lb b/src/modm/driver/display/ssd1306_common.lb new file mode 100644 index 0000000000..10c5fa5b65 --- /dev/null +++ b/src/modm/driver/display/ssd1306_common.lb @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2023, Raphael Lehmann +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + + +def init(module): + module.name = ":driver:ssd1306.common" + module.description = "Code shared by :ssd1306.i2c and :ssd1306.spi modules" + +def prepare(module, options): + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/display" + env.copy("ssd1306_common.hpp") diff --git a/src/modm/driver/display/ssd1306_i2c.hpp b/src/modm/driver/display/ssd1306_i2c.hpp new file mode 100644 index 0000000000..7665997a7c --- /dev/null +++ b/src/modm/driver/display/ssd1306_i2c.hpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014, 2016-2017, Sascha Schade + * Copyright (c) 2014-2016, 2018, Niklas Hauser + * 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_SSD1306_I2C_HPP +#define MODM_SSD1306_I2C_HPP + +#include +#include +#include +#include +#include "ssd1306_common.hpp" +#include + +namespace modm +{ + +/// @ingroup modm_driver_ssd1306 +struct ssd1306_i2c +{ +public: + enum class + Transfer : uint8_t + { + COMMAND_BURST = 0x00, + DATA_BURST = 0x40, + COMMAND = 0x80, + DATA = 0xC0 + }; + + /// @cond + class Ssd1306_I2cWriteTransaction : public modm::I2cWriteTransaction + { + public: + Ssd1306_I2cWriteTransaction(uint8_t address); + + bool + configureDisplayWrite(const uint8_t *buffer, std::size_t size); + + protected: + Writing + writing() override; + + void + detaching(modm::I2c::DetachCause cause) override; + + inline bool + isWritable() const + { return !transfer_active; } + + private: + uint8_t transfer_type; + bool transfer_active; + }; + /// @endcond +}; // struct ssd1306_i2c + +/** + * Driver for SSD1306 based OLED-displays using I2C. + * This display is only rated to be driven with 400kHz, which limits + * the frame rate to about 40Hz. + * + * @author Niklas Hauser + * @author Thomas Sommer + * @ingroup modm_driver_ssd1306 + */ +template +class Ssd1306I2c : public Ssd1306Common, + public I2cDevice +{ +public: + Ssd1306I2c(uint8_t address = 0x3C); + + /// Pings the display + bool inline + pingBlocking() + { + return RF_CALL_BLOCKING(this->ping()); + } + + /// initializes for 3V3 with charge-pump + bool inline + initializeBlocking() + { + return RF_CALL_BLOCKING(initialize()); + } + + /// Update the display with the content of the RAM buffer. + void + update() + { + RF_CALL_BLOCKING(startWriteDisplay()); + } + + /// Use this method to synchronize writing to the displays buffer + /// to avoid tearing. + /// @return `true` if the frame buffer is not being copied to the display + bool + isWritable() const + { + return this->transaction.isWriteable(); + } + + // MARK: - TASKS + /// initializes for 3V3 with charge-pump asynchronously + modm::ResumableResult + initialize(); + + // starts a frame transfer and waits for completion + virtual modm::ResumableResult + writeDisplay(); + +protected: + modm::ResumableResult + writeCommands(std::size_t length) override; + + virtual modm::ResumableResult + initializeMemoryMode(); + + virtual modm::ResumableResult + startWriteDisplay(); + + bool transaction_success; +}; + +// DEPRECATE 2024q4 +template +using Ssd1306 [[deprecated("Use `modm::Ssd1306I2c` instead!")]] = Ssd1306I2c; + +} // namespace modm + +#include "ssd1306_i2c_impl.hpp" + +#endif // MODM_SSD1306_I2C_HPP diff --git a/src/modm/driver/display/ssd1306.lb b/src/modm/driver/display/ssd1306_i2c.lb similarity index 74% rename from src/modm/driver/display/ssd1306.lb rename to src/modm/driver/display/ssd1306_i2c.lb index 0b138e8811..3c2526657b 100644 --- a/src/modm/driver/display/ssd1306.lb +++ b/src/modm/driver/display/ssd1306_i2c.lb @@ -12,19 +12,20 @@ def init(module): - module.name = ":driver:ssd1306" - module.description = "SSD1306 Display" + module.name = ":driver:ssd1306.i2c" + module.description = "SSD1306 Display in I2C mode" def prepare(module, options): module.depends( ":architecture:i2c.device", + ":driver:ssd1306.common", ":processing:timer", ":ui:display") return True def build(env): env.outbasepath = "modm/src/modm/driver/display" - env.copy("ssd1306.hpp") - env.copy("ssd1306_impl.hpp") - env.copy("ssd1306_register.hpp") + env.copy("ssd1306.hpp") # DEPRECATE 2024q4 + env.copy("ssd1306_i2c.hpp") + env.copy("ssd1306_i2c_impl.hpp") env.copy("ssd1306_i2c_transaction_impl.cpp") diff --git a/src/modm/driver/display/ssd1306_i2c_impl.hpp b/src/modm/driver/display/ssd1306_i2c_impl.hpp new file mode 100644 index 0000000000..b33dd25820 --- /dev/null +++ b/src/modm/driver/display/ssd1306_i2c_impl.hpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014-2015, 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_SSD1306_I2C_HPP +#error "Don't include this file directly, use 'ssd1306_i2c.hpp' instead!" +#endif + +template +modm::Ssd1306I2c::Ssd1306I2c(uint8_t address) + : I2cDevice(address) +{} + +// ---------------------------------------------------------------------------- +// MARK: - Tasks +template +modm::ResumableResult +modm::Ssd1306I2c::initialize() +{ + RF_BEGIN(); + transaction_success = true; + + this->commandBuffer[0] = std::to_underlying(ssd1306::FundamentalCommands::DisplayOff); + this->commandBuffer[1] = std::to_underlying(ssd1306::TimingAndDrivingCommands::DisplayClockDivideRatio); + this->commandBuffer[2] = 8 << 4; // Frequency (influences scrolling speed too) + this->commandBuffer[2] |= 0; // Prescaler + this->commandBuffer[3] = std::to_underlying(ssd1306::HardwareConfigCommands::MultiplexRatio); + this->commandBuffer[4] = 63; // Range 0-63 + this->commandBuffer[5] = std::to_underlying(ssd1306::HardwareConfigCommands::DisplayOffset); + this->commandBuffer[6] = 0; // Range 0-63 + transaction_success &= RF_CALL(writeCommands(7)); + + RF_CALL(initializeMemoryMode()); + + this->commandBuffer[0] = std::to_underlying(ssd1306::TimingAndDrivingCommands::ChargePump); + this->commandBuffer[1] = std::to_underlying(ssd1306::ChargePump::V7_5); + this->commandBuffer[2] = std::to_underlying(ssd1306::HardwareConfigCommands::SegmentRemap127); + this->commandBuffer[3] = std::to_underlying(ssd1306::HardwareConfigCommands::ComOutputScanDirectionDecrement); + this->commandBuffer[4] = std::to_underlying(ssd1306::HardwareConfigCommands::DisplayStartLine); + this->commandBuffer[4] |= 0; // Range 0-63 + transaction_success &= RF_CALL(writeCommands(5)); + + this->commandBuffer[0] = std::to_underlying(ssd1306::HardwareConfigCommands::ComPinsOrder); + this->commandBuffer[1] = Height == 64 ? 0x12 : 0x02; + this->commandBuffer[2] = std::to_underlying(ssd1306::FundamentalCommands::ContrastControl); + this->commandBuffer[3] = 0xCF; // Strange non-linear beahaviour + this->commandBuffer[4] = std::to_underlying(ssd1306::TimingAndDrivingCommands::PreChargePeriod); + this->commandBuffer[5] = 1; // [3:0] Phase 1 period + this->commandBuffer[5] |= 15 << 4;// [7:4] Phase 2 period + transaction_success &= RF_CALL(writeCommands(6)); + + this->commandBuffer[0] = std::to_underlying(ssd1306::TimingAndDrivingCommands::V_DeselectLevel); + this->commandBuffer[1] = 4 << 4; // [7:4] See Datasheet + this->commandBuffer[2] = std::to_underlying(ssd1306::ScrollingCommands::DisableScroll); + this->commandBuffer[3] = std::to_underlying(ssd1306::FundamentalCommands::EntireDisplayResumeToRam); + this->commandBuffer[4] = std::to_underlying(ssd1306::FundamentalCommands::NormalDisplay); + transaction_success &= RF_CALL(writeCommands(5)); + + this->commandBuffer[0] = std::to_underlying(ssd1306::FundamentalCommands::DisplayOn); + transaction_success &= RF_CALL(writeCommands(1)); + + RF_END_RETURN(transaction_success); +} + +/** + * @brief MemoryMode::HORIZONTAL and MemoryMode::VERTICAL + * have the best performance cause the whole buffer + * is send in one transaction. + */ +template +modm::ResumableResult +modm::Ssd1306I2c::initializeMemoryMode() +{ + RF_BEGIN(); + this->commandBuffer[0] = std::to_underlying(ssd1306::AdressingCommands::MemoryMode); + this->commandBuffer[1] = std::to_underlying(ssd1306::MemoryMode::HORIZONTAL); + transaction_success &= RF_CALL(writeCommands(2)); + + // Default on Power-up - can be omitted + this->commandBuffer[0] = std::to_underlying(ssd1306::AdressingCommands::ColumnAddress); + this->commandBuffer[1] = 0; + this->commandBuffer[2] = 127; + this->commandBuffer[3] = std::to_underlying(ssd1306::AdressingCommands::PageAddress); + this->commandBuffer[4] = 0; + this->commandBuffer[5] = 7; + transaction_success &= RF_CALL(writeCommands(6)); + + RF_END(); +} + +// ---------------------------------------------------------------------------- +template +modm::ResumableResult +modm::Ssd1306I2c::startWriteDisplay() +{ + RF_BEGIN(); + + RF_WAIT_UNTIL( + this->transaction.configureDisplayWrite(&this->buffer[0][0], sizeof(this->buffer)) and + this->startTransaction()); + + RF_END(); +} + +template +modm::ResumableResult +modm::Ssd1306I2c::writeDisplay() +{ + RF_BEGIN(); + + RF_CALL(startWriteDisplay()); + + RF_WAIT_WHILE(this->isTransactionRunning()); + + RF_END_RETURN(this->wasTransactionSuccessful()); +} + +// ---------------------------------------------------------------------------- +// MARK: write command +template +modm::ResumableResult +modm::Ssd1306I2c::writeCommands(std::size_t length) +{ + RF_BEGIN(); + + RF_WAIT_UNTIL(this->startWrite(this->commandBuffer.data(), length)); + + RF_WAIT_WHILE(this->isTransactionRunning()); + + RF_END_RETURN(this->wasTransactionSuccessful()); +} diff --git a/src/modm/driver/display/ssd1306_i2c_transaction_impl.cpp b/src/modm/driver/display/ssd1306_i2c_transaction_impl.cpp index 7ab1876784..02f0a73033 100644 --- a/src/modm/driver/display/ssd1306_i2c_transaction_impl.cpp +++ b/src/modm/driver/display/ssd1306_i2c_transaction_impl.cpp @@ -11,26 +11,27 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // ---------------------------------------------------------------------------- -#include "ssd1306.hpp" +#include "ssd1306_i2c.hpp" +#include // ---------------------------------------------------------------------------- -modm::ssd1306::Ssd1306_I2cWriteTransaction::Ssd1306_I2cWriteTransaction(uint8_t address) : - I2cWriteTransaction(address), transfer_type(ssd1306::Transfer::COMMAND_BURST), transfer_active(false) +modm::ssd1306_i2c::Ssd1306_I2cWriteTransaction::Ssd1306_I2cWriteTransaction(uint8_t address) : + I2cWriteTransaction(address), transfer_type(std::to_underlying(Transfer::COMMAND_BURST)), transfer_active(false) {} bool -modm::ssd1306::Ssd1306_I2cWriteTransaction::configureDisplayWrite(const uint8_t *buffer, std::size_t size) +modm::ssd1306_i2c::Ssd1306_I2cWriteTransaction::configureDisplayWrite(const uint8_t *buffer, std::size_t size) { if (I2cWriteTransaction::configureWrite(buffer, size)) { - transfer_type = Transfer::DATA_BURST; + transfer_type = std::to_underlying(Transfer::DATA_BURST); return true; } return false; } modm::I2cTransaction::Writing -modm::ssd1306::Ssd1306_I2cWriteTransaction::writing() +modm::ssd1306_i2c::Ssd1306_I2cWriteTransaction::writing() { if (!transfer_active) { @@ -41,12 +42,12 @@ modm::ssd1306::Ssd1306_I2cWriteTransaction::writing() } void -modm::ssd1306::Ssd1306_I2cWriteTransaction::detaching(modm::I2c::DetachCause cause) +modm::ssd1306_i2c::Ssd1306_I2cWriteTransaction::detaching(modm::I2c::DetachCause cause) { I2cWriteTransaction::detaching(cause); if (transfer_active or (cause != modm::I2c::DetachCause::NormalStop)) { - transfer_type = Transfer::COMMAND_BURST; + transfer_type = std::to_underlying(Transfer::COMMAND_BURST); transfer_active = false; } } diff --git a/src/modm/driver/display/ssd1306_impl.hpp b/src/modm/driver/display/ssd1306_impl.hpp deleted file mode 100644 index cde8634f40..0000000000 --- a/src/modm/driver/display/ssd1306_impl.hpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2014-2015, 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_SSD1306_HPP -#error "Don't include this file directly, use 'ssd1306.hpp' instead!" -#endif - -template -modm::Ssd1306::Ssd1306(uint8_t address) - : I2cDevice(address) -{} - -// ---------------------------------------------------------------------------- -// MARK: - Tasks -template -modm::ResumableResult -modm::Ssd1306::initialize() -{ - RF_BEGIN(); - transaction_success = true; - - commandBuffer[0] = FundamentalCommands::DisplayOff; - commandBuffer[1] = TimingAndDrivingCommands::DisplayClockDivideRatio; - commandBuffer[2] = 8 << 4; // Frequency (influences scrolling speed too) - commandBuffer[2] |= 0; // Prescaler - commandBuffer[3] = HardwareConfigCommands::MultiplexRatio; - commandBuffer[4] = 63; // Range 0-63 - commandBuffer[5] = HardwareConfigCommands::DisplayOffset; - commandBuffer[6] = 0; // Range 0-63 - transaction_success &= RF_CALL(writeCommands(7)); - - RF_CALL(initializeMemoryMode()); - - commandBuffer[0] = TimingAndDrivingCommands::ChargePump; - commandBuffer[1] = ChargePump::V7_5; - commandBuffer[2] = HardwareConfigCommands::SegmentRemap127; - commandBuffer[3] = HardwareConfigCommands::ComOutputScanDirectionDecrement; - commandBuffer[4] = HardwareConfigCommands::DisplayStartLine; - commandBuffer[4] |= 0; // Range 0-63 - transaction_success &= RF_CALL(writeCommands(5)); - - commandBuffer[0] = HardwareConfigCommands::ComPinsOrder; - commandBuffer[1] = Height == 64 ? 0x12 : 0x02; - commandBuffer[2] = FundamentalCommands::ContrastControl; - commandBuffer[3] = 0xCF; // Strange non-linear beahaviour - commandBuffer[4] = TimingAndDrivingCommands::PreChargePeriod; - commandBuffer[5] = 1; // [3:0] Phase 1 period - commandBuffer[5] |= 15 << 4;// [7:4] Phase 2 period - transaction_success &= RF_CALL(writeCommands(6)); - - commandBuffer[0] = TimingAndDrivingCommands::V_DeselectLevel; - commandBuffer[1] = 4 << 4; // [7:4] See Datasheet - commandBuffer[2] = ScrollingCommands::DisableScroll; - commandBuffer[3] = FundamentalCommands::EntireDisplayResumeToRam; - commandBuffer[4] = FundamentalCommands::NormalDisplay; - transaction_success &= RF_CALL(writeCommands(5)); - - commandBuffer[0] = FundamentalCommands::DisplayOn; - transaction_success &= RF_CALL(writeCommands(1)); - - RF_END_RETURN(transaction_success); -} - -/** - * @brief MemoryMode::HORIZONTAL and MemoryMode::VERTICAL - * have the best performance cause the whole buffer - * is send in one transaction. - */ -template -modm::ResumableResult -modm::Ssd1306::initializeMemoryMode() -{ - RF_BEGIN(); - commandBuffer[0] = AdressingCommands::MemoryMode; - commandBuffer[1] = MemoryMode::HORIZONTAL; - transaction_success &= RF_CALL(writeCommands(2)); - - // Default on Power-up - can be omitted - commandBuffer[0] = AdressingCommands::ColumnAddress; - commandBuffer[1] = 0; - commandBuffer[2] = 127; - commandBuffer[3] = AdressingCommands::PageAddress; - commandBuffer[4] = 0; - commandBuffer[5] = 7; - transaction_success &= RF_CALL(writeCommands(6)); - - RF_END(); -} - -// ---------------------------------------------------------------------------- -template -modm::ResumableResult -modm::Ssd1306::startWriteDisplay() -{ - RF_BEGIN(); - - RF_WAIT_UNTIL( - this->transaction.configureDisplayWrite((uint8_t*)(&this->buffer), sizeof(this->buffer)) and - this->startTransaction()); - - RF_END(); -} - -template -modm::ResumableResult -modm::Ssd1306::writeDisplay() -{ - RF_BEGIN(); - - RF_CALL(startWriteDisplay()); - - RF_WAIT_WHILE(this->isTransactionRunning()); - - RF_END_RETURN(this->wasTransactionSuccessful()); -} - -template -modm::ResumableResult -modm::Ssd1306::setOrientation(glcd::Orientation orientation) -{ - RF_BEGIN(); - - if (orientation == glcd::Orientation::Landscape0) - { - commandBuffer[0] = HardwareConfigCommands::SegmentRemap127; - commandBuffer[1] = HardwareConfigCommands::ComOutputScanDirectionDecrement; - } - else if (orientation == glcd::Orientation::Landscape180) - { - commandBuffer[0] = HardwareConfigCommands::SegmentRemap0; - commandBuffer[1] = HardwareConfigCommands::ComOutputScanDirectionIncrement; - } - - RF_END_RETURN_CALL(writeCommands(2)); -} - -template -modm::ResumableResult -modm::Ssd1306::configureScroll(uint8_t origin, uint8_t size, - ScrollDirection direction, ScrollStep steps) -{ - RF_BEGIN(); - - if (!RF_CALL(disableScroll())) - RF_RETURN(false); - - { - uint8_t beginY = (origin > 7) ? 7 : origin; - - uint8_t endY = ((origin + size) > 7) ? 7 : (origin + size); - if (endY < beginY) endY = beginY; - - commandBuffer[0] = uint8_t(direction); - commandBuffer[1] = 0x00; - commandBuffer[2] = beginY; - commandBuffer[3] = uint8_t(steps); - commandBuffer[4] = endY; - commandBuffer[5] = 0x00; - commandBuffer[6] = 0xFF; - } - - RF_END_RETURN_CALL(writeCommands(7)); -} - -// ---------------------------------------------------------------------------- -// MARK: write command -template -modm::ResumableResult -modm::Ssd1306::writeCommands(std::size_t length) -{ - RF_BEGIN(); - - RF_WAIT_UNTIL(this->startWrite(commandBuffer, length)); - - RF_WAIT_WHILE(this->isTransactionRunning()); - - RF_END_RETURN(this->wasTransactionSuccessful()); -} diff --git a/src/modm/driver/display/ssd1306_register.hpp b/src/modm/driver/display/ssd1306_register.hpp deleted file mode 100644 index ef6916f8a6..0000000000 --- a/src/modm/driver/display/ssd1306_register.hpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 - -namespace modm -{ - -/// @ingroup modm_driver_ssd1306 -struct ssd1306_register -{ -protected: - enum Transfer : uint8_t - { - COMMAND_BURST = 0x00, - DATA_BURST = 0x40, - COMMAND = 0x80, - DATA = 0xC0 - }; - - enum FundamentalCommands : uint8_t - { - ContrastControl = 0x81, // Range 1-255 - EntireDisplayResumeToRam = 0xA4, - EntireDisplayIgnoreRam = 0xA5, - NormalDisplay = 0xA6, - InvertedDisplay = 0xA7, - DisplayOff = 0xAE, - DisplayOn = 0xAF, - }; - - enum AdressingCommands : uint8_t - { - MemoryMode = 0x20, // enum MemoryMode - // HORIZONTAL and VERTICAL addressing only - ColumnAddress = 0x21, // Range 0-127 - PageAddress = 0x22, // Range 0-7 - // PAGE addressing only - PageStartAddress = 0xB0, // Range 0-7 - LowerColumnStartAddress = 0x00, - HigherColumnStartAddress = 0x10, - }; - - enum HardwareConfigCommands : uint8_t - { - DisplayStartLine = 0x40, - SegmentRemap0 = 0xA0, - SegmentRemap127 = 0xA1, - MultiplexRatio = 0xA8, // Range 16-64 - ComOutputScanDirectionIncrement = 0xC0, - ComOutputScanDirectionDecrement = 0xC8, - DisplayOffset = 0xD3, // Range 0-63 - ComPinsOrder = 0xDA, // enum ComPinsOrder - }; - - enum ScrollingCommands : uint8_t - { - HorizontalScrollRight = 0x26, - HorizontalScrollLeft = 0x27, - VerticalAndHorizontalScrollRight = 0x29, - VerticalAndHorizontalScrollLeft = 0x2A, - VerticalScrollArea = 0xA3, - DisableScroll = 0x2E, - EnableScroll = 0x2F, - }; - - enum MemoryMode : uint8_t - { - HORIZONTAL = 0, - VERTICAL = 1, - PAGE = 2 - }; - - enum TimingAndDrivingCommands : uint8_t - { - ChargePump = 0x8D, // enum ChargePump - DisplayClockDivideRatio = 0xD5, // [7:4] Frequency [3:0] Prescaler - PreChargePeriod = 0xD9, - V_DeselectLevel = 0xDB, // 0: ~0.65 x VCC, 1: 0.71 x VCC, 2: 0.77 x VCC, 3: 0.83 x VCC - Nop = 0xE3 - }; - - enum ChargePump : uint8_t - { - DISABLE = 0x00, - V7_5 = 0x14, - V8_5 = 0x94, - V9 = 0x95, - }; -}; - -} // namespace modm diff --git a/src/modm/driver/module.lb b/src/modm/driver/module.lb index ee5b6c7449..c6074c561a 100644 --- a/src/modm/driver/module.lb +++ b/src/modm/driver/module.lb @@ -16,6 +16,11 @@ def init(module): module.description = "External Device Drivers" def prepare(module, options): + # DEPRECATED: 2024q4 + # module.add_alias( + # Alias(name="ssd1306", + # destination=":driver:ssd1306.i2c", + # description="DEPRECATED: Please use :driver:ssd1306.i2c instead.")) return True def build(env): From a06da057f7b62028ab665d0f73a886d47a7cabea Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Tue, 10 Oct 2023 01:35:36 +0200 Subject: [PATCH 2/5] [examples] Update SSD1306 examples --- examples/generic/ros/environment/project.xml | 2 +- examples/generic/ros/environment/thread_display.hpp | 4 ++-- .../display/{ssd1306 => ssd1306_i2c}/main.cpp | 4 ++-- .../display/{ssd1306 => ssd1306_i2c}/project.xml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename examples/stm32f4_discovery/display/{ssd1306 => ssd1306_i2c}/main.cpp (92%) rename examples/stm32f4_discovery/display/{ssd1306 => ssd1306_i2c}/project.xml (77%) diff --git a/examples/generic/ros/environment/project.xml b/examples/generic/ros/environment/project.xml index 67bb72a1d0..3699e1a504 100644 --- a/examples/generic/ros/environment/project.xml +++ b/examples/generic/ros/environment/project.xml @@ -7,7 +7,7 @@ modm:driver:bme280 - modm:driver:ssd1306 + modm:driver:ssd1306.i2c modm:ros modm:communication:ros modm:processing:timer diff --git a/examples/generic/ros/environment/thread_display.hpp b/examples/generic/ros/environment/thread_display.hpp index 15440cfa12..f95b20be23 100644 --- a/examples/generic/ros/environment/thread_display.hpp +++ b/examples/generic/ros/environment/thread_display.hpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include "hardware.hpp" @@ -55,7 +55,7 @@ class DisplayThread: public modm::pt::Protothread } protected: - modm::Ssd1306 display; + modm::Ssd1306I2c display; modm::ShortTimeout boot_timeout; bool _dirty; int32_t _seq; diff --git a/examples/stm32f4_discovery/display/ssd1306/main.cpp b/examples/stm32f4_discovery/display/ssd1306_i2c/main.cpp similarity index 92% rename from examples/stm32f4_discovery/display/ssd1306/main.cpp rename to examples/stm32f4_discovery/display/ssd1306_i2c/main.cpp index aeea40c8a4..c18ccd4ffc 100644 --- a/examples/stm32f4_discovery/display/ssd1306/main.cpp +++ b/examples/stm32f4_discovery/display/ssd1306_i2c/main.cpp @@ -10,7 +10,7 @@ */ #include -#include +#include /** * Example to demonstrate a MODM graphics display SSD1306. @@ -24,7 +24,7 @@ typedef GpioB9 Sda; typedef GpioB8 Scl; typedef I2cMaster1 MyI2cMaster; -modm::Ssd1306 display; +modm::Ssd1306I2c display; // ---------------------------------------------------------------------------- int diff --git a/examples/stm32f4_discovery/display/ssd1306/project.xml b/examples/stm32f4_discovery/display/ssd1306_i2c/project.xml similarity index 77% rename from examples/stm32f4_discovery/display/ssd1306/project.xml rename to examples/stm32f4_discovery/display/ssd1306_i2c/project.xml index 02ecafaea8..901a274fab 100644 --- a/examples/stm32f4_discovery/display/ssd1306/project.xml +++ b/examples/stm32f4_discovery/display/ssd1306_i2c/project.xml @@ -1,10 +1,10 @@ modm:disco-f407vg - + - modm:driver:ssd1306 + modm:driver:ssd1306.i2c modm:platform:gpio modm:platform:i2c:1 modm:build:scons From 77ba35af419ea485763e797b3e230dec14618c59 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Tue, 10 Oct 2023 17:24:00 +0200 Subject: [PATCH 3/5] [examples] Add SH1106/SSD1306 I2C display example for Nucleo-G474RE --- examples/nucleo_g474re/sh1106_i2c/main.cpp | 68 +++++++++++++++++++ examples/nucleo_g474re/sh1106_i2c/project.xml | 13 ++++ 2 files changed, 81 insertions(+) create mode 100644 examples/nucleo_g474re/sh1106_i2c/main.cpp create mode 100644 examples/nucleo_g474re/sh1106_i2c/project.xml diff --git a/examples/nucleo_g474re/sh1106_i2c/main.cpp b/examples/nucleo_g474re/sh1106_i2c/main.cpp new file mode 100644 index 0000000000..eb2eaa6c63 --- /dev/null +++ b/examples/nucleo_g474re/sh1106_i2c/main.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, Raphael Lehmann + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +// #include + +/// SH1106 display in I2C mode +using MyI2cMaster = modm::platform::I2cMaster1; +using Scl = Board::D15; +using Sda = Board::D14; + +using Display = modm::Sh1106I2c; +// using Display = modm::Ssd1306; + +Display display; + +int +main() +{ + Board::initialize(); + + modm::delay(100ms); + + MyI2cMaster::connect(); + // Use 20kHz I2C and internal pull-ups, works without external pull-ups + // if the jumper wires are short enought + MyI2cMaster::connect(MyI2cMaster::PullUps::Internal); + + modm::delay(100ms); + + RF_CALL_BLOCKING(display.initialize()); + RF_CALL_BLOCKING(display.setOrientation(modm::glcd::Orientation::Landscape0)); + RF_CALL_BLOCKING(display.setDisplayMode(Display::DisplayMode::Inverted)); + RF_CALL_BLOCKING(display.setContrast(80)); + + display.setFont(modm::font::Assertion); + + modm::ShortPeriodicTimer timer(333ms); + uint16_t counter(0); + + while (true) + { + if (timer.execute()) + { + display.clear(); + display.setCursor(1, 1); + display << "Hello World!"; + display.setCursor(1, 17); + display << counter++; + display.setCursor(1, 33); + display << "Line 3"; + display.setCursor(1, 49); + display << "Line 4"; + + display.update(); + } + } + + return 0; +} diff --git a/examples/nucleo_g474re/sh1106_i2c/project.xml b/examples/nucleo_g474re/sh1106_i2c/project.xml new file mode 100644 index 0000000000..39d7ef1946 --- /dev/null +++ b/examples/nucleo_g474re/sh1106_i2c/project.xml @@ -0,0 +1,13 @@ + + modm:nucleo-g474re + + + + + modm:driver:sh1106.i2c + modm:driver:ssd1306.i2c + modm:platform:gpio + modm:platform:i2c:1 + modm:build:scons + + From 275c635b8109b4863b3051da20e6184230aa9ba4 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Tue, 10 Oct 2023 01:33:22 +0200 Subject: [PATCH 4/5] [driver] Add SSD1306 SPI driver --- README.md | 9 +- src/modm/driver/display/ssd1306_spi.hpp | 73 ++++++++++++ src/modm/driver/display/ssd1306_spi.lb | 29 +++++ src/modm/driver/display/ssd1306_spi_impl.hpp | 118 +++++++++++++++++++ tools/scripts/synchronize_docs.py | 5 +- 5 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 src/modm/driver/display/ssd1306_spi.hpp create mode 100644 src/modm/driver/display/ssd1306_spi.lb create mode 100644 src/modm/driver/display/ssd1306_spi_impl.hpp diff --git a/README.md b/README.md index 2f6843f035..5051377c6a 100644 --- a/README.md +++ b/README.md @@ -729,7 +729,7 @@ your specific needs. CAT24AA CYCLE-COUNTER -DRV832X +DRV832X-SPI DS1302 DS1631 DS18B20 @@ -797,22 +797,23 @@ your specific needs. SIEMENS-S75 SK6812 SK9822 -SSD1306 +SSD1306-I2C +SSD1306-SPI ST7586S ST7789 STTS22H STUSB4500 SX1276 -SX128X +SX128X TCS3414 TCS3472 TLC594x TMP102 TMP12x -TMP175 +TMP175 TOUCH2046 VL53L0 VL6180 diff --git a/src/modm/driver/display/ssd1306_spi.hpp b/src/modm/driver/display/ssd1306_spi.hpp new file mode 100644 index 0000000000..8411f907a9 --- /dev/null +++ b/src/modm/driver/display/ssd1306_spi.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, 2016-2017, Sascha Schade + * Copyright (c) 2014-2016, 2018, Niklas Hauser + * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2023, Raphael Lehmann + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_SSD1306_SPI_HPP +#define MODM_SSD1306_SPI_HPP + +#include +#include +#include +#include +#include "ssd1306_common.hpp" +#include + +namespace modm +{ + +/** + * Driver for SSD1306 based OLED-displays using SPI 4-wire mode. + * + * @author Raphael Lehmann + * @ingroup modm_driver_ssd1306 + */ +template +class Ssd1306Spi : public Ssd1306Common, + public SpiDevice, + protected modm::NestedResumable<2> +{ +public: + enum class + InitSequences : uint8_t + { + Hp12832_02, /// HP12832-02 display module, 0.91inch, 128x32px, 4-wire + /// SPI, blue, external 7.5V supply + }; + + modm::ResumableResult + initialize(InitSequences initSeq = InitSequences::Hp12832_02); + + /// Update the display with the content of the RAM buffer + modm::ResumableResult + writeDisplay(); + + /// Update the display with the content of the RAM buffer + /// @warning This function is blocking as long as the transfer of the + /// whole display buffer via SPI takes. + // Better use @see writeDisplay() instead. + void + update() + { + RF_CALL_BLOCKING(writeDisplay()); + } + +protected: + modm::ResumableResult + writeCommands(std::size_t length) override; +}; + +} // namespace modm + +#include "ssd1306_spi_impl.hpp" + +#endif // MODM_SSD1306_SPI_HPP diff --git a/src/modm/driver/display/ssd1306_spi.lb b/src/modm/driver/display/ssd1306_spi.lb new file mode 100644 index 0000000000..a4aa60430e --- /dev/null +++ b/src/modm/driver/display/ssd1306_spi.lb @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2023, Raphael Lehmann +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + + +def init(module): + module.name = ":driver:ssd1306.spi" + module.description = "SSD1306 Display in SPI 4-wire mode" + +def prepare(module, options): + module.depends( + ":architecture:spi.device", + ":driver:ssd1306.common", + ":processing:timer", + ":ui:display") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/display" + env.copy("ssd1306_spi.hpp") + env.copy("ssd1306_spi_impl.hpp") diff --git a/src/modm/driver/display/ssd1306_spi_impl.hpp b/src/modm/driver/display/ssd1306_spi_impl.hpp new file mode 100644 index 0000000000..07e2b4bd1a --- /dev/null +++ b/src/modm/driver/display/ssd1306_spi_impl.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2014-2015, Niklas Hauser + * Copyright (c) 2023, Raphael Lehmann + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_SSD1306_SPI_HPP +#error "Don't include this file directly, use 'ssd1306_spi.hpp' instead!" +#endif + +namespace modm +{ + +template +ResumableResult +Ssd1306Spi::initialize(InitSequences initSeq) +{ + RF_BEGIN(); + + this->attachConfigurationHandler([]() { + SpiMaster::setDataMode(SpiMaster::DataMode::Mode0); + SpiMaster::setDataOrder(SpiMaster::DataOrder::MsbFirst); + }); + Cs::setOutput(modm::Gpio::High); + Dc::setOutput(); + + if (initSeq == InitSequences::Hp12832_02) + { + // Initialize sequence from HP12832-02-TSBG12P091-A-VER1.0.pdf page 14 + + this->commandBuffer[0] = std::to_underlying(ssd1306::FundamentalCommands::DisplayOff); + this->commandBuffer[1] = std::to_underlying(ssd1306::TimingAndDrivingCommands::DisplayClockDivideRatio); + this->commandBuffer[2] = 0x80; // 0x90 or 0x80 (page 14)? + this->commandBuffer[3] = std::to_underlying(ssd1306::HardwareConfigCommands::MultiplexRatio); + this->commandBuffer[4] = 0x1F; // 0x3F or 0x1F (page 14)? + this->commandBuffer[5] = std::to_underlying(ssd1306::HardwareConfigCommands::DisplayOffset); + this->commandBuffer[6] = 0; + this->commandBuffer[7] = std::to_underlying(ssd1306::AdressingCommands::MemoryMode); + this->commandBuffer[8] = std::to_underlying(ssd1306::MemoryMode::HORIZONTAL); + this->commandBuffer[9] = std::to_underlying(ssd1306::AdressingCommands::ColumnAddress); + this->commandBuffer[10] = 0; + this->commandBuffer[11] = 127; + this->commandBuffer[12] = std::to_underlying(ssd1306::AdressingCommands::PageAddress); + this->commandBuffer[13] = 0; + this->commandBuffer[14] = (Height == 64) ? 7 : 3; + this->commandBuffer[15] = std::to_underlying(ssd1306::HardwareConfigCommands::DisplayStartLine) | 0; + this->commandBuffer[16] = std::to_underlying(ssd1306::HardwareConfigCommands::SegmentRemap0); + this->commandBuffer[17] = std::to_underlying(ssd1306::HardwareConfigCommands::ComOutputScanDirectionIncrement); + this->commandBuffer[18] = std::to_underlying(ssd1306::HardwareConfigCommands::ComPinsOrder); + this->commandBuffer[19] = 0x02; + this->commandBuffer[20] = std::to_underlying(ssd1306::FundamentalCommands::ContrastControl); + this->commandBuffer[21] = 0x8F; + this->commandBuffer[22] = std::to_underlying(ssd1306::TimingAndDrivingCommands::PreChargePeriod); + this->commandBuffer[23] = 0x1F; + this->commandBuffer[24] = std::to_underlying(ssd1306::TimingAndDrivingCommands::V_DeselectLevel); + this->commandBuffer[25] = 0x30; + this->commandBuffer[26] = std::to_underlying(ssd1306::FundamentalCommands::EntireDisplayResumeToRam); + this->commandBuffer[27] = std::to_underlying(ssd1306::FundamentalCommands::NormalDisplay); + this->commandBuffer[28] = std::to_underlying(ssd1306::TimingAndDrivingCommands::ChargePump); + this->commandBuffer[29] = std::to_underlying(ssd1306::ChargePump::DISABLE); + this->commandBuffer[30] = std::to_underlying(ssd1306::FundamentalCommands::DisplayOn); + + RF_RETURN_CALL(writeCommands(31)); + } + else { + RF_RETURN(false); + } + + RF_END(); +} + +template +ResumableResult +Ssd1306Spi::writeDisplay() +{ + RF_BEGIN(); + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + Dc::set(); + + RF_CALL(SpiMaster::transfer((uint8_t*)(&this->buffer), nullptr, sizeof(this->buffer))); + + if (this->releaseMaster()) { + Cs::set(); + } + + RF_END_RETURN(true); +} + +template +ResumableResult +Ssd1306Spi::writeCommands(std::size_t length) +{ + RF_BEGIN(); + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + Dc::reset(); + + RF_CALL(SpiMaster::transfer(this->commandBuffer.data(), nullptr, length)); + + if (this->releaseMaster()) { + Cs::set(); + } + + RF_END_RETURN(true); +} + +} // namespace modm diff --git a/tools/scripts/synchronize_docs.py b/tools/scripts/synchronize_docs.py index 969051ecd1..363ec920e3 100755 --- a/tools/scripts/synchronize_docs.py +++ b/tools/scripts/synchronize_docs.py @@ -74,10 +74,9 @@ def name(raw_name): .replace("-BITBANG", " BitBang")\ .replace("GPIO-SAMPLER", "Gpio Sampler")\ .replace("BLOCK-", "")\ - .replace("SPI-FLASH", "SPI Flash")\ - .replace("-SPI", "") + .replace("SPI-FLASH", "SPI Flash") if result in ["DEVICE", "LIS3-TRANSPORT", "MEMORY-BUS", "TERMINAL", "ALLOCATOR", - "MIRROR", "ADC-SAMPLER", "FAT", "HEAP", "--PYCACHE--", "FILE", "SPI-STACK-FLASH"]: + "MIRROR", "ADC-SAMPLER", "FAT", "HEAP", "--PYCACHE--", "FILE", "SPI-STACK-FLASH", "SSD1306-COMMON"]: return None return result From 4c0e511c6e36899738d061444b9517f631839138 Mon Sep 17 00:00:00 2001 From: Raphael Lehmann Date: Tue, 10 Oct 2023 01:36:09 +0200 Subject: [PATCH 5/5] [examples] Add SSD1306 SPI display example for Nucleo-G474RE --- examples/nucleo_g474re/ssd1306_spi/main.cpp | 85 +++++++++++++++++++ .../nucleo_g474re/ssd1306_spi/project.xml | 12 +++ 2 files changed, 97 insertions(+) create mode 100644 examples/nucleo_g474re/ssd1306_spi/main.cpp create mode 100644 examples/nucleo_g474re/ssd1306_spi/project.xml diff --git a/examples/nucleo_g474re/ssd1306_spi/main.cpp b/examples/nucleo_g474re/ssd1306_spi/main.cpp new file mode 100644 index 0000000000..6e3673ba56 --- /dev/null +++ b/examples/nucleo_g474re/ssd1306_spi/main.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023, Raphael Lehmann + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +/** + * SSD1306 display in SPI 4-wire mode + * + * SCK <-> Arduino D3 / GpioB3 + * MOSI <-> Arduino D4 / GpioB5 + * CS <-> Arduino D5 / GpioB4 + * D/C# <-> Arduino D2 / GpioA10 + * RESET# <-> Arduino D7 / GpioA8 + * + * En7V5 <-> Arduino D6 / GpioB10 + * + * TODO: Describe charge pump setup for 7.5V + */ + +using MySpiMaster = modm::platform::SpiMaster1; +using Sck = Board::D3; +using Mosi = Board::D4; +using Cs = Board::D5; +using Dc = Board::D2; +using Reset = Board::D7; +using En7V5 = Board::D6; +using Display = modm::Ssd1306Spi; +Display display; + +int +main() +{ + Board::initialize(); + + En7V5::reset(); + En7V5::setOutput(); + Reset::reset(); + Reset::setOutput(); + + Cs::setOutput(); + Dc::setOutput(); + MySpiMaster::connect(); + MySpiMaster::initialize(); + + Cs::set(); + En7V5::set(); + modm::delay(500ms); + + Reset::set(); + modm::delay(1ms); + + RF_CALL_BLOCKING(display.initialize()); + RF_CALL_BLOCKING(display.setOrientation(modm::glcd::Orientation::Landscape0)); + RF_CALL_BLOCKING(display.setDisplayMode(Display::DisplayMode::Inverted)); + RF_CALL_BLOCKING(display.setContrast(80)); + + display.setFont(modm::font::Assertion); + + modm::ShortPeriodicTimer timer(333ms); + uint16_t counter(0); + + while (true) + { + if (timer.execute()) + { + display.clear(); + display.setCursor(1, 1); + display << "Hello World!"; + display.setCursor(1,17); + display << counter++; + + display.update(); + } + } + + return 0; +} diff --git a/examples/nucleo_g474re/ssd1306_spi/project.xml b/examples/nucleo_g474re/ssd1306_spi/project.xml new file mode 100644 index 0000000000..c1c6902d35 --- /dev/null +++ b/examples/nucleo_g474re/ssd1306_spi/project.xml @@ -0,0 +1,12 @@ + + modm:nucleo-g474re + + + + + modm:driver:ssd1306.spi + modm:platform:gpio + modm:platform:spi:1 + modm:build:scons + +