summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxwell Beck <max@rastertail.net>2026-10-26 20:32:56 -0500
committerMaxwell Beck <max@rastertail.net>2026-10-26 20:32:56 -0500
commit2a590986e1ee2aca934181e0ced958c520e97332 (patch)
treeb731744bc2c26b44646d65a302c489253ba7412e /src
parentaa9936120a933f14d52c213a29c6b2b87ec90201 (diff)
ssd1306 driver sketch
Diffstat (limited to 'src')
-rw-r--r--src/pico1541.c25
-rw-r--r--src/ssd1306.c75
-rw-r--r--src/ssd1306.h138
3 files changed, 238 insertions, 0 deletions
diff --git a/src/pico1541.c b/src/pico1541.c
index e69de29..2c7ad35 100644
--- a/src/pico1541.c
+++ b/src/pico1541.c
@@ -0,0 +1,25 @@
+#include <hardware/i2c.h>
+#include <hardware/gpio.h>
+#include <pico/error.h>
+#include <pico/time.h>
+
+#include "ssd1306.h"
+
+int main() {
+ gpio_init(20);
+ gpio_init(21);
+
+ gpio_set_function(20, GPIO_FUNC_I2C);
+ gpio_set_function(21, GPIO_FUNC_I2C);
+
+ gpio_set_pulls(20, true, false);
+ gpio_set_pulls(21, true, false);
+
+ i2c_init(i2c0, 400 * 1000);
+
+ ssd1306_t display;
+ ssd_init(&display, i2c0, 0x3C);
+ ssd_power_on(&display);
+
+ return 0;
+}
diff --git a/src/ssd1306.c b/src/ssd1306.c
new file mode 100644
index 0000000..c766d5f
--- /dev/null
+++ b/src/ssd1306.c
@@ -0,0 +1,75 @@
+#include "ssd1306.h"
+
+#include <hardware/gpio.h>
+#include <pico/error.h>
+
+int ssd_init(ssd1306_t *display, i2c_inst_t *i2c, uint8_t addr) {
+ display->i2c = i2c;
+ display->addr = addr;
+ display->len = 0;
+
+ return PICO_OK;
+}
+
+int ssd_push(ssd1306_t *display, uint8_t byte) {
+ if (display->len < SSD1306_BUFFER_LEN) {
+ display->buf[display->len] = byte;
+ display->len++;
+ return PICO_OK;
+ } else {
+ return PICO_ERROR_BUFFER_TOO_SMALL;
+ }
+}
+
+int ssd_send(ssd1306_t *display) {
+ int ret = i2c_write_blocking(
+ display->i2c,
+ display->addr,
+ display->buf,
+ display->len,
+ false
+ );
+ display->len = 0;
+
+ return ret;
+}
+
+int ssd_power_on(ssd1306_t *display) {
+ ssd_push(display, SSD1306_CTRL_CMD);
+
+ ssd_push(display, SSD1306_DISPLAY_OFF);
+
+ ssd_push(display, SSD1306_SET_CLOCK_DIV);
+ ssd_push(display, 0x80);
+
+ ssd_push(display, SSD1306_SET_MULTIPLEX);
+ ssd_push(display, 0x1f);
+
+ ssd_push(display, SSD1306_START_LINE(0));
+
+ ssd_push(display, SSD1306_SET_CHARGE_PUMP);
+ ssd_push(display, SSD1306_CHARGE_PUMP_ON);
+
+ ssd_push(display, SSD1306_SET_MEMORY_MODE);
+ ssd_push(display, SSD1306_MEM_MODE_HORIZONTAL);
+
+ ssd_push(display, SSD1306_SEG_REMAP_COL127);
+ ssd_push(display, SSD1306_COM_SCAN_REMAP);
+ ssd_push(display, SSD1306_COM_PINS_SEQUENTIAL);
+
+ ssd_push(display, SSD1306_SET_CONTRAST);
+ ssd_push(display, 0xcf);
+
+ ssd_push(display, SSD1306_SET_PRECHARGE);
+ ssd_push(display, 0xf1);
+
+ ssd_push(display, SSD1306_SET_VCOMH);
+ ssd_push(display, SSD1306_VCOMH_0_77);
+
+ ssd_push(display, SSD1306_ENTIRE_DISPLAY_RESUME);
+ ssd_push(display, SSD1306_NORMAL_DISPLAY);
+ ssd_push(display, SSD1306_SCROLL_DEACTIVATE);
+ ssd_push(display, SSD1306_DISPLAY_ON);
+
+ return ssd_send(display);
+}
diff --git a/src/ssd1306.h b/src/ssd1306.h
new file mode 100644
index 0000000..7f6755e
--- /dev/null
+++ b/src/ssd1306.h
@@ -0,0 +1,138 @@
+#ifndef PICO1541_SSD1306_H
+#define PICO1541_SSD1306_H
+
+#include <stdint.h>
+#include <hardware/i2c.h>
+
+/* ------------------------------------------------------------------ */
+/* I2C addressing and control bytes */
+/* ------------------------------------------------------------------ */
+#define SSD1306_I2C_ADDR 0x3C /* 7-bit, SA0 = 0 (D/C# pin low) */
+#define SSD1306_I2C_ADDR_ALT 0x3D /* 7-bit, SA0 = 1 */
+
+#define SSD1306_CTRL_CMD 0x00 /* following bytes are commands */
+#define SSD1306_CTRL_DATA 0x40 /* following bytes are GDDRAM */
+#define SSD1306_CTRL_CMD_SINGLE 0x80 /* one command byte follows */
+#define SSD1306_CTRL_DATA_SINGLE 0xC0 /* one data byte follows */
+
+/* ------------------------------------------------------------------ */
+/* Fundamental commands */
+/* ------------------------------------------------------------------ */
+#define SSD1306_SET_CONTRAST 0x81 /* 1 arg: 0x00..0xFF, reset 0x7F */
+#define SSD1306_ENTIRE_DISPLAY_RESUME 0xA4 /* output follows RAM (reset) */
+#define SSD1306_ENTIRE_DISPLAY_ON 0xA5 /* all pixels on, ignores RAM */
+#define SSD1306_NORMAL_DISPLAY 0xA6 /* 1 = pixel on */
+#define SSD1306_INVERSE_DISPLAY 0xA7 /* 1 = pixel off */
+#define SSD1306_DISPLAY_OFF 0xAE /* sleep mode */
+#define SSD1306_DISPLAY_ON 0xAF
+
+/* ------------------------------------------------------------------ */
+/* Scrolling commands */
+/* ------------------------------------------------------------------ */
+#define SSD1306_SCROLL_RIGHT 0x26 /* 6 args: continuous, right */
+#define SSD1306_SCROLL_LEFT 0x27 /* 6 args: continuous, left */
+#define SSD1306_SCROLL_VERT_RIGHT 0x29 /* 5 args: vertical + horizontal */
+#define SSD1306_SCROLL_VERT_LEFT 0x2A /* 5 args: vertical + horizontal */
+#define SSD1306_SCROLL_DEACTIVATE 0x2E
+#define SSD1306_SCROLL_ACTIVATE 0x2F
+#define SSD1306_SET_VERT_SCROLL_AREA 0xA3 /* 2 args: top fixed, scroll rows */
+
+/* Argument layout, 0x26 / 0x27:
+ * dummy 0x00, start page, frame interval, end page, dummy 0x00, dummy 0xFF
+ * Argument layout, 0x29 / 0x2A:
+ * dummy 0x00, start page, frame interval, end page, vertical offset
+ */
+
+/* ------------------------------------------------------------------ */
+/* Addressing commands */
+/* ------------------------------------------------------------------ */
+#define SSD1306_SET_MEMORY_MODE 0x20 /* 1 arg, see MODE_* below */
+#define SSD1306_SET_COL_ADDR 0x21 /* 2 args: column start, end */
+#define SSD1306_SET_PAGE_ADDR 0x22 /* 2 args: page start, end */
+
+/* Page addressing mode only: start address is split across two commands. */
+#define SSD1306_SET_LOWER_COL_START 0x00 /* 0x00..0x0F = low nibble */
+#define SSD1306_SET_HIGHER_COL_START 0x10 /* 0x10..0x1F = high nibble */
+#define SSD1306_SET_PAGE_START 0xB0 /* 0xB0..0xB7 = page 0..7 */
+
+/* ------------------------------------------------------------------ */
+/* Hardware configuration */
+/* ------------------------------------------------------------------ */
+#define SSD1306_SET_START_LINE 0x40 /* 0x40..0x7F, line 0..63 */
+#define SSD1306_SEG_REMAP_COL0 0xA0 /* column 0 -> SEG0 (reset) */
+#define SSD1306_SEG_REMAP_COL127 0xA1 /* column 127 -> SEG0 (mirror X) */
+#define SSD1306_SET_MULTIPLEX 0xA8 /* 1 arg: N+1 rows, 0x0F..0x3F */
+#define SSD1306_COM_SCAN_NORMAL 0xC0 /* COM0 -> COM[N-1] */
+#define SSD1306_COM_SCAN_REMAP 0xC8 /* COM[N-1] -> COM0 (mirror Y) */
+#define SSD1306_SET_DISPLAY_OFFSET 0xD3 /* 1 arg: shift 0..63 by COM */
+#define SSD1306_SET_COM_PINS 0xDA /* 1 arg, see COM_PINS_* below */
+
+/* ------------------------------------------------------------------ */
+/* Timing and driving scheme */
+/* ------------------------------------------------------------------ */
+#define SSD1306_SET_CLOCK_DIV 0xD5 /* 1 arg: lo = div-1, hi = freq */
+ /* reset 0x80 */
+#define SSD1306_SET_PRECHARGE 0xD9 /* 1 arg: phase2/phase1 */
+ /* reset 0x22, 0xF1 for ext VCC*/
+#define SSD1306_SET_VCOMH 0xDB /* 1 arg, see VCOMH_* below */
+#define SSD1306_NOP 0xE3
+#define SSD1306_SET_CHARGE_PUMP 0x8D /* 1 arg: 0x14 on, 0x10 off */
+ /* (SSD1306B / modules only) */
+
+/* ------------------------------------------------------------------ */
+/* Parameter constants */
+/* ------------------------------------------------------------------ */
+#define SSD1306_MEM_MODE_HORIZONTAL 0x00
+#define SSD1306_MEM_MODE_VERTICAL 0x01
+#define SSD1306_MEM_MODE_PAGE 0x02 /* reset default */
+
+#define SSD1306_COM_PINS_SEQUENTIAL 0x02
+#define SSD1306_COM_PINS_ALTERNATIVE 0x12 /* 128x64 default */
+#define SSD1306_COM_PINS_SEQ_REMAP 0x22
+#define SSD1306_COM_PINS_ALT_REMAP 0x32
+
+#define SSD1306_VCOMH_0_65 0x00 /* ~0.65 x VCC */
+#define SSD1306_VCOMH_0_77 0x20 /* ~0.77 x VCC, reset */
+#define SSD1306_VCOMH_0_83 0x30 /* ~0.83 x VCC */
+
+#define SSD1306_CHARGE_PUMP_ON 0x14
+#define SSD1306_CHARGE_PUMP_OFF 0x10
+
+#define SSD1306_SCROLL_5_FRAMES 0x00 /* 5 frames per step */
+#define SSD1306_SCROLL_64_FRAMES 0x01
+#define SSD1306_SCROLL_128_FRAMES 0x02
+#define SSD1306_SCROLL_256_FRAMES 0x03
+#define SSD1306_SCROLL_3_FRAMES 0x04
+#define SSD1306_SCROLL_4_FRAMES 0x05
+#define SSD1306_SCROLL_25_FRAMES 0x06
+#define SSD1306_SCROLL_2_FRAMES 0x07
+
+/* ------------------------------------------------------------------ */
+/* Helper macros for building command bytes */
+/* ------------------------------------------------------------------ */
+#define SSD1306_COL_ADDR_LO(c) (uint8_t)(SSD1306_SET_LOWER_COL_START | ((c) & 0x0F))
+#define SSD1306_COL_ADDR_HI(c) (uint8_t)(SSD1306_SET_HIGHER_COL_START | (((c) >> 4) & 0x0F))
+#define SSD1306_PAGE(c) (uint8_t)(SSD1306_SET_PAGE_START | ((c) & 0x07))
+#define SSD1306_START_LINE(l) (uint8_t)(SSD1306_SET_START_LINE | ((l) & 0x3F))
+#define SSD1306_CONTRAST(v) (uint8_t)(v)
+#define SSD1306_MUX_RATIO(rows) (uint8_t)((rows) - 1) /* rows = 16..64 */
+#define SSD1306_CLOCK(div, freq) (uint8_t)((((freq) & 0x0F) << 4) | (((div) - 1) & 0x0F))
+
+#define SSD1306_BUFFER_LEN 256
+
+typedef struct ssd1306 {
+ i2c_inst_t *i2c;
+ uint8_t addr;
+
+ uint8_t buf[SSD1306_BUFFER_LEN];
+ size_t len;
+} ssd1306_t;
+
+int ssd_init(ssd1306_t *display, i2c_inst_t *i2c, uint8_t addr);
+
+int ssd_push(ssd1306_t *display, uint8_t byte);
+int ssd_send(ssd1306_t *display);
+
+int ssd_power_on(ssd1306_t *display);
+
+#endif