diff --git a/README.md b/README.md index 9b671e12bf..5ffd35aa59 100644 --- a/README.md +++ b/README.md @@ -580,20 +580,21 @@ you specific needs. PCA9548A PCA9685 +SH1106 SIEMENS-S65 SIEMENS-S75 SK6812 SK9822 SSD1306 -STUSB4500 +STUSB4500 SX1276 TCS3414 TCS3472 TLC594X TMP102 -TMP175 +TMP175 TOUCH2046 VL53L0 VL6180 diff --git a/src/modm/driver/display/max7219_matrix_horizontal.hpp b/src/modm/driver/display/max7219_matrix_horizontal.hpp index 6a8de4ddd4..29b35ee893 100644 --- a/src/modm/driver/display/max7219_matrix_horizontal.hpp +++ b/src/modm/driver/display/max7219_matrix_horizontal.hpp @@ -95,7 +95,7 @@ Max7219MatrixHorizontal::update() // a group of eight pixels horizontal for (uint8_t col = 0; col < COLUMNS; ++col) { - buf[--idx] = this->buffer[row * 8 + ledCol][col]; + buf[--idx] = this->buffer[col][row * 8 + ledCol]; } } diff --git a/src/modm/driver/display/sh1106.hpp b/src/modm/driver/display/sh1106.hpp new file mode 100644 index 0000000000..42e867e511 --- /dev/null +++ b/src/modm/driver/display/sh1106.hpp @@ -0,0 +1,77 @@ +/* + * 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_SH1106_HPP +#define MODM_SH1106_HPP + +#include "ssd1306.hpp" +namespace modm +{ +/** + * SH1106 is said to be 'compatible' with SSD1306. However + * there's a relevant difference: SH1106 does only support + * MemoryMode::PAGE. This requires a little more extensive + * writeDisplay() routine. + * We have to alternate between setting Page-address and + * sending page-data instead of sending the whole buffer at once + * like is for SSD1306 in MemoryMode::HORIZONTAL / MemoryMode::VERTICAL + */ + +/// @ingroup modm_driver_sh1106 +template +class Sh1106 : public Ssd1306 +{ +public: + Sh1106(uint8_t address = 0x3C) : Ssd1306(address) {} + +protected: + modm::ResumableResult + startWriteDisplay() override + { + RF_BEGIN(); + + 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)); + + RF_WAIT_UNTIL( + this->transaction.configureDisplayWrite((uint8_t*)&this->buffer[page], 128)); + RF_WAIT_UNTIL(this->startTransaction()); + RF_WAIT_WHILE(this->isTransactionRunning()); + this->transaction_success &= this->wasTransactionSuccessful(); + }; + + RF_END_RETURN(this->transaction_success); + } + + modm::ResumableResult + initializeMemoryMode() override + { + RF_BEGIN(); + // Default on Power-up - can be omitted + this->commandBuffer[0] = ssd1306::AdressingCommands::MemoryMode; + this->commandBuffer[1] = ssd1306::MemoryMode::PAGE; + this->transaction_success &= RF_CALL(this->writeCommands(2)); + RF_END(); + } + +private: + size_t page; +}; +} // namespace modm + +#endif // MODM_SH1106_HPP \ No newline at end of file diff --git a/src/modm/driver/display/sh1106.lb b/src/modm/driver/display/sh1106.lb new file mode 100644 index 0000000000..533aa77adc --- /dev/null +++ b/src/modm/driver/display/sh1106.lb @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + + +def init(module): + module.name = ":driver:sh1106" + module.description = "SH1106 Display" + +def prepare(module, options): + module.depends( + ":driver:ssd1306") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/display" + env.copy("sh1106.hpp") \ No newline at end of file diff --git a/src/modm/driver/display/ssd1306.hpp b/src/modm/driver/display/ssd1306.hpp index 0db73ed44c..54b980c592 100644 --- a/src/modm/driver/display/ssd1306.hpp +++ b/src/modm/driver/display/ssd1306.hpp @@ -1,6 +1,7 @@ /* * 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. * @@ -18,71 +19,16 @@ #include #include +#include "ssd1306_register.hpp" + namespace modm { /// @ingroup modm_driver_ssd1306 -struct ssd1306 +struct ssd1306 : public ssd1306_register { -protected: - /// @cond - enum Command : uint8_t - { - // fundamental commands - SetContrastControl = 0x81, - SetChargePump = 0x8D, - SetEntireDisplayResumeToRam = 0xA4, - SetEntireDisplayIgnoreRam = 0xA5, - SetNormalDisplay = 0xA6, - SetInvertedDisplay = 0xA7, - SetDisplayOff = 0xAE, - SetDisplayOn = 0xAF, - - // scrolling commands - SetHorizontalScrollRight = 0x26, - SetHorizontalScrollLeft = 0x27, - SetVerticalAndHorizontalScrollRight = 0x29, - SetVerticalAndHorizontalScrollLeft = 0x2A, - SetDisableScroll = 0x2E, - SetEnableScroll = 0x2F, - SetVerticalScrollArea = 0xA3, - - // addressing commands - SetLowerColumnStartAddress = 0x00, - SetHigherColumnStartAddress = 0x10, - SetMemoryMode = 0x20, - SetColumnAddress = 0x21, - SetPageAddress = 0x22, - SetPageStartAddress = 0xB0, - - // Hardware configuration - SetDisplayStartLine = 0x40, - SetSegmentRemap0 = 0xA0, - SetSegmentRemap127 = 0xA1, - SetMultiplexRatio = 0xA8, - SetComOutputScanDirectionIncrement = 0xC0, - SetComOutputScanDirectionDecrement = 0xC8, - SetDisplayOffset = 0xD3, - SetComPins = 0xDA, - - // timing configuration - SetDisplayClockDivideRatio = 0xD5, - SetPreChargePeriod = 0xD9, - SetV_DeselectLevel = 0xDB, - Nop = 0xE3, - }; - /// @endcond - public: - enum class - Rotation : bool - { - Normal, - UpsideDown - }; - - enum class - ScrollStep : uint8_t + enum class ScrollStep : uint8_t { Frames2 = 0b111, Frames3 = 0b100, @@ -94,40 +40,28 @@ struct ssd1306 Frames256 = 0b011 }; - enum class - ScrollDirection : uint8_t + enum class ScrollDirection : uint8_t { - Right = SetHorizontalScrollRight, - Left = SetHorizontalScrollLeft + Right = HorizontalScrollRight, + Left = HorizontalScrollLeft, + // RightBottom = VerticalAndHorizontalScrollRight, + // LeftBottom = VerticalAndHorizontalScrollLeft, }; - enum class - DisplayMode : uint8_t + enum class DisplayMode : uint8_t { - Normal = SetNormalDisplay, - Inverted = SetInvertedDisplay + Normal = NormalDisplay, + Inverted = InvertedDisplay }; -protected: - /// @cond - static constexpr uint8_t - i(ScrollDirection direction) { return uint8_t(direction); } - static constexpr uint8_t - i(ScrollStep step) { return uint8_t(step); } - public: - template < uint8_t Height > - class DataTransmissionAdapter : public modm::I2cWriteTransaction + class Ssd1306_I2cWriteTransaction : public modm::I2cWriteTransaction { public: - DataTransmissionAdapter(uint8_t address); - - void modm_always_inline - setCommandBuffer(uint8_t *buffer) - { commands = buffer; } + Ssd1306_I2cWriteTransaction(uint8_t address); bool - configureDisplayWrite(uint8_t (*buffer)[Height / 8], std::size_t size); + configureDisplayWrite(const uint8_t *buffer, std::size_t size); protected: virtual Writing @@ -136,13 +70,18 @@ struct ssd1306 virtual void detaching(modm::I2c::DetachCause cause) override; + inline bool + isWritable() + { + return !transfer_active; + } + private: - uint8_t *commands; - public: - bool writeable; + uint8_t transfer_type; + bool transfer_active; }; /// @endcond -}; // struct ssd1306 +}; // struct ssd1306 /** * Driver for SSD1306 based OLED-displays using I2C. @@ -152,9 +91,10 @@ struct ssd1306 * @author Niklas Hauser * @ingroup modm_driver_ssd1306 */ -template < class I2cMaster, uint8_t Height = 64 > -class Ssd1306 : public ssd1306, public MonochromeGraphicDisplayVertical<128, Height>, - public I2cDevice> +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!"); @@ -162,18 +102,10 @@ class Ssd1306 : public ssd1306, public MonochromeGraphicDisplayVertical<128, Hei Ssd1306(uint8_t address = 0x3C); /// Pings the display - bool inline - pingBlocking() - { - return RF_CALL_BLOCKING(this->ping()); - } + bool inline pingBlocking() { return RF_CALL_BLOCKING(this->ping()); } /// initializes for 3V3 with charge-pump - bool inline - initializeBlocking() - { - return RF_CALL_BLOCKING(initialize()); - } + bool inline initializeBlocking() { return RF_CALL_BLOCKING(initialize()); } /// Update the display with the content of the RAM buffer. void @@ -185,8 +117,7 @@ class Ssd1306 : public ssd1306, public MonochromeGraphicDisplayVertical<128, Hei /// 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 modm_always_inline - isWritable() + bool isWritable() { return this->transaction.writeable; } @@ -197,62 +128,63 @@ class Ssd1306 : public ssd1306, public MonochromeGraphicDisplayVertical<128, Hei initialize(); // starts a frame transfer and waits for completion - modm::ResumableResult + virtual modm::ResumableResult writeDisplay(); - - modm::ResumableResult modm_always_inline + modm::ResumableResult setDisplayMode(DisplayMode mode = DisplayMode::Normal) - { return writeCommand(static_cast(mode)); } + { + commandBuffer[0] = mode; + return writeCommands(1); + } - modm::ResumableResult modm_always_inline + modm::ResumableResult setContrast(uint8_t contrast = 0xCE) - { return writeCommand(Command::SetContrastControl, contrast); } + { + 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 = glcd::Orientation::Landscape0); - + setOrientation(glcd::Orientation orientation); modm::ResumableResult - configureScroll(uint8_t origin, uint8_t size, - ScrollDirection direction, ScrollStep steps); + configureScroll(uint8_t origin, uint8_t size, ScrollDirection direction, ScrollStep steps); - modm::ResumableResult modm_always_inline + modm::ResumableResult enableScroll() - { return writeCommand(Command::SetEnableScroll); } + { + commandBuffer[0] = ScrollingCommands::EnableScroll; + return writeCommands(1); + } - modm::ResumableResult modm_always_inline + modm::ResumableResult disableScroll() - { return writeCommand(Command::SetDisableScroll); } + { + commandBuffer[0] = ScrollingCommands::DisableScroll; + return writeCommands(1); + } protected: - /// Write a command without data modm::ResumableResult - writeCommand(uint8_t command); + writeCommands(std::size_t length); - /// Write a command with one byte data - modm::ResumableResult - writeCommand(uint8_t command, uint8_t data); + virtual modm::ResumableResult + initializeMemoryMode(); - /// Write a command with two bytes data - modm::ResumableResult - writeCommand(uint8_t command, uint8_t data1, uint8_t data2); - -private: - modm::ResumableResult + virtual modm::ResumableResult startWriteDisplay(); - bool - startTransactionWithLength(uint8_t length); - -private: - uint8_t commandBuffer[14]; + uint8_t commandBuffer[7]; + bool transaction_success; }; -} // namespace modm +} // namespace modm #include "ssd1306_impl.hpp" -#include "ssd1306_transmission_impl.hpp" - -#endif // MODM_SSD1306_HPP +#endif // MODM_SSD1306_HPP diff --git a/src/modm/driver/display/ssd1306.lb b/src/modm/driver/display/ssd1306.lb index 8868c522eb..0b138e8811 100644 --- a/src/modm/driver/display/ssd1306.lb +++ b/src/modm/driver/display/ssd1306.lb @@ -26,4 +26,5 @@ def build(env): env.outbasepath = "modm/src/modm/driver/display" env.copy("ssd1306.hpp") env.copy("ssd1306_impl.hpp") - env.copy("ssd1306_transmission_impl.hpp") + env.copy("ssd1306_register.hpp") + env.copy("ssd1306_i2c_transaction_impl.cpp") diff --git a/src/modm/driver/display/ssd1306_i2c_transaction_impl.cpp b/src/modm/driver/display/ssd1306_i2c_transaction_impl.cpp new file mode 100644 index 0000000000..ad05dc7702 --- /dev/null +++ b/src/modm/driver/display/ssd1306_i2c_transaction_impl.cpp @@ -0,0 +1,51 @@ +// coding: utf-8 +/* + * Copyright (c) 2015, Niklas Hauser + * Copyright (c) 2017, 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/. + */ +// ---------------------------------------------------------------------------- +#include "ssd1306.hpp" + +// ---------------------------------------------------------------------------- +modm::ssd1306::Ssd1306_I2cWriteTransaction::Ssd1306_I2cWriteTransaction(uint8_t address) : + I2cWriteTransaction(address), transfer_type(ssd1306::Transfer::COMMAND_BURST), transfer_active(false) +{} + +bool +modm::ssd1306::Ssd1306_I2cWriteTransaction::configureDisplayWrite(const uint8_t *buffer, std::size_t size) +{ + if (I2cWriteTransaction::configureWrite(buffer, size)) + { + transfer_type = Transfer::DATA_BURST; + return true; + } + return false; +} + +modm::I2cTransaction::Writing +modm::ssd1306::Ssd1306_I2cWriteTransaction::writing() +{ + if (!transfer_active) + { + transfer_active = true; + return Writing(&transfer_type, 1, OperationAfterWrite::Write); + } + return I2cWriteTransaction::writing(); +} + +void +modm::ssd1306::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_active = false; + } +} diff --git a/src/modm/driver/display/ssd1306_impl.hpp b/src/modm/driver/display/ssd1306_impl.hpp index aa884659d2..1bf0a4d9e8 100644 --- a/src/modm/driver/display/ssd1306_impl.hpp +++ b/src/modm/driver/display/ssd1306_impl.hpp @@ -10,64 +10,106 @@ // ---------------------------------------------------------------------------- #ifndef MODM_SSD1306_HPP -# error "Don't include this file directly, use 'ssd1306.hpp' instead!" +#error "Don't include this file directly, use 'ssd1306.hpp' instead!" #endif -template < class I2cMaster, uint8_t Height > +template modm::Ssd1306::Ssd1306(uint8_t address) -: I2cDevice>(address), - commandBuffer{0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0} -{ - this->transaction.setCommandBuffer(commandBuffer); -} + : I2cDevice(address) +{} // ---------------------------------------------------------------------------- // MARK: - Tasks -template < class I2cMaster, uint8_t Height > +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; + commandBuffer[5] = HardwareConfigCommands::DisplayOffset; + commandBuffer[6] = 0; + 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; + transaction_success &= RF_CALL(writeCommands(5)); + + commandBuffer[0] = HardwareConfigCommands::ComPinsOrder; + commandBuffer[1] = Height == 64 ? 0x12 : 0x02; + commandBuffer[2] = FundamentalCommands::ContrastControl; + commandBuffer[3] = 0xCF; + 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; + 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 we can transfer the whole + * buffer at once. + */ +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)); - commandBuffer[11] = true; - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetDisplayOff)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetDisplayClockDivideRatio, 0xF0)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetMultiplexRatio, 0x3F)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetDisplayOffset, 0x00)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetDisplayStartLine | 0x00)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetChargePump, 0x14)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetMemoryMode, 0x01)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetSegmentRemap127)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetComOutputScanDirectionDecrement)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetComPins, ((Height == 64) ? 0x12 : 0x02))); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetContrastControl, 0xCE)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetPreChargePeriod, 0xF1)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetV_DeselectLevel, 0x40)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetEntireDisplayResumeToRam)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetNormalDisplay)); -// commandBuffer[11] &= RF_CALL(writeCommand(Command::SetColumnAddress, 0, 127)); -// commandBuffer[11] &= RF_CALL(writeCommand(Command::SetPageAddress, 0, 7)); - commandBuffer[11] &= RF_CALL(writeCommand(Command::SetDisplayOn)); - - RF_END_RETURN(bool(commandBuffer[11])); + RF_END(); } // ---------------------------------------------------------------------------- -template < class I2cMaster, uint8_t Height > +template modm::ResumableResult modm::Ssd1306::startWriteDisplay() { RF_BEGIN(); RF_WAIT_UNTIL( - this->transaction.configureDisplayWrite( - this->buffer, - this->getBufferWidth() * this->getBufferHeight()) and this->startTransaction()); + this->transaction.configureDisplayWrite((uint8_t*)(&this->buffer), sizeof(this->buffer)) and + this->startTransaction()); RF_END(); } -template < class I2cMaster, uint8_t Height > +template modm::ResumableResult modm::Ssd1306::writeDisplay() { @@ -80,26 +122,29 @@ modm::Ssd1306::writeDisplay() RF_END_RETURN(this->wasTransactionSuccessful()); } -template < class I2cMaster, uint8_t Height > +template modm::ResumableResult modm::Ssd1306::setOrientation(glcd::Orientation orientation) { RF_BEGIN(); - if ( RF_CALL(writeCommand((orientation == glcd::Orientation::Landscape0) ? - Command::SetSegmentRemap127 : Command::SetSegmentRemap0)) ) + if (orientation == glcd::Orientation::Landscape0) + { + commandBuffer[0] = HardwareConfigCommands::SegmentRemap127; + commandBuffer[1] = HardwareConfigCommands::ComOutputScanDirectionDecrement; + } else if (orientation == glcd::Orientation::Landscape180) { - RF_RETURN_CALL(writeCommand((orientation == glcd::Orientation::Landscape0) ? - Command::SetComOutputScanDirectionDecrement : Command::SetComOutputScanDirectionIncrement)); + commandBuffer[0] = HardwareConfigCommands::SegmentRemap0; + commandBuffer[1] = HardwareConfigCommands::ComOutputScanDirectionIncrement; } - RF_END_RETURN(false); + RF_END_RETURN_CALL(writeCommands(2)); } -template < class I2cMaster, uint8_t Height > +template modm::ResumableResult modm::Ssd1306::configureScroll(uint8_t origin, uint8_t size, - ScrollDirection direction, ScrollStep steps) + ScrollDirection direction, ScrollStep steps) { RF_BEGIN(); @@ -112,73 +157,29 @@ modm::Ssd1306::configureScroll(uint8_t origin, uint8_t size, uint8_t endY = ((origin + size) > 7) ? 7 : (origin + size); if (endY < beginY) endY = beginY; - commandBuffer[1] = static_cast(direction); - commandBuffer[3] = 0x00; - commandBuffer[5] = beginY; - commandBuffer[7] = i(steps); - commandBuffer[9] = endY; - commandBuffer[11] = 0x00; - commandBuffer[13] = 0xFF; + 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_WAIT_UNTIL( startTransactionWithLength(14) ); - - RF_WAIT_WHILE(this->isTransactionRunning()); - - RF_END_RETURN(this->wasTransactionSuccessful()); + RF_END_RETURN_CALL(writeCommands(7)); } // ---------------------------------------------------------------------------- // MARK: write command -template < class I2cMaster, uint8_t Height > +template modm::ResumableResult -modm::Ssd1306::writeCommand(uint8_t command) +modm::Ssd1306::writeCommands(std::size_t length) { RF_BEGIN(); - commandBuffer[1] = command; - RF_WAIT_UNTIL( startTransactionWithLength(2) ); - - RF_WAIT_WHILE( this->isTransactionRunning() ); + RF_WAIT_UNTIL(this->startWrite(commandBuffer, length)); - RF_END_RETURN( this->wasTransactionSuccessful() ); -} - -template < class I2cMaster, uint8_t Height > -modm::ResumableResult -modm::Ssd1306::writeCommand(uint8_t command, uint8_t data) -{ - RF_BEGIN(); - - commandBuffer[1] = command; - commandBuffer[3] = data; - RF_WAIT_UNTIL( startTransactionWithLength(4) ); - - RF_WAIT_WHILE( this->isTransactionRunning() ); - - RF_END_RETURN( this->wasTransactionSuccessful() ); -} - -template < class I2cMaster, uint8_t Height > -modm::ResumableResult -modm::Ssd1306::writeCommand(uint8_t command, uint8_t data1, uint8_t data2) -{ - RF_BEGIN(); - - commandBuffer[1] = command; - commandBuffer[3] = data1; - commandBuffer[5] = data2; - RF_WAIT_UNTIL( startTransactionWithLength(6) ); - - RF_WAIT_WHILE( this->isTransactionRunning() ); - - RF_END_RETURN( this->wasTransactionSuccessful() ); -} + RF_WAIT_WHILE(this->isTransactionRunning()); -// ---------------------------------------------------------------------------- -template < class I2cMaster, uint8_t Height > -bool -modm::Ssd1306::startTransactionWithLength(uint8_t length) -{ - return this->startWrite(commandBuffer, length); -} + RF_END_RETURN(this->wasTransactionSuccessful()); +} \ No newline at end of file diff --git a/src/modm/driver/display/ssd1306_register.hpp b/src/modm/driver/display/ssd1306_register.hpp new file mode 100644 index 0000000000..67178d07fe --- /dev/null +++ b/src/modm/driver/display/ssd1306_register.hpp @@ -0,0 +1,101 @@ +/* + * 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_REGISTER_HPP +#define MODM_SSD1306_REGISTER_HPP + +namespace modm +{ +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 + +#endif // MODM_SSD1306_REGISTER_HPP \ No newline at end of file diff --git a/src/modm/driver/display/ssd1306_transmission_impl.hpp b/src/modm/driver/display/ssd1306_transmission_impl.hpp deleted file mode 100644 index 3b59501053..0000000000 --- a/src/modm/driver/display/ssd1306_transmission_impl.hpp +++ /dev/null @@ -1,80 +0,0 @@ -// coding: utf-8 -/* - * Copyright (c) 2015, Niklas Hauser - * Copyright (c) 2017, Sascha Schade - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_SSD1306_HPP -# error "Don't include this file directly, use 'ssd1306.hpp' instead!" -#endif - -// ---------------------------------------------------------------------------- -template < uint8_t Height > -modm::ssd1306::DataTransmissionAdapter::DataTransmissionAdapter(uint8_t address) : - I2cWriteTransaction(address), writeable(true) -{} - -template < uint8_t Height > -bool -modm::ssd1306::DataTransmissionAdapter::configureDisplayWrite(uint8_t (*buffer)[Height / 8], std::size_t size) -{ - if (I2cWriteTransaction::configureWrite(&buffer[0][0], size)) - { - commands[13] = 0xfe; - writeable = false; - return true; - } - return false; -} - -template < uint8_t Height > -modm::I2cTransaction::Writing -modm::ssd1306::DataTransmissionAdapter::writing() -{ - // we first tell the display the column address again - if (commands[13] == 0xfe) - { - commands[1] = Command::SetColumnAddress; - commands[3] = 0; - commands[5] = 127; - commands[13] = 0xfd; - return Writing(commands, 6, OperationAfterWrite::Restart); - } - // then the page address. again. - if (commands[13] == 0xfd) - { - commands[1] = Command::SetPageAddress; - commands[3] = (Height == 64 ? 0 : 4); - commands[5] = 7; - commands[13] = 0xfc; - return Writing(commands, 6, OperationAfterWrite::Restart); - } - // then we set the D/C bit to tell it data is coming - if (commands[13] == 0xfc) - { - commands[13] = 0x40; - return Writing(&commands[13], 1, OperationAfterWrite::Write); - } - - // now we write the entire frame buffer into it. - return Writing(buffer, size, OperationAfterWrite::Stop); -} - -template < uint8_t Height > -void -modm::ssd1306::DataTransmissionAdapter::detaching(modm::I2c::DetachCause cause) -{ - I2cWriteTransaction::detaching(cause); - if ((commands[13] == 0x40) or (cause != modm::I2c::DetachCause::NormalStop)) - { - commands[13] = 0; - writeable = true; - } -} diff --git a/src/modm/ui/display/monochrome_graphic_display.hpp b/src/modm/ui/display/monochrome_graphic_display.hpp index aefba749aa..195bdd4abe 100644 --- a/src/modm/ui/display/monochrome_graphic_display.hpp +++ b/src/modm/ui/display/monochrome_graphic_display.hpp @@ -36,7 +36,7 @@ namespace modm * \author Thomas Sommer * \ingroup modm_ui_display */ -template +template class MonochromeGraphicDisplay : public GraphicDisplay { static_assert(Width > 0, "width must be greater than 0"); @@ -76,7 +76,28 @@ class MonochromeGraphicDisplay : public GraphicDisplay clear() final; protected: - uint8_t buffer[BufferWidth][BufferHeight]; + uint8_t buffer[BufferHeight][BufferWidth]; + + inline bool + xOnScreen(const int16_t x) const + { + return x >= 0 and x < int16_t(Width); + } + inline bool + yOnScreen(const int16_t y) const + { + return y >= 0 and y < int16_t(Height); + } + inline bool + xValidBuffer(const std::size_t x) const + { + return x >= 0 and x < BufferWidth; + } + inline bool + yValidBuffer(const std::size_t y) const + { + return y >= 0 and y < BufferHeight; + } }; } // namespace modm diff --git a/src/modm/ui/display/monochrome_graphic_display_horizontal.hpp b/src/modm/ui/display/monochrome_graphic_display_horizontal.hpp index 43ce0be339..4b57817859 100644 --- a/src/modm/ui/display/monochrome_graphic_display_horizontal.hpp +++ b/src/modm/ui/display/monochrome_graphic_display_horizontal.hpp @@ -31,7 +31,7 @@ namespace modm * * \ingroup modm_ui_display */ -template +template class MonochromeGraphicDisplayHorizontal : public MonochromeGraphicDisplay { @@ -41,7 +41,6 @@ class MonochromeGraphicDisplayHorizontal public: virtual ~MonochromeGraphicDisplayHorizontal() = default; -protected: void setPixel(int16_t x, int16_t y) final; diff --git a/src/modm/ui/display/monochrome_graphic_display_horizontal_impl.hpp b/src/modm/ui/display/monochrome_graphic_display_horizontal_impl.hpp index 41fe64fd38..e18174043d 100644 --- a/src/modm/ui/display/monochrome_graphic_display_horizontal_impl.hpp +++ b/src/modm/ui/display/monochrome_graphic_display_horizontal_impl.hpp @@ -14,26 +14,28 @@ namespace modm { -template +template void MonochromeGraphicDisplayHorizontal::setPixel(int16_t x, int16_t y) { - if ((x < Width) and (y < Height)) { buffer[y][x / 8] |= (1 << (x % 8)); } + if (this->xOnScreen(x) and this->yOnScreen(y)) + this->buffer[x / 8][y] |= (1 << (x % 8)); } -template +template void MonochromeGraphicDisplayHorizontal::clearPixel(int16_t x, int16_t y) { - if ((x < Width) and (y < Height)) { buffer[y][x / 8] &= ~(1 << (x % 8)); } + if (this->xOnScreen(x) and this->yOnScreen(y)) + this->buffer[x / 8][y] &= ~(1 << (x % 8)); } -template +template bool -MonochromeGraphicDisplayHorizontal::getPixel(int16_t x, int16_t y) +MonochromeGraphicDisplayHorizontal::getPixel(int16_t x, int16_t y) const { - if ((x < Width) and (y < Height)) - return (buffer[y][x / 8] & (1 << (x % 8))); + if (this->xOnScreen(x) and this->yOnScreen(y)) + return (this->buffer[x / 8][y] & (1 << (x % 8))); else return false; } diff --git a/src/modm/ui/display/monochrome_graphic_display_impl.hpp b/src/modm/ui/display/monochrome_graphic_display_impl.hpp index 53adb5f429..84ab4a4dc7 100644 --- a/src/modm/ui/display/monochrome_graphic_display_impl.hpp +++ b/src/modm/ui/display/monochrome_graphic_display_impl.hpp @@ -18,10 +18,10 @@ #error "Don't include this file directly, use 'monochrome_graphic_display.hpp' instead!" #endif -template +template void modm::MonochromeGraphicDisplay::clear() { - std::fill(&buffer[0][0], &buffer[BufferWidth][BufferHeight], 0); - this->cursor = glcd::Point(0, 0); + std::fill(&buffer[0][0], &buffer[0][0] + sizeof(buffer), 0); + this->cursor = {0, 0}; } \ No newline at end of file diff --git a/src/modm/ui/display/monochrome_graphic_display_vertical.hpp b/src/modm/ui/display/monochrome_graphic_display_vertical.hpp index 3377d476b9..5fa869929e 100644 --- a/src/modm/ui/display/monochrome_graphic_display_vertical.hpp +++ b/src/modm/ui/display/monochrome_graphic_display_vertical.hpp @@ -34,7 +34,7 @@ namespace modm * \author Fabian Greif * \ingroup modm_ui_display */ -template +template class MonochromeGraphicDisplayVertical : public MonochromeGraphicDisplay { diff --git a/src/modm/ui/display/monochrome_graphic_display_vertical_impl.hpp b/src/modm/ui/display/monochrome_graphic_display_vertical_impl.hpp index 3384a1c029..85304de815 100644 --- a/src/modm/ui/display/monochrome_graphic_display_vertical_impl.hpp +++ b/src/modm/ui/display/monochrome_graphic_display_vertical_impl.hpp @@ -16,121 +16,88 @@ #error "Don't include this file directly, use 'monochrome_graphic_display_vertical.hpp' instead!" #endif -template +#include + +template void modm::MonochromeGraphicDisplayVertical::drawHorizontalLine(glcd::Point start, uint16_t length) { - if (start.y >= 0 and start.y < Height) + if (this->yOnScreen(start.y)) { - const int16_t y = start.y / 8; - - // TODO Implement draw / clear pixels for monochrome displays - // if (draw mode) - // { const uint8_t byte = 1 << (start.y % 8); - for (int_fast16_t x = start.x; x < static_cast(start.x + length); ++x) - { - if (x < Width) { this->buffer[x][y] |= byte; } - } - // } else - // { - // const uint8_t byte = ~(1 << (start.y % 8)); - // for (int_fast16_t x = start.x; x < static_cast(start.x + length); - // ++x) - // { - // if (x < Width and y < Height) { this->buffer[x][y] &= byte; } - // } - // } + const size_t yb = start.y / 8; + const size_t x_max = std::min(start.x + length, Width - 1); + + for (size_t x = start.x; x < x_max; x++) + this->buffer[yb][x] |= byte; } } -template +template void modm::MonochromeGraphicDisplayVertical::drawVerticalLine(glcd::Point start, uint16_t length) { - if (start.x >= 0 and start.x < Width) + if (this->xOnScreen(start.x)) { - const int8_t end_y = start.y + length; - const uint8_t y_last = end_y / 8; + size_t yb = start.y / 8; + const size_t y_max = std::min((start.y + length), Height); + const size_t yb_max = y_max / 8; - uint_fast8_t y = start.y / 8; - // Mask out start - uint_fast8_t byte = 0xFF << start.y % 8; - while (y != y_last) - { - if (y < Height / 8) - { - this->buffer[start.x][y] |= byte; - byte = 0xFF; - } - y++; - } - // Mask out end - if (y < Height / 8) + uint8_t byte = 0xFF << start.y % 8; // Mask out start + + while (yb != yb_max) { - byte &= 0xFF >> (8 - end_y % 8); - this->buffer[start.x][y] |= byte; + this->buffer[yb][start.x] |= byte; + byte = 0xFF; + yb++; } + + byte &= 0xFF >> (8 - y_max % 8); // Mask out end + this->buffer[yb][start.x] |= byte; } } -template +template void modm::MonochromeGraphicDisplayVertical::drawImageRaw( glcd::Point start, uint16_t width, uint16_t height, modm::accessor::Flash data) { - if ((start.y % 8) == 0) + if (!start.y % 8 && !height % 8) { - uint16_t row = start.y / 8; - uint16_t rowCount = (height + 7) / 8; // always round up + const size_t x_max = std::min(start.x + width, Width - 1); + const size_t yb_max = std::min(start.y / 8 + height, Height / 8 - 1); + size_t i = 0; - if ((height % 8) == 0) - { - for (uint_fast16_t i = 0; i < width; i++) - { - for (uint_fast16_t k = 0; k < rowCount; k++) - { - uint16_t x = start.x + i; - uint16_t y = k + row; - - if (x < Width and y < Height) - { - this->buffer[x][y] = data[i + k * width]; - } - } - } - return; - } - } - - GraphicDisplay::drawImageRaw(start, width, height, data); + for (size_t x = start.x; x < x_max; x++) + for (size_t yb = start.y / 8; yb <= yb_max; yb++) this->buffer[yb][x] = data[i++]; + } else + GraphicDisplay::drawImageRaw(start, width, height, data); } -template +template void modm::MonochromeGraphicDisplayVertical::setPixel(int16_t x, int16_t y) { - if (x < Width and y < Height) { this->buffer[x][y / 8] |= (1 << y % 8); } + if (this->xOnScreen(x) and this->yOnScreen(y)) + this->buffer[y / 8][x] |= (1 << y % 8); } -template +template void modm::MonochromeGraphicDisplayVertical::clearPixel(int16_t x, int16_t y) { - if (x < Width and y < Height) { this->buffer[x][y / 8] &= ~(1 << y % 8); } + if (this->xOnScreen(x) and this->yOnScreen(y)) + this->buffer[y / 8][x] &= ~(1 << y % 8); } -template +template bool modm::MonochromeGraphicDisplayVertical::getPixel(int16_t x, int16_t y) const { - if (x < Width and y < Height) - { - return (this->buffer[x][y / 8] & (1 << y % 8)); - } else - { + if (this->xOnScreen(x) and this->yOnScreen(y)) + return (this->buffer[y / 8][x] & (1 << y % 8)); + else return false; - } } \ No newline at end of file