..

OLED Using SH1107: Text

The reason I bought the Adafruit Monochrome 1.12" 128x128 OLED Graphic Display is to display text. And to figure out how to use it.

The first “part” is here, but it’s time to see what the SH1107 can do. What I really want is to connect it to the Enviro Grow (it has a Qw/ST connector) and display some information, because unfortunately it’s not possible to use SWD with it, and in any case I want some logging.

Text

To display text I need a font. There are many bitmap fonts on the internet (and many of them are free), and I’ve decided to use https://github.com/dhepper/font8x8 because it’s already in the shape of a C header, and it doesn’t look too bad. I also looked at https://robhagemans.github.io/monobit/ and found the lovely ZX Spectrum font, but the legal question is too hard for me, even if it’s only meant for a hobby project.

So the first attempt is to simply show some text on the screen. I don’t care about efficiency at this point, and drawing one pixel at a time is absolutely acceptable.

A very naive implementation:

void sh1107_write_char(char ch, int x, int y)
{
    if (ch < 0 || ch > 127) {
        return;
    }
    char *char_data = font8x8_basic[ch];
    int py = y << 3;
    for (int i = 0; i < 8; i++) {
        uint8_t row = *char_data++;
        int px = x << 3;
        for (int j = 0; j < 8; j++) {
            if (row & 0x1) {
                sh1107_set_pixel(px, py);
            }
            row >>= 1;
            px++;
        }
        py++;
    }
}

The letters are tiny but that’s OK :-)

The reason I don’t care about the implementation is that I’d like to play with two SH1107 commands:

  1. Set Display Offset
  2. Set Display Start Line

I don’t really understand what they do, so let’s see. First, I need some text.

text

No text wrapping, but I’m not concerned about that (that’s not what I want anyway).

Set Display Offset

According to the datasheet, it “specifies the mapping of display start line to one of COM0-127.”

If I set it to 50, does it mean that column 50 will be at the start? Let’s give it a go.

Result: Yes, that’s exactly what happened.

It doesn’t matter what the addressing mode is - the display start line is the given column.

display-offset

Set Display Start Line

According to the datasheet:

Specify Column address to determine the initial display line or COM0. The RAM display data becomes the top line of OLED screen.

It mentions “smooth scrolling”, but setting the display offset already achieved that. Anyway, let’s see what happens.

Unfortunately (?) it does exactly the same thing. It’s not a bad thing, because at least it works, but I don’t really understand the difference between the two commands. I’ll use the “set display start line” because the documentation mentions scrolling which is what I care about at the moment.

Vertical Scrolling

The commands above enable horizontal scrolling. Unfortunately, I can’t find a way to perform vertical scrolling, other than to physically rotate the display and update the way text is written. But maybe that’s just life.

Page addressing mode makes sense, because it allows me to copy a byte at a time, rather than draw each letter one pixel at a time. It also means that an off-screen buffer is not required (!) which is great.

To draw a character:

  1. Set the page to the column number
  2. Set the column to 8 x row number
  3. Copy 8 bytes

To scroll:

  1. Clear the top row (that can be done by setting the addressing mode!)
  2. Scroll 8 “rows” (really columns)

Sounds simple, let’s hope it really is.

Ended up with this for displaying a string:

void sh1107_write_string(const char *s)
{
    char ch;
    while ((ch = *s++)) {
        if (ch != '\n') {
            if (text_info.cursor_x == 16) {
                text_info.cursor_x = 0;
                if (++text_info.cursor_y >= 16) {
                    scroll_text();
                }
            }
            sh1107_write_char(ch, text_info.cursor_x, text_info.cursor_y);
            text_info.cursor_x++;
        } else {
            text_info.cursor_x = 0;
            if (++text_info.cursor_y >= 16) {
                scroll_text();
            }
        }
    }
}

And this for the scroll:

static void scroll_text(void)
{
    clear_line(text_info.row_offset);
    text_info.row_offset += 8;
    text_info.row_offset &= 0x7f;
    sh1107_set_display_offset(text_info.row_offset);
}

This seems to work.

scrolling

Note that this is slowed down significantly - scrolling without any delays is much, much faster.