Skip to content

Commit

Permalink
Monochrome implementation of ST7586S display
Browse files Browse the repository at this point in the history
This panel is 4-level grayscale, but this color mode isn't
supported by modm yet.
  • Loading branch information
twasilczyk committed Sep 4, 2021
1 parent 364bf3f commit 8aaee52
Show file tree
Hide file tree
Showing 10 changed files with 475 additions and 8 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,20 +613,21 @@ you specific needs.
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk9822">SK9822</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306">SSD1306</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7586s">ST7586S</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stts22h">STTS22H</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stusb4500">STUSB4500</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx1276">SX1276</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3414">TCS3414</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3472">TCS3472</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tlc594x">TLC594X</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tlc594x">TLC594X</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp102">TMP102</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp175">TMP175</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-touch2046">TOUCH2046</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl53l0">VL53L0</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl6180">VL6180</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ws2812">WS2812</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ws2812">WS2812</a></td>
</tr>
</table>
<!--/drivertable-->
Expand Down
27 changes: 27 additions & 0 deletions examples/srxe/display/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2021, Tomasz Wasilczyk
*
* 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 <modm/board.hpp>

int
main()
{
Board::initialize();

auto& display = Board::display;

display.setCursor(115, 54);
display.setFont(modm::font::Ubuntu_36);
display << "Hello World";
display.drawRoundedRectangle({100, 48}, 184, 40, 10);

display.update();
}
9 changes: 9 additions & 0 deletions examples/srxe/display/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<library>
<extends>modm:srxe</extends>
<options>
<option name="modm:build:build.path">../../../build/srxe/display</option>
</options>
<modules>
<module>modm:build:scons</module>
</modules>
</library>
29 changes: 27 additions & 2 deletions src/modm/board/srxe/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#include <modm/platform.hpp>

#include <modm/driver/display/st7586s.hpp>

using namespace modm::platform;

/// @ingroup modm_board_srxe
Expand All @@ -25,21 +27,44 @@ using SystemClock = modm::platform::SystemClock;
using LedDebug = GpioB0;
using Leds = SoftwareGpioPort<LedDebug>;

namespace Display {
namespace spi {

using Sck = GpioB1;
using Mosi = GpioB2;
using Miso = GpioB3;
using SpiMaster = modm::platform::SpiMaster;

} // namespace spi

namespace DisplayGpio {

using DC = GpioD6;
using CS = GpioE7;
using RST = GpioG2;

} // namespace Display
} // namespace DisplayGpio

using Display = modm::St7586s<spi::SpiMaster, DisplayGpio::CS, DisplayGpio::RST, DisplayGpio::DC,
384, 136>;
extern Display display;

inline void
initialize() {
SystemClock::enable();

LedDebug::setOutput();

spi::SpiMaster::connect<spi::Sck::Sck, spi::Mosi::Mosi, spi::Miso::Miso>();
/* Display controller datasheet requires minimum 60+140ns clock pulse width (that's 3.5MHz
* for 200ns cycle or 5MHz for 280ns), but it seems to work just fine with 8MHz.
*/
spi::SpiMaster::initialize<SystemClock, 4_MHz>();
// Clock is high when inactive, data is sampled on clock trailing edge.
spi::SpiMaster::setDataMode(spi::SpiMaster::DataMode::Mode3);

enableInterrupts();

display.initialize();
}

} // namespace Board
18 changes: 18 additions & 0 deletions src/modm/board/srxe/board_display.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2021, Tomasz Wasilczyk
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#include "board.hpp"

namespace Board {

Display display;

} // namespace Board
8 changes: 4 additions & 4 deletions src/modm/board/srxe/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def init(module):
Smart Response XE is an obsolete classroom clicker, sold for as little as 5 USD on well known online auction site.
It's a compelling platform that's fully reverse engineered and ready to hack out of box, featuring:
- ATmega128RFA1 MCU
- 384x160 LCD display
- 384x136 LCD display
- QWERTY keyboard
- External 1M SPI flash
- Exposed ISP and JTAG headers
Expand All @@ -36,13 +36,13 @@ def prepare(module, options):
return False

module.depends(
":architecture:clock",
":architecture:interrupt",
":debug",
":platform:clock",
":platform:core",
":platform:gpio",
":platform:uart:0")
":platform:spi",
":platform:uart:0",
":driver:st7586s")
return True

def build(env):
Expand Down
44 changes: 44 additions & 0 deletions src/modm/driver/display/st7586s.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2021, Tomasz Wasilczyk
*
* 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
#define MODM_ST7586S_HPP

#include <modm/ui/display/monochrome_graphic_display_horizontal.hpp>

#include "st7586s_protocol.hpp"

namespace modm
{

template <typename SPI, typename CS, typename RST, typename DC, int Width = 384, int Height = 160>
// TODO: this controller has pixels packed by 3, not 8 per byte
class St7586s : public MonochromeGraphicDisplayHorizontal<Width, Height>
{
using Command = impl::st7586s::Command;
static constexpr uint8_t pixelsPerByte = 3;

void sendCommand(Command cmd, const void *data = nullptr, size_t len = 0);
template <typename Data>
void sendCommand(Command cmd, Data data) {
sendCommand(cmd, &data, sizeof(data));
}

void setClipping(uint16_t x, uint16_t y, uint16_t w, uint16_t h);

public:
void initialize();
virtual void update() override;
};

} // namespace modm

#include "st7586s_impl.hpp"
28 changes: 28 additions & 0 deletions src/modm/driver/display/st7586s.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021, Tomasz Wasilczyk
#
# 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:st7586s"
module.description = "ST7586S 4-level grayscale LCD controller"

def prepare(module, options):
module.depends(
":architecture:delay",
":ui:display")
return True

def build(env):
env.outbasepath = "modm/src/modm/driver/display"
env.copy("st7586s.hpp")
env.copy("st7586s_impl.hpp")
env.copy("st7586s_protocol.hpp")
128 changes: 128 additions & 0 deletions src/modm/driver/display/st7586s_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright (c) 2021, Tomasz Wasilczyk
*
* 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
#ifndef MODM_ST7586S_HPP
#error "Do not include this file directly. Include st7586s.hpp instead."
#endif

#include <modm/architecture/interface/assert.hpp>
#include <modm/architecture/interface/gpio.hpp>

namespace modm
{

template <typename SPI, typename CS, typename RST, typename DC, int Width, int Height>
void
St7586s<SPI, CS, RST, DC, Width, Height>::sendCommand(Command cmd, const void *data, size_t len)
{
CS::reset();
DC::reset(); // command mode
SPI::transferBlocking(static_cast<uint8_t>(cmd));
DC::set(); // data mode
if (len > 0) {
SPI::transferBlocking(reinterpret_cast<const uint8_t*>(data), nullptr, len);
}
CS::set();
// exit with data mode on
}

template <typename SPI, typename CS, typename RST, typename DC, int Width, int Height>
void
St7586s<SPI, CS, RST, DC, Width, Height>::initialize()
{
namespace payload = impl::st7586s::payload;

// Configure GPIO
RST::reset();
RST::setOutput();
CS::set();
CS::setOutput();
DC::setOutput();

// Reset display
modm::delay_us(10);
RST::set();
modm::delay_ms(120);

// Power ON operation flow (see datasheet)
sendCommand(Command::SleepOff);
sendCommand(Command::DisplayOff);
modm::delay_ms(50); // t_{ON-V2}
sendCommand(Command::SetVop, payload::SetVop(13.52f));
sendCommand(Command::SetBias, payload::SetBias::Ratio_1_11);
sendCommand(Command::SetBooster, payload::SetBooster::x8);
sendCommand(Command::AnalogControl, payload::AnalogControl::Enable);
sendCommand<payload::NLineInversion_t>(Command::NLineInversion,
payload::LineInversionType_t(payload::LineInversionType::FrameInversion));
sendCommand(Command::DisplayModeMono); // TODO: grayscale
sendCommand(Command::EnableDdram, payload::EnableDdram::Enable);
sendCommand(Command::ScanDirection, payload::DisplayControl::ComInc
| payload::DisplayControl::SegInc);
sendCommand(Command::DisplayDuty, uint8_t(Height - 1));
sendCommand(Command::InverseOff);
setClipping(0, 0, Width, Height);
update(); // clear the display
sendCommand(Command::DisplayOn);
}

template <typename SPI, typename CS, typename RST, typename DC, int Width, int Height>
void
St7586s<SPI, CS, RST, DC, Width, Height>::setClipping(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
namespace payload = impl::st7586s::payload;

modm_assert(x < Width, "st4586s.sc.x", "x >= Width", x);
modm_assert(x + w <= Width, "st4586s.sc.xw", "x + w >= Width", x + w);
modm_assert(y < Height, "st4586s.sc.y", "y >= Height", y);
modm_assert(y + h <= Height, "st4586s.sc.yh", "y + h >= Height", y + h);
modm_assert((x % pixelsPerByte) == 0, "st4586s.sc.x%", "x is not a multiply of PBB", x);
modm_assert((w % pixelsPerByte) == 0, "st4586s.sc.w%", "w is not a multiply of PBB", w);

const payload::SetColumnRow columnRange(x / pixelsPerByte, (x + w) / pixelsPerByte - 1);
sendCommand(Command::SetColumn, columnRange);

const payload::SetColumnRow rowRange(y, y + h - 1);
sendCommand(Command::SetRow, rowRange);
}

template <typename SPI, typename CS, typename RST, typename DC, int Width, int Height>
void
St7586s<SPI, CS, RST, DC, Width, Height>::update()
{
sendCommand(Command::WriteDisplayData);
CS::reset();
// TODO: support windows other than full screen (then make setClipping public)
// TODO: transfer the whole memory area, not individual pixels
for (uint16_t y = 0; y < Height; y++) {
const uint8_t* row = this->buffer[y];
uint16_t currentByte = 0;
uint8_t validBits = 0;
for (uint16_t x = 0; x + 2 < Width; x += 3) {
if (validBits < 3) {
currentByte |= (*row++) << validBits;
validBits += 8;
}

uint8_t cell = 0;
if (currentByte & 0b001) cell |= (0b11 << 6);
if (currentByte & 0b010) cell |= (0b11 << 3);
if (currentByte & 0b100) cell |= (0b11 << 0);
validBits -= 3;
currentByte >>= 3;

SPI::transferBlocking(cell);
}
}
CS::set();
}

} // namespace modm
Loading

0 comments on commit 8aaee52

Please sign in to comment.