diff options
| -rw-r--r-- | CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/pico1541.c | 25 | ||||
| -rw-r--r-- | src/ssd1306.c | 75 | ||||
| -rw-r--r-- | src/ssd1306.h | 138 |
4 files changed, 243 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d14c44..7d35aa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,14 @@ project(pico1541) pico_sdk_init() add_executable(pico1541 + src/ssd1306.c src/pico1541.c ) +target_include_directories(pico1541 PRIVATE + src +) target_link_libraries(pico1541 + hardware_i2c pico_stdlib ) 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 |
