mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-27 14:21:25 +01:00
Merge pull request #140 from DCC-EX/neil-i2c2
Add I2C support functions
This commit is contained in:
commit
5e5f994e48
@ -17,6 +17,8 @@
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <Wire.h>
|
||||
#include "I2CManager.h"
|
||||
|
||||
// If not already initialised, initialise I2C (wire).
|
||||
@ -48,10 +50,80 @@ void I2CManagerClass::forceClock(uint32_t speed) {
|
||||
|
||||
// Check if specified I2C address is responding.
|
||||
// Returns 0 if OK, or error code.
|
||||
uint8_t I2CManagerClass::exists(uint8_t address) {
|
||||
uint8_t I2CManagerClass::checkAddress(uint8_t address) {
|
||||
begin();
|
||||
Wire.beginTransmission(address);
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
|
||||
bool I2CManagerClass::exists(uint8_t address) {
|
||||
return checkAddress(address)==0;
|
||||
}
|
||||
|
||||
// Write a complete transmission to I2C using a supplied buffer of data
|
||||
uint8_t I2CManagerClass::write(uint8_t address, const uint8_t buffer[], uint8_t size) {
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(buffer, size);
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
|
||||
// Write a complete transmission to I2C using a supplied buffer of data in Flash
|
||||
uint8_t I2CManagerClass::write_P(uint8_t address, const uint8_t buffer[], uint8_t size) {
|
||||
uint8_t ramBuffer[size];
|
||||
memcpy_P(ramBuffer, buffer, size);
|
||||
return write(address, ramBuffer, size);
|
||||
}
|
||||
|
||||
|
||||
// Write a complete transmission to I2C using a list of data
|
||||
uint8_t I2CManagerClass::write(uint8_t address, int nBytes, ...) {
|
||||
uint8_t buffer[nBytes];
|
||||
va_list args;
|
||||
va_start(args, nBytes);
|
||||
for (uint8_t i=0; i<nBytes; i++)
|
||||
buffer[i] = va_arg(args, int);
|
||||
va_end(args);
|
||||
return write(address, buffer, nBytes);
|
||||
}
|
||||
|
||||
// Write a command and read response, returns number of bytes received.
|
||||
// Different modules use different ways of accessing registers:
|
||||
// PCF8574 I/O expander justs needs the address (no data);
|
||||
// PCA9685 needs a two byte command to select the register(s) to be read;
|
||||
// MCP23016 needs a one-byte command to select the register.
|
||||
// Some devices use 8-bit registers exclusively and some have 16-bit registers.
|
||||
// Therefore the following function is general purpose, to apply to any
|
||||
// type of I2C device.
|
||||
//
|
||||
uint8_t I2CManagerClass::read(uint8_t address, uint8_t readBuffer[], uint8_t readSize,
|
||||
uint8_t writeBuffer[], uint8_t writeSize) {
|
||||
if (writeSize > 0) {
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(writeBuffer, writeSize);
|
||||
Wire.endTransmission(false); // Don't free bus yet
|
||||
}
|
||||
Wire.requestFrom(address, readSize);
|
||||
uint8_t nBytes = 0;
|
||||
while (Wire.available() && nBytes < readSize)
|
||||
readBuffer[nBytes++] = Wire.read();
|
||||
return nBytes;
|
||||
}
|
||||
|
||||
// Overload of read() to allow command to be specified as a series of bytes.
|
||||
uint8_t I2CManagerClass::read(uint8_t address, uint8_t readBuffer[], uint8_t readSize,
|
||||
uint8_t writeSize, ...) {
|
||||
va_list args;
|
||||
// Copy the series of bytes into an array.
|
||||
va_start(args, writeSize);
|
||||
uint8_t writeBuffer[writeSize];
|
||||
for (uint8_t i=0; i<writeSize; i++)
|
||||
writeBuffer[i] = va_arg(args, int);
|
||||
va_end(args);
|
||||
return read(address, readBuffer, readSize, writeBuffer, writeSize);
|
||||
}
|
||||
|
||||
uint8_t I2CManagerClass::read(uint8_t address, uint8_t readBuffer[], uint8_t readSize) {
|
||||
return read(address, readBuffer, readSize, NULL, 0);
|
||||
}
|
||||
|
||||
I2CManagerClass I2CManager = I2CManagerClass();
|
21
I2CManager.h
21
I2CManager.h
@ -20,7 +20,7 @@
|
||||
#ifndef I2CManager_h
|
||||
#define I2CManager_h
|
||||
|
||||
#include <Wire.h>
|
||||
#include "FSH.h"
|
||||
|
||||
/*
|
||||
* Helper class to manage access to the I2C 'Wire' subsystem.
|
||||
@ -48,12 +48,27 @@ public:
|
||||
// Force clock speed
|
||||
void forceClock(uint32_t speed);
|
||||
// Check if specified I2C address is responding.
|
||||
uint8_t exists(uint8_t address);
|
||||
uint8_t checkAddress(uint8_t address);
|
||||
bool exists(uint8_t address);
|
||||
// Write a complete transmission to I2C from an array in RAM
|
||||
uint8_t write(uint8_t address, const uint8_t buffer[], uint8_t size);
|
||||
// Write a complete transmission to I2C from an array in Flash
|
||||
uint8_t write_P(uint8_t address, const uint8_t buffer[], uint8_t size);
|
||||
// Write a transmission to I2C from a list of bytes.
|
||||
uint8_t write(uint8_t address, int nBytes, ...);
|
||||
// Write a command from an array in RAM and read response
|
||||
uint8_t read(uint8_t address, uint8_t writeBuffer[], uint8_t writeSize,
|
||||
uint8_t readBuffer[], uint8_t readSize);
|
||||
// Write a command from an arbitrary list of bytes and read response
|
||||
uint8_t read(uint8_t address, uint8_t readBuffer[], uint8_t readSize,
|
||||
uint8_t writeSize, ...);
|
||||
// Write a null command and read the response.
|
||||
uint8_t read(uint8_t address, uint8_t readBuffer[], uint8_t readSize);
|
||||
|
||||
private:
|
||||
bool _beginCompleted = false;
|
||||
bool _clockSpeedFixed = false;
|
||||
uint32_t _clockSpeed = 1000000L; // 1MHz max on Arduino.
|
||||
uint32_t _clockSpeed = 400000L; // 400kHz max on Arduino.
|
||||
};
|
||||
|
||||
extern I2CManagerClass I2CManager;
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include "I2CManager.h"
|
||||
#include "SSD1306Ascii.h"
|
||||
#include "Wire.h"
|
||||
SSD1306AsciiWire LCDDriver;
|
||||
|
||||
// DEVICE SPECIFIC LCDDisplay Implementation for OLED
|
||||
@ -34,8 +33,7 @@ LCDDisplay::LCDDisplay() {
|
||||
I2CManager.begin();
|
||||
I2CManager.setClock(400000L); // Set max supported I2C speed
|
||||
for (byte address = 0x3c; address <= 0x3d; address++) {
|
||||
byte error = I2CManager.exists(address);
|
||||
if (!error) {
|
||||
if (I2CManager.exists(address)) {
|
||||
// Device found
|
||||
DIAG(F("OLED display found at 0x%x"), address);
|
||||
interfake(OLED_DRIVER, 0);
|
||||
@ -65,7 +63,7 @@ void LCDDisplay::interfake(int p1, int p2, int p3) {
|
||||
void LCDDisplay::clearNative() { LCDDriver.clear(); }
|
||||
|
||||
void LCDDisplay::setRowNative(byte row) {
|
||||
// Positions text write to start of row 1..n and clears previous text
|
||||
// Positions text write to start of row 1..n
|
||||
int y = row;
|
||||
LCDDriver.setCursor(0, y);
|
||||
}
|
||||
|
@ -15,32 +15,13 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
* along with CommandStation-EX. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "LiquidCrystal_I2C.h"
|
||||
#include "I2CManager.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define printIIC(args) Wire.write(args)
|
||||
inline size_t LiquidCrystal_I2C::write(uint8_t value) {
|
||||
send(value, Rs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
|
||||
#define printIIC(args) Wire.send(args)
|
||||
inline void LiquidCrystal_I2C::write(uint8_t value) { send(value, Rs); }
|
||||
|
||||
#endif
|
||||
#include "Wire.h"
|
||||
|
||||
// When the display powers up, it is configured as follows:
|
||||
//
|
||||
// 1. Display clear
|
||||
@ -72,7 +53,7 @@ void LiquidCrystal_I2C::init() { init_priv(); }
|
||||
|
||||
void LiquidCrystal_I2C::init_priv() {
|
||||
I2CManager.begin();
|
||||
I2CManager.setClock(100000L); // PCF8574 is limited to 100kHz.
|
||||
I2CManager.setClock(100000L); // PCF8574 is spec'd to 100kHz.
|
||||
|
||||
_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
|
||||
begin(_cols, _rows);
|
||||
@ -85,10 +66,9 @@ void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines) {
|
||||
_numlines = lines;
|
||||
(void)cols; // Suppress compiler warning.
|
||||
|
||||
// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
|
||||
// according to datasheet, we need at least 40ms after power rises above 2.7V
|
||||
// before sending commands. Arduino can turn on way befer 4.5V so we'll allow
|
||||
// 100 milliseconds after pulling both RS and R/W and backlight pin low
|
||||
// 100 milliseconds after pulling both RS and R/W and backlight pin low
|
||||
expanderWrite(
|
||||
_backlightval); // reset expander and turn backlight off (Bit 8 =1)
|
||||
delay(100);
|
||||
@ -167,23 +147,11 @@ void LiquidCrystal_I2C::backlight(void) {
|
||||
expanderWrite(0);
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::setBacklight(uint8_t new_val) {
|
||||
if (new_val) {
|
||||
backlight(); // turn backlight on
|
||||
} else {
|
||||
noBacklight(); // turn backlight off
|
||||
}
|
||||
}
|
||||
|
||||
void LiquidCrystal_I2C::printstr(const char c[]) {
|
||||
// This function is not identical to the function used for "real" I2C displays
|
||||
// it's here so the user sketch doesn't have to be changed
|
||||
print(c);
|
||||
}
|
||||
|
||||
/*********** mid level commands, for sending data/cmds */
|
||||
|
||||
inline void LiquidCrystal_I2C::command(uint8_t value) { send(value, 0); }
|
||||
inline void LiquidCrystal_I2C::command(uint8_t value) {
|
||||
send(value, 0);
|
||||
}
|
||||
|
||||
/************ low level data pushing commands **********/
|
||||
|
||||
@ -209,37 +177,37 @@ inline void LiquidCrystal_I2C::command(uint8_t value) { send(value, 0); }
|
||||
* transmission and start another one is a stop bit, a start bit, 8 address bits,
|
||||
* an ack, 8 data bits and another ack; this is at least 20 bits, i.e. >50us
|
||||
* at 400kHz and >200us at 100kHz. Therefore, we don't need additional delay.
|
||||
*
|
||||
* Similarly, the Enable must be set/reset for at least 450ns. This is
|
||||
* well within the I2C clock cycle time of 2.5us at 400kHz. Data is clocked in
|
||||
* to the HD44780 on the trailing edge of the Enable pin, so we set the Enable
|
||||
* as we present the data, then in the next byte we reset Enable without changing
|
||||
* the data.
|
||||
*/
|
||||
|
||||
// write either command or data (8 bits) to the HD44780 as
|
||||
// a single I2C transmission.
|
||||
// write either command or data (8 bits) to the HD44780 LCD controller as
|
||||
// a single I2C transmission.
|
||||
void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) {
|
||||
uint8_t highnib = value & 0xf0;
|
||||
uint8_t lownib = (value << 4) & 0xf0;
|
||||
mode |= _backlightval;
|
||||
uint8_t highnib = (value & 0xf0) | mode;
|
||||
uint8_t lownib = ((value << 4) & 0xf0) | mode;
|
||||
// Send both nibbles
|
||||
Wire.beginTransmission(_Addr);
|
||||
write4bits(highnib | mode, true);
|
||||
write4bits(lownib | mode, true);
|
||||
Wire.endTransmission();
|
||||
byte buffer[] = {(byte)(highnib|En), highnib, (byte)(lownib|En), lownib};
|
||||
I2CManager.write(_Addr, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
// write 4 bits to the HD44780 interface. If inTransmission is false
|
||||
// then the nibble will be sent in its own I2C transmission.
|
||||
void LiquidCrystal_I2C::write4bits(uint8_t value, bool inTransmission) {
|
||||
int _data = (int)value | _backlightval;
|
||||
if (!inTransmission) Wire.beginTransmission(_Addr);
|
||||
// write 4 bits to the HD44780 LCD controller.
|
||||
void LiquidCrystal_I2C::write4bits(uint8_t value) {
|
||||
uint8_t _data = value | _backlightval;
|
||||
// Enable must be set/reset for at least 450ns. This is well within the
|
||||
// I2C clock cycle time of 2.5us at 400kHz. Data is clocked in to the
|
||||
// HD44780 on the trailing edge of the Enable pin.
|
||||
printIIC(_data | En);
|
||||
printIIC(_data);
|
||||
if (!inTransmission) Wire.endTransmission();
|
||||
byte buffer[] = {(byte)(_data|En), _data};
|
||||
I2CManager.write(_Addr, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
// write a byte to the PCF8574 I2C interface
|
||||
// write a byte to the PCF8574 I2C interface. We don't need to set
|
||||
// the enable pin for this.
|
||||
void LiquidCrystal_I2C::expanderWrite(uint8_t value) {
|
||||
int _data = (int)value | _backlightval;
|
||||
Wire.beginTransmission(_Addr);
|
||||
printIIC(_data);
|
||||
Wire.endTransmission();
|
||||
I2CManager.write(_Addr, 1, value | _backlightval);
|
||||
}
|
@ -21,9 +21,7 @@
|
||||
#ifndef LiquidCrystal_I2C_h
|
||||
#define LiquidCrystal_I2C_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Print.h"
|
||||
#include <Wire.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
// commands
|
||||
#define LCD_CLEARDISPLAY 0x01
|
||||
@ -82,25 +80,15 @@ public:
|
||||
void backlight();
|
||||
|
||||
void setCursor(uint8_t, uint8_t);
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
virtual size_t write(uint8_t);
|
||||
#else
|
||||
virtual void write(uint8_t);
|
||||
#endif
|
||||
void command(uint8_t);
|
||||
void init();
|
||||
void oled_init();
|
||||
|
||||
////compatibility API function aliases
|
||||
void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight()
|
||||
void printstr(const char[]);
|
||||
|
||||
private:
|
||||
void init_priv();
|
||||
void send(uint8_t, uint8_t);
|
||||
void write4bits(uint8_t, bool inTransmission=false);
|
||||
void write4bits(uint8_t);
|
||||
void expanderWrite(uint8_t);
|
||||
void pulseEnable(uint8_t);
|
||||
uint8_t _Addr;
|
||||
uint8_t _displayfunction;
|
||||
uint8_t _displaycontrol;
|
||||
|
@ -23,7 +23,6 @@
|
||||
* BSD license, all text above must be included in any redistribution
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include "PWMServoDriver.h"
|
||||
#include "DIAG.h"
|
||||
#include "I2CManager.h"
|
||||
@ -60,7 +59,7 @@ bool PWMServoDriver::setup(int board) {
|
||||
uint8_t i2caddr=PCA9685_I2C_ADDRESS + board;
|
||||
|
||||
// Test if device is available
|
||||
byte error = I2CManager.exists(i2caddr);
|
||||
byte error = I2CManager.checkAddress(i2caddr);
|
||||
if (error) {
|
||||
DIAG(F("I2C Servo device 0x%x Not Found %d"),i2caddr, error);
|
||||
failFlags|=1<<board;
|
||||
@ -85,20 +84,14 @@ void PWMServoDriver::setServo(byte servoNum, uint16_t value) {
|
||||
|
||||
if (setup(board)) {
|
||||
DIAG(F("SetServo %d %d"),servoNum,value);
|
||||
Wire.beginTransmission(PCA9685_I2C_ADDRESS + board);
|
||||
Wire.write(PCA9685_FIRST_SERVO + 4 * pin); // 4 registers per pin
|
||||
Wire.write(0);
|
||||
Wire.write(0);
|
||||
Wire.write(value);
|
||||
Wire.write(value >> 8);
|
||||
byte error=Wire.endTransmission();
|
||||
uint8_t buffer[] = {(uint8_t)(PCA9685_FIRST_SERVO + 4 * pin), // 4 registers per pin
|
||||
0, 0, (uint8_t)(value & 0xff), (uint8_t)(value >> 8)};
|
||||
if (value == 4095) buffer[2] = 0x10; // Full on
|
||||
byte error=I2CManager.write(PCA9685_I2C_ADDRESS + board, buffer, sizeof(buffer));
|
||||
if (error!=0) DIAG(F("SetServo error %d"),error);
|
||||
}
|
||||
}
|
||||
|
||||
void PWMServoDriver::writeRegister(uint8_t i2caddr,uint8_t hardwareRegister, uint8_t d) {
|
||||
Wire.beginTransmission(i2caddr);
|
||||
Wire.write(hardwareRegister);
|
||||
Wire.write(d);
|
||||
Wire.endTransmission();
|
||||
I2CManager.write(i2caddr, 2, hardwareRegister, d);
|
||||
}
|
||||
|
166
SSD1306Ascii.cpp
166
SSD1306Ascii.cpp
@ -16,27 +16,38 @@
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "SSD1306Ascii.h"
|
||||
#include "I2CManager.h"
|
||||
#include "FSH.h"
|
||||
|
||||
|
||||
// Maximum number of bytes we can send per transmission is 32.
|
||||
const uint8_t FLASH SSD1306AsciiWire::blankPixels[32] =
|
||||
{0x40, // First byte specifies data mode
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
|
||||
//==============================================================================
|
||||
// SSD1306Ascii Method Definitions
|
||||
|
||||
// SSD1306AsciiWire Method Definitions
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::clear() {
|
||||
void SSD1306AsciiWire::clear() {
|
||||
clear(0, displayWidth() - 1, 0, displayRows() - 1);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::clear(uint8_t c0, uint8_t c1, uint8_t r0, uint8_t r1) {
|
||||
void SSD1306AsciiWire::clear(uint8_t columnStart, uint8_t columnEnd,
|
||||
uint8_t rowStart, uint8_t rowEnd) {
|
||||
const int maxBytes = sizeof(blankPixels); // max number of bytes sendable over Wire
|
||||
// Ensure only rows on display will be cleared.
|
||||
if (r1 >= displayRows()) r1 = displayRows() - 1;
|
||||
|
||||
for (uint8_t r = r0; r <= r1; r++) {
|
||||
setCursor(c0, r);
|
||||
for (uint8_t c = c0; c <= c1; c++) ssd1306WriteRamBuf(0);
|
||||
if (rowEnd >= displayRows()) rowEnd = displayRows() - 1;
|
||||
for (uint8_t r = rowStart; r <= rowEnd; r++) {
|
||||
setCursor(columnStart, r); // Position at start of row to be erased
|
||||
for (uint8_t c = columnStart; c <= columnEnd; c += maxBytes-1) {
|
||||
uint8_t len = min((uint8_t)(columnEnd-c+1), maxBytes-1) + 1;
|
||||
I2CManager.write_P(m_i2cAddr, blankPixels, len); // Write up to 31 blank columns
|
||||
}
|
||||
}
|
||||
setCursor(c0, r0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::init(const DevType* dev) {
|
||||
void SSD1306AsciiWire::begin(const DevType* dev, uint8_t i2cAddr) {
|
||||
m_i2cAddr = i2cAddr;
|
||||
m_col = 0;
|
||||
m_row = 0;
|
||||
#ifdef __AVR__
|
||||
@ -48,125 +59,50 @@ void SSD1306Ascii::init(const DevType* dev) {
|
||||
m_displayWidth = readFontByte(&dev->lcdWidth);
|
||||
m_displayHeight = readFontByte(&dev->lcdHeight);
|
||||
m_colOffset = readFontByte(&dev->colOffset);
|
||||
for (uint8_t i = 0; i < size; i++) {
|
||||
ssd1306WriteCmd(readFontByte(table + i));
|
||||
}
|
||||
clear();
|
||||
I2CManager.write_P(m_i2cAddr, table, size);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::setCol(uint8_t col) {
|
||||
if (col < m_displayWidth) {
|
||||
m_col = col;
|
||||
col += m_colOffset;
|
||||
ssd1306WriteCmd(SSD1306_SETLOWCOLUMN | (col & 0XF));
|
||||
ssd1306WriteCmd(SSD1306_SETHIGHCOLUMN | (col >> 4));
|
||||
void SSD1306AsciiWire::setContrast(uint8_t value) {
|
||||
I2CManager.write(m_i2cAddr, 2,
|
||||
0x00, // Set to command mode
|
||||
SSD1306_SETCONTRAST, value);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306AsciiWire::setCursor(uint8_t col, uint8_t row) {
|
||||
if (row < displayRows() && col < m_displayWidth) {
|
||||
m_row = row;
|
||||
m_col = col + m_colOffset;
|
||||
I2CManager.write(m_i2cAddr, 4,
|
||||
0x00, // Set to command mode
|
||||
SSD1306_SETLOWCOLUMN | (col & 0XF),
|
||||
SSD1306_SETHIGHCOLUMN | (col >> 4),
|
||||
SSD1306_SETSTARTPAGE | m_row);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::setContrast(uint8_t value) {
|
||||
ssd1306WriteCmd(SSD1306_SETCONTRAST);
|
||||
ssd1306WriteCmd(value);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::setCursor(uint8_t col, uint8_t row) {
|
||||
setCol(col);
|
||||
setRow(row);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::setFont(const uint8_t* font) {
|
||||
void SSD1306AsciiWire::setFont(const uint8_t* font) {
|
||||
m_font = font;
|
||||
m_fontFirstChar = readFontByte(m_font + FONT_FIRST_CHAR);
|
||||
m_fontCharCount = readFontByte(m_font + FONT_CHAR_COUNT);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::setRow(uint8_t row) {
|
||||
if (row < displayRows()) {
|
||||
m_row = row;
|
||||
ssd1306WriteCmd(SSD1306_SETSTARTPAGE | m_row);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::ssd1306WriteRam(uint8_t c) {
|
||||
if (m_col < m_displayWidth) {
|
||||
writeDisplay(c, SSD1306_MODE_RAM);
|
||||
m_col++;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306Ascii::ssd1306WriteRamBuf(uint8_t c) {
|
||||
if (m_col < m_displayWidth) {
|
||||
writeDisplay(c, SSD1306_MODE_RAM_BUF);
|
||||
m_col++;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
size_t SSD1306Ascii::write(uint8_t ch) {
|
||||
if (!m_font) {
|
||||
return 0;
|
||||
}
|
||||
size_t SSD1306AsciiWire::write(uint8_t ch) {
|
||||
const uint8_t* base = m_font + FONT_WIDTH_TABLE;
|
||||
|
||||
if (ch < m_fontFirstChar || ch >= (m_fontFirstChar + m_fontCharCount))
|
||||
return 0;
|
||||
ch -= m_fontFirstChar;
|
||||
base += fontWidth * ch;
|
||||
for (uint8_t c = 0; c < fontWidth; c++) {
|
||||
uint8_t b = readFontByte(base + c);
|
||||
ssd1306WriteRamBuf(b);
|
||||
}
|
||||
for (uint8_t i = 0; i < letterSpacing; i++) {
|
||||
ssd1306WriteRamBuf(0);
|
||||
}
|
||||
flushDisplay();
|
||||
uint8_t buffer[1+fontWidth+letterSpacing];
|
||||
buffer[0] = 0x40; // set SSD1306 controller to data mode
|
||||
uint8_t bufferPos = 1;
|
||||
// Copy character pixel columns
|
||||
for (uint8_t i = 0; i < fontWidth; i++)
|
||||
buffer[bufferPos++] = readFontByte(base++);
|
||||
// Add blank pixels between letters
|
||||
for (uint8_t i = 0; i < letterSpacing; i++)
|
||||
buffer[bufferPos++] = 0;
|
||||
// Write the data to I2C display
|
||||
I2CManager.write(m_i2cAddr, buffer, bufferPos);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// SSD1306AsciiWire method definitions
|
||||
|
||||
#define m_oledWire Wire
|
||||
|
||||
void SSD1306AsciiWire::begin(const DevType* dev, uint8_t i2cAddr) {
|
||||
#if OPTIMIZE_I2C
|
||||
m_nData = 0;
|
||||
#endif // OPTIMIZE_I2C
|
||||
m_i2cAddr = i2cAddr;
|
||||
init(dev);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306AsciiWire::writeDisplay(uint8_t b, uint8_t mode) {
|
||||
#if OPTIMIZE_I2C
|
||||
if (m_nData > 16 || (m_nData && mode == SSD1306_MODE_CMD)) {
|
||||
m_oledWire.endTransmission();
|
||||
m_nData = 0;
|
||||
}
|
||||
if (m_nData == 0) {
|
||||
m_oledWire.beginTransmission(m_i2cAddr);
|
||||
m_oledWire.write(mode == SSD1306_MODE_CMD ? 0X00 : 0X40);
|
||||
}
|
||||
m_oledWire.write(b);
|
||||
if (mode == SSD1306_MODE_RAM_BUF) {
|
||||
m_nData++;
|
||||
} else {
|
||||
m_oledWire.endTransmission();
|
||||
m_nData = 0;
|
||||
}
|
||||
#else // OPTIMIZE_I2C
|
||||
m_oledWire.beginTransmission(m_i2cAddr);
|
||||
m_oledWire.write(mode == SSD1306_MODE_CMD ? 0X00 : 0X40);
|
||||
m_oledWire.write(b);
|
||||
m_oledWire.endTransmission();
|
||||
#endif // OPTIMIZE_I2C
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SSD1306AsciiWire::flushDisplay() {
|
||||
#if OPTIMIZE_I2C
|
||||
if (m_nData) {
|
||||
m_oledWire.endTransmission();
|
||||
m_nData = 0;
|
||||
}
|
||||
#endif // OPTIMIZE_I2C
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
183
SSD1306Ascii.h
183
SSD1306Ascii.h
@ -12,127 +12,41 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the Arduino SSD1306Ascii Library. If not, see
|
||||
* along with this software. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @file SSD1306AsciiWire.h
|
||||
* @brief Class for I2C displays using Wire.
|
||||
*/
|
||||
#ifndef SSD1306AsciiWire_h
|
||||
#define SSD1306AsciiWire_h
|
||||
|
||||
#include <Wire.h>
|
||||
#ifndef SSD1306Ascii_h
|
||||
#define SSD1306Ascii_h
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "LCDDisplay.h"
|
||||
#include "SSD1306font.h"
|
||||
#include "SSD1306init.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/** SSD1306Ascii version basis */
|
||||
#define SDD1306_ASCII_VERSION 1.3.0
|
||||
//------------------------------------------------------------------------------
|
||||
// Configuration options.
|
||||
|
||||
/** Use larger faster I2C code. */
|
||||
#define OPTIMIZE_I2C 1
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Values for writeDisplay() mode parameter.
|
||||
/** Write to Command register. */
|
||||
#define SSD1306_MODE_CMD 0
|
||||
/** Write one byte to display RAM. */
|
||||
#define SSD1306_MODE_RAM 1
|
||||
/** Write to display RAM with possible buffering. */
|
||||
#define SSD1306_MODE_RAM_BUF 2
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @class SSD1306Ascii
|
||||
* @brief SSD1306 base class
|
||||
*/
|
||||
class SSD1306Ascii : public Print {
|
||||
class SSD1306AsciiWire : public Print {
|
||||
public:
|
||||
using Print::write;
|
||||
SSD1306Ascii() {}
|
||||
/**
|
||||
* @brief Clear the display and set the cursor to (0, 0).
|
||||
*/
|
||||
SSD1306AsciiWire() {}
|
||||
// Initialize the display controller.
|
||||
void begin(const DevType* dev, uint8_t i2cAddr);
|
||||
// Clear the display and set the cursor to (0, 0).
|
||||
void clear();
|
||||
/**
|
||||
* @brief Clear a region of the display.
|
||||
*
|
||||
* @param[in] c0 Starting column.
|
||||
* @param[in] c1 Ending column.
|
||||
* @param[in] r0 Starting row;
|
||||
* @param[in] r1 Ending row;
|
||||
* @note The final cursor position will be (c0, r0).
|
||||
*/
|
||||
// Clear a region of the display.
|
||||
void clear(uint8_t c0, uint8_t c1, uint8_t r0, uint8_t r1);
|
||||
/**
|
||||
* @brief Clear a field of n fieldWidth() characters.
|
||||
*
|
||||
* @param[in] col Field start column.
|
||||
*
|
||||
* @param[in] row Field start row.
|
||||
*
|
||||
* @param[in] n Number of characters in the field.
|
||||
*
|
||||
*/
|
||||
void clearField(uint8_t col, uint8_t row, uint8_t n);
|
||||
/**
|
||||
* @brief Clear the display to the end of the current line.
|
||||
* @note The number of rows cleared will be determined by the height
|
||||
* of the current font.
|
||||
* @note The cursor will be returned to the original position.
|
||||
*/
|
||||
void clearToEOL();
|
||||
/**
|
||||
* @return The current column in pixels.
|
||||
*/
|
||||
// The current column in pixels.
|
||||
inline uint8_t col() const { return m_col; }
|
||||
/**
|
||||
* @return The display hight in pixels.
|
||||
*/
|
||||
// The display hight in pixels.
|
||||
inline uint8_t displayHeight() const { return m_displayHeight; }
|
||||
/**
|
||||
* @brief Set display to normal or 180 degree remap mode.
|
||||
*
|
||||
* @param[in] mode true for normal mode, false for remap mode.
|
||||
*
|
||||
* @note Adafruit and many ebay displays use remap mode.
|
||||
* Use normal mode to rotate these displays 180 degrees.
|
||||
*/
|
||||
void displayRemap(bool mode);
|
||||
/**
|
||||
* @return The display height in rows with eight pixels to a row.
|
||||
*/
|
||||
// The display height in rows with eight pixels to a row.
|
||||
inline uint8_t displayRows() const { return m_displayHeight / 8; }
|
||||
/**
|
||||
* @return The display width in pixels.
|
||||
*/
|
||||
// The display width in pixels.
|
||||
inline uint8_t displayWidth() const { return m_displayWidth; }
|
||||
/**
|
||||
* @brief Set the cursor position to (0, 0).
|
||||
*/
|
||||
// Set the cursor position to (0, 0).
|
||||
inline void home() { setCursor(0, 0); }
|
||||
/**
|
||||
* @brief Initialize the display controller.
|
||||
*
|
||||
* @param[in] dev A display initialization structure.
|
||||
*/
|
||||
// Initialize the display controller.
|
||||
void init(const DevType* dev);
|
||||
/**
|
||||
* @return the current row number with eight pixels to a row.
|
||||
*/
|
||||
// the current row number with eight pixels to a row.
|
||||
inline uint8_t row() const { return m_row; }
|
||||
/**
|
||||
* @brief Set the current column number.
|
||||
*
|
||||
* @param[in] col The desired column number in pixels.
|
||||
*/
|
||||
void setCol(uint8_t col);
|
||||
/**
|
||||
* @brief Set the display contrast.
|
||||
*
|
||||
@ -152,34 +66,6 @@ class SSD1306Ascii : public Print {
|
||||
* @param[in] font Pointer to a font table.
|
||||
*/
|
||||
void setFont(const uint8_t* font);
|
||||
/**
|
||||
* @brief Set the current row number.
|
||||
*
|
||||
* @param[in] row the row number in eight pixel rows.
|
||||
*/
|
||||
void setRow(uint8_t row);
|
||||
/**
|
||||
* @brief Write a command byte to the display controller.
|
||||
*
|
||||
* @param[in] c The command byte.
|
||||
* @note The byte will immediately be sent to the controller.
|
||||
*/
|
||||
inline void ssd1306WriteCmd(uint8_t c) { writeDisplay(c, SSD1306_MODE_CMD); }
|
||||
/**
|
||||
* @brief Write a byte to RAM in the display controller.
|
||||
*
|
||||
* @param[in] c The data byte.
|
||||
* @note The byte will immediately be sent to the controller.
|
||||
*/
|
||||
void ssd1306WriteRam(uint8_t c);
|
||||
/**
|
||||
* @brief Write a byte to RAM in the display controller.
|
||||
*
|
||||
* @param[in] c The data byte.
|
||||
* @note The byte may be buffered until a call to ssd1306WriteCmd
|
||||
* or ssd1306WriteRam or flushDisplay.
|
||||
*/
|
||||
void ssd1306WriteRamBuf(uint8_t c);
|
||||
/**
|
||||
* @brief Display a character.
|
||||
*
|
||||
@ -188,47 +74,24 @@ class SSD1306Ascii : public Print {
|
||||
*/
|
||||
size_t write(uint8_t c);
|
||||
|
||||
protected:
|
||||
virtual void writeDisplay(uint8_t b, uint8_t mode) = 0;
|
||||
virtual void flushDisplay() = 0;
|
||||
private:
|
||||
uint8_t m_col; // Cursor column.
|
||||
uint8_t m_row; // Cursor RAM row.
|
||||
uint8_t m_displayWidth; // Display width.
|
||||
uint8_t m_displayHeight; // Display height.
|
||||
uint8_t m_colOffset; // Column offset RAM to SEG.
|
||||
const uint8_t* m_font = nullptr; // Current font.
|
||||
const uint8_t* m_font = NULL; // Current font.
|
||||
|
||||
// Only fixed size 5x7 fonts in a 6x8 cell are supported.
|
||||
const int fontWidth = 5;
|
||||
const int fontHeight = 7;
|
||||
const uint8_t fontWidth = 5;
|
||||
const uint8_t fontHeight = 7;
|
||||
const uint8_t letterSpacing = 1;
|
||||
uint8_t m_fontFirstChar;
|
||||
uint8_t m_fontCharCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class SSD1306AsciiWire
|
||||
* @brief Class for I2C displays using Wire.
|
||||
*/
|
||||
class SSD1306AsciiWire : public SSD1306Ascii {
|
||||
public:
|
||||
/**
|
||||
* @brief Initialize the display controller.
|
||||
*
|
||||
* @param[in] dev A device initialization structure.
|
||||
* @param[in] i2cAddr The I2C address of the display controller.
|
||||
*/
|
||||
void begin(const DevType* dev, uint8_t i2cAddr);
|
||||
|
||||
protected:
|
||||
void writeDisplay(uint8_t b, uint8_t mode);
|
||||
void flushDisplay();
|
||||
|
||||
protected:
|
||||
uint8_t m_i2cAddr;
|
||||
#if OPTIMIZE_I2C
|
||||
uint8_t m_nData;
|
||||
#endif // OPTIMIZE_I2C
|
||||
|
||||
static const uint8_t blankPixels[];
|
||||
};
|
||||
|
||||
#endif // SSD1306AsciiWire_h
|
||||
#endif // SSD1306Ascii_h
|
||||
|
@ -118,6 +118,7 @@ struct DevType {
|
||||
/** Initialization commands for a 128x32 SSD1306 oled display. */
|
||||
static const uint8_t MEM_TYPE Adafruit128x32init[] = {
|
||||
// Init sequence for Adafruit 128x32 OLED module
|
||||
0x00, // Set to command mode
|
||||
SSD1306_DISPLAYOFF,
|
||||
SSD1306_SETDISPLAYCLOCKDIV, 0x80, // the suggested ratio 0x80
|
||||
SSD1306_SETMULTIPLEX, 0x1F, // ratio 32
|
||||
@ -148,6 +149,7 @@ static const DevType MEM_TYPE Adafruit128x32 = {
|
||||
/** Initialization commands for a 128x64 SSD1306 oled display. */
|
||||
static const uint8_t MEM_TYPE Adafruit128x64init[] = {
|
||||
// Init sequence for Adafruit 128x64 OLED module
|
||||
0x00, // Set to command mode
|
||||
SSD1306_DISPLAYOFF,
|
||||
SSD1306_SETDISPLAYCLOCKDIV, 0x80, // the suggested ratio 0x80
|
||||
SSD1306_SETMULTIPLEX, 0x3F, // ratio 64
|
||||
@ -177,6 +179,7 @@ static const DevType MEM_TYPE Adafruit128x64 = {
|
||||
// This section is based on https://github.com/stanleyhuangyc/MultiLCD
|
||||
/** Initialization commands for a 128x64 SH1106 oled display. */
|
||||
static const uint8_t MEM_TYPE SH1106_128x64init[] = {
|
||||
0x00, // Set to command mode
|
||||
SSD1306_DISPLAYOFF,
|
||||
SSD1306_SETSTARTPAGE | 0X0, // set page address
|
||||
SSD1306_SETCONTRAST, 0x80, // 128
|
||||
|
Loading…
Reference in New Issue
Block a user