sparkfun / HyperDisplay_UG2856KLBAG01_ArduinoLibrary

This is a HyperDisplay level 3 driver for the Transparent Graphical OLED
https://www.sparkfun.com/
Other
7 stars 7 forks source link

buffered output fails to be rendered #2

Open antevens opened 4 years ago

antevens commented 4 years ago

Subject of the issue

Using buffered output for text fails to render to screen

Your workbench

Arduino 33 BLE Sense On a breadboard, powered from USB SPI interface at 10Mhz

Steps to reproduce

include "HyperDisplay_UG2856KLBAG01.h"

uint8_t windowMemZero[10*128/8]; // not sure if this is the right way to reserve monochrome pixel memory

////////////////////////////// // Pinout and Hardware // //////////////////////////////

define SERIAL_PORT Serial

define SPI_PORT SPI

define CS_PIN 9

define DC_PIN 10

// END USER SETUP

// Set colors for the TOLED display uint8_t color = 0x01; uint8_t noColor = 0x00;

// Set up Transparent Organic LED Display and SPI communications UG2856KLBAG01_SPI TOLED;

// Create Windows wind_info_t windowZero, windowOne, windowTwo; // Create some window objects

void setup() { // Start serial communications Serial.begin(9600);

// Start TOLED/SPI communications SPI_PORT.begin(); TOLED.begin(CS_PIN, DC_PIN, SPI_PORT);

TOLED.setWindowDefaults(&windowZero); TOLED.setWindowDefaults(&windowOne); TOLED.setWindowDefaults(&windowTwo);

TOLED.setWindowColorSequence(&windowZero, (color_t)&color); TOLED.setWindowColorSequence(&windowOne, (color_t)&color); TOLED.setWindowColorSequence(&windowTwo, (color_t)&color);

TOLED.setWindowMemory(&windowZero, (color_t)windowMemZero, 10*128);

windowZero.xMin = 0; windowZero.yMin = 0; windowZero.xMax = 127; windowZero.yMax = 9;

windowOne.xMin = 0; windowOne.yMin = 10; windowOne.xMax = 127; windowOne.yMax = 19;

windowTwo.xMin = 0; windowTwo.yMin = 20; windowTwo.xMax = 127; windowTwo.yMax = 29;

TOLED.pCurrentWindow = &windowZero; TOLED.buffer(); TOLED.println("buffered fails"); TOLED.resetTextCursor(); TOLED.show(&windowZero);

TOLED.pCurrentWindow = &windowOne; TOLED.println("two direct"); TOLED.resetTextCursor();

TOLED.pCurrentWindow = &windowTwo; TOLED.println("three will be cleared"); TOLED.resetTextCursor();

delay(3000); TOLED.windowClear();

Serial.println(F("Transparent Graphical OLED")); }

void loop() { Serial.println(F("looping")); delay(1000); TOLED.show(); TOLED.show(&windowZero); TOLED.pCurrentWindow = &windowZero; TOLED.direct(); TOLED.println("direct works"); TOLED.resetTextCursor(); TOLED.buffer(); TOLED.println("buffered fails"); // but the cursor is still moved ... TOLED.resetTextCursor(); TOLED.show(); delay(1000); }

Expected behaviour

Buffered text should be rendered to screen alternating with direct text

Actual behaviour

Buffered text is never shown

oclyke commented 4 years ago

Ooh nice catch. This was/is a tricky one to figure out.

Long story short: Currently HyperDisplay is not happy working with color definitions that are less than a byte wide. This bug applies to non-text output as well

When we call TOLED.println in buffered mode:

  1. The inherited write function calls pixel
  2. pixel detects buffered mode and calls swpixel
  3. TOLED has no specialized version of swpixel so it calls the HD default
  4. HD swpixel uses the window standard method to determine a pixel offset (within the windows buffer) given the window coordinates of that pixel (that is left->right and top->bottom)
  5. The pixel offset within the window is used in the getOffsetColor function to determine where in the window's buffer to put the color information
  6. memcpy copies n bytes from the supplied color information to the window.
  7. Because of the current imlementation of getOffsetColor for the SSD1309 the number of bytes copied to window memory is always zero and thus the desired data is not saved.

There are more problems, I suspect.

  1. Even if bytes were copied into window memory properly they would obliterate existing pixel information. We need a way to write individual bits into memory if the hardware implementation requires it.

Possible Solutions:

  1. Implement a 'storeColor' function in HyperDisplay that can operate on sub-byte wide color types and use it in the swpixel function to control access to window buffers rather than just copying bytes

I will start prototyping a fix. Thanks for reporting this issue!

antevens commented 4 years ago

That makes sense, I spent a couple of hours looking through the code trying to find the reason but didn't see anything obvious, no wonder :)

As a work-around to speed up text updates I added a flag in my branch to set all pixels in a character area not used by the character to 0x00 at the same time the character pixels are set to avoid having to clear the whole window before updating, it's probably not as good as the buffered update but usable for now.

Thanks for the update and all the hard work, it's appreciated!

oclyke commented 4 years ago

Alright, I've got a fix in place. It feels really quite hacky but fortunately it doesn't affect the integrity of HyperDisplay, only the middle layer (SSD1309 driver). Try updating to the latest of all the HyperDisplay libraries (Top and middle layers have changed)

P.s. if it is speed you are after the 'buffered' mode is probably not going to help. When the show method is called it triggers an update of the entire window (pixel-by-pixel, and without any optimization)

In the non-buffered mode the SSD1309 will only change pixels that are directly called.

However the implementation is not fully optimized - only pixelwise access is implemented and for that to work the memory addressing mode, column address, and page address all need to be updated in addition to the actual data.

Implementing line and rectangle optimizations will help in some cases but I am not sure if there is a guaranteed way to have pixelwise access that is any faster.

Happy new year!

antevens commented 4 years ago

Thanks for the update, I guess I'll stick with re-rendering characters for now, based on the documentation I figured buffered mode would at least only change those pixels that needed updating since the last "show()", I open an issue in the SSD1309 repo with my findings.

Happy New Year and thanks again for addressing this!

antevens commented 4 years ago

I think I've found a couple of minor issues:

  1. Clearing a buffered window "fills" it instead, probably a mixup between the set/unset
  2. Once memory is assigned to a window direct prints to the window don't show up even if the window has not been set as buffered.
#include "HyperDisplay_UG2856KLBAG01.h"

uint8_t windowMemZero[10*128];

//////////////////////////////
//    Pinout and Hardware   //
//////////////////////////////
#define SERIAL_PORT Serial
#define SPI_PORT SPI
#define CS_PIN 9
#define DC_PIN 10
// END USER SETUP

// Set up Transparent Organic LED Display and SPI communications
UG2856KLBAG01_SPI TOLED;

uint8_t color = 0x01;
uint8_t noColor = 0x00;

// Create Windows
wind_info_t windowZero;  // Create some window objects

void setup() {
  // Start serial communications
  Serial.begin(9600);

  // Start TOLED/SPI communications
  SPI_PORT.begin();
  TOLED.begin(CS_PIN, DC_PIN, SPI_PORT);

  TOLED.setWindowDefaults(&windowZero);
  TOLED.setWindowColorSequence(&windowZero, (color_t)&color);
  TOLED.setWindowMemory(&windowZero, (color_t)windowMemZero, 10*128);

  windowZero.xMin = 0;
  windowZero.yMin = 0;
  windowZero.xMax = 127;
  windowZero.yMax = 9;

  TOLED.pCurrentWindow = &windowZero;
  TOLED.buffer();
  TOLED.println("buffered fails");
  TOLED.resetTextCursor();
  TOLED.show();

  delay(3000);
  TOLED.windowClear();
  Serial.println(F("Transparent Graphical OLED"));
}

void loop() {
   Serial.println(F("looping"));
   delay(1000);
   TOLED.println("print more");
   TOLED.resetTextCursor();
   TOLED.show();
}
oclyke commented 4 years ago

Ahh you are being a much more thorough tester than I have been! (Thanks!)

I was worried that something like this might happen because I've made the getOffsetColor function run double duty for the SSD1309

Usually it should only have to return a pointer to the next user color type that was N pixels after the base. In this case I've made it so that it also can return literal values (0 and 1) that will indicate to the drawing function to render something directly. This mode is activated when the window data pointer is non-null. So that's probably close to the culprit.

dani0303 commented 4 years ago

hello there I was wondering if you could send an example of the the getCharInfo and write function I tried using serial print it did not work. I just need example of how to plug in the data in the correct coordinates.

oclyke commented 4 years ago

@dani0303

I assume you want to create a custom font - if not then the libraries default font should work out of the box.

To make your own custom font you only need to implement either getCharInfo or write. If you make your own write function then you are completely in control and you will only use getCharInfo if you personally decide to. If you want to rely on the default write function in HyperDisplay then you will only need to make your own getCharInfo.

The best example of getCharInfo might be the default implementation in the HyperDisplay source code

You can use that as a starting point, as well as the other examples posted here

Honestly - HyperDisplay needs a dedicated solution for font handling and I haven't had any bandwidth to figure it out. This will get you started in the right direction and once you've tried it out maybe you will have ideas on how to make it better.