Add ability to invert display on SSD1306

This commit is contained in:
Dale Whinham committed 2020-11-08 18:24:32 +00:00
1 parent f5d31ff369
commit e79897eb9a
8 files changed
+85 -53

No files matched your search

+4
View File
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Ability to invert the display orientation for SSD1306 (new configuration file option).
## [0.6.2] - 2020-10-18
### Fixed
+1
View File
@@ -38,6 +38,7 @@ CFG(type, TLCDType, m_LCDType, TLCDType::None )
CFG(width, int, m_nLCDWidth, 20 )
CFG(height, int, m_nLCDHeight, 2 )
CFG(i2c_lcd_address, int, m_nLCDI2CLCDAddress, 0x3c, true )
CFG(rotation, TLCDRotation, m_nLCDRotation, TLCDRotation::Normal )
END_SECTION
#undef BEGIN_SECTION
+4
View File
@@ -21,6 +21,7 @@
#ifndef _config_h
#define _config_h
#include "lcd/ssd1306.h"
#include "mt32synth.h"
#include "rommanager.h"
#include "utility.h"
@@ -40,6 +41,8 @@ public:
using TMT32EmuMIDIChannels = CMT32SynthBase::TMIDIChannels;
using TMT32EmuROMSet = CROMManager::TROMSet;
using TLCDRotation = CSSD1306::TLCDRotation;
#define ENUM_LCDTYPE(ENUM) \
ENUM(None, none) \
ENUM(HD44780FourBit, hd44780_4bit) \
@@ -71,6 +74,7 @@ private:
static bool ParseOption(const char* pString, TMT32EmuMIDIChannels* pOut);
static bool ParseOption(const char* pString, TMT32EmuROMSet* pOut);
static bool ParseOption(const char* pString, TLCDType* pOut);
static bool ParseOption(const char* pString, TLCDRotation* pOut);
static CConfig* s_pThis;
};
+9 -3
View File
@@ -26,11 +26,18 @@
#include "lcd/mt32lcd.h"
#include "mt32synth.h"
#include "utility.h"
class CSSD1306 : public CMT32LCD
{
public:
CSSD1306(CI2CMaster* pI2CMaster, u8 nAddress = 0x3c, u8 nHeight = 32);
#define ENUM_LCDROTATION(ENUM) \
ENUM(Normal, normal) \
ENUM(Inverted, inverted)
CONFIG_ENUM(TLCDRotation, ENUM_LCDROTATION);
CSSD1306(CI2CMaster* pI2CMaster, u8 nAddress = 0x3c, u8 nHeight = 32, TLCDRotation Rotation = TLCDRotation::Normal);
// CCharacterLCD
virtual bool Initialize() override;
@@ -51,11 +58,10 @@ private:
CI2CMaster* m_pI2CMaster;
u8 m_nAddress;
u8 m_nHeight;
TLCDRotation m_Rotation;
// +1 to store the 0x40 command at the beginning
u8 m_Framebuffer[128 * 64 / 8 + 1];
static const u8 InitSequence[];
};
#endif
+11
View File
@@ -177,3 +177,14 @@ height = 32
#
# Values: 00-80 (3c*)
i2c_lcd_address = 3c
# Rotate the display output (graphical LCDs only).
#
# Some graphical displays support rotation. Use this option if you need to turn
# the display around.
#
# Values: normal*, inverted
#
# normal: No rotation
# inverted: The display output is upside down
rotation = normal
+2
View File
@@ -60,6 +60,7 @@ CONFIG_ENUM_STRINGS(TMT32EmuResamplerQuality, ENUM_RESAMPLERQUALITY);
CONFIG_ENUM_STRINGS(TMT32EmuMIDIChannels, ENUM_MIDICHANNELS);
CONFIG_ENUM_STRINGS(TMT32EmuROMSet, ENUM_ROMSET);
CONFIG_ENUM_STRINGS(TLCDType, ENUM_LCDTYPE);
CONFIG_ENUM_STRINGS(TLCDRotation, ENUM_LCDROTATION);
CConfig* CConfig::s_pThis = nullptr;
@@ -141,3 +142,4 @@ CONFIG_ENUM_PARSER(TMT32EmuResamplerQuality);
CONFIG_ENUM_PARSER(TMT32EmuMIDIChannels);
CONFIG_ENUM_PARSER(TMT32EmuROMSet);
CONFIG_ENUM_PARSER(TLCDType);
CONFIG_ENUM_PARSER(TLCDRotation);
+1 -1
View File
@@ -131,7 +131,7 @@ bool CKernel::Initialize(void)
else if (m_Config.m_LCDType == CConfig::TLCDType::HD44780I2C)
m_pLCD = new CHD44780I2C(&m_I2CMaster, m_Config.m_nLCDI2CLCDAddress, m_Config.m_nLCDWidth, m_Config.m_nLCDHeight);
else if (m_Config.m_LCDType == CConfig::TLCDType::SSD1306I2C)
m_pLCD = new CSSD1306(&m_I2CMaster, m_Config.m_nLCDI2CLCDAddress, m_Config.m_nLCDHeight);
m_pLCD = new CSSD1306(&m_I2CMaster, m_Config.m_nLCDI2CLCDAddress, m_Config.m_nLCDHeight, m_Config.m_nLCDRotation);
if (m_pLCD)
{
+53 -49
View File
@@ -86,52 +86,12 @@ namespace
constexpr auto FontSingle = Font<Utility::ArraySize(Font6x8), decltype(SingleColumn)>(Font6x8, SingleColumn);
constexpr auto FontDouble = Font<Utility::ArraySize(Font6x8), decltype(DoubleColumn)>(Font6x8, DoubleColumn);
const u8 CSSD1306::InitSequence[] =
{
0xAE, // Screen off
0x81, // Set contrast
0x7F, // 00-FF, default to half
0xA6, // Normal display
0x20, // Set memory addressing mode
0x0, // 00 = horizontal
0x21, // Set column start and end address
0x00,
0x7F,
0x22, // Set page address range
0x00,
0x03,
0xA1, // Set segment remap
0xA8, // Set multiplex ratio
0x1F, // Screen height - 1 (31)
0xC8, // Set COM output scan direction
0xD3, // Set display offset
0x00, // None
0xDA, // Set com pins hardware configuration
0x02, // Alternate COM config and disable COM left/right
0xD5, // Set display oscillator
0x80, // Default value
0xD9, // Set precharge period
0x22, // Default value
0xDB, // Set VCOMH deselected level
0x20, // Default
0x8D, // Set charge pump
0x14, // VCC generated by internal DC/DC circuit
0xA4, // Resume to RAM content display
0xAF // Set display on
};
CSSD1306::CSSD1306(CI2CMaster *pI2CMaster, u8 nAddress, u8 nHeight)
CSSD1306::CSSD1306(CI2CMaster *pI2CMaster, u8 nAddress, u8 nHeight, TLCDRotation Rotation)
: CMT32LCD(),
m_pI2CMaster(pI2CMaster),
m_nAddress(nAddress),
m_nHeight(nHeight),
m_Rotation(Rotation),
m_Framebuffer{0x40}
{
@@ -145,8 +105,52 @@ bool CSSD1306::Initialize()
if (!(m_nHeight == 32 || m_nHeight == 64))
return false;
const u8 segRemap = m_Rotation == TLCDRotation::Inverted ? 0xA0 : 0xA1;
const u8 comScanDir = m_Rotation == TLCDRotation::Inverted ? 0xC0 : 0xC8;
const u8 initSequence[] =
{
0xAE, // Screen off
0x81, // Set contrast
0x7F, // 00-FF, default to half
0xA6, // Normal display
0x20, // Set memory addressing mode
0x0, // 00 = horizontal
0x21, // Set column start and end address
0x00,
0x7F,
0x22, // Set page address range
0x00,
0x03,
segRemap, // Set segment remap
0xA8, // Set multiplex ratio
0x1F, // Screen height - 1 (31)
comScanDir, // Set COM output scan direction
0xD3, // Set display offset
0x00, // None
0xDA, // Set com pins hardware configuration
0x02, // Alternate COM config and disable COM left/right
0xD5, // Set display oscillator
0x80, // Default value
0xD9, // Set precharge period
0x22, // Default value
0xDB, // Set VCOMH deselected level
0x20, // Default value
0x8D, // Set charge pump
0x14, // VCC generated by internal DC/DC circuit
0xA4, // Resume to RAM content display
0xAF // Set display on
};
u8 buffer[] = {0x80, 0};
for (auto byte : InitSequence)
for (auto byte : initSequence)
{
buffer[1] = byte;
m_pI2CMaster->Write(m_nAddress, buffer, 2);
@@ -164,21 +168,21 @@ void CSSD1306::WriteFramebuffer() const
void CSSD1306::SetPixel(u8 nX, u8 nY)
{
// Ensure range is within 0-127 for x, 0-63 for y
nX &= 0x7f;
nY &= 0x3f;
nX &= 0x7F;
nY &= 0x3F;
// The framebuffer starts with the 0x40 byte so that we can write out the entire
// buffer to the I2C device in one shot, so offset by 1
m_Framebuffer[((nY & 0xf8) << 4) + nX + 1] |= 1 << (nY & 7);
m_Framebuffer[((nY & 0xF8) << 4) + nX + 1] |= 1 << (nY & 7);
}
void CSSD1306::ClearPixel(u8 nX, u8 nY)
{
// Ensure range is within 0-127 for x, 0-63 for y
nX &= 0x7f;
nY &= 0x3f;
nX &= 0x7F;
nY &= 0x3F;
m_Framebuffer[((nY & 0xf8) << 4) + nX + 1] &= ~(1 << (nY & 7));
m_Framebuffer[((nY & 0xF8) << 4) + nX + 1] &= ~(1 << (nY & 7));
}
void CSSD1306::DrawChar(char chChar, u8 nCursorX, u8 nCursorY, bool bInverted, bool bDoubleWidth)