Skip to content

Commit

Permalink
Fixed a bug in SSD1351
Browse files Browse the repository at this point in the history
  • Loading branch information
mattjlewis committed Mar 26, 2024
1 parent 8b23a2a commit 630ba5d
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ public abstract class ColourSsdOled extends SsdOled {
private final byte[] buffer;

public ColourSsdOled(int controller, int chipSelect, DigitalOutputDevice dcPin, DigitalOutputDevice resetPin,
int width, int height, int imageType) {
this(new SpiCommunicationChannel(controller, chipSelect, SPI_FREQUENCY, dcPin, resetPin), width, height, imageType);
int width, int height, int imageType) {
this(new SpiCommunicationChannel(controller, chipSelect, SPI_FREQUENCY, dcPin, resetPin), width, height,
imageType);
}

public ColourSsdOled(SsdOledCommunicationChannel commChannel, int width, int height, int imageType) {
Expand Down Expand Up @@ -83,7 +84,7 @@ public void display(BufferedImage image) {
Logger.warn("Source image type ({}) doesn't match native image type ({}); converting",
Integer.valueOf(image.getType()), Integer.valueOf(imageType));
image_to_display = new BufferedImage(width, height, imageType);
Graphics2D g2d = image_to_display.createGraphics();
final Graphics2D g2d = image_to_display.createGraphics();

g2d.drawImage(image, 0, 0, null);
g2d.dispose();
Expand Down
167 changes: 87 additions & 80 deletions diozero-core/src/main/java/com/diozero/devices/oled/SH1106.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,100 +34,107 @@
import java.util.List;

/**
* OLED that typically has an internal ram of 132 bytes by 8 rows (1056 bytes). Each row is "paged" to the display,
* so each image written is several updates. When used in 128-column mode, an automatic offset of {@code 2} is
* applied, which typically will center the image in the display.
* OLED that typically has an internal ram of 132 bytes by 8 rows (1056 bytes). Each row is "paged" to the display, so
* each image written is several updates. When used in 128-column mode, an automatic offset of {@code 2} is applied,
* which typically will center the image in the display.
*
* <p>
* Links
* </p>
* <ul>
* <li><a href="https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SH1106">Adafruit Circuit Python SH1106</a></li>
* <li><a href="https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SH1106">Adafruit Circuit Python
* SH1106</a></li>
* </ul>
*/
public class SH1106 extends MonochromeSsdOled {
static final List<byte[]> _INIT_SEQUENCE = List.of(
new byte[] { DISPLAY_CLOCK_DIV_OSC_FREQ, (byte)0x80 }, // divide ratio/oscillator: divide by 2, fOsc (POR)}
new byte[] { SET_MULTIPLEX_RATIO, (byte)0x3f }, // multiplex ratio = 64 (POR)
new byte[] { SET_DISPLAY_OFFSET, (byte)0x00 }, // set display offset mode = 0x0
new byte[] { SET_DISPLAY_START_LINE_0 }, // set start line
new byte[] { SET_IREF_INTERNAL, (byte)0x8b }, // turn on DC/DC
new byte[] { SET_SEGMENT_REMAP_ON }, // segment remap = 1 (POR=0, down rotation)
new byte[] { COM_OUTPUT_SCAN_DIR_REMAPPED }, // scan decrement
new byte[] { SET_COM_PINS_HW_CONFIG, (byte)0x12 }, // set com pins
new byte[] { SET_CONTRAST, (byte)0xff }, // contrast setting = 0xff
new byte[] { SET_PRECHARGE_PERIOD, (byte)0x1f }, // pre-charge/dis-charge period mode: 2 DCLKs/2 DCLKs (POR)
new byte[] { SET_VCOMH_DESELECT_LEVEL, (byte)0x40 }, // VCOM deselect level = 0.770 (POR)
new byte[] { SET_MEMORY_ADDR_MODE, (byte)0x20 }, //
new byte[] { (byte)0x33 }, // turn on VPP to 9V
new byte[] { NORMAL_DISPLAY }, // normal (not reversed) display
new byte[] { RESUME_TO_RAM_CONTENT_DISPLAY } // entire display off, retain RAM, normal status (POR)
);
private final byte rowOffset;
static final List<byte[]> _INIT_SEQUENCE = List.of( //
// divide ratio/oscillator: divide by 2, fOsc (POR)}
new byte[] { DISPLAY_CLOCK_DIV_OSC_FREQ, (byte) 0x80 },
new byte[] { SET_MULTIPLEX_RATIO, (byte) 0x3f }, // multiplex ratio = 64 (POR)
new byte[] { SET_DISPLAY_OFFSET, (byte) 0x00 }, // set display offset mode = 0x0
new byte[] { SET_DISPLAY_START_LINE_0 }, // set start line
new byte[] { SET_IREF_INTERNAL, (byte) 0x8b }, // turn on DC/DC
new byte[] { SET_SEGMENT_REMAP_ON }, // segment remap = 1 (POR=0, down rotation)
new byte[] { COM_OUTPUT_SCAN_DIR_REMAPPED }, // scan decrement
new byte[] { SET_COM_PINS_HW_CONFIG, (byte) 0x12 }, // set com pins
new byte[] { SET_CONTRAST, (byte) 0xff }, // contrast setting = 0xff
// pre-charge/dis-charge period mode: 2 DCLKs/2 DCLKs (POR)
new byte[] { SET_PRECHARGE_PERIOD, (byte) 0x1f },
new byte[] { SET_VCOMH_DESELECT_LEVEL, (byte) 0x40 }, // VCOM deselect level = 0.770 (POR)
new byte[] { SET_MEMORY_ADDR_MODE, (byte) 0x20 }, //
new byte[] { (byte) 0x33 }, // turn on VPP to 9V
new byte[] { NORMAL_DISPLAY }, // normal (not reversed) display
new byte[] { RESUME_TO_RAM_CONTENT_DISPLAY } // entire display off, retain RAM, normal status (POR)
);
private final byte rowOffset;

/**
* Most common constructor.
*
* @param device the connection
*/
public SH1106(SsdOledCommunicationChannel device) {
this(device,DEFAULT_WIDTH, Height.TALL);
}
/**
* Most common constructor.
*
* @param device the connection
*/
public SH1106(SsdOledCommunicationChannel device) {
this(device, DEFAULT_WIDTH, Height.TALL);
}

/**
* Constructor for variable sizes.
*
* @param device the connection
* @param width the width of the display
* @param heightType the height of the display
*/
public SH1106(SsdOledCommunicationChannel device, int width, Height heightType) {
super(device, width, heightType.lines);
// because this driver actually has 132 ram bytes, offset by 2 to get the display "centered"
if (width == DEFAULT_WIDTH) rowOffset = 2;
else rowOffset = 0;
}
/**
* Constructor for variable sizes.
*
* @param device the connection
* @param width the width of the display
* @param heightType the height of the display
*/
public SH1106(SsdOledCommunicationChannel device, int width, Height heightType) {
super(device, width, heightType.lines);
// because this driver actually has 132 ram bytes, offset by 2 to get the display "centered"
if (width == DEFAULT_WIDTH) {
rowOffset = 2;
} else {
rowOffset = 0;
}
}

/**
* Constructor for variable sizes and "centering" of the display (e.g. 132 bytes RAM, but 128 display)
*
* @param device the connection
* @param width the width of the display
* @param height the height of the display
*/
public SH1106(SsdOledCommunicationChannel device, int width, int height, byte rowOffset) {
super(device, width, height);
this.rowOffset = rowOffset;
}
/**
* Constructor for variable sizes and "centering" of the display (e.g. 132 bytes RAM, but 128 display)
*
* @param device the connection
* @param width the width of the display
* @param height the height of the display
*/
public SH1106(SsdOledCommunicationChannel device, int width, int height, byte rowOffset) {
super(device, width, height);
this.rowOffset = rowOffset;
}

@Override
protected void init() {
setDisplayOn(false);
_INIT_SEQUENCE.forEach((this::command));
setDisplayOn(true);
}
@Override
protected void init() {
setDisplayOn(false);
_INIT_SEQUENCE.forEach((this::command));
setDisplayOn(true);
}

@Override
protected void goTo(int x, int y) {
throw new UnsupportedOperationException("Not currently supported in this class of display.");
}
@Override
protected void goTo(int x, int y) {
throw new UnsupportedOperationException("Not currently supported in this class of display.");
}

/**
* {@inheritDoc}
* <p>>
* Over-ridden for page mode. This <i>may</i> be more generic and thus can be moved to the super class.
* </p>
*/
public void show() {
byte columnStart = (byte)(SET_LOWER_COLUMN_START_ADDR | rowOffset);
/**
* {@inheritDoc}
* <p>
* > Overridden for page mode. This <em>may</em> be more generic and thus can be moved to the super class.
* </p>
*/
@Override
public void show() {
byte columnStart = (byte) (SET_LOWER_COLUMN_START_ADDR | rowOffset);

for (int p = 0; p < pages; p++) {
byte selectRow = (byte)(SET_PAGE_START_ADDR | p);
for (int p = 0; p < pages; p++) {
byte selectRow = (byte) (SET_PAGE_START_ADDR | p);

device.sendCommand(SET_HIGHER_COLUMN_START_ADDR);
device.sendCommand(columnStart);
device.sendCommand(selectRow);
device.sendData(getBuffer(), width * p, width);
}
}
channel.sendCommand(SET_HIGHER_COLUMN_START_ADDR);
channel.sendCommand(columnStart);
channel.sendCommand(selectRow);
channel.sendData(getBuffer(), width * p, width);
}
}
}
87 changes: 45 additions & 42 deletions diozero-core/src/main/java/com/diozero/devices/oled/SSD1351.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,19 @@

import java.awt.image.BufferedImage;


import com.diozero.api.DigitalOutputDevice;

/**
* <p>Encapsulates the serial interface to the 16-bit (5-6-5 RGB) and 18-bit
* (6-6-6 RGB) colour SSD1351 128x128 OLED display hardware. On creation, an
* initialisation sequence is pumped to the display to properly configure it.
* Further control commands can then be called to affect the brightness and
* other settings.</p>
* <p>
* Encapsulates the serial interface to the 16-bit (5-6-5 RGB) and 18-bit (6-6-6 RGB) colour SSD1351 128x128 OLED
* display hardware. On creation, an initialisation sequence is pumped to the display to properly configure it. Further
* control commands can then be called to affect the brightness and other settings.
* </p>
*
* <p>Wiring:</p>
* <p>
* Wiring:
* </p>
*
* <pre>
* GND .... Ground
* Vcc .... 3v3
Expand All @@ -54,12 +56,14 @@
* CS .... Chip Select (SPI)
* </pre>
*
* <p>Links:</p>
* <p>
* Links:
* </p>
* <ul>
* <li><a href="https://www.newhavendisplay.com/app_notes/SSD1351.pdf">Datasheet</a></li>
* <li><a href="https://github.com/freetronics/FTOLED/">FTOLED</a></li>
* <li><a href="https://github.com/kirberich/teensy_ssd1351">Teensy</a></li>
* <li><a href="https://github.com/adafruit/Adafruit-SSD1351-library">Adafruit</a></li>
* <li><a href= "https://www.newhavendisplay.com/app_notes/SSD1351.pdf">Datasheet</a></li>
* <li><a href="https://github.com/freetronics/FTOLED/">FTOLED</a></li>
* <li><a href="https://github.com/kirberich/teensy_ssd1351">Teensy</a></li>
* <li><a href= "https://github.com/adafruit/Adafruit-SSD1351-library">Adafruit</a></li>
* </ul>
*/
public class SSD1351 extends ColourSsdOled {
Expand Down Expand Up @@ -104,20 +108,20 @@ public SSD1351(int controller, int chipSelect, DigitalOutputDevice dcPin, Digita
private void commandAndData(byte command, byte... data) {
// Single byte command (D/C# = 0)
// Multiple byte command (D/C# = 0 for first byte, D/C# = 1 for other bytes)
device.write(command);
device.write(data);
channel.sendCommand(command);
channel.sendData(data);
}

@Override
protected void data() {
super.data();
command(WRITE_RAM_COMMAND);
channel.sendData(getBuffer());
channel.sendCommand(WRITE_RAM_COMMAND);
}

@Override
protected void data(int offset, int length) {
super.data(offset, length);
command(WRITE_RAM_COMMAND);
channel.sendData(getBuffer(), offset, length);
channel.sendCommand(WRITE_RAM_COMMAND);
}

@Override
Expand Down Expand Up @@ -151,27 +155,27 @@ protected void init() {
// A[7:6]=00/01, 65k Colour depth
// A[7:6]=10, 262k Colour depth
// A[7:6]=11, 262k Colour depth (16-bit format 2)
commandAndData(REMAP_AND_COLOUR_DEPTH, (byte) 0b00110100); // 0b00110000
//commandAndData(REMAP_AND_COLOUR_DEPTH, (byte) 0x74); // 0b01110100
commandAndData(REMAP_AND_COLOUR_DEPTH, (byte) 0b00110100); // 0b00110000
// commandAndData(REMAP_AND_COLOUR_DEPTH, (byte) 0x74); // 0b01110100
// A[6:0]: Start Address. [reset=0]
// B[6:0]: End Address. [reset=127]
commandAndData(SET_COLUMN_ADDRESS, (byte) 0x00, (byte) (width-1));
commandAndData(SET_COLUMN_ADDRESS, (byte) 0x00, (byte) (width - 1));
// A[6:0]: Start Address. [reset=0]
// B[6:0]: End Address. [reset=127]
commandAndData(SET_ROW_ADDRESS, (byte) 0x00, (byte) (height-1));
commandAndData(SET_ROW_ADDRESS, (byte) 0x00, (byte) (height - 1));
// Set start line - this needs to be 0 for a 128x128 display and 96 for a 128x96 display
commandAndData(DISPLAY_START_LINE, (byte) 0x00);
commandAndData(DISPLAY_OFFSET, (byte) 0x00);
// A[1:0] GPIO0:
// 00 pin HiZ, Input disabled
// 01 pin HiZ, Input enabled
// 10 pin output LOW [reset]
// 11 pin output HIGH
// 00 pin HiZ, Input disabled
// 01 pin HiZ, Input enabled
// 10 pin output LOW [reset]
// 11 pin output HIGH
// A[3:2] GPIO1:
// 00 pin HiZ, Input disabled
// 01 pin HiZ, Input enabled
// 10 pin output LOW [reset]
// 11 pin output HIGH
// 00 pin HiZ, Input disabled
// 01 pin HiZ, Input enabled
// 10 pin output LOW [reset]
// 11 pin output HIGH
commandAndData(SET_GPIO, (byte) 0x00);
// A[0]=0b, Disable internal VDD regulator (for power save during sleep mode only)
// A[0]=1b, Enable internal VDD regulator [reset]
Expand All @@ -191,20 +195,20 @@ protected void init() {
// C[7:0] Contrast Value Color C [reset=10001010b = 0x8A]
setContrast((byte) 0xC8, (byte) 0x80, (byte) 0xC8);
// A[3:0] :
// 0000b reduce output currents for all colors to 1/16
// 0001b reduce output currents for all colors to 2/16
// 1110b reduce output currents for all colors to 15/16
// 1111b no change [reset]
// 0000b reduce output currents for all colors to 1/16
// 0001b reduce output currents for all colors to 2/16
// 1110b reduce output currents for all colors to 15/16
// 1111b no change [reset]
commandAndData(MASTER_CONTRAST_CURRENT_CONTROL, (byte) 0x0F);
// A[1:0]=00 External VSL [reset]
// A[1:0]=01,10,11 are invalid
commandAndData(SET_VSL, (byte) 0b10100000, (byte) 0b10110101, (byte) 0b01010101); // 0xA0, 0xB5, 0x55
// A[3:0] Set Second Pre-charge Period
// 0000b invalid
// 0001b 1 DCLKS
// 0010b 2 DCLKS
// 1000b 8 DCLKS [reset]
// 1111b 15 DCLKS
// 0000b invalid
// 0001b 1 DCLKS
// 0010b 2 DCLKS
// 1000b 8 DCLKS [reset]
// 1111b 15 DCLKS
commandAndData(SET_SECOND_PRECHARGE_PERIOD, (byte) 0x01);

clear();
Expand All @@ -224,9 +228,8 @@ public void invertDisplay(boolean invert) {
}

/**
* This command is used to set Contrast Setting of the display. The chip has 256
* contrast steps from 00h to FFh. The segment output current ISEG increases
* linearly with the contrast step, which results in brighter display.
* This command is used to set Contrast Setting of the display. The chip has 256 contrast steps from 00h to FFh. The
* segment output current ISEG increases linearly with the contrast step, which results in brighter display.
*/
@Override
public void setContrast(byte level) {
Expand Down
Loading

0 comments on commit 630ba5d

Please sign in to comment.