-
Notifications
You must be signed in to change notification settings - Fork 972
Add example spi/ssd1309_spi #731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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) |
| 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 | ||
| |=== |
| 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 | ||
| }; | ||
| 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) | ||
mjcross marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // | ||
| // `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)) { | ||
lurch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 | ||
mjcross marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This example doesn't seem to actually use stdio?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.