Initial commit

This commit is contained in:
shylie 2025-06-04 14:41:08 -04:00
commit 23c1df474c
5 changed files with 247 additions and 0 deletions

12
CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.13)
add_library(picoled
include/picoled.h
include/picoled/SSD1306.h
src/picoled.cpp
src/SSD1306.cpp
)
target_link_libraries(picoled PUBLIC hardware_i2c pico_stdlib)
target_include_directories(picoled PUBLIC include)

59
include/picoled.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef PICOLED_H
#define PICOLED_H
#include <cstddef>
namespace picoled
{
class pixel
{
public:
pixel() :
pixel(false)
{}
pixel(bool on) :
on(on)
{}
bool is_on() const { return on; }
void invert() { on = !on; }
private:
bool on;
};
class oled
{
public:
oled(size_t width, size_t height);
virtual ~oled();
oled(const oled&) = delete;
oled(oled&&) = delete;
oled& operator=(const oled&) = delete;
oled& operator=(oled&&) = delete;
pixel& operator()(int x, int y);
const pixel& operator()(int x, int y) const;
void clear();
size_t get_width() const { return width; }
size_t get_height() const { return height; }
virtual void update() const = 0;
protected:
size_t width;
size_t height;
private:
pixel* pixels;
};
}
#endif//PICOLED_H

31
include/picoled/SSD1306.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef PICOLED_SSD1306_H
#define PICOLED_SSD1306_H
#include <cstdint>
#include <hardware/i2c.h>
#include "picoled.h"
namespace picoled
{
class ssd1306 : public oled
{
public:
ssd1306(i2c_inst* i2c, uint8_t address);
virtual ~ssd1306();
void update() const override;
private:
void command(uint8_t cmd) const;
i2c_inst* i2c;
uint8_t address;
uint8_t* buffer;
};
}
#endif//PICOLED_SSD1306_H

110
src/SSD1306.cpp Normal file
View File

@ -0,0 +1,110 @@
#include "picoled/SSD1306.h"
#include <cstring>
// https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
namespace
{
constexpr uint8_t MEMORY_MODE = 0x20;
constexpr uint8_t COLUMN_ADDRESS = 0x21;
constexpr uint8_t PAGE_ADDRESS = 0x22;
constexpr uint8_t CONTRAST = 0x81;
constexpr uint8_t CHARGE_PUMP = 0x8D;
constexpr uint8_t SEGMENT_REMAP = 0xA0;
constexpr uint8_t ALLON_RESUME = 0xA4;
constexpr uint8_t ALLON = 0xA5;
constexpr uint8_t NORMAL_DISPLAY = 0xA6;
constexpr uint8_t INVERT_DISPLAY = 0xA7;
constexpr uint8_t MULTIPLEX = 0xA8;
constexpr uint8_t OFF = 0xAE;
constexpr uint8_t ON = 0xAF;
constexpr uint8_t COMSCAN_INC = 0xC0;
constexpr uint8_t COMSCAN_DEC = 0xC8;
constexpr uint8_t OFFSET = 0xD3;
constexpr uint8_t CLKDIV = 0xD5;
constexpr uint8_t PRECHARGE = 0xD9;
constexpr uint8_t COMPINS = 0xDA;
constexpr uint8_t VCOM_DETECT = 0xDB;
constexpr uint8_t LOW_COLUMN = 0x00;
constexpr uint8_t HIGH_COLUMN = 0x10;
constexpr uint8_t STARTLINE = 0x40;
constexpr uint8_t RIGHT_HSCROLL = 0x26;
constexpr uint8_t LEFT_HSCROLL = 0x27;
constexpr uint8_t VRIGHT_HSCROLL = 0x29;
constexpr uint8_t VLEFT_HSCROLL = 0x2A;
constexpr uint8_t SCROLL_OFF = 0x2E;
constexpr uint8_t SCROLL_ON = 0x2F;
constexpr uint8_t VSCROLL_AREA = 0xA3;
}
namespace picoled
{
ssd1306::ssd1306(i2c_inst* i2c, uint8_t address) :
oled(128, 64),
i2c(i2c),
address(address),
buffer(new uint8_t[128 * 64 / 8 + 1])
{
// the user is expected to set GPIO func and pullup
// as well as initialize the i2c instance
command(OFF);
command(CLKDIV); command(0x80);
command(MULTIPLEX); command(63);
command(OFFSET); command(0);
command(STARTLINE | 0);
command(MEMORY_MODE); command(0);
command(SEGMENT_REMAP | 0);
command(COMSCAN_DEC);
command(COMPINS); command(0x12);
command(CONTRAST); command(0xCF);
command(PRECHARGE); command(0x22);
command(VCOM_DETECT); command(0x40);
command(NORMAL_DISPLAY);
command(SCROLL_OFF);
command(CHARGE_PUMP); command(0x14);
command(ALLON_RESUME);
command(ON);
buffer[0] = 0x40;
}
ssd1306::~ssd1306()
{
delete[] buffer;
}
void ssd1306::update() const
{
// clear display to zero
memset(&buffer[1], 0, width * height * sizeof(*buffer));
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (!(*this)(x, y).is_on()) { continue; }
const int index = (width - x - 1) + (y / 8) * width;
const uint8_t bits = 1 << (y & 7);
// add one to leave room for 0x40 data indicator at beginning of array
buffer[index + 1] |= bits;
}
}
command(PAGE_ADDRESS); command(0); command(7);
command(COLUMN_ADDRESS); command(0); command(width - 1);
i2c_write_blocking(i2c, address, buffer, width * height / 8 + 1, false);
}
void ssd1306::command(uint8_t cmd) const
{
const uint8_t values[2] = { 0x00, cmd };
i2c_write_blocking(i2c, address, values, 2, false);
}
}

35
src/picoled.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "picoled.h"
namespace picoled
{
oled::oled(size_t width, size_t height) :
pixels(new pixel[width * height]),
width(width),
height(height)
{}
oled::~oled()
{
delete[] pixels;
}
pixel& oled::operator()(int x, int y)
{
return pixels[x + y * width];
}
const pixel& oled::operator()(int x, int y) const
{
return pixels[x + y * width];
}
void oled::clear()
{
for (int i = 0; i < width * height; i++)
{
pixels[i] = false;
}
}
}