Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ App|Description
[spi_master_slave](spi/spi_master_slave) | Demonstrate SPI communication as master and slave.
[max7219_8x7seg_spi](spi/max7219_8x7seg_spi) | Attaching a Max7219 driving an 8 digit 7 segment display via SPI.
[max7219_32x8_spi](spi/max7219_32x8_spi) | Attaching a Max7219 driving an 32x8 LED display via SPI.
[ssd1309_spi](spi/ssd1309_spi) | Display text on a SSD1309-driven OLED display via SPI.

### Status LED

Expand Down
1 change: 1 addition & 0 deletions spi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ if (TARGET hardware_spi)
add_subdirectory_exclude_platforms(spi_dma)
add_subdirectory_exclude_platforms(spi_master_slave)
add_subdirectory_exclude_platforms(spi_flash)
add_subdirectory_exclude_platforms(ssd1309_spi)
add_subdirectory_exclude_platforms(max7219_32x8_spi)
add_subdirectory_exclude_platforms(max7219_8x7seg_spi)
else()
Expand Down
12 changes: 12 additions & 0 deletions spi/ssd1309_spi/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_executable(ssd1309_spi
ssd1309_spi.c
)

# pull in common dependencies and additional spi hardware support
target_link_libraries(ssd1309_spi pico_stdlib hardware_spi)

# create map/bin/hex file etc.
pico_add_extra_outputs(ssd1309_spi)

# add url via pico_set_program_url
example_auto_set_url(ssd1309_spi)
59 changes: 59 additions & 0 deletions spi/ssd1309_spi/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
= Connecting an OLED display (SSD1309) via SPI

An example showing simple use of a small monochrome OLED display based on the widely-used SSD1309 controller via the SPI interface. It should also work on compatible displays such as those based on the SSD1306.

Such displays typically support either I2C or SPI. For this example you will need one configured for SPI.

The interface uses one of the Pico's onboard SPI peripherals and two extra GPIO pins. The code simply initialises the display and shows some text in a basic font: it aims to be easy-to-follow rather than a full-featured driver.

For details of the commands supported by the SSD1309 controller and its addressing modes see the manufacturer's datasheet: https://www.hpinfotech.ro/SSD1309.pdf.

Note that the SPI interface for the SSD1309 is transmit-only.


== Wiring information

Wiring up the device requires seven jumpers as follows:

* Display CS (chip select) -> Pico GP17 (SPI0 CSn), pin 22
* Display DC (data/command) -> Pico GP20, pin 26
* Display RES (reset) -> Pico GP21, pin 27
* Display SDA (MOSI) -> Pico GP19 (SPI0 TX), pin 25
* Display SCLK (SPI clock) -> Pico GP18 (SPI0 SCK), pin 24
* Display VDD (3.3v) -> Pico 3V3_OUT, pin 36
* Display VSS (0v, gnd) -> Pico GND, pin 23

The example uses SPI device 0 and powers the display from the Pico 3.3v output. If you power the display from an external supply then ensure that the Pico's logic pins are not exposed to any voltage higher than 3.3v.

[NOTE]
======
The pins on your board may be labelled slightly differently to the list above. Check the documentation for your display.

You can change the code to use different pins if you like, but the ones for CSn, TX and SCK must match your SPI device: see the GPIO Function Select Table in the datasheet.
======


[[wiring_diagram.png]]
[pdfwidth=75%]
.Wiring Diagram for SSD1309 with SPI interface
image::wiring_diagram.png[]

== List of Files

CMakeLists.txt:: A file to configure the CMake build system for the example.
ssd1309_spi.c:: The example code.
ssd1309_font.h:: A basic 8x8 bit font table used by the example.


== Bill of Materials

.A list of materials required for the example
[[SSD1309-bom-table]]
[cols=3]
|===
| *Item* | *Quantity* | Details
| Breadboard | 1 | generic part
| Raspberry Pi Pico or Pico 2 | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
| SSD1309-based OLED display panel with SPI interface| 1 | generic part
| M/F Jumper wires (assorted colours) | 7 | generic part
|===
55 changes: 55 additions & 0 deletions spi/ssd1309_spi/ssd1309_font.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

// Indices of different parts of the font table, to help
// when converting different types of characters
#define FONT_INDEX_SPACE 0
#define FONT_INDEX_A 1
#define FONT_INDEX_0 27


// Vertical bitmaps, A-Z, 0-9. Each is 8 pixels high and wide
// These are defined vertically to make them quick to copy to FB

static uint8_t font[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Nothing
0x78, 0x14, 0x12, 0x11, 0x12, 0x14, 0x78, 0x00, //A
0x7f, 0x49, 0x49, 0x49, 0x49, 0x49, 0x7e, 0x00, //B
0x7e, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, //C
0x7f, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7e, 0x00, //D
0x7f, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x00, //E
0x7f, 0x09, 0x09, 0x09, 0x09, 0x01, 0x01, 0x00, //F
0x7e, 0x41, 0x41, 0x41, 0x51, 0x51, 0x72, 0x00, //G
0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x7f, 0x00, //H
0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, //I
0x00, 0x21, 0x41, 0x41, 0x3f, 0x01, 0x01, 0x00, //J
0x00, 0x7f, 0x08, 0x08, 0x14, 0x22, 0x41, 0x00, //K
0x00, 0x7f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, //L
0x7f, 0x02, 0x04, 0x08, 0x04, 0x02, 0x7f, 0x00, //M
0x7f, 0x02, 0x04, 0x08, 0x10, 0x20, 0x7f, 0x00, //N
0x3e, 0x41, 0x41, 0x41, 0x41, 0x41, 0x3e, 0x00, //O
0x7f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x00, //P
0x3e, 0x41, 0x41, 0x49, 0x51, 0x61, 0x7e, 0x00, //Q
0x7f, 0x11, 0x11, 0x11, 0x31, 0x51, 0x0e, 0x00, //R
0x46, 0x49, 0x49, 0x49, 0x49, 0x30, 0x00, 0x00, //S
0x01, 0x01, 0x01, 0x7f, 0x01, 0x01, 0x01, 0x00, //T
0x3f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x3f, 0x00, //U
0x0f, 0x10, 0x20, 0x40, 0x20, 0x10, 0x0f, 0x00, //V
0x7f, 0x20, 0x10, 0x08, 0x10, 0x20, 0x7f, 0x00, //W
0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x00, //X
0x01, 0x02, 0x04, 0x78, 0x04, 0x02, 0x01, 0x00, //Y
0x41, 0x61, 0x59, 0x45, 0x43, 0x41, 0x00, 0x00, //Z
0x00, 0x3e, 0x41, 0x49, 0x41, 0x3e, 0x00, 0x00, //0
0x00, 0x00, 0x42, 0x7f, 0x40, 0x00, 0x00, 0x00, //1
0x00, 0x62, 0x51, 0x49, 0x49, 0x46, 0x00, 0x00, //2
0x00, 0x49, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00, //3
0x00, 0x3f, 0x20, 0x78, 0x20, 0x20, 0x00, 0x00, //4
0x4f, 0x49, 0x49, 0x49, 0x49, 0x30, 0x00, 0x00, //5
0x3e, 0x49, 0x49, 0x49, 0x49, 0x30, 0x00, 0x00, //6
0x01, 0x01, 0x61, 0x11, 0x0d, 0x03, 0x00, 0x00, //7
0x00, 0x36, 0x49, 0x49, 0x49, 0x49, 0x36, 0x00, //8
0x00, 0x06, 0x09, 0x09, 0x09, 0x7e, 0x00, 0x00 //9
};
152 changes: 152 additions & 0 deletions spi/ssd1309_spi/ssd1309_spi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* Copyright (c) 2025 mjcross
*
* SPDX-License-Identifier: BSD-3-Clause
**/

// A simple example to display text on an ssd1309-based OLED display with
// an SPI interface. It should also work on a ssd1306 device (not tested).
//
// To understand the commands and addressing modes for the display refer to
// the manufacturer's datasheet at https://www.hpinfotech.ro/SSD1309.pdf


#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "ctype.h"
#include "ssd1309_font.h"


// We will use the spi0 peripheral and the following GPIO pins (see the
// GPIO function select table in the RPi Pico datasheet).
#define SPI_DEVICE spi0
#define PIN_CS 17 // chip select (active low)
#define PIN_SCK 18 // SPI clock
#define PIN_MOSI 19 // SPI data transmit (MOSI)
#define PIN_DC 20 // data/command mode (low for command)
#define PIN_R 21 // reset (active low)

// ssd1309 accepts a maximum SPI clock rate of 10 Mbit/sec, but for
// this simple example we don't need to go that fast.
#define SPI_BITRATE 1 * 1000 * 1000

// dimensions of the display
#define NUM_X_PIXELS 128
#define NUM_Y_PIXELS 64

// modes for the data/command pin
#define DC_COMMAND_MODE 0
#define DC_DATA_MODE 1


// send a command byte to the display
void send_command(uint8_t cmd_byte) {
gpio_put(PIN_DC, DC_COMMAND_MODE);
spi_write_blocking(SPI_DEVICE, &cmd_byte, 1);
}

// send a data byte to the display
void send_data(uint8_t data_byte) {
gpio_put(PIN_DC, DC_DATA_MODE);
spi_write_blocking(SPI_DEVICE, &data_byte, 1);
}

// set the text cursor position (row, col)
//
// `row` is the text row, from 0 (top) to (NUM_Y_PIXELS / 8) - 1
// `col` is the text column, from 0 (left) to (NUM_X_PIXELS / 8) - 1
void set_cursor_pos(uint text_row, uint text_col) {
send_command(0xb0 + (text_row & 0x07)); // set the text row ('page') start address
uint col = text_col * 8;
send_command(col & 0x0f); // set the column start address (low nibble)
send_command(0x10 + ((col & 0xf0) >> 4)); // set the column start address (high nibble)
}

// display a single text character at the cursor position
void display_char(char c) {
uint font_index = FONT_INDEX_SPACE; // default (space)
c = toupper(c);
if (isalpha(c)) {
font_index = c - 'A' + FONT_INDEX_A; // a-z and A-Z
} else if (isdigit(c)) {
font_index = c - '0' + FONT_INDEX_0; // 0-9
}
gpio_put(PIN_DC, DC_DATA_MODE);
spi_write_blocking(SPI_DEVICE, &(font[font_index * 8]), 8);
}

// display a null-terminated string at the cursor position
void display_string(const char *str) {
while(*str) {
display_char(*str);
str += 1;
}
}

// clear the display
void display_clear() {
for (int text_row = 0; text_row < (NUM_Y_PIXELS / 8); text_row += 1) {
set_cursor_pos(text_row, 0);
for (int col = 0; col < NUM_X_PIXELS; col += 1) {
send_data(0x00);
}
}
set_cursor_pos(0, 0);
}

// initialise the SPI interface and reset the display
void display_init() {
// ssd1309 communicates using Motorola SPI mode 0
spi_init(SPI_DEVICE, SPI_BITRATE);
spi_set_format(SPI_DEVICE, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); // mode 0

// configure and initialise our interface pins
gpio_set_function(PIN_CS, GPIO_FUNC_SPI);
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
gpio_init(PIN_R);
gpio_set_dir(PIN_R, GPIO_OUT);
gpio_init(PIN_DC);
gpio_set_dir(PIN_DC, GPIO_OUT);

// send an active-low reset pulse to the display
gpio_put(PIN_R, 0);
sleep_ms(1);
gpio_put(PIN_R, 1);

// after a short pause, send the 'display on' command
sleep_ms(1);
send_command(0xaf);
display_clear();
}


int main() {
stdio_init_all();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example doesn't seem to actually use stdio?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct - but I thought it might be wise to enable it in case of runtime error messages... What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave this one up to @peterharperuk 🙂


// initialise the interface and reset the display
display_init();

// Show some text
//
// Note: in the display's default addressing mode, over-length
// strings will just wrap on the same row (see the datasheet)

set_cursor_pos(0, 0);
display_string("abcdefghijklmnop");

set_cursor_pos(1, 3);
display_string("qrstuvwxyz");

// show a steadily increasing counter
uint count = 0;
char count_str[10];
while(true) {
snprintf(count_str, sizeof(count_str), "%u", count);
set_cursor_pos(3, 7);
display_string(count_str);
count += 1;
sleep_ms(250);
}
}
Binary file added spi/ssd1309_spi/wiring_diagram.fzz
Binary file not shown.
Binary file added spi/ssd1309_spi/wiring_diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.