Skip to content

Commit b45d71e

Browse files
committed
[driver] Add SSD1306 SPI driver
1 parent 8032724 commit b45d71e

File tree

4 files changed

+321
-3
lines changed

4 files changed

+321
-3
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -797,26 +797,29 @@ your specific needs.
797797
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s75">SIEMENS-S75</a></td>
798798
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk6812">SK6812</a></td>
799799
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk9822">SK9822</a></td>
800-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306">SSD1306</a></td>
800+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306-common">SSD1306-COMMON</a></td>
801801
</tr><tr>
802+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306-i2c">SSD1306-I2C</a></td>
803+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306-spi">SSD1306</a></td>
802804
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7586s">ST7586S</a></td>
803805
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7789">ST7789</a></td>
804806
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stts22h">STTS22H</a></td>
805807
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stusb4500">STUSB4500</a></td>
808+
</tr><tr>
806809
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx1276">SX1276</a></td>
807810
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx128x">SX128X</a></td>
808-
</tr><tr>
809811
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3414">TCS3414</a></td>
810812
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3472">TCS3472</a></td>
811813
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tlc594x">TLC594x</a></td>
812814
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp102">TMP102</a></td>
815+
</tr><tr>
813816
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp12x">TMP12x</a></td>
814817
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp175">TMP175</a></td>
815-
</tr><tr>
816818
<td align="center"><a href="https://modm.io/reference/module/modm-driver-touch2046">TOUCH2046</a></td>
817819
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl53l0">VL53L0</a></td>
818820
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl6180">VL6180</a></td>
819821
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ws2812">WS2812</a></td>
822+
</tr><tr>
820823
</tr>
821824
</table>
822825
<!--/drivertable-->
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (c) 2014, 2016-2017, Sascha Schade
3+
* Copyright (c) 2014-2016, 2018, Niklas Hauser
4+
* Copyright (c) 2021, Thomas Sommer
5+
* Copyright (c) 2023, Raphael Lehmann
6+
*
7+
* This file is part of the modm project.
8+
*
9+
* This Source Code Form is subject to the terms of the Mozilla Public
10+
* License, v. 2.0. If a copy of the MPL was not distributed with this
11+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
12+
*/
13+
// ----------------------------------------------------------------------------
14+
15+
#ifndef MODM_SSD1306_SPI_HPP
16+
#define MODM_SSD1306_SPI_HPP
17+
18+
#include <modm/architecture/interface/spi_device.hpp>
19+
#include <modm/architecture/utils.hpp>
20+
#include <modm/processing/timer.hpp>
21+
#include <modm/ui/display/monochrome_graphic_display_vertical.hpp>
22+
23+
#include "ssd1306_common.hpp"
24+
25+
namespace modm
26+
{
27+
28+
/**
29+
* Driver for SSD1306 based OLED-displays using SPI 4-wire mode.
30+
*
31+
* @author Raphael Lehmann
32+
* @ingroup modm_driver_ssd1306
33+
*/
34+
template<class SpiMaster, class Cs, class Dc, uint8_t Height = 64>
35+
class Ssd1306Spi : public ssd1306,
36+
public MonochromeGraphicDisplayVertical<128, Height>,
37+
public SpiDevice<SpiMaster>,
38+
protected modm::NestedResumable<2>
39+
{
40+
static_assert((Height == 64) or (Height == 32), "Display height must be either 32 or 64 pixel!");
41+
42+
public:
43+
/// Update the display with the content of the RAM buffer
44+
/// @warning This function is blocking as long as the transfer of the
45+
/// whole display buffer via SPI takes.
46+
// Better use @see writeDisplay() instead.
47+
void
48+
update() override
49+
{
50+
RF_CALL_BLOCKING(writeDisplay());
51+
}
52+
53+
enum class
54+
InitSequences : uint8_t
55+
{
56+
Hp12832_02, /// HP12832-02 display module, 0.91inch, 128x32px, 4-wire
57+
/// SPI, blue, external 7.5V supply
58+
};
59+
60+
modm::ResumableResult<bool>
61+
initialize(InitSequences initSeq = InitSequences::Hp12832_02);
62+
63+
/// Update the display with the content of the RAM buffer
64+
virtual modm::ResumableResult<bool>
65+
writeDisplay();
66+
67+
modm::ResumableResult<bool>
68+
setDisplayMode(DisplayMode mode = DisplayMode::Normal)
69+
{
70+
commandBuffer[0] = uint8_t(mode);
71+
return writeCommands(1);
72+
}
73+
74+
modm::ResumableResult<bool>
75+
setContrast(uint8_t contrast = 0xCE)
76+
{
77+
commandBuffer[0] = uint8_t(FundamentalCommands::ContrastControl);
78+
commandBuffer[1] = uint8_t(contrast);
79+
return writeCommands(2);
80+
}
81+
82+
/**
83+
* \param orientation glcd::Orientation::Landscape0 or glcd::Orientation::Landscape180
84+
*/
85+
modm::ResumableResult<bool>
86+
setOrientation(glcd::Orientation orientation);
87+
88+
modm::ResumableResult<bool>
89+
configureScroll(uint8_t origin, uint8_t size, ScrollDirection direction, ScrollStep steps);
90+
91+
modm::ResumableResult<bool>
92+
enableScroll()
93+
{
94+
commandBuffer[0] = uint8_t(ScrollingCommands::EnableScroll);
95+
return writeCommands(1);
96+
}
97+
98+
modm::ResumableResult<bool>
99+
disableScroll()
100+
{
101+
commandBuffer[0] = uint8_t(ScrollingCommands::DisableScroll);
102+
return writeCommands(1);
103+
}
104+
105+
protected:
106+
modm::ResumableResult<bool>
107+
writeCommands(std::size_t length);
108+
109+
private:
110+
uint8_t commandBuffer[31];
111+
};
112+
113+
} // namespace modm
114+
115+
#include "ssd1306_spi_impl.hpp"
116+
117+
#endif // MODM_SSD1306_SPI_HPP
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2023, Raphael Lehmann
5+
#
6+
# This file is part of the modm project.
7+
#
8+
# This Source Code Form is subject to the terms of the Mozilla Public
9+
# License, v. 2.0. If a copy of the MPL was not distributed with this
10+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
11+
# -----------------------------------------------------------------------------
12+
13+
14+
def init(module):
15+
module.name = ":driver:ssd1306.spi"
16+
module.description = "SSD1306 Display in SPI 4-wire mode"
17+
18+
def prepare(module, options):
19+
module.depends(
20+
":architecture:spi.device",
21+
":driver:ssd1306.common",
22+
":processing:timer",
23+
":ui:display")
24+
return True
25+
26+
def build(env):
27+
env.outbasepath = "modm/src/modm/driver/display"
28+
env.copy("ssd1306_spi.hpp")
29+
env.copy("ssd1306_spi_impl.hpp")
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (c) 2014-2015, Niklas Hauser
3+
* Copyright (c) 2023, Raphael Lehmann
4+
*
5+
* This file is part of the modm project.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
10+
*/
11+
// ----------------------------------------------------------------------------
12+
13+
#ifndef MODM_SSD1306_SPI_HPP
14+
#error "Don't include this file directly, use 'ssd1306_spi.hpp' instead!"
15+
#endif
16+
17+
namespace modm
18+
{
19+
20+
template<class SpiMaster, class Cs, class Dc, uint8_t Height>
21+
ResumableResult<bool>
22+
Ssd1306Spi<SpiMaster, Cs, Dc, Height>::initialize(InitSequences initSeq)
23+
{
24+
RF_BEGIN();
25+
26+
this->attachConfigurationHandler([]() {
27+
SpiMaster::setDataMode(SpiMaster::DataMode::Mode0);
28+
SpiMaster::setDataOrder(SpiMaster::DataOrder::MsbFirst);
29+
});
30+
Cs::setOutput(modm::Gpio::High);
31+
Dc::setOutput();
32+
33+
if (initSeq == InitSequences::Hp12832_02)
34+
{
35+
// Initialize sequence from HP12832-02-TSBG12P091-A-VER1.0.pdf page 14
36+
37+
commandBuffer[0] = uint8_t(FundamentalCommands::DisplayOff);
38+
commandBuffer[1] = uint8_t(TimingAndDrivingCommands::DisplayClockDivideRatio);
39+
commandBuffer[2] = 0x80; // 0x90 or 0x80 (page 14)?
40+
commandBuffer[3] = uint8_t(HardwareConfigCommands::MultiplexRatio);
41+
commandBuffer[4] = 0x1F; // 0x3F or 0x1F (page 14)?
42+
commandBuffer[5] = uint8_t(HardwareConfigCommands::DisplayOffset);
43+
commandBuffer[6] = 0;
44+
commandBuffer[7] = uint8_t(AdressingCommands::MemoryMode);
45+
commandBuffer[8] = uint8_t(MemoryMode::HORIZONTAL);
46+
commandBuffer[9] = uint8_t(AdressingCommands::ColumnAddress);
47+
commandBuffer[10] = 0;
48+
commandBuffer[11] = 127;
49+
commandBuffer[12] = uint8_t(AdressingCommands::PageAddress);
50+
commandBuffer[13] = 0;
51+
commandBuffer[14] = (Height == 64) ? 7 : 3;
52+
commandBuffer[15] = uint8_t(HardwareConfigCommands::DisplayStartLine) | 0;
53+
commandBuffer[16] = uint8_t(HardwareConfigCommands::SegmentRemap0);
54+
commandBuffer[17] = uint8_t(HardwareConfigCommands::ComOutputScanDirectionIncrement);
55+
commandBuffer[18] = uint8_t(HardwareConfigCommands::ComPinsOrder);
56+
commandBuffer[19] = 0x02;
57+
commandBuffer[20] = uint8_t(FundamentalCommands::ContrastControl);
58+
commandBuffer[21] = 0x8F;
59+
commandBuffer[22] = uint8_t(TimingAndDrivingCommands::PreChargePeriod);
60+
commandBuffer[23] = 0x1F;
61+
commandBuffer[24] = uint8_t(TimingAndDrivingCommands::V_DeselectLevel);
62+
commandBuffer[25] = 0x30;
63+
commandBuffer[26] = uint8_t(FundamentalCommands::EntireDisplayResumeToRam);
64+
commandBuffer[27] = uint8_t(FundamentalCommands::NormalDisplay);
65+
commandBuffer[28] = uint8_t(TimingAndDrivingCommands::ChargePump);
66+
commandBuffer[29] = uint8_t(ChargePump::DISABLE);
67+
commandBuffer[30] = uint8_t(FundamentalCommands::DisplayOn);
68+
69+
RF_RETURN_CALL(writeCommands(31));
70+
}
71+
else {
72+
RF_RETURN(false);
73+
}
74+
75+
RF_END();
76+
}
77+
78+
template<class SpiMaster, class Cs, class Dc, uint8_t Height>
79+
ResumableResult<bool>
80+
Ssd1306Spi<SpiMaster, Cs, Dc, Height>::setOrientation(glcd::Orientation orientation)
81+
{
82+
RF_BEGIN();
83+
84+
if (orientation == glcd::Orientation::Landscape0)
85+
{
86+
commandBuffer[0] = uint8_t(HardwareConfigCommands::SegmentRemap127);
87+
commandBuffer[1] = uint8_t(HardwareConfigCommands::ComOutputScanDirectionDecrement);
88+
}
89+
else if (orientation == glcd::Orientation::Landscape180)
90+
{
91+
commandBuffer[0] = uint8_t(HardwareConfigCommands::SegmentRemap0);
92+
commandBuffer[1] = uint8_t(HardwareConfigCommands::ComOutputScanDirectionIncrement);
93+
}
94+
else {
95+
RF_RETURN(false);
96+
}
97+
98+
RF_END_RETURN_CALL(writeCommands(2));
99+
}
100+
101+
template<class SpiMaster, class Cs, class Dc, uint8_t Height>
102+
ResumableResult<bool>
103+
Ssd1306Spi<SpiMaster, Cs, Dc, Height>::configureScroll(uint8_t origin, uint8_t size,
104+
ScrollDirection direction, ScrollStep steps)
105+
{
106+
RF_BEGIN();
107+
108+
if (!RF_CALL(disableScroll()))
109+
RF_RETURN(false);
110+
111+
{
112+
uint8_t beginY = (origin > 7) ? 7 : origin;
113+
114+
uint8_t endY = ((origin + size) > 7) ? 7 : (origin + size);
115+
if (endY < beginY) endY = beginY;
116+
117+
commandBuffer[0] = uint8_t(direction);
118+
commandBuffer[1] = 0x00;
119+
commandBuffer[2] = beginY;
120+
commandBuffer[3] = uint8_t(steps);
121+
commandBuffer[4] = endY;
122+
commandBuffer[5] = 0x00;
123+
commandBuffer[6] = 0xFF;
124+
}
125+
126+
RF_END_RETURN_CALL(writeCommands(7));
127+
}
128+
129+
template<class SpiMaster, class Cs, class Dc, uint8_t Height>
130+
ResumableResult<bool>
131+
Ssd1306Spi<SpiMaster, Cs, Dc, Height>::writeDisplay()
132+
{
133+
RF_BEGIN();
134+
135+
RF_WAIT_UNTIL(this->acquireMaster());
136+
Cs::reset();
137+
138+
Dc::set();
139+
140+
RF_CALL(SpiMaster::transfer((uint8_t*)(&this->buffer), nullptr, sizeof(this->buffer)));
141+
142+
if (this->releaseMaster()) {
143+
Cs::set();
144+
}
145+
146+
RF_END_RETURN(true);
147+
}
148+
149+
template<class SpiMaster, class Cs, class Dc, uint8_t Height>
150+
ResumableResult<bool>
151+
Ssd1306Spi<SpiMaster, Cs, Dc, Height>::writeCommands(std::size_t length)
152+
{
153+
RF_BEGIN();
154+
155+
RF_WAIT_UNTIL(this->acquireMaster());
156+
Cs::reset();
157+
158+
Dc::reset();
159+
160+
RF_CALL(SpiMaster::transfer(commandBuffer, nullptr, length));
161+
162+
if (this->releaseMaster()) {
163+
Cs::set();
164+
}
165+
166+
RF_END_RETURN(true);
167+
}
168+
169+
} // namespace modm

0 commit comments

Comments
 (0)