Add partial ST7789 support
This commit is contained in:
parent
dfeccd50b9
commit
846f909cfe
@ -5,11 +5,14 @@ add_library(picoled
|
||||
include/picoled/buffer.h
|
||||
include/picoled/gfx/2D.h
|
||||
include/picoled/SSD1306.h
|
||||
include/picoled/ST7789.h
|
||||
|
||||
src/picoled.cpp
|
||||
src/buffer.cpp
|
||||
src/2D.cpp
|
||||
src/SSD1306.cpp
|
||||
src/ST7789.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(picoled PUBLIC hardware_i2c pico_stdlib)
|
||||
target_link_libraries(picoled PUBLIC pico_stdlib hardware_i2c hardware_spi)
|
||||
target_include_directories(picoled PUBLIC include)
|
||||
|
@ -8,7 +8,6 @@
|
||||
namespace picoled
|
||||
{
|
||||
|
||||
// rgb565
|
||||
class pixel
|
||||
{
|
||||
public:
|
||||
@ -59,7 +58,7 @@ public:
|
||||
size_t get_width() const { return buffer_a.get_width(); }
|
||||
size_t get_height() const { return buffer_a.get_height(); }
|
||||
|
||||
void write(int x, int y, const pixel& p) { active_buffer().write(x, y, p); }
|
||||
virtual void write(int x, int y, const pixel& p) { active_buffer().write(x, y, p); }
|
||||
|
||||
buffer<pixel>& active_buffer() { return active ? buffer_b : buffer_a; }
|
||||
|
||||
@ -67,9 +66,10 @@ public:
|
||||
void swap_buffers();
|
||||
|
||||
protected:
|
||||
virtual void update_impl() = 0;
|
||||
virtual void update_impl() const = 0;
|
||||
|
||||
buffer<pixel>& inactive_buffer() { return active ? buffer_a : buffer_b; }
|
||||
const buffer<pixel>& inactive_buffer() const { return active ? buffer_a : buffer_b; }
|
||||
|
||||
private:
|
||||
bool active;
|
||||
|
@ -1,8 +1,6 @@
|
||||
#ifndef PICOLED_SSD1306_H
|
||||
#define PICOLED_SSD1306_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <hardware/i2c.h>
|
||||
|
||||
#include "picoled.h"
|
||||
@ -17,7 +15,7 @@ public:
|
||||
virtual ~ssd1306();
|
||||
|
||||
protected:
|
||||
void update_impl() override;
|
||||
void update_impl() const override;
|
||||
|
||||
private:
|
||||
void command(uint8_t cmd) const;
|
||||
|
39
include/picoled/ST7789.h
Normal file
39
include/picoled/ST7789.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef PICOLED_ST7889_H
|
||||
#define PICOLED_ST7889_H
|
||||
|
||||
#include <hardware/spi.h>
|
||||
|
||||
#include "picoled.h"
|
||||
|
||||
namespace picoled
|
||||
{
|
||||
|
||||
class st7789 : public oled
|
||||
{
|
||||
public:
|
||||
st7789(size_t width, size_t height, spi_inst_t* spi, uint8_t cs_pin, uint8_t dc_pin);
|
||||
virtual ~st7789() = default;
|
||||
|
||||
void write(int x, int y, const pixel& p) override
|
||||
{
|
||||
oled::write(x, y, format(p));
|
||||
}
|
||||
|
||||
protected:
|
||||
void update_impl() const override;
|
||||
|
||||
private:
|
||||
void command(uint8_t cmd) const;
|
||||
void command(uint8_t cmd, uint8_t param) const;
|
||||
void command(uint8_t cmd, const uint8_t* params, size_t param_count) const;
|
||||
|
||||
spi_inst_t* spi;
|
||||
uint8_t cs_pin;
|
||||
uint8_t dc_pin;
|
||||
|
||||
static pixel format(const pixel& p);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif//PICOLED_ST7889_H
|
@ -2,10 +2,45 @@
|
||||
#define PICOLED_BUFFER_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace picoled
|
||||
{
|
||||
|
||||
class fixed
|
||||
{
|
||||
public:
|
||||
fixed();
|
||||
|
||||
fixed(float value);
|
||||
operator int16_t() const;
|
||||
|
||||
fixed operator-() const;
|
||||
fixed operator~() const;
|
||||
|
||||
fixed operator+(const fixed& other) const;
|
||||
fixed operator-(const fixed& other) const;
|
||||
fixed operator*(const fixed& other) const;
|
||||
fixed operator/(const fixed& other) const;
|
||||
|
||||
fixed& operator+=(const fixed& other);
|
||||
fixed& operator-=(const fixed& other);
|
||||
fixed& operator*=(const fixed& other);
|
||||
fixed& operator/=(const fixed& other);
|
||||
|
||||
bool operator<(const fixed& other) const;
|
||||
bool operator<=(const fixed& other) const;
|
||||
bool operator>(const fixed& other) const;
|
||||
bool operator>=(const fixed& other) const;
|
||||
bool operator==(const fixed& other) const;
|
||||
|
||||
static fixed min(fixed a, fixed b);
|
||||
static fixed max(fixed a, fixed b);
|
||||
|
||||
private:
|
||||
int32_t value;
|
||||
};
|
||||
|
||||
// behavior when out of bounds
|
||||
enum class buffer_read_mode
|
||||
{
|
||||
@ -45,9 +80,9 @@ public:
|
||||
data[x + y * width] = pixel;
|
||||
}
|
||||
|
||||
T read_uv(float x, float y, buffer_read_mode mode = buffer_read_mode::clamp) const
|
||||
T read_uv(fixed x, fixed y, buffer_read_mode mode = buffer_read_mode::clamp) const
|
||||
{
|
||||
return read(x * width, y * height, mode);
|
||||
return read(x * fixed(width), y * fixed(height), mode);
|
||||
}
|
||||
|
||||
T read(int x, int y, buffer_read_mode mode = buffer_read_mode::clamp) const
|
||||
@ -67,6 +102,11 @@ public:
|
||||
return T();
|
||||
}
|
||||
|
||||
const uint8_t* data_pointer() const
|
||||
{
|
||||
return reinterpret_cast<const uint8_t*>(data);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (int i = 0; i < width * height; i++)
|
||||
|
@ -9,31 +9,31 @@ namespace picoled::gfx
|
||||
class vec2
|
||||
{
|
||||
public:
|
||||
float x;
|
||||
float y;
|
||||
fixed x;
|
||||
fixed y;
|
||||
|
||||
vec2 operator+(const vec2& other) const { return { x + other.x, y + other.y }; }
|
||||
vec2 operator-(const vec2& other) const { return { x - other.x, y - other.y }; }
|
||||
vec2 operator*(const vec2& other) const { return { x * other.x, y * other.y }; }
|
||||
vec2 operator/(const vec2& other) const { return { x / other.x, y / other.y }; }
|
||||
|
||||
float operator%(const vec2& other) const { return x * other.x + y * other.y; }
|
||||
float operator^(const vec2& other) const { return x * other.y - y * other.x; }
|
||||
fixed operator%(const vec2& other) const { return x * other.x + y * other.y; }
|
||||
fixed operator^(const vec2& other) const { return x * other.y - y * other.x; }
|
||||
};
|
||||
|
||||
class vec3
|
||||
{
|
||||
public:
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
fixed x;
|
||||
fixed y;
|
||||
fixed z;
|
||||
|
||||
vec3 operator+(const vec3& other) const { return { x + other.x, y + other.y, z + other.z }; }
|
||||
vec3 operator-(const vec3& other) const { return { x - other.x, y - other.y, z - other.z }; }
|
||||
vec3 operator*(const vec3& other) const { return { x * other.x, y * other.y, z * other.z }; }
|
||||
vec3 operator/(const vec3& other) const { return { x / other.x, y / other.y, z / other.z }; }
|
||||
|
||||
float operator%(const vec3& other) const { return x * other.x + y * other.y + z * other.z; }
|
||||
fixed operator%(const vec3& other) const { return x * other.x + y * other.y + z * other.z; }
|
||||
vec3 operator^(const vec3& other) const
|
||||
{
|
||||
return
|
||||
@ -67,7 +67,7 @@ public:
|
||||
vec2 max() const;
|
||||
|
||||
private:
|
||||
float area_double() const;
|
||||
fixed area_double() const;
|
||||
};
|
||||
|
||||
class gfx2d
|
||||
|
36
src/2D.cpp
36
src/2D.cpp
@ -10,7 +10,7 @@ namespace picoled::gfx
|
||||
|
||||
triangle triangle::fix_winding() const
|
||||
{
|
||||
if (((b.position - a.position) ^ (c.position - a.position)) < 0)
|
||||
if (((b.position - a.position) ^ (c.position - a.position)) < fixed(0))
|
||||
{
|
||||
return { a, c, b };
|
||||
}
|
||||
@ -22,27 +22,27 @@ triangle triangle::fix_winding() const
|
||||
|
||||
vec3 triangle::barycentric(const vec2& p) const
|
||||
{
|
||||
const float total = area_double();
|
||||
const float u = triangle{ b, a, p }.area_double() / total;
|
||||
const float v = triangle{ a, c, p }.area_double() / total;
|
||||
return vec3{ u, v, 1 - u - v };
|
||||
const fixed total = area_double();
|
||||
const fixed u = triangle{ b, a, p }.area_double() / total;
|
||||
const fixed v = triangle{ a, c, p }.area_double() / total;
|
||||
return vec3{ u, v, fixed(1) - u - v };
|
||||
}
|
||||
|
||||
bool triangle::contains(const vec2& p) const
|
||||
{
|
||||
const float abp = (b.position - a.position) ^ (p - a.position);
|
||||
const float bcp = (c.position - b.position) ^ (p - b.position);
|
||||
const float cap = (a.position - c.position) ^ (p - c.position);
|
||||
const fixed abp = (b.position - a.position) ^ (p - a.position);
|
||||
const fixed bcp = (c.position - b.position) ^ (p - b.position);
|
||||
const fixed cap = (a.position - c.position) ^ (p - c.position);
|
||||
|
||||
return abp >= 0 && bcp >= 0 && cap >= 0;
|
||||
return abp >= fixed(0) && bcp >= fixed(0) && cap >= fixed(0);
|
||||
}
|
||||
|
||||
vec2 triangle::min() const
|
||||
{
|
||||
return
|
||||
{
|
||||
fminf(fminf(a.position.x, b.position.x), c.position.x),
|
||||
fminf(fminf(a.position.y, b.position.y), c.position.y)
|
||||
fixed::min(fixed::min(a.position.x, b.position.x), c.position.x),
|
||||
fixed::min(fixed::min(a.position.y, b.position.y), c.position.y)
|
||||
};
|
||||
}
|
||||
|
||||
@ -50,12 +50,12 @@ vec2 triangle::max() const
|
||||
{
|
||||
return
|
||||
{
|
||||
fmaxf(fmaxf(a.position.x, b.position.x), c.position.x),
|
||||
fmaxf(fmaxf(a.position.y, b.position.y), c.position.y)
|
||||
fixed::max(fixed::max(a.position.x, b.position.x), c.position.x),
|
||||
fixed::max(fixed::max(a.position.y, b.position.y), c.position.y)
|
||||
};
|
||||
}
|
||||
|
||||
float triangle::area_double() const
|
||||
fixed triangle::area_double() const
|
||||
{
|
||||
// don't divide by two since it would cancel out
|
||||
// in the barycentric coordinate calculation anyway
|
||||
@ -69,16 +69,16 @@ void gfx2d::draw_triangle(triangle tri, const buffer<pixel>& texture, bool ignor
|
||||
const vec2 min = tri.min();
|
||||
const vec2 max = tri.max();
|
||||
|
||||
for (float i = min.x; i <= max.x; i += 0.75f)
|
||||
for (fixed i = min.x; i <= max.x; i += 1)
|
||||
{
|
||||
for (float j = min.y; j <= max.y; j += 0.75f)
|
||||
for (fixed j = min.y; j <= max.y; j += 1)
|
||||
{
|
||||
const vec2 position = { i, j };
|
||||
if (tri.contains(position))
|
||||
{
|
||||
const vec3 barycentric = tri.barycentric(position);
|
||||
const float u = barycentric % vec3{ tri.a.texture_coordinates.x, tri.b.texture_coordinates.x, tri.c.texture_coordinates.x };
|
||||
const float v = barycentric % vec3{ tri.a.texture_coordinates.y, tri.b.texture_coordinates.y, tri.c.texture_coordinates.y };
|
||||
const fixed u = barycentric % vec3{ tri.a.texture_coordinates.x, tri.b.texture_coordinates.x, tri.c.texture_coordinates.x };
|
||||
const fixed v = barycentric % vec3{ tri.a.texture_coordinates.y, tri.b.texture_coordinates.y, tri.c.texture_coordinates.y };
|
||||
const pixel read = texture.read_uv(u, v, buffer_read_mode::clamp);
|
||||
screen->write(i, j, read);
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ ssd1306::~ssd1306()
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void ssd1306::update_impl()
|
||||
void ssd1306::update_impl() const
|
||||
{
|
||||
// clear display to zero
|
||||
memset(&buffer[1], 0, get_width() * get_height() * sizeof(*buffer) / 8);
|
||||
|
92
src/ST7789.cpp
Normal file
92
src/ST7789.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "picoled/ST7789.h"
|
||||
|
||||
#include <pico/stdlib.h>
|
||||
|
||||
namespace picoled
|
||||
{
|
||||
|
||||
st7789::st7789(size_t width, size_t height, spi_inst_t* spi, uint8_t cs_pin, uint8_t dc_pin) :
|
||||
oled(width, height),
|
||||
spi(spi),
|
||||
cs_pin(cs_pin),
|
||||
dc_pin(dc_pin)
|
||||
{
|
||||
command(0x01); sleep_ms(150); // reset device
|
||||
command(0x11); sleep_ms(10); // stop sleeping
|
||||
command(0x3A, 0x66); sleep_ms(10); // set display format
|
||||
{
|
||||
constexpr size_t PARAM_COUNT = 4;
|
||||
// no clue where these numbers come from
|
||||
// see https://github.com/adafruit/Adafruit-ST7735-Library/blob/62112b90eddcb2ecc51f474e9fe98b68eb26cb2a/Adafruit_ST7789.cpp#L111-L117
|
||||
const uint8_t params[PARAM_COUNT] =
|
||||
{
|
||||
0x00,
|
||||
0x35,
|
||||
0x00,
|
||||
0xBB
|
||||
};
|
||||
|
||||
command(0x2A, params, PARAM_COUNT); // set column address range
|
||||
}
|
||||
{
|
||||
constexpr size_t PARAM_COUNT = 4;
|
||||
// no clue where these numbers come from
|
||||
// see https://github.com/adafruit/Adafruit-ST7735-Library/blob/62112b90eddcb2ecc51f474e9fe98b68eb26cb2a/Adafruit_ST7789.cpp#L111-L117
|
||||
const uint8_t params[PARAM_COUNT] =
|
||||
{
|
||||
0x00,
|
||||
0x28,
|
||||
0x01,
|
||||
0x17
|
||||
};
|
||||
|
||||
command(0x2B, params, PARAM_COUNT); // set row address range
|
||||
}
|
||||
command(0x36, 0b11000000); // set memory addressing mode
|
||||
command(0x21); // turn on display inversion
|
||||
command(0x13); sleep_ms(10); // set display to normal mode
|
||||
command(0x29); sleep_ms(10); // turn on display
|
||||
}
|
||||
|
||||
void st7789::update_impl() const
|
||||
{
|
||||
command(0x2C, inactive_buffer().data_pointer(), get_width() * get_height() * 3);
|
||||
}
|
||||
|
||||
void st7789::command(uint8_t cmd) const
|
||||
{
|
||||
command(cmd, nullptr, 0);
|
||||
}
|
||||
|
||||
void st7789::command(uint8_t cmd, uint8_t param) const
|
||||
{
|
||||
command(cmd, ¶m, 1);
|
||||
}
|
||||
|
||||
void st7789::command(uint8_t cmd, const uint8_t* params, size_t param_count) const
|
||||
{
|
||||
gpio_put(cs_pin, false); // select chip
|
||||
|
||||
gpio_put(dc_pin, false); // command mode
|
||||
spi_write_blocking(spi, &cmd, 1);
|
||||
|
||||
gpio_put(dc_pin, true); // parameters are sent via data mode
|
||||
spi_write_blocking(spi, params, param_count);
|
||||
|
||||
gpio_put(cs_pin, true); // deselect chip
|
||||
}
|
||||
|
||||
pixel st7789::format(const pixel& p)
|
||||
{
|
||||
const uint16_t red_scaled = p.red() * 0x3F / 0xFF; // scale to 6-bit color
|
||||
const uint16_t green_scaled = p.green() * 0x3F / 0xFF; // scale to 6-bit color
|
||||
const uint16_t blue_scaled = p.blue() * 0x3F / 0xFF; // scale to 6-bit color
|
||||
|
||||
const uint8_t red = (red_scaled & 0xFF) << 2;
|
||||
const uint8_t green = (green_scaled & 0xFF) << 2;
|
||||
const uint8_t blue = (blue_scaled & 0xFF) << 2;
|
||||
|
||||
return { red, green, blue };
|
||||
}
|
||||
|
||||
}
|
110
src/buffer.cpp
Normal file
110
src/buffer.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
#include "picoled/buffer.h"
|
||||
|
||||
#include <pico/stdio.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr int FRACTIONAL_BITS = 16;
|
||||
|
||||
}
|
||||
|
||||
namespace picoled
|
||||
{
|
||||
|
||||
fixed::fixed() :
|
||||
value(0)
|
||||
{}
|
||||
|
||||
fixed::fixed(float value) :
|
||||
value(value * (1 << FRACTIONAL_BITS))
|
||||
{}
|
||||
|
||||
fixed::operator int16_t() const
|
||||
{
|
||||
return value >> FRACTIONAL_BITS;
|
||||
}
|
||||
|
||||
fixed fixed::operator-() const
|
||||
{
|
||||
fixed f;
|
||||
f.value = -value;
|
||||
return f;
|
||||
}
|
||||
|
||||
fixed fixed::operator~() const
|
||||
{
|
||||
fixed f;
|
||||
f.value = ~value;
|
||||
return f;
|
||||
}
|
||||
|
||||
fixed fixed::operator+(const fixed& other) const
|
||||
{
|
||||
fixed copy = *this;
|
||||
copy += other;
|
||||
return copy;
|
||||
}
|
||||
|
||||
fixed fixed::operator-(const fixed& other) const
|
||||
{
|
||||
fixed copy = *this;
|
||||
copy -= other;
|
||||
return copy;
|
||||
}
|
||||
|
||||
fixed fixed::operator*(const fixed& other) const
|
||||
{
|
||||
fixed copy = *this;
|
||||
copy *= other;
|
||||
return copy;
|
||||
}
|
||||
|
||||
fixed fixed::operator/(const fixed& other) const
|
||||
{
|
||||
fixed copy = *this;
|
||||
copy /= other;
|
||||
return copy;
|
||||
}
|
||||
|
||||
fixed& fixed::operator+=(const fixed& other)
|
||||
{
|
||||
value += other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
fixed& fixed::operator-=(const fixed& other)
|
||||
{
|
||||
value -= other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
fixed& fixed::operator*=(const fixed& other)
|
||||
{
|
||||
value = (int64_t(value) * int64_t(other.value)) >> FRACTIONAL_BITS;
|
||||
return *this;
|
||||
}
|
||||
|
||||
fixed& fixed::operator/=(const fixed& other)
|
||||
{
|
||||
value = (int64_t(value) << FRACTIONAL_BITS) / other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool fixed::operator>(const fixed& other) const { return value > other.value; }
|
||||
bool fixed::operator>=(const fixed& other) const { return value >= other.value; }
|
||||
bool fixed::operator<(const fixed& other) const { return value < other.value; }
|
||||
bool fixed::operator<=(const fixed& other) const { return value <= other.value; }
|
||||
bool fixed::operator==(const fixed& other) const { return value == other.value; }
|
||||
|
||||
fixed fixed::min(fixed a, fixed b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
fixed fixed::max(fixed a, fixed b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user