diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c77a991..9226a6e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(main main.cc + framebuffer.cc ) target_link_libraries(main pico_stdlib hardware_i2c hardware_uart hardware_irq diff --git a/src/framebuffer.cc b/src/framebuffer.cc new file mode 100644 index 0000000..f91fd1a --- /dev/null +++ b/src/framebuffer.cc @@ -0,0 +1,61 @@ +#include "framebuffer.hh" + +#include +#include +#include + +#define INVALID_CH (0xff8181ff) + +static uint32_t charset[128] = { + INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, + INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, + INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, + INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, + INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, + INVALID_CH, INVALID_CH, 0x00000000, 0x00bf0000, 0x03000300, 0x5ce85ce8, + 0x24ff4a24, 0xc3cc33c3, 0x4834a458, 0x00030000, 0x81423c00, 0x3c428100, + 0x09060609, 0x00083e08, 0x00408000, 0x08080808, 0x00030300, 0x030c30c0, + 0x7E81817E, 0x80FF8280, 0x868991E2, 0x76898162, 0xff121418, 0x7189894f, + 0x7289897e, 0x0709f101, 0x76898976, 0x7e898946, 0x00666600, 0x00264600, + 0x82442810, 0x28282828, 0x10284482, 0x0609b102, INVALID_CH, 0xfe1111fe, + 0x768989ff, 0x6681817e, 0x7e8181ff, 0x818989ff, 0x010909ff, 0x7691817e, + 0xff0808ff, 0x81ffff81, 0x01ff8141, 0xe31408ff, 0x808080ff, 0xff0606ff, + 0xff0804ff, 0x7E81817E, 0x060909ff, 0xbe41413e, 0xee1111ff, 0x66918966, + 0x01ff0101, 0x7f80807f, 0x1fe0e01f, 0xff6060ff, 0xe71818e7, 0x7f90904f, + 0x878991e1, 0x818181ff, 0xc0300c03, 0xff818181, 0x0c03030c, 0x80808080, + 0x00060600, 0xf0a8a840, 0x609090fe, 0x50888870, 0xfe909060, 0x30a8a870, + 0x000212fc, 0x60a48478, 0xe01010fe, 0x00f40000, 0x00748040, 0x885020fe, + 0x80807e02, 0xf03030f0, 0xe01010f8, 0x60909060, 0x182424f8, 0xb0484830, + 0x000808f0, 0x48a49448, 0x0080907c, 0x70808070, 0x70c0c070, 0x70e0e070, + 0x90606090, 0x7c90904c, 0x90b0d090, 0x81816618, 0x00ff0000, 0x81816618, + 0x10081008, INVALID_CH}; + +void Framebuffer::FrameBufferBase::fill(std::uint8_t *data, std::size_t len, std::uint8_t val) { + for (size_t i = 0; i < len; i++) { + data[i] = val; + } +} + +void Framebuffer::FrameBufferBase::putc(std::uint8_t *data, std::size_t row_height, std::size_t len, std::size_t x, std::size_t y, char c) { + assert(c < 128); + + uint32_t char_to_write = charset[c]; + + size_t idx = x + (row_height * y); + + data[idx] = char_to_write & 0xFF; + if (idx + 1 >= len) return; + data[idx + 1] = (char_to_write >> 8) & 0xFF; + if (idx + 2 >= len) return; + data[idx + 2] = (char_to_write >> 16) & 0xFF; + if (idx + 3 >= len) return; + data[idx + 3] = (char_to_write >> 24) & 0xFF; +} + +void Framebuffer::FrameBufferBase::puts(std::uint8_t *data, std::size_t row_height, std::size_t len, std::size_t x, std::size_t y, const char *s) { + while (*s != '\0') { + putc(data, row_height, len, x, y, *s); + x+=4; // We can just increment x, as the buffer is linear. It also checks for bounds. + s++; + } +} diff --git a/src/framebuffer.hh b/src/framebuffer.hh new file mode 100644 index 0000000..199c645 --- /dev/null +++ b/src/framebuffer.hh @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include + +namespace Framebuffer { + class FramebufferListener { + public: + void update(std::uint8_t column, std::uint8_t row, const std::uint8_t *data, std::uint8_t len); + }; + + class FrameBufferBase { + public: + void fill(std::uint8_t *data, std::size_t len, std::uint8_t val); + void putc(std::uint8_t *data, std::size_t row_height, std::size_t len, std::size_t x, std::size_t y, char c); + void puts(std::uint8_t *data, std::size_t row_height, std::size_t len, std::size_t x, std::size_t y, const char *s); + std::uint8_t *data(); + std::size_t size(); + }; + + template class Framebuffer: private FrameBufferBase { + private: + static constexpr size_t ROW_HEIGHT = 8; + static constexpr size_t ROWS = (FB_HEIGHT/ROW_HEIGHT); + static constexpr size_t SIZE = FB_WIDTH * ROWS; + std::array m_buffer; + FramebufferListener &m_listener; + + void update_listener() { + m_listener.update(0, 0, m_buffer.data(), m_buffer.size()); + } + + public: + Framebuffer(FramebufferListener &listener) + : m_listener(listener) { + } + + void fill(uint8_t val) { FrameBufferBase::fill(m_buffer.data(), m_buffer.size(), val); } + void putc(size_t x, size_t y, char c) { FrameBufferBase::putc(m_buffer.data(), ROW_HEIGHT, m_buffer.size(), x, y, c); } + void puts(size_t x, size_t y, const char *s) { FrameBufferBase::putc(m_buffer.data(), ROW_HEIGHT, m_buffer.size(), x, y, s); } + uint8_t *data() { return m_buffer.data(); } + size_t size() { return m_buffer.size(); } + }; +} + + + diff --git a/src/main.cc b/src/main.cc index 8ecad84..78ecc74 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,4 +1,5 @@ #include "main.hh" +#include "framebuffer.hh" #include "hardware/gpio.h" #include "hardware/i2c.h" @@ -15,38 +16,6 @@ static bool failure = false; -#define INVALID_CH (0xff8181ff) - -static uint32_t charset[127] = { - INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, - INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, - INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, - INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, INVALID_CH, 0x00000000, // this is space - // !, ", #, $, %, &, ', (, - 0x00bf0000, 0x03000300, 0x5ce85ce8, 0x24ff4a24, 0xc3cc33c3, 0x4834a458, 0x00030000, 0x81423c00, - // ), *, +, ,, -, ., /, 0 - 0x3c428100, 0x09060609, 0x00083e08, 0x00408000, 0x08080808, 0x00030300, 0x030c30c0, 0x7E81817E, - // 1, 2, 3, 4, 5, 6, 7, 8 - 0x80FF8280, 0x868991E2, 0x76898162, 0xff121418, 0x7189894f, 0x7289897e, 0x0709f101, 0x76898976, - // 9, :, ;, <, =, >, ?, @ - 0x7e898946, 0x00666600, 0x00264600, 0x82442810, 0x28282828, 0x10284482, 0x0609b102, INVALID_CH, - // A, B, C, D, E, F, G, H - 0xfe1111fe, 0x768989ff, 0x6681817e, 0x7e8181ff, 0x818989ff, 0x010909ff, 0x7691817e, 0xff0808ff, - // I, J, K, L, M, N, O, P - 0x81ffff81, 0x01ff8141, 0xe31408ff, 0x808080ff, 0xff0606ff, 0xff0804ff, 0x7E81817E, 0x060909ff, - // Q, R, S, T, U, V, W, X - 0xbe41413e, 0xee1111ff, 0x66918966, 0x01ff0101, 0x7f80807f, 0x1fe0e01f, 0xff6060ff, 0xe71818e7, - // Y, Z, [, \, ], ^, _, * - 0x7f90904f, 0x878991e1, 0x818181ff, 0xc0300c03, 0xff818181, 0x0c03030c, 0x80808080, 0x00060600, - // a, b, c, d, e, f, g, h - 0xf0a8a840, 0x609090fe, 0x50888870, 0xfe909060, 0x30a8a870, 0x000212fc, 0x60a48478, 0xe01010fe, - // i, j, k, l, m, n, o, p - 0x00f40000, 0x00748040, 0x885020fe, 0x80807e02, 0xf03030f0, 0xe01010f8, 0x60909060, 0x182424f8, - // q, r, s, t, u, v, w, x - 0xb0484830, 0x000808f0, 0x48a49448, 0x0080907c, 0x70808070, 0x70c0c070, 0x70e0e070, 0x90606090, - // y, z, {, |, }, ~, DEL - 0x7c90904c, 0x90b0d090, 0x81816618, 0x00ff0000, 0x81816618, 0x10081008, INVALID_CH, -}; static uint8_t encoder_pos = 0; @@ -97,11 +66,11 @@ int main(void) { busy_wait_ms(100); - disp.fill(0); + disp.m_frame_buffer.fill(0); while (true) { - disp.fill(0); - disp.put_char(4, 0, (encoder_pos & 0xF) >= 0xA ? (encoder_pos&0xf) - 0xA + 'A':(encoder_pos&0xf)+'0'); - disp.put_char(0, 0, ((encoder_pos>>4) & 0xF) >= 0xA? ((encoder_pos>>4) & 0xf) - 0xA + 'A':((encoder_pos>>4)&0xf)+'0'); + disp.m_frame_buffer.fill(0); + disp.m_frame_buffer.putc(4, 0, (encoder_pos & 0xF) >= 0xA ? (encoder_pos&0xf) - 0xA + 'A':(encoder_pos&0xf)+'0'); + disp.m_frame_buffer.putc(0, 0, ((encoder_pos>>4) & 0xF) >= 0xA? ((encoder_pos>>4) & 0xf) - 0xA + 'A':((encoder_pos>>4)&0xf)+'0'); disp.flush(); busy_wait_ms(50); } @@ -120,7 +89,8 @@ static void write_cmd(uint8_t cmd) { } } -ssd1306::ssd1306() { +ssd1306::ssd1306() + : m_frame_buffer(*this) { write_cmd(SET_DISP | 0x00); // Off write_cmd(SET_MUX_RATIO); // The height @@ -165,16 +135,6 @@ ssd1306::ssd1306() { } } -void ssd1306::test() { - memcpy(m_display_buffer+1, &charset['i'-1], 4*16); -} - -void ssd1306::fill(uint8_t val) { - for(size_t i = 1; i < sizeof(m_display_buffer); i++) { - m_display_buffer[i] = val; - } -} - void ssd1306::set_coordinates(uint8_t start_x, uint8_t end_x, uint8_t start_y, uint8_t end_y){ write_cmd(SET_COL_ADDR); write_cmd(start_x); @@ -189,7 +149,7 @@ void ssd1306::flush() { write_cmd(SET_MEM_ADDR); write_cmd(0x00); // Horizontal - if (i2c_write_timeout_us(i2c0, ADDR, m_display_buffer, sizeof(m_display_buffer), false, 1000000) < 0) { + if (i2c_write_timeout_us(i2c0, ADDR, m_frame_buffer.data(), m_frame_buffer.size(), false, 1000000) < 0) { failure = true; printf("data fail\n"); } else { @@ -197,19 +157,10 @@ void ssd1306::flush() { } } -void ssd1306::put_char(uint8_t row, uint8_t col, char c) { - memcpy(m_display_buffer+(1+(row + (128*col))), &charset[c-1], 4); -} - -void ssd1306::puts(uint8_t row, uint8_t col, const char *str) { - // Start index - size_t buf_idx = 1+(row + (128*col)); - - while(*str != '\0') { - memcpy(m_display_buffer+buf_idx, &charset[*str-1], 4); - - buf_idx+=4; - str++; - } - +void ssd1306::update(size_t x, size_t y, uint8_t *data, size_t len) { + set_coordinates(x, 128, y, 7); + write_cmd(SET_MEM_ADDR); + write_cmd(0x00); // Horizontal + + // TODO: Something with nostop and then spitting raw bytes? might work... } diff --git a/src/main.hh b/src/main.hh index 55e580e..2fadd57 100644 --- a/src/main.hh +++ b/src/main.hh @@ -2,6 +2,7 @@ #include #include +#include "framebuffer.hh" #define SET_CONTRAST (0x81) #define SET_ENTIRE_ON (0xA4) @@ -24,23 +25,16 @@ #define WIDTH 128 #define HEIGHT 64 -class ssd1306 { -private: - uint8_t m_display_buffer[WIDTH*(HEIGHT/8)+1] = {0x40}; +class ssd1306 : public Framebuffer::FramebufferListener { public: + Framebuffer::Framebuffer m_frame_buffer; + ssd1306(); void poweron(); - void test(); - - void fill(uint8_t val); - void flush(); - void set_coordinates(uint8_t start_x, uint8_t end_x, uint8_t start_y, uint8_t end_y); - void put_char(uint8_t row, uint8_t col, char c); - - void puts(uint8_t row, uint8_t col, const char* str); + void update(size_t x, size_t y, uint8_t *data, size_t len); };