Skip to content

Commit 7f5462f

Browse files
Fix buffer clean for horizontal flips and add misc cleanups
1 parent 0eddcb6 commit 7f5462f

File tree

1 file changed

+8
-123
lines changed

1 file changed

+8
-123
lines changed

src/qwiic_grch1120.cpp

Lines changed: 8 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,13 @@
44
// This is a library written for SparkFun Qwiic OLED boards that use the
55
// CH1120. This driver is VERY similar to the SSD1306 driver, but with a few
66
// subtle differences. Currently, our only device with the CH1120 driver is
7-
// the Qwiic OLED 1.5", on which row addressing has not been verified.
8-
// So, this library currently writes out the entire screen buffer each refresh
9-
// instead of only the dirty bits like the SSD1306 driver does. This makes this
10-
// display operate slower than the others in the library.
7+
// the Qwiic OLED 1.5".
118
//
129
// SparkFun sells these at its website: www.sparkfun.com
1310
//
1411
// Do you like this library? Help support SparkFun. Buy a board!
1512
//
16-
// Micro OLED https://www.sparkfun.com/products/14532
17-
// Transparent OLED https://www.sparkfun.com/products/15173
18-
// "Narrow" OLED https://www.sparkfun.com/products/17153
19-
//
13+
// Qwiic OLED 1.5in https://www.sparkfun.com/products/29530
2014
//
2115
// Written by SparkFun Electronics, August 2025
2216
//
@@ -76,8 +70,7 @@
7670
//
7771
// These macros work with the pageState_t struct type.
7872
//
79-
// Define unique values just outside of the screen buffer (SSD1306) page range
80-
// (0 base) Note: A page is 128 bits in length
73+
// Define unique values just outside of the screen buffer (valid is 0 - 159)
8174

8275
#define kPageMin -1 // outside bounds - low value
8376
#define kPageMax 160 // outside bounds - high value
@@ -151,7 +144,7 @@
151144

152145
#define kCmdRowStartEnd ((uint8_t)0x22) // Two byte command - command, start, stop
153146
#define kCmdColStartEnd ((uint8_t)0x21) // Two byte command - command, start, stop
154-
#define kCmdStartRow ((uint8_t)0xB0) // TODO: this is analagous to the start "page" for the SSD1306
147+
#define kCmdStartRow ((uint8_t)0xB0)
155148
#define kCmdStartColLow ((uint8_t)0x0F) // Start Column = StartColHigh (MSB) | StartColLow (LSB)
156149
#define kCmdStartColHigh ((uint8_t)0x10)
157150
#define kCmdStartLine ((uint8_t)0xA2) // Notice how this is different from the same cmd on the 1306 (0x40)
@@ -269,7 +262,7 @@ const std::map<uint8_t, uint8_t> scrollIntervals = {
269262
////////////////////////////////////////////////////////////////////////////////////
270263
// setup defaults - called from constructors
271264
//
272-
// Just a bunch of member variable inits (TODO: should these be the defaults on reset or what we want them to be after init?)
265+
// Just a bunch of member variable inits
273266

274267
void QwGrCH1120::setupDefaults(void)
275268
{
@@ -279,9 +272,7 @@ void QwGrCH1120::setupDefaults(void)
279272
m_rop = {grROPCopy};
280273
m_i2cBus = {nullptr};
281274
m_i2cAddress = {0x3C}; // address of the device (0x3D for closed)
282-
// m_initHWComPins = {kDefaultPinConfig};
283275
m_initPreCharge = {kDefaultPreCharge};
284-
// m_initVCOMDeselect = {kDefaultVCOMDeselect};
285276
m_initContrast = {kDefaultContrast};
286277
m_isInitialized = {false};
287278
}
@@ -412,28 +403,18 @@ void QwGrCH1120::setupOLEDDevice(bool clearDisplay){
412403
if (clearDisplay)
413404
sendDevCommand(kCmdDisplayOff);
414405

415-
// sendDevCommand(kCmdRowStartEnd, kDefaultRowStart, kDefaultRowEnd);
416-
// sendDevCommand(kCmdColStartEnd, kDefaultColStart, kDefaultColEnd);
417-
// TODO: This may have to happen when the display is on or be broken out into its separate commands
418406
setScreenBufferAddress(kDefaultRowStart, kDefaultRowEnd);
419407

420408
sendDevCommand(kCmdStartLine, kDefaultDisplayStart);
421-
422-
// ELI HAD THIS AS 0x0F, 200...
423409
sendDevCommand(kCmdContrastControl, m_initContrast);
424410
sendDevCommand(kCmdGrayMono, kDefaultMonoMode);
425411
sendDevCommand(kCmdHorizAddressing, kDefaultHorizontalAddressing);
426412
sendDevCommand(kCmdSegRemapDown);
427-
// sendDevCommand(kCmdComOutScan0Last);
428413
sendDevCommand(kCmdComOutScan0First);
429414
sendDevCommand(kCmdDisplayRotation, kDefaultRotateDisplayNinety);
430-
// sendDevCommand(kCmdDisplayRotation, 0x00);
431415
sendDevCommand(kCmdDisableEntireDisplay);
432416

433-
// sendDevCommand(kCmdNormalDisplay);
434-
// sendDevCommand(kCmdMultiplexRatio, kDefaultMultiplexRatio);
435417
sendDevCommand(kCmdDisplayOffset, kDefaultDisplayOffset);
436-
// sendDevCommand(kCmdDisplayOffset, 0);
437418
sendDevCommand(kCmdDischargeFront, kDefaultDischargeFront);
438419
sendDevCommand(kCmdDischargeBack, kDefaultDischargeBack);
439420
sendDevCommand(kCmdPreCharge, m_initPreCharge);
@@ -483,25 +464,10 @@ void QwGrCH1120::setBuffer(uint8_t *pBuffer)
483464
void QwGrCH1120::clearScreenBuffer(void)
484465
{
485466
// Clear out the screen buffer on the device
486-
// each row is m_nPages bytes wide
487-
// and we have m_viewport.height rows
488-
// uint8_t emptyRow[m_nPages] = {0};
489-
490-
// setScreenBufferAddress(0, 0); // Warning: This function works-ish but only for even-numbered rows.
491-
// so we can use it here, but do not expect it to work in all instances
492-
493-
// setScreenBufferAddress(0, 32);
494-
495-
// for (int i = 0; i < m_viewport.height; i++)
496-
// {
497-
// sendDevData((uint8_t *)emptyRow, m_nPages); // clear out row
498-
// }
499467
uint8_t emptyPage[kPageMax] = {0};
500468

501469
for (int i = 0 ; i < kMaxPageNumber; i++)
502470
{
503-
// setScreenBufferAddress(i * kPageHeight, (i + 1) * kPageHeight);
504-
// setScreenBufferAddress(i, 0);
505471
setScreenBufferAddress(0, i);
506472
sendDevData(emptyPage, kPageMax);
507473
}
@@ -516,8 +482,6 @@ void QwGrCH1120::initBuffers(void)
516482
{
517483
int i;
518484

519-
// TODO: the concept of ('width' and 'height' might be sorta swapped for this as opposed to old driver)
520-
// that might not matter so much since this is square 128x128
521485
// clear out the local graphics buffer
522486
if (m_pBuffer)
523487
memset(m_pBuffer, 0, m_viewport.width * m_nPages);
@@ -560,11 +524,7 @@ void QwGrCH1120::resendGraphics(void)
560524
//
561525
// Flip the onscreen graphics vertically.
562526
void QwGrCH1120::flipVert(bool bFlip){
563-
// If we are already formatted for the flipped display, just return
564-
// sendDevCommand(bFlip ? kCmdSegRemapUp : kCmdSegRemapDown);
565527
sendDevCommand(bFlip ? kCmdComOutScan0Last : kCmdComOutScan0First);
566-
567-
resendGraphics();
568528
}
569529

570530
////////////////////////////////////////////////////////////////////////////////////
@@ -574,32 +534,23 @@ void QwGrCH1120::flipVert(bool bFlip){
574534
// graphics data to the device/screen buffer.
575535
void QwGrCH1120::flipHorz(bool bFlip){
576536

577-
// sendDevCommand(kCmdDisplayOff); // TODO: verify is this necessary?
578-
579-
// TODO: Implement this for the new way with individual pixel addressing.
580-
// we might have to have a variable that is used by setScreenBufferAddress to account for the necessary shift
581-
// when flipped
582537
if (bFlip){
583538
// If we are flipping to horizontal, we need to adjust row start and end to the end of the display memory
584539
// This is because when we horizontally flip, if our viewport is smaller than the total area, we will flip in some garbage
585-
// When flipping we need to offset by the max width minus the viewport width (or height, I've kind of lost the plot on which is which with this square display in 90 degree rotation mode)
586-
// uint8_t offset = kMaxCH1120Width - m_viewport.width; // TODO: Should these really be widths or heights?
587-
// sendDevCommand(kCmdRowStartEnd, kDefaultRowStart + offset, kDefaultRowEnd + offset);
540+
// When flipping we need to offset by the max width minus the viewport width (maybe should be height, may have to update with non-square display)
588541
horz_flip_offset = kMaxCH1120Width - m_viewport.width;
589542

590543
sendDevCommand(kCmdSegRemapUp);
591544
}
592545

593546
else{
594547
// If in normal mode, just set to the defaults
595-
// sendDevCommand(kCmdRowStartEnd, kDefaultRowStart, kDefaultRowEnd);
596548
horz_flip_offset = 0;
597549

598550
sendDevCommand(kCmdSegRemapDown);
599551
}
600552

601-
// sendDevCommand(kCmdDisplayOn); // TODO: verify is this necessary?
602-
553+
clearScreenBuffer();
603554
resendGraphics();
604555
}
605556

@@ -731,41 +682,12 @@ void QwGrCH1120::displayPower(bool enable)
731682
// This function sort of becomes useless because the entire page is rewritten each time now, so there isn't so much a concept of "erasing"
732683
void QwGrCH1120::erase(void)
733684
{
734-
// Serial.println("Calling erase()");
735685
if (!m_pBuffer)
736686
return;
737687

738-
// Print all the page states and corresponding memset they will have:
739-
// for (uint8_t i = 0 ; i < m_nPages; i++){
740-
// Serial.print("Page ");
741-
// Serial.print(i);
742-
// Serial.print(" xmin: ");
743-
// Serial.print(m_pageState[i].xmin);
744-
// Serial.print(" xmax: ");
745-
// Serial.println(m_pageState[i].xmax);
746-
747-
// Serial.print("Corresponding memset: ");
748-
// Serial.print("memset(m_pBuffer + ");
749-
// Serial.print(i * m_viewport.width + m_pageState[i].xmin);
750-
// Serial.print(", 0, ");
751-
// Serial.print(m_pageState[i].xmax - m_pageState[i].xmin + 1);
752-
// Serial.println(");");
753-
// }
754-
755688
// Cleanup the dirty parts of each page in the graphics buffer.
756689
for (uint8_t i = 0; i < m_nPages; i++)
757690
{
758-
// Serial.print("Erasing page ");
759-
// Serial.println(i);
760-
761-
// Now, print the arguments we are about to pass to memset:
762-
// Serial.print("First Arg to Memset (offset into buffer): ");
763-
// Serial.println(i * m_viewport.width + m_pageState[i].xmin);
764-
// Serial.print("Second Arg to Memset: ");
765-
// Serial.println(0);
766-
// Serial.print("Third Arg to Memset: ");
767-
// Serial.println(m_pageState[i].xmax - m_pageState[i].xmin + 1);
768-
// m_pageState
769691
// The current "dirty" areas of the graphics [local] buffer.
770692
// Areas that haven't been sent to the screen/device but are
771693
// "dirty"
@@ -783,26 +705,15 @@ void QwGrCH1120::erase(void)
783705
memset(m_pBuffer + i * m_viewport.width + m_pageState[i].xmin, 0,
784706
m_pageState[i].xmax - m_pageState[i].xmin + 1); // add one b/c values are 0 based
785707

786-
// Serial.print("setting page clean ");
787-
// Serial.println(i);
788708
// clear out any pending dirty range for this page - it's erased
789709
pageSetClean(m_pageState[i]);
790710
}
791711

792712
// Indicate that the data transfer to the device should include the erase
793713
// region
794-
// Serial.println("Erasing pages:");
795714
m_pendingErase = true;
796715
}
797716

798-
// //TODO: remove this if it's unused
799-
// void QwGrCH1120::erase(void) {
800-
// // memset the entire buffer to 0
801-
// memset(m_pBuffer, 0, m_nPages * m_viewport.width);
802-
803-
// m_pendingErase = true;
804-
// }
805-
806717
////////////////////////////////////////////////////////////////////////////////////
807718
//
808719
// draw_pixel()
@@ -813,7 +724,6 @@ void QwGrCH1120::erase(void)
813724

814725
void QwGrCH1120::drawPixel(uint8_t x, uint8_t y, uint8_t clr)
815726
{
816-
//Serial.println("Calling drawPixel with x: " + String(x) + ", y: " + String(y) + ", color: " + String(clr));
817727
// quick sanity check on range
818728
if (x >= m_viewport.width || y >= m_viewport.height)
819729
return; // out of bounds
@@ -824,8 +734,6 @@ void QwGrCH1120::drawPixel(uint8_t x, uint8_t y, uint8_t clr)
824734
(clr ? bit : 0), bit); // which bit to set in byte
825735

826736
// print Buffer after drawing pixel:
827-
// printBuffer();
828-
//rawPrintBuffer();
829737
pageCheckBounds(m_pageState[y / kByteNBits],
830738
x); // update dirty range for page
831739
}
@@ -839,7 +747,6 @@ void QwGrCH1120::drawLineHorz(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, ui
839747
{
840748
// Basically we set a bit within a range in a page of our graphics buffer.
841749

842-
// Serial.println("Calling Draw Line Horz with x0: " + String(x0) + ", y0: " + String(y0) + ", x1: " + String(x1) + ", y1: " + String(y1));
843750
// in range
844751
if (y0 >= m_viewport.height)
845752
return;
@@ -1078,7 +985,6 @@ void QwGrCH1120::drawBitmap(uint8_t x0, uint8_t y0, uint8_t dst_width, uint8_t d
1078985

1079986
bool QwGrCH1120::setScreenBufferAddress(uint8_t row, uint8_t column)
1080987
{
1081-
// Serial.println("Calling setScreenBufferAddress with row: " + String(row) + ", column: " + String(column));
1082988
if (row >= m_viewport.height || column >= m_viewport.width)
1083989
return false;
1084990

@@ -1125,28 +1031,15 @@ void QwGrCH1120::rawPrintBuffer() {
11251031
// display()
11261032
//
11271033

1128-
// OLD:
11291034
// Send the "dirty" areas of the graphics buffer to the device's screen buffer.
11301035
// Only send the areas that need to be updated. The update region is based on
11311036
// new graphics to display, and any currently displayed items that need to be
11321037
// erased.
11331038

1134-
// NEW:
1135-
// Send the ENTIRE graphics buffer to the device's screen buffer. Include things that are updated or not updated
1136-
// This is necessary because we cannot directly index to pixels since the row setting command is broken.
1137-
// In the future, if we ever get a row setting command that works, we can re-instate the fancy (only-update-dirty methodology)
1138-
11391039
void QwGrCH1120::display()
11401040
{
1141-
//sendDevData(m_pBuffer, m_nPages * m_viewport.height); // send the entire buffer to the device
1142-
11431041
// Loop over our page descriptors - if a page is dirty, send the graphics
11441042
// buffer dirty region to the device for the current page
1145-
1146-
// Print the buffer and print the raw buffer
1147-
// printBuffer();
1148-
// rawPrintBuffer();
1149-
11501043
pageState_t transferRange;
11511044

11521045
for (int i = 0 ; i < m_nPages; i++) {
@@ -1155,14 +1048,6 @@ void QwGrCH1120::display()
11551048

11561049
transferRange = m_pageState[i];
11571050

1158-
// print the transferRange:
1159-
// Serial.print("Transfer range for page ");
1160-
// Serial.print(i);
1161-
// Serial.print(": min: ");
1162-
// Serial.print(transferRange.xmin);
1163-
// Serial.print(" - max: ");
1164-
// Serial.println(transferRange.xmax);
1165-
11661051
// If an erase has happend, we need to transfer/include erase update range
11671052
if (m_pendingErase)
11681053
pageCheckBoundsDesc(transferRange, m_pageErase[i]);
@@ -1173,7 +1058,7 @@ void QwGrCH1120::display()
11731058

11741059
// set the start address to write the updated data to the devices screen
11751060
// buffer
1176-
// TODO: should the offset be applied to the first or second arg?
1061+
11771062
// write out the xmin and xmax for each page descriptor
11781063
// setScreenBufferAddress(i, transferRange.xmin + horz_flip_offset);
11791064
setScreenBufferAddress(transferRange.xmin + horz_flip_offset, i);

0 commit comments

Comments
 (0)