Add support for SH1106 OLED displays

This commit is contained in:
Dale Whinham committed 2021-02-03 05:06:40 +00:00
1 parent 126d04f2dc
commit 22dc67e540
8 files changed
+95 -9

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- USB plug & play support for MIDI interfaces.
* You can now connect/disconnect USB MIDI devices at runtime and they will be used instead of the GPIO MIDI interface when present.
- Support for SH1106 OLED displays - many thanks to @arananet for donating a screen!
### Changed
+1
View File
@@ -14,6 +14,7 @@ OBJS := src/config.o \
src/lcd/hd44780.o \
src/lcd/hd44780fourbit.o \
src/lcd/hd44780i2c.o \
src/lcd/sh1106.o \
src/lcd/ssd1306.o \
src/lcd/synthlcd.o \
src/main.o \
+1
View File
@@ -61,6 +61,7 @@ public:
ENUM(None, none) \
ENUM(HD44780FourBit, hd44780_4bit) \
ENUM(HD44780I2C, hd44780_i2c) \
ENUM(SH1106I2C, sh1106_i2c) \
ENUM(SSD1306I2C, ssd1306_i2c)
CONFIG_ENUM(TSystemDefaultSynth, ENUM_SYSTEMDEFAULTSYNTH);
+12 -2
View File
@@ -51,7 +51,7 @@ public:
virtual void Update(CMT32Synth& Synth) override;
virtual void Update(CSoundFontSynth& Synth) override;
private:
protected:
struct TFrameBufferUpdatePacket
{
u8 DataControlByte;
@@ -60,7 +60,7 @@ private:
PACKED;
void WriteCommand(u8 nCommand) const;
void WriteFrameBuffer() const;
virtual void WriteFrameBuffer(bool bForceFullUpdate = false) const;
void SwapFrameBuffers();
void SetPixel(u8 nX, u8 nY);
void ClearPixel(u8 nX, u8 nY);
@@ -83,4 +83,14 @@ private:
u8 m_nCurrentFrameBuffer;
};
class CSH1106 : public CSSD1306
{
public:
CSH1106(CI2CMaster* pI2CMaster, u8 nAddress = 0x3C, u8 nWidth = 128, u8 nHeight = 32, TLCDRotation Rotation = TLCDRotation::Normal);
private:
void WriteData(const u8* pData, size_t nSize) const;
virtual void WriteFrameBuffer(bool bForceFullUpdate = false) const override;
};
#endif
+3 -2
View File
@@ -272,14 +272,15 @@ polyphony = 256
# your device via the i2c_lcd_address option. Consult its datasheet, or see
# our documentation for tested models and their configurations.
#
# Values: none*, hd44780_4bit, hd44780_i2c, ssd1306_i2c
# Values: none*, hd44780_4bit, hd44780_i2c, sh1106_i2c, ssd1306_i2c
#
# none: No LCD
# hd44780_4bit: Hitachi HD44780 or compatible (e.g. WS0010, RS0010) character
# LCD connected to GPIO pins in 4-bit mode (see documentation for
# pinout)
# hd44780_i2c: As above, but using an I2C "backpack"
# ssd1306_i2c: Small I2C-based OLED graphical display
# sh1106_i2c: Small I2C-based OLED graphical display (usually 1.3")
# ssd1306_i2c: Small I2C-based OLED graphical display (usually 0.96")
type = none
# Set the width of the LCD.
+68
View File
@@ -0,0 +1,68 @@
//
// sh1106.cpp
//
// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi
// Copyright (C) 2020-2021 Dale Whinham <daleyo@gmail.com>
//
// This file is part of mt32-pi.
//
// mt32-pi is free software: you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// mt32-pi is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// mt32-pi. If not, see <http://www.gnu.org/licenses/>.
//
#include "lcd/ssd1306.h"
enum TSH1106Command : u8
{
SetColumnAddressLow = 0x00,
SetColumnAddressHigh = 0x10,
SetStartLine = 0x40,
SetPageAddress = 0xB0,
};
CSH1106::CSH1106(CI2CMaster *pI2CMaster, u8 nAddress, u8 nWidth, u8 nHeight, TLCDRotation Rotation)
: CSSD1306(pI2CMaster, nAddress, nWidth, nHeight, Rotation)
{
}
void CSH1106::WriteFrameBuffer(bool bForceFullUpdate) const
{
// Reset start line
WriteCommand(SetStartLine | 0x00);
const size_t nPages = m_nHeight / 8;
constexpr size_t nPageSize = 128;
// Copy framebuffer one page at a time
for (u8 nPage = 0; nPage < nPages; ++nPage)
{
const size_t nOffset = nPage * nPageSize;
const bool bNeedsUpdate = bForceFullUpdate || memcmp(&m_FrameBuffers[0].FrameBuffer[nOffset], &m_FrameBuffers[1].FrameBuffer[nOffset], nPageSize) != 0;
if (bNeedsUpdate)
{
WriteCommand(SetPageAddress | nPage);
// SH1106 displays have a 132x64 pixel memory, but most modules have a visible width of 128 centred on this buffer
WriteCommand(SetColumnAddressLow | 0x02);
WriteCommand(SetColumnAddressHigh | 0x00);
// Prefix this page's pixel data with a data control byte
u8 Buffer[nPageSize + 1] = { 0x40 };
memcpy(Buffer + 1, &m_FrameBuffers[m_nCurrentFrameBuffer].FrameBuffer[nOffset], nPageSize);
m_pI2CMaster->Write(m_nAddress, Buffer, sizeof(Buffer));
}
}
}
+4 -4
View File
@@ -210,14 +210,14 @@ void CSSD1306::WriteCommand(u8 nCommand) const
m_pI2CMaster->Write(m_nAddress, Buffer, sizeof(Buffer));
}
void CSSD1306::WriteFrameBuffer() const
void CSSD1306::WriteFrameBuffer(bool bForceFullUpdate) const
{
// Reset start line
WriteCommand(SetStartLine | 0x00);
// Compare two framebuffers
const size_t nFrameBufferSize = m_nWidth * m_nHeight / 8;
const bool bNeedsUpdate = memcmp(m_FrameBuffers[0].FrameBuffer, m_FrameBuffers[1].FrameBuffer, nFrameBufferSize) != 0;
const bool bNeedsUpdate = bForceFullUpdate || memcmp(m_FrameBuffers[0].FrameBuffer, m_FrameBuffers[1].FrameBuffer, nFrameBufferSize) != 0;
// Copy entire framebuffer
if (bNeedsUpdate)
@@ -450,7 +450,7 @@ void CSSD1306::Print(const char* pText, u8 nCursorX, u8 nCursorY, bool bClearLin
}
if (bImmediate)
WriteFrameBuffer();
WriteFrameBuffer(true);
}
void CSSD1306::Clear(bool bImmediate)
@@ -459,7 +459,7 @@ void CSSD1306::Clear(bool bImmediate)
memset(pFrameBuffer, 0, m_nWidth * m_nHeight / 8);
if (bImmediate)
WriteFrameBuffer();
WriteFrameBuffer(true);
}
void CSSD1306::SetBacklightEnabled(bool bEnabled)
+5 -1
View File
@@ -116,6 +116,8 @@ bool CMT32Pi::Initialize(bool bSerialMIDIAvailable)
m_pLCD = new CHD44780FourBit(pConfig->LCDWidth, pConfig->LCDHeight);
else if (pConfig->LCDType == CConfig::TLCDType::HD44780I2C)
m_pLCD = new CHD44780I2C(m_pI2CMaster, pConfig->LCDI2CLCDAddress, pConfig->LCDWidth, pConfig->LCDHeight);
else if (pConfig->LCDType == CConfig::TLCDType::SH1106I2C)
m_pLCD = new CSH1106(m_pI2CMaster, pConfig->LCDI2CLCDAddress, pConfig->LCDWidth, pConfig->LCDHeight, pConfig->LCDRotation);
else if (pConfig->LCDType == CConfig::TLCDType::SSD1306I2C)
m_pLCD = new CSSD1306(m_pI2CMaster, pConfig->LCDI2CLCDAddress, pConfig->LCDWidth, pConfig->LCDHeight, pConfig->LCDRotation);
@@ -244,13 +246,15 @@ bool CMT32Pi::Initialize(bool bSerialMIDIAvailable)
CCPUThrottle::Get()->DumpStatus();
SetPowerSaveTimeout(pConfig->SystemPowerSaveTimeout);
// Attach LCD to synths
// Attach LCD to synths and clear
if (m_pLCD)
{
if (m_pMT32Synth)
m_pMT32Synth->SetLCD(m_pLCD);
if (m_pSoundFontSynth)
m_pSoundFontSynth->SetLCD(m_pLCD);
m_pLCD->Clear();
}
// Start audio