-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
There are two undocumented (yet hinted at) commands for one-pixel scroll, 0x2C and 0x2D (right and left, respectively).
From this answer to Scroll long text on OLED display:
The SSD1306 chip provides commands to enable both continuous scrolling and 1 pixel scroll. For our purpose of scrolling long text, the continuous scroll is not useful, as we want to scroll exactly one pixel. Therefore, we need to use the 1 pixel scroll command. The commands take the same parameters, except for the first opcode byte. For some reason in the widely circulated (and pretty old) datasheet online, the 1 pixel scroll command is missing, but it is there in HW.
#define CMD_CONTINUOUS_SCROLL_H_RIGHT 0x26 #define CMD_CONTINUOUS_SCROLL_H_LEFT 0x27 #define CMD_ONE_COLUMN_SCROLL_H_RIGHT 0x2C #define CMD_ONE_COLUMN_SCROLL_H_LEFT 0x2DUse the latter two.
It is easy enough to implement these undocumented commands, by adding two new member functions. One way is to duplicate and modify the two existing functions for continuous horizontal scroll, startscrollright() and startscrollleft(). See SSD1306 - Is there a way to scroll a certain number of pixels only?, for more details.
Implementing the missing commands
For example, duplicating and then renaming startscrollright() and startscrollleft() as startscrollrightone() and startscrollleftone():
Add to Adafruit_SSD1306.h the following #define lines:
#define SSD1306_RIGHT_HORIZONTAL_SCROLL_ONE 0x2C ///< Init right scroll one pixel
#define SSD1306_LEFT_HORIZONTAL_SCROLL_ONE 0x2D ///< Init left scroll one pixel
and the following two declarations:
void startscrollrightone(uint8_t start, uint8_t stop);
void startscrollleftone(uint8_t start, uint8_t stop);
Add to Adafruit_SSD1306.cpp the following two definitions
/*!
@brief Activate a 1 pixel right-handed scroll for all or part of the display.
@param start
First row.
@param stop
Last row.
@return None (void).
*/
// To scroll the whole display, run: display.startscrollrightone(0x00, 0x0F)
void Adafruit_SSD1306::startscrollrightone(uint8_t start, uint8_t stop) {
TRANSACTION_START
static const uint8_t PROGMEM scrollList1a[] = {
SSD1306_RIGHT_HORIZONTAL_SCROLL_ONE, 0X00};
ssd1306_commandList(scrollList1a, sizeof(scrollList1a));
ssd1306_command1(start);
ssd1306_command1(0X00);
ssd1306_command1(stop);
static const uint8_t PROGMEM scrollList1b[] = {0X00, 0XFF,
SSD1306_ACTIVATE_SCROLL};
ssd1306_commandList(scrollList1b, sizeof(scrollList1b));
TRANSACTION_END
}
/*!
@brief Activate a 1 pixel left-handed scroll for all or part of the display.
@param start
First row.
@param stop
Last row.
@return None (void).
*/
// To scroll the whole display, run: display.startscrollleftone(0x00, 0x0F)
void Adafruit_SSD1306::startscrollleftone(uint8_t start, uint8_t stop) {
TRANSACTION_START
static const uint8_t PROGMEM scrollList2a[] = {SSD1306_LEFT_HORIZONTAL_SCROLL_ONE,
0X00};
ssd1306_commandList(scrollList2a, sizeof(scrollList2a));
ssd1306_command1(start);
ssd1306_command1(0X00);
ssd1306_command1(stop);
static const uint8_t PROGMEM scrollList2b[] = {0X00, 0XFF,
SSD1306_ACTIVATE_SCROLL};
ssd1306_commandList(scrollList2b, sizeof(scrollList2b));
TRANSACTION_END
}
Examples
Example#1 - Text scroll
Scrolls some text by 50 pixels to the right:
void testscrolltext_by50pixels(void) {
display.clearDisplay();
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 0);
display.println(F("scroll"));
display.display(); // Show initial text
delay(100);
int wait_time = 12; // For the Xiao: 15 is the minimum (maybe 12... but definitely not 11)
for (int i = 0; i < 50; i++) {
display.startscrollrightone(0x00, 0x0F);
delay(wait_time);
}
delay(2000);
}
Note: I found, on the Xiao at least, that a delay is required. Without any delay() I found that no scrolling occurred at all. If wait_time is less than 12 ms delay, then that causes some of the scrolls to fail, and the full 50 pixels are not scrolled. I found that the closer that the delay is to zero, the less distance, in pixels scrolled, the text/images actually move. The minimum length of the required delay probably depends upon the frequency of the µController, so you may need to experiment for your particular setup.
Example#2 - Scrolling landscape
Making use of the pages feature of the SSD1306 - in conjunction with varying single pixel scroll rates - here is a scrolling landscape:
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define LOGO_HEIGHT 64
#define LOGO_WIDTH 128
// Generated from https://javl.github.io/image2cpp/
// 'StarWarsLandscape', 128x64px
const unsigned char epd_bitmap_StarWarsLandscape_WonB[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0xc1, 0x80, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x00, 0x80, 0x00, 0x0f, 0x8e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
0x00, 0x0e, 0x00, 0xc0, 0x00, 0x18, 0xf8, 0x30, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xc8, 0xf0, 0x00,
0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x60, 0x10, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x0d, 0x08, 0x00,
0x00, 0x30, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x0c, 0x00,
0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x10, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x04, 0x00,
0x00, 0x40, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x8c, 0x00,
0x00, 0x20, 0x00, 0xc0, 0x01, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x01, 0x80, 0x78, 0x00,
0x00, 0x20, 0x20, 0x80, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x30, 0x00,
0x00, 0x18, 0x31, 0x80, 0x01, 0x20, 0x00, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x05, 0x00, 0x20, 0x00,
0x00, 0x0c, 0x6b, 0x00, 0x01, 0xc0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x60, 0x01, 0x80, 0x60, 0x00,
0x00, 0x03, 0x8e, 0x00, 0x00, 0x80, 0x10, 0x08, 0x00, 0x00, 0x00, 0x40, 0x03, 0xc1, 0xc0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x18, 0x18, 0x00, 0x00, 0x00, 0x60, 0x0e, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x38, 0x70, 0x00, 0x00, 0x00, 0x3e, 0xf8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xec, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
0x00, 0x00, 0x03, 0x88, 0x00, 0x03, 0xff, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x18, 0x00,
0x00, 0x00, 0x06, 0x08, 0x00, 0x06, 0x00, 0x80, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x38, 0x00,
0x80, 0x00, 0x0c, 0x0f, 0x00, 0x0c, 0x00, 0xc0, 0x00, 0x02, 0x1f, 0x80, 0x00, 0x01, 0xe8, 0x00,
0xe0, 0x00, 0x78, 0x00, 0xe0, 0x18, 0x00, 0x40, 0x00, 0x06, 0x00, 0xc0, 0x00, 0x03, 0x0c, 0x01,
0x20, 0x00, 0xc0, 0x00, 0x30, 0x30, 0x00, 0x40, 0x00, 0x04, 0x00, 0x40, 0x00, 0x0c, 0x07, 0xc3,
0x30, 0x03, 0x00, 0x00, 0x0c, 0x60, 0x00, 0x20, 0x00, 0x0c, 0x00, 0x40, 0x00, 0x18, 0x00, 0x76,
0x10, 0x02, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x20, 0x00, 0x18, 0x00, 0x7c, 0x00, 0x30, 0x00, 0x1c,
0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x07, 0x80, 0x20, 0x00, 0x04,
0x0f, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x60, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00,
0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0xc0, 0x00, 0x00, 0x20, 0xc0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x80, 0x00, 0x00, 0x1f, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x18, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x3c, 0x0e, 0x03, 0x0c, 0x00, 0x1c, 0x00, 0x00, 0x3f, 0x8e, 0x00, 0x07, 0xe0, 0x1e, 0x00,
0x2e, 0x63, 0xb9, 0xce, 0x04, 0x67, 0xe6, 0xe0, 0x00, 0x60, 0xcf, 0x00, 0x6d, 0xf8, 0x73, 0x80,
0xe3, 0xc0, 0xe0, 0x78, 0x07, 0xfc, 0x03, 0xbe, 0x01, 0xc0, 0x79, 0xfd, 0xf8, 0xcf, 0xc0, 0xff,
0x41, 0x80, 0x40, 0x30, 0x00, 0x18, 0x03, 0x03, 0xff, 0x00, 0x19, 0x87, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
void setup() {
Serial.begin(9600);
// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
//display.begin(SSD1306_SWITCHCAPVCC); // Waaay too basic
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // Don't proceed, loop forever
}
// init done
// Clear the buffer.
display.clearDisplay();
// bitmap display
display.drawBitmap(0, 0, epd_bitmap_StarWarsLandscape_WonB, 128, 64, 1);
display.display();
}
void loop() {
scroll_landscape();
}
void scroll_landscape(void) {
int wait_time = 12; // For the Xiao: 15 is the minimum (maybe 12... but definitely not 11)
for (int j = 0; j < 2; j++) {
for (int i = 0; i < 2; i++) {
display.startscrollleftone(0x06, 0x07);
delay(wait_time);
}
display.startscrollleftone(0x03, 0x05);
delay(wait_time);
}
display.startscrollleftone(0x00, 0x02);
delay(wait_time);
}