Overview

The plan is to build a music player from existing components.

Shopping list:

The RP2040 is the microcontroller, which will be used to drive both the OLED display and the MP3 player.

And first up, it’s the OLED display.

OLED Display (SH1107)

The display uses SH1107. There is a datasheet and a PDF from adafruit.

It uses I2C which the RP2040 supports. The protocol describes how commands and data are sent using control bytes and data bytes. Each control byte says whether the following bytes are command bytes or data bytes. Commands are described in the datasheet.

The SH1107 supports many features, but I’m happy to use a small subset that makes sense.

Also, the default settings seem reasonable enough to just turn the display on and try to send some display data. It starts with:

  • Column address = 0 (7 bits)
  • Page addressing mode
  • Display offset = COM0

In page addressing mode, the display is divided into 16 pages (PAGE0..PAGE15) and there are 128 columns (COL0..COL127). Each page consists of 8 rows and data bytes map to a page’s rows (one byte per column in a given page). Columns wrap around (apparently - I’ve not tried it yet), which if I understand correctly, it is possible to start writing into COL37, and subsequent bytes will be written into COL38, COL39 etc. up to COL127 and then COL0, COL1 etc. The page address doesn’t change during this time. There is a specific command to set the page address.

The other mode is column addressing mode which works one column at a time (across all pages), but I’ll start with page addressing mode.

So the next thing is to try to write some data. Writing 0x11, 0x22, 0x44, 0x88 in sequence should (?) create a pattern of diagonal lines on a single “page” (8 rows).

The display shows some random data, but also the pattern. The pattern appears on the side of the display, so a page on this display is only 64 pixels wide, and “vertical” (this is of course arbitrary, but I want to use the display for text, which means that it should have 128 pixels horizontally and 64 vertically).

Some tweaking required then, but at least it works. Writing the data requires a single control byte (0x40 = no more control bytes, data bytes follow) + data bytes. The code is fairly straight-forward:

    uint8_t control = 0x40;
    uint8_t page[129] = { control };
    for (int i = 0; i < 128; i++) {
        page[i + 1] = i;
    }
    i2c_write_blocking(I2C_PORT, SH1107_ADDRESS, page, sizeof page, false);

SH1107 Configuration

After playing with the code for a bit, it turns out that we need the following settings:

  • vertical addressing mode - it makes it more intuitive, but of course it’s arbitrary (it’s always possible to use horizontal addressing mode and just change the order of bytes we send)
  • display offset 0x60 - this was just copied from the sample code from adafruit
  • scan from COM[n-1] to COM0 - makes the update “top to bottom” (in vertical mode)
  • segment re-map disabled - makes the update “left to right” (in vertical mode)

With these settings, the update is in bytes from left to right, setting after each row (which is really a column) the page address to 0 and the column address to the next column. Each byte is written LSB to MSB in the same direction (that is, 0xf1 display from left to right one pixel on, 3 pixels off, 4 pixels on).

That’s quite enough to start writing graphic functions and text.