2021-01-16 16:38:58 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include "pico/stdlib.h"
|
|
|
|
#include "hardware/i2c.h"
|
|
|
|
|
|
|
|
#include "pico_scroll.hpp"
|
2021-04-21 13:35:26 +00:00
|
|
|
#include "pico_scroll_font.hpp"
|
2021-01-16 16:38:58 +00:00
|
|
|
|
|
|
|
enum pin {
|
|
|
|
SDA = 4,
|
|
|
|
SCL = 5,
|
|
|
|
A = 12,
|
|
|
|
B = 13,
|
|
|
|
X = 14,
|
|
|
|
Y = 15,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum reg {
|
|
|
|
MODE_REGISTER = 0x00,
|
|
|
|
FRAME_REGISTER = 0x01,
|
|
|
|
AUTOPLAY1_REGISTER = 0x02,
|
|
|
|
AUTOPLAY2_REGISTER = 0x03,
|
|
|
|
BLINK_REGISTER = 0x05,
|
|
|
|
AUDIOSYNC_REGISTER = 0x06,
|
|
|
|
BREATH1_REGISTER = 0x08,
|
|
|
|
BREATH2_REGISTER = 0x09,
|
|
|
|
SHUTDOWN_REGISTER = 0x0a,
|
|
|
|
GAIN_REGISTER = 0x0b,
|
|
|
|
ADC_REGISTER = 0x0c,
|
|
|
|
ENABLE_OFFSET = 0x00,
|
|
|
|
BANK_ADDRESS = 0xfd,
|
|
|
|
COLOR_OFFSET = 0x24
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace pimoroni {
|
|
|
|
|
2023-03-13 19:39:33 +00:00
|
|
|
PicoScroll::~PicoScroll() {
|
|
|
|
clear();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2021-01-16 16:38:58 +00:00
|
|
|
void PicoScroll::init() {
|
|
|
|
// setup i2c interface
|
|
|
|
i2c_init(i2c0, 400000);
|
|
|
|
|
|
|
|
gpio_set_function(pin::SDA, GPIO_FUNC_I2C); gpio_pull_up(pin::SDA);
|
|
|
|
gpio_set_function(pin::SCL, GPIO_FUNC_I2C); gpio_pull_up(pin::SCL);
|
|
|
|
|
|
|
|
i2c_write(reg::BANK_ADDRESS, "\x0b", 1);
|
|
|
|
i2c_write(reg::MODE_REGISTER, "\x00", 1);
|
|
|
|
i2c_write(reg::AUDIOSYNC_REGISTER, "\x00", 1);
|
|
|
|
i2c_write(reg::SHUTDOWN_REGISTER, "\x01", 1);
|
|
|
|
i2c_write(reg::BANK_ADDRESS, "\x0b", 1);
|
|
|
|
i2c_write(reg::FRAME_REGISTER, "\x00", 1);
|
|
|
|
i2c_write(reg::BANK_ADDRESS, "\x00", 1);
|
|
|
|
i2c_write(reg::ENABLE_OFFSET, "\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x7f\x00", 18);
|
|
|
|
|
|
|
|
// setup button inputs
|
|
|
|
gpio_set_function(pin::A, GPIO_FUNC_SIO); gpio_set_dir(pin::A, GPIO_IN); gpio_pull_up(pin::A);
|
|
|
|
gpio_set_function(pin::B, GPIO_FUNC_SIO); gpio_set_dir(pin::B, GPIO_IN); gpio_pull_up(pin::B);
|
|
|
|
gpio_set_function(pin::X, GPIO_FUNC_SIO); gpio_set_dir(pin::X, GPIO_IN); gpio_pull_up(pin::X);
|
|
|
|
gpio_set_function(pin::Y, GPIO_FUNC_SIO); gpio_set_dir(pin::Y, GPIO_IN); gpio_pull_up(pin::Y);
|
2021-01-21 16:56:04 +00:00
|
|
|
|
|
|
|
// reset the screen
|
|
|
|
clear();
|
|
|
|
update();
|
2021-01-16 16:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PicoScroll::set_pixel(uint8_t x, uint8_t y, uint8_t v) {
|
2021-01-19 18:43:43 +00:00
|
|
|
if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;
|
2021-01-16 16:38:58 +00:00
|
|
|
|
2021-01-19 18:43:43 +00:00
|
|
|
y = (HEIGHT - 1) - y;
|
2021-01-16 16:38:58 +00:00
|
|
|
if(x > 8) {
|
|
|
|
x = x - 8;
|
2021-01-19 18:43:43 +00:00
|
|
|
y = (HEIGHT - 1) - (y + 8);
|
2021-01-16 16:38:58 +00:00
|
|
|
}else{
|
|
|
|
x = 8 - x;
|
|
|
|
}
|
|
|
|
|
2021-01-19 18:43:43 +00:00
|
|
|
uint8_t o = x * (WIDTH - 1) + y;
|
2021-01-16 16:38:58 +00:00
|
|
|
__fb[o] = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PicoScroll::is_pressed(uint8_t button) {
|
|
|
|
return !gpio_get(button);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PicoScroll::clear() {
|
2021-01-19 18:43:43 +00:00
|
|
|
memset(__fb, 0, BUFFER_SIZE);
|
2021-01-16 16:38:58 +00:00
|
|
|
}
|
|
|
|
|
2021-04-21 13:35:26 +00:00
|
|
|
void PicoScroll::set_pixels(const char *pixels) {
|
|
|
|
for (int y = 0; y < HEIGHT; y++) {
|
|
|
|
for (int x = 0; x < WIDTH; x++) {
|
|
|
|
set_pixel(x, y, pixels[y * WIDTH + x]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PicoScroll::set_bitmap_1d(const char *bitmap, size_t bitmap_len, int brightness, int offset) {
|
|
|
|
for (int x = 0; x < WIDTH; x++) {
|
|
|
|
int k = offset + x;
|
|
|
|
if ((k >= 0) && (k < (int)bitmap_len)) {
|
|
|
|
unsigned char col = bitmap[k];
|
|
|
|
for (int y = 0; y < HEIGHT; y++) {
|
|
|
|
int val = brightness * ((col >> y) & 1);
|
|
|
|
set_pixel(x, y, val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PicoScroll::set_text(const char *text, size_t text_len, int brightness, int offset) {
|
|
|
|
int draw_buffer_len = PicoScroll::WIDTH + 7;
|
|
|
|
unsigned char draw_buffer[draw_buffer_len];
|
|
|
|
|
|
|
|
// clear the scroll, so only need to write visible bytes
|
|
|
|
clear();
|
|
|
|
|
|
|
|
if ((offset < -WIDTH) || (offset > (int)(6 * text_len))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// compute what can actually be seen, render only that...
|
|
|
|
// modify offset and bfr_len accordingly
|
|
|
|
if (offset < 0) {
|
|
|
|
int space = 1 + (WIDTH + offset) / 6;
|
|
|
|
if (space < (int)text_len) {
|
|
|
|
text_len = space;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int start = offset / 6;
|
|
|
|
offset -= start * 6;
|
|
|
|
text_len = text_len - start;
|
|
|
|
if (text_len > 4) {
|
|
|
|
text_len = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (draw_buffer_len > (int)(6 * text_len)) {
|
|
|
|
draw_buffer_len = 6 * text_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
render_text(text, text_len, draw_buffer, draw_buffer_len);
|
|
|
|
set_bitmap_1d((const char *)draw_buffer, draw_buffer_len, brightness, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PicoScroll::scroll_text(const char *text, size_t text_len, int brightness, int delay_ms) {
|
|
|
|
int draw_buffer_len = 6 * text_len;
|
|
|
|
|
|
|
|
unsigned char *draw_buffer = (unsigned char *)malloc(sizeof(unsigned char) * draw_buffer_len);
|
|
|
|
render_text(text, text_len, draw_buffer, draw_buffer_len);
|
|
|
|
|
|
|
|
for (int offset = -WIDTH; offset < draw_buffer_len; offset++) {
|
|
|
|
clear();
|
|
|
|
set_bitmap_1d((const char *)draw_buffer, draw_buffer_len, brightness, offset);
|
|
|
|
update();
|
|
|
|
sleep_ms(delay_ms);
|
|
|
|
}
|
|
|
|
free(draw_buffer);
|
|
|
|
}
|
|
|
|
|
2021-01-16 16:38:58 +00:00
|
|
|
void PicoScroll::update() {
|
2021-01-19 18:43:43 +00:00
|
|
|
i2c_write(COLOR_OFFSET, (const char *)__fb, BUFFER_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PicoScroll::i2c_write(uint8_t reg, const char *data, uint8_t len) {
|
|
|
|
uint8_t buffer[256];
|
|
|
|
buffer[0] = reg;
|
|
|
|
memcpy(&buffer[1], data, len);
|
|
|
|
i2c_write_blocking(i2c0, DEFAULT_ADDRESS, buffer, len + 1, true);
|
2021-01-16 16:38:58 +00:00
|
|
|
}
|
2023-03-16 16:59:31 +00:00
|
|
|
|
|
|
|
void PicoScroll::update(PicoGraphics *graphics) {
|
|
|
|
if(graphics->pen_type == PicoGraphics::PEN_RGB888) {
|
|
|
|
uint32_t *p = (uint32_t *)graphics->frame_buffer;
|
|
|
|
|
|
|
|
for(int y = 0; y < HEIGHT; y++) {
|
|
|
|
for(int x = 0; x < WIDTH; x++) {
|
|
|
|
uint32_t col = *p;
|
|
|
|
uint8_t r = (col & 0xff0000) >> 16;
|
|
|
|
uint8_t g = (col & 0x00ff00) >> 8;
|
|
|
|
uint8_t b = (col & 0x0000ff) >> 0;
|
|
|
|
p++;
|
|
|
|
|
|
|
|
set_pixel(x, y, (r + g + b) / 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
else if(graphics->pen_type == PicoGraphics::PEN_RGB565) {
|
|
|
|
uint16_t *p = (uint16_t *)graphics->frame_buffer;
|
|
|
|
for(int y = 0; y < HEIGHT; y++) {
|
|
|
|
for(int x = 0; x < WIDTH; x++) {
|
|
|
|
uint16_t col = __builtin_bswap16(*p);
|
|
|
|
uint8_t r = (col & 0b1111100000000000) >> 8;
|
|
|
|
uint8_t g = (col & 0b0000011111100000) >> 3;
|
|
|
|
uint8_t b = (col & 0b0000000000011111) << 3;
|
|
|
|
p++;
|
|
|
|
|
|
|
|
set_pixel(x, y, (r + g + b) / 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
else if(graphics->pen_type == PicoGraphics::PEN_P8 || graphics->pen_type == PicoGraphics::PEN_P4) {
|
|
|
|
int offset = 0;
|
|
|
|
graphics->frame_convert(PicoGraphics::PEN_RGB888, [this, offset](void *data, size_t length) mutable {
|
|
|
|
uint32_t *p = (uint32_t *)data;
|
|
|
|
for(auto i = 0u; i < length / 4; i++) {
|
|
|
|
int x = offset % WIDTH;
|
|
|
|
int y = offset / WIDTH;
|
|
|
|
|
|
|
|
uint32_t col = *p;
|
|
|
|
uint8_t r = (col & 0xff0000) >> 16;
|
|
|
|
uint8_t g = (col & 0x00ff00) >> 8;
|
|
|
|
uint8_t b = (col & 0x0000ff) >> 0;
|
|
|
|
|
|
|
|
set_pixel(x, y, (r + g + b) / 3);
|
|
|
|
offset++;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
2021-01-16 16:38:58 +00:00
|
|
|
}
|