Merge pull request #327 from pimoroni/feature/merged-st7789

ST7789: Create generic display driver
This commit is contained in:
Philip Howard 2022-05-18 13:37:56 +01:00 committed by GitHub
commit db60553322
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1289 additions and 573 deletions

View File

@ -3,9 +3,6 @@
#include <cstdlib>
#include <math.h>
#include "hardware/dma.h"
#include "hardware/pwm.h"
namespace pimoroni {
uint8_t madctl;
uint16_t caset[2] = {0, 0};
@ -20,11 +17,6 @@ namespace pimoroni {
HORIZ_ORDER = 0b00000100
};
#define ROT_240_240_0 0
#define ROT_240_240_90 MADCTL::SWAP_XY | MADCTL::HORIZ_ORDER | MADCTL::COL_ORDER
#define ROT_240_240_180 MADCTL::SCAN_ORDER | MADCTL::HORIZ_ORDER | MADCTL::COL_ORDER | MADCTL::ROW_ORDER
#define ROT_240_240_270 MADCTL::SWAP_XY | MADCTL::HORIZ_ORDER | MADCTL::ROW_ORDER
enum reg {
SWRESET = 0x01,
TEOFF = 0x34,
@ -54,155 +46,118 @@ namespace pimoroni {
PWMFRSEL = 0xCC
};
void ST7789::init(bool auto_init_sequence, bool round, uint32_t spi_baud) {
// configure spi interface and pins
spi_init(spi, spi_baud);
void ST7789::init() {
command(reg::SWRESET);
gpio_set_function(dc, GPIO_FUNC_SIO);
gpio_set_dir(dc, GPIO_OUT);
sleep_ms(150);
gpio_set_function(cs, GPIO_FUNC_SIO);
gpio_set_dir(cs, GPIO_OUT);
// Common init
command(reg::TEON); // enable frame sync signal if used
command(reg::COLMOD, 1, "\x05"); // 16 bits per pixel
gpio_set_function(sck, GPIO_FUNC_SPI);
gpio_set_function(mosi, GPIO_FUNC_SPI);
command(reg::PORCTRL, 5, "\x0c\x0c\x00\x33\x33");
command(reg::LCMCTRL, 1, "\x2c");
command(reg::VDVVRHEN, 1, "\x01");
command(reg::VRHS, 1, "\x12");
command(reg::VDVS, 1, "\x20");
command(reg::PWCTRL1, 2, "\xa4\xa1");
command(reg::FRCTRL2, 1, "\x0f");
if(miso != PIN_UNUSED) {
gpio_set_function(miso, GPIO_FUNC_SPI);
if(width == 240 && height == 240) {
command(reg::GCTRL, 1, "\x14");
command(reg::VCOMS, 1, "\x37");
command(reg::GMCTRP1, 14, "\xD0\x04\x0D\x11\x13\x2B\x3F\x54\x4C\x18\x0D\x0B\x1F\x23");
command(reg::GMCTRN1, 14, "\xD0\x04\x0C\x11\x13\x2C\x3F\x44\x51\x2F\x1F\x1F\x20\x23");
}
// if supported by the display then the vsync pin is
// toggled high during vertical blanking period
if(vsync != PIN_UNUSED) {
gpio_set_function(vsync, GPIO_FUNC_SIO);
gpio_set_dir(vsync, GPIO_IN);
gpio_set_pulls(vsync, false, true);
if((width == 320 && height == 240)
|| (width == 240 && height == 320)) {
command(reg::GCTRL, 1, "\x35");
command(reg::VCOMS, 1, "\x1f");
command(0xd6, 1, "\xa1"); // ???
command(reg::GMCTRP1, 14, "\xD0\x08\x11\x08\x0C\x15\x39\x33\x50\x36\x13\x14\x29\x2D");
command(reg::GMCTRN1, 14, "\xD0\x08\x10\x08\x06\x06\x39\x44\x51\x0B\x16\x14\x2F\x31");
}
// if a backlight pin is provided then set it up for
// pwm control
command(reg::INVON); // set inversion mode
command(reg::SLPOUT); // leave sleep mode
command(reg::DISPON); // turn display on
sleep_ms(100);
configure_display(false);
if(bl != PIN_UNUSED) {
pwm_config cfg = pwm_get_default_config();
pwm_set_wrap(pwm_gpio_to_slice_num(bl), 65535);
pwm_init(pwm_gpio_to_slice_num(bl), &cfg, true);
gpio_set_function(bl, GPIO_FUNC_PWM);
set_backlight(0); // Turn backlight off initially to avoid nasty surprises
update(); // Send the new buffer to the display to clear any previous content
sleep_ms(50); // Wait for the update to apply
set_backlight(255); // Turn backlight on now surprises have passed
}
}
// if auto_init_sequence then send initialisation sequence
// for our standard displays based on the width and height
if(auto_init_sequence) {
command(reg::SWRESET);
sleep_ms(150);
command(reg::TEON); // enable frame sync signal if used
command(reg::COLMOD, 1, "\x05"); // 16 bits per pixel
if(width == 240 && height == 240) {
command(reg::PORCTRL, 5, "\x0c\x0c\x00\x33\x33");
command(reg::GCTRL, 1, "\x14");
command(reg::VCOMS, 1, "\x37");
command(reg::LCMCTRL, 1, "\x2c");
command(reg::VDVVRHEN, 1, "\x01");
command(reg::VRHS, 1, "\x12");
command(reg::VDVS, 1, "\x20");
command(reg::PWCTRL1, 2, "\xa4\xa1");
command(reg::FRCTRL2, 1, "\x0f");
command(reg::GMCTRP1, 14, "\xD0\x04\x0D\x11\x13\x2B\x3F\x54\x4C\x18\x0D\x0B\x1F\x23");
command(reg::GMCTRN1, 14, "\xD0\x04\x0C\x11\x13\x2C\x3F\x44\x51\x2F\x1F\x1F\x20\x23");
}
if(width == 320 && height == 240) {
command(reg::PORCTRL, 5, "\x0c\x0c\x00\x33\x33");
command(reg::GCTRL, 1, "\x35");
command(reg::VCOMS, 1, "\x1f");
command(reg::LCMCTRL, 1, "\x2c");
command(reg::VDVVRHEN, 1, "\x01");
command(reg::VRHS, 1, "\x12");
command(reg::VDVS, 1, "\x20");
command(reg::FRCTRL2, 1, "\x0f");
command(reg::PWCTRL1, 2, "\xa4\xa1");
command(0xd6, 1, "\xa1"); // ???
command(reg::GMCTRP1, 14, "\xD0\x08\x11\x08\x0C\x15\x39\x33\x50\x36\x13\x14\x29\x2D");
command(reg::GMCTRN1, 14, "\xD0\x08\x10\x08\x06\x06\x39\x44\x51\x0B\x16\x14\x2F\x31");
}
command(reg::INVON); // set inversion mode
command(reg::SLPOUT); // leave sleep mode
command(reg::DISPON); // turn display on
sleep_ms(100);
// setup correct addressing window
if(width == 240 && height == 240) {
caset[0] = 0;
caset[1] = 239;
raset[0] = round ? 40 : 0;
raset[1] = round ? 279 : 239;
madctl = MADCTL::HORIZ_ORDER;
}
if(width == 240 && height == 135) {
caset[0] = 40; // 240 cols
caset[1] = 279;
raset[0] = 53; // 135 rows
raset[1] = 187;
madctl = MADCTL::COL_ORDER | MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
}
if(width == 135 && height == 240) {
caset[0] = 52; // 135 cols
caset[1] = 186;
raset[0] = 40; // 240 rows
void ST7789::configure_display(bool rotate180) {
// 240x240 Square and Round LCD Breakouts
// TODO: How can we support 90 degree rotations here?
if(width == 240 && height == 240) {
caset[0] = 0;
caset[1] = 239;
if(round) {
raset[0] = 40;
raset[1] = 279;
madctl = 0;
}
if(width == 320 && height == 240) {
caset[0] = 0;
caset[1] = 319;
raset[0] = 0;
raset[1] = 239;
madctl = 0x70;
}
// Byte swap the 16bit rows/cols values
caset[0] = __builtin_bswap16(caset[0]);
caset[1] = __builtin_bswap16(caset[1]);
raset[0] = __builtin_bswap16(raset[0]);
raset[1] = __builtin_bswap16(raset[1]);
command(reg::CASET, 4, (char *)caset);
command(reg::RASET, 4, (char *)raset);
command(reg::MADCTL, 1, (char *)&madctl);
if(bl != PIN_UNUSED) {
update(); // Send the new buffer to the display to clear any previous content
sleep_ms(50); // Wait for the update to apply
set_backlight(255); // Turn backlight on now surprises have passed
} else {
raset[0] = rotate180 ? 80 : 0;
raset[1] = rotate180 ? 329 : 239;
}
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
madctl |= MADCTL::HORIZ_ORDER;
}
// the dma transfer works but without vsync it's not that useful as you could
// be updating the framebuffer during transfer...
//
// this could be avoided by creating another buffer to draw into and flip
// buffers (but costs another ~100kb of ram)
//
// it's probably not worth it for this particular usecase but will consider it
// some more...
// Pico Display
if(width == 240 && height == 135) {
caset[0] = 40; // 240 cols
caset[1] = 279;
raset[0] = 53; // 135 rows
raset[1] = 187;
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
}
// setup spi for 16-bit transfers
// spi_set_format(spi, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
// Pico Display at 90 degree rotation
if(width == 135 && height == 240) {
caset[0] = 52; // 135 cols
caset[1] = 186;
raset[0] = 40; // 240 rows
raset[1] = 279;
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
}
// initialise dma channel for transmitting pixel data to screen
// dma_channel = dma_claim_unused_channel(true);
// dma_channel_config config = dma_channel_get_default_config(dma_channel);
// channel_config_set_transfer_data_size(&config, DMA_SIZE_16);
// channel_config_set_dreq(&config, spi_get_index(spi) ? DREQ_SPI1_TX : DREQ_SPI0_TX);
// dma_channel_configure(
// dma_channel, &config, &spi_get_hw(spi)->dr, frame_buffer, width * height, false);
// Pico Display 2.0
if(width == 320 && height == 240) {
caset[0] = 0;
caset[1] = 319;
raset[0] = 0;
raset[1] = 239;
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
}
// Pico Display 2.0 at 90 degree rotation
if(width == 240 && height == 320) {
caset[0] = 0;
caset[1] = 239;
raset[0] = 0;
raset[1] = 319;
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
}
// Byte swap the 16bit rows/cols values
caset[0] = __builtin_bswap16(caset[0]);
caset[1] = __builtin_bswap16(caset[1]);
raset[0] = __builtin_bswap16(raset[0]);
raset[1] = __builtin_bswap16(raset[1]);
command(reg::CASET, 4, (char *)caset);
command(reg::RASET, 4, (char *)raset);
command(reg::MADCTL, 1, (char *)&madctl);
}
spi_inst_t* ST7789::get_spi() const {
@ -230,8 +185,6 @@ namespace pimoroni {
}
void ST7789::command(uint8_t command, size_t len, const char *data) {
//dma_channel_wait_for_finish_blocking(dma_channel);
gpio_put(cs, 0);
gpio_put(dc, 0); // command mode
@ -245,25 +198,8 @@ namespace pimoroni {
gpio_put(cs, 1);
}
void ST7789::update(bool dont_block) {
void ST7789::update() {
command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)frame_buffer);
/*if(dma_channel_is_busy(dma_channel) && dont_block) {
return;
}
dma_channel_wait_for_finish_blocking(dma_channel);
uint8_t r = reg::RAMWR;
gpio_put(cs, 0);
gpio_put(dc, 0); // command mode
spi_write_blocking(spi, &r, 1);
gpio_put(dc, 1); // data mode
dma_channel_set_read_addr(dma_channel, frame_buffer, true);*/
}
void ST7789::set_backlight(uint8_t brightness) {
@ -274,12 +210,7 @@ namespace pimoroni {
pwm_set_gpio_level(bl, value);
}
void ST7789::vsync_callback(gpio_irq_callback_t callback) {
gpio_set_irq_enabled_with_callback(vsync, GPIO_IRQ_EDGE_RISE, true, callback);
}
void ST7789::flip(){
madctl ^= MADCTL::ROW_ORDER | MADCTL::COL_ORDER;
command(reg::MADCTL, 1, (char *)&madctl);
configure_display(true);
}
}

View File

@ -2,14 +2,15 @@
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"
#include "../../common/pimoroni_common.hpp"
namespace pimoroni {
class ST7789 {
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
//--------------------------------------------------
// Variables
//--------------------------------------------------
@ -17,60 +18,67 @@ namespace pimoroni {
// screen properties
uint16_t width;
uint16_t height;
uint16_t row_stride;
uint32_t dma_channel;
bool round;
// interface pins with our standard defaults where appropriate
uint cs = SPI_BG_FRONT_CS;
uint dc = SPI_DEFAULT_MISO;
uint sck = SPI_DEFAULT_SCK;
uint mosi = SPI_DEFAULT_MOSI;
uint miso = PIN_UNUSED; // used as data/command
uint bl = SPI_BG_FRONT_PWM;
uint cs;
uint dc;
uint sck;
uint mosi;
uint bl;
uint vsync = PIN_UNUSED; // only available on some products
// The ST7789 requires 16 ns between SPI rising edges.
// 16 ns = 62,500,000 Hz
static const uint32_t SPI_BAUD = 62'500'000;
public:
// frame buffer where pixel data is stored
uint16_t *frame_buffer;
ST7789(uint16_t width, uint16_t height, uint16_t *frame_buffer, BG_SPI_SLOT slot) :
width(width), height(height), frame_buffer(frame_buffer) {
switch(slot) {
case PICO_EXPLORER_ONBOARD:
cs = SPI_BG_FRONT_CS;
bl = PIN_UNUSED;
break;
case BG_SPI_FRONT:
cs = SPI_BG_FRONT_CS;
bl = SPI_BG_FRONT_PWM;
break;
case BG_SPI_BACK:
cs = SPI_BG_BACK_CS;
bl = SPI_BG_BACK_PWM;
break;
}
}
ST7789(uint16_t width, uint16_t height, uint16_t *frame_buffer) :
width(width), height(height), frame_buffer(frame_buffer) {}
ST7789(uint16_t width, uint16_t height, uint16_t *frame_buffer,
ST7789(uint16_t width, uint16_t height, bool round, uint16_t *frame_buffer,
spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint miso = PIN_UNUSED, uint bl = PIN_UNUSED) :
uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED) :
spi(spi),
width(width), height(height),
cs(cs), dc(dc), sck(sck), mosi(mosi), miso(miso), bl(bl), frame_buffer(frame_buffer) {}
width(width), height(height), round(round),
cs(cs), dc(dc), sck(sck), mosi(mosi), bl(bl), frame_buffer(frame_buffer) {
if(!this->frame_buffer) {
this->frame_buffer = new uint16_t[width * height];
}
// configure spi interface and pins
spi_init(spi, SPI_BAUD);
gpio_set_function(dc, GPIO_FUNC_SIO);
gpio_set_dir(dc, GPIO_OUT);
gpio_set_function(cs, GPIO_FUNC_SIO);
gpio_set_dir(cs, GPIO_OUT);
gpio_set_function(sck, GPIO_FUNC_SPI);
gpio_set_function(mosi, GPIO_FUNC_SPI);
// if a backlight pin is provided then set it up for
// pwm control
if(bl != PIN_UNUSED) {
pwm_config cfg = pwm_get_default_config();
pwm_set_wrap(pwm_gpio_to_slice_num(bl), 65535);
pwm_init(pwm_gpio_to_slice_num(bl), &cfg, true);
gpio_set_function(bl, GPIO_FUNC_PWM);
set_backlight(0); // Turn backlight off initially to avoid nasty surprises
}
}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void init(bool auto_init_sequence = true, bool round = false, uint32_t spi_baud = SPI_BAUD);
void init();
void configure_display(bool rotate180);
spi_inst_t* get_spi() const;
uint get_cs() const;
@ -80,10 +88,33 @@ namespace pimoroni {
uint get_bl() const;
void command(uint8_t command, size_t len = 0, const char *data = NULL);
void vsync_callback(gpio_irq_callback_t callback);
void update(bool dont_block = false);
void update();
void set_backlight(uint8_t brightness);
void flip();
static uint get_slot_cs(BG_SPI_SLOT slot) {
switch(slot) {
case PICO_EXPLORER_ONBOARD:
return SPI_BG_FRONT_CS;
case BG_SPI_FRONT:
return SPI_BG_FRONT_CS;
case BG_SPI_BACK:
return SPI_BG_BACK_CS;
}
return PIN_UNUSED;
};
static uint get_slot_bl(BG_SPI_SLOT slot) {
switch(slot) {
case PICO_EXPLORER_ONBOARD:
return PIN_UNUSED;
case BG_SPI_FRONT:
return SPI_BG_FRONT_PWM;
case BG_SPI_BACK:
return SPI_BG_BACK_PWM;
}
return PIN_UNUSED;
};
};
}

View File

@ -6,7 +6,7 @@ add_executable(
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_colourlcd240x240)
target_link_libraries(${OUTPUT_NAME} pico_stdlib generic_st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

View File

@ -1,15 +1,17 @@
#include <math.h>
#include <vector>
#include "breakout_colourlcd240x240.hpp"
#include "generic_st7789.hpp"
using namespace pimoroni;
uint16_t buffer[BreakoutColourLCD240x240::WIDTH * BreakoutColourLCD240x240::HEIGHT];
BreakoutColourLCD240x240 lcd(buffer);
const int WIDTH = 240;
const int HEIGHT = 240;
ST7789Generic lcd(WIDTH, HEIGHT, false, nullptr, BG_SPI_FRONT);
int main() {
lcd.init();
//lcd.configure_display(false);
lcd.set_backlight(255);
struct pt {
@ -24,9 +26,11 @@ int main() {
std::vector<pt> shapes;
for(int i = 0; i < 100; i++) {
pt shape;
shape.x = rand() % lcd.bounds.w;
shape.y = rand() % lcd.bounds.h;
shape.r = (rand() % 10) + 3;
shape.x = rand() % (lcd.bounds.w - (shape.r * 2));
shape.y = rand() % (lcd.bounds.h - (shape.r * 2));
shape.x += shape.r;
shape.y += shape.r;
shape.dx = float(rand() % 255) / 64.0f;
shape.dy = float(rand() % 255) / 64.0f;
shape.pen = lcd.create_pen(rand() % 255, rand() % 255, rand() % 255);
@ -40,15 +44,18 @@ int main() {
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < 0) shape.dx *= -1;
if(shape.x >= lcd.bounds.w) shape.dx *= -1;
if(shape.y < 0) shape.dy *= -1;
if(shape.y >= lcd.bounds.h) shape.dy *= -1;
if(shape.x < shape.r) shape.dx *= -1;
if(shape.x >= lcd.bounds.w - shape.r) shape.dx *= -1;
if(shape.y < shape.r) shape.dy *= -1;
if(shape.y >= lcd.bounds.h - shape.r) shape.dy *= -1;
lcd.set_pen(shape.pen);
lcd.circle(Point(shape.x, shape.y), shape.r);
}
lcd.set_pen(255, 255, 255);
lcd.text("Hello World", Point(0, 0), 240);
// update screen
lcd.update();
}

View File

@ -6,7 +6,7 @@ add_executable(
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_roundlcd)
target_link_libraries(${OUTPUT_NAME} pico_stdlib generic_st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

View File

@ -3,7 +3,7 @@
#include <vector>
#include <cstdlib>
#include "breakout_roundlcd.hpp"
#include "generic_st7789.hpp"
#include "time.h"
// Place a 1.3 Round SPI LCD in the *front* slot of breakout garden.
@ -11,10 +11,12 @@
using namespace pimoroni;
uint16_t buffer[BreakoutRoundLCD::WIDTH * BreakoutRoundLCD::HEIGHT];
BreakoutRoundLCD display(buffer, BG_SPI_FRONT);
const int WIDTH = 240;
const int HEIGHT = 240;
constexpr float RADIUS = BreakoutRoundLCD::WIDTH / 2;
ST7789Generic display(WIDTH, HEIGHT, true, nullptr, BG_SPI_FRONT);
constexpr float RADIUS = WIDTH / 2;
Pen from_hsv(float h, float s, float v) {
uint8_t r = 0, g = 0, b = 0;
@ -39,7 +41,6 @@ Pen from_hsv(float h, float s, float v) {
}
int main() {
display.init();
display.set_backlight(255);
uint32_t steps = 70;

View File

@ -1,11 +1,11 @@
add_executable(
display
pico_display_demo
demo.cpp
image_data.cpp
)
# Pull in pico libraries that we need
target_link_libraries(display pico_stdlib hardware_spi hardware_pwm hardware_dma pico_display)
target_link_libraries(pico_display_demo pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled pico_display generic_st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(display)
pico_add_extra_outputs(pico_display_demo)

View File

@ -4,84 +4,25 @@
#include <cstdlib>
#include "pico_display.hpp"
#include "generic_st7789.hpp"
#include "rgbled.hpp"
using namespace pimoroni;
extern unsigned char image_tif[];
extern unsigned int image_tif_len;
const bool ROTATE_180 = false;
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
PicoDisplay pico_display(buffer);
/*
void pixel(int x, int y, uint16_t c) {
x *= 2;
y *= 2;
pico_display.frame_buffer[x + y * 240] = c;
pico_display.frame_buffer[x + 1 + y * 240] = c;
pico_display.frame_buffer[x + 1 + (y + 1) * 240] = c;
pico_display.frame_buffer[x + (y + 1) * 240] = c;
}
void rect(int x, int y, int w, int h, uint16_t c) {
for(int rx = x; rx < x + w; rx++) {
for(int ry = y; ry < y + h; ry++) {
pixel(rx, ry, c);
}
}
}*/
// Swap WIDTH and HEIGHT to rotate 90 degrees
ST7789Generic pico_display(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, buffer);
uint8_t arrow[] = {
0b00010000,
0b00110000,
0b01110000,
0b11111111,
0b11111111,
0b01110000,
0b00110000,
0b00010000
};
RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B);
uint8_t tick[] = {
0b00000000,
0b00000010,
0b00000111,
0b01001110,
0b11111100,
0b00111000,
0b00010000,
0b00000000,
};
/*
void sprite(uint8_t *p, int x, int y, bool flip, uint16_t c) {
for(int ay = 0; ay < 8; ay++) {
uint8_t sl = p[ay];
for(int ax = 0; ax < 8; ax++) {
if(flip) {
if((0b10000000 >> ax) & sl) {
pixel(ax + x, ay + y, c);
}
}else{
if((0b1 << ax) & sl) {
pixel(ax + x, ay + y, c);
}
}
}
}
}*/
int main() {
pico_display.init();
pico_display.configure_display(ROTATE_180);
pico_display.set_backlight(100);
// uint16_t white = pico_display.create_pen(255, 255, 255);
// uint16_t black = pico_display.create_pen(0, 0, 0);
// uint16_t red = pico_display.create_pen(255, 0, 0);
// uint16_t green = pico_display.create_pen(0, 255, 0);
// uint16_t dark_grey = pico_display.create_pen(20, 40, 60);
// uint16_t dark_green = pico_display.create_pen(10, 100, 10);
// uint16_t blue = pico_display.create_pen(0, 0, 255);
struct pt {
float x;
float y;
@ -92,7 +33,7 @@ int main() {
};
std::vector<pt> shapes;
for(int i = 0; i < 1000; i++) {
for(int i = 0; i < 100; i++) {
pt shape;
shape.x = rand() % 240;
shape.y = rand() % 135;
@ -121,8 +62,8 @@ int main() {
}
float led_step = fmod(i / 20.0f, M_PI * 2.0f);
int r = (sin(led_step) * 25.0f) + 25.0f;
pico_display.set_led(r, r / 1.2f, r);
int r = (sin(led_step) * 32.0f) + 32.0f;
led.set_rgb(r, r / 1.2f, r);
std::vector<Point> poly;
@ -134,9 +75,9 @@ int main() {
poly.push_back(Point(30, 45));
pico_display.set_pen(255, 255, 0);
//pico_display.pixel(Point(0, 0));
pico_display.polygon(poly);
pico_display.set_pen(0, 255, 255);
pico_display.triangle(Point(50, 50), Point(130, 80), Point(80, 110));
@ -155,39 +96,10 @@ int main() {
pico_display.line(Point(120, 67), Point(cx + 120, cy + 67));
}
}
/*
if(pico_display.is_pressed(pico_display.A)) {
pico_display.rectangle(0, 0, 18, 18);
//sprite(tick, 5, 5, true, green);
}else{
//sprite(arrow, 10 + bounce, 10, true, white);
}
if(pico_display.is_pressed(pico_display.B)) {
pico_display.rectangle(0, 49, 18, 18);
//sprite(tick, 5, 54, true, green);
}else{
//sprite(arrow, 10 - bounce, 50, true, white);
}
if(pico_display.is_pressed(pico_display.X)) {
pico_display.rectangle(102, 0, 18, 18);
//sprite(tick, 107, 5, true, green);
}else{
//sprite(arrow, 102 - bounce, 10, false, white);
}
if(pico_display.is_pressed(pico_display.Y)) {
pico_display.rectangle(102, 49, 18, 18);
//sprite(tick, 107, 54, true, green);
}else{
//sprite(arrow, 102 + bounce, 50, false, white);
}
*/
// update screen
pico_display.update();
sleep_ms(1000 / 60);
i++;
}

View File

@ -1,4 +1,4 @@
set(OUTPUT_NAME display_2)
set(OUTPUT_NAME pico_display2_demo)
add_executable(
${OUTPUT_NAME}
@ -6,7 +6,7 @@ add_executable(
)
# Pull in pico libraries that we need
target_link_libraries( ${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma pico_display_2)
target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button pico_display_2 generic_st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs( ${OUTPUT_NAME})
pico_add_extra_outputs(${OUTPUT_NAME})

View File

@ -4,11 +4,25 @@
#include <cstdlib>
#include "pico_display_2.hpp"
#include "generic_st7789.hpp"
#include "rgbled.hpp"
#include "button.hpp"
using namespace pimoroni;
const bool ROTATE_180 = false;
uint16_t buffer[PicoDisplay2::WIDTH * PicoDisplay2::HEIGHT];
PicoDisplay2 pico_display(buffer);
// Swap WIDTH and HEIGHT to rotate 90 degrees
ST7789Generic pico_display(PicoDisplay2::WIDTH, PicoDisplay2::HEIGHT, buffer);
RGBLED led(PicoDisplay2::LED_R, PicoDisplay2::LED_G, PicoDisplay2::LED_B);
Button button_a(PicoDisplay2::A);
Button button_b(PicoDisplay2::B);
Button button_x(PicoDisplay2::X);
Button button_y(PicoDisplay2::Y);
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
// Outputs are rgb in the range 0-255 for each channel
@ -31,7 +45,7 @@ void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
}
int main() {
pico_display.init();
pico_display.configure_display(ROTATE_180);
pico_display.set_backlight(255);
struct pt {
@ -58,11 +72,11 @@ int main() {
Point text_location(0, 0);
while(true) {
if(pico_display.is_pressed(pico_display.A)) text_location.x -= 1;
if(pico_display.is_pressed(pico_display.B)) text_location.x += 1;
if(button_a.raw()) text_location.x -= 1;
if(button_b.raw()) text_location.x += 1;
if(pico_display.is_pressed(pico_display.X)) text_location.y -= 1;
if(pico_display.is_pressed(pico_display.Y)) text_location.y += 1;
if(button_x.raw()) text_location.y -= 1;
if(button_y.raw()) text_location.y += 1;
pico_display.set_pen(120, 40, 60);
pico_display.clear();
@ -97,7 +111,7 @@ int main() {
// we want a full colour cycle to take. 5000 = 5 sec.
uint8_t r = 0, g = 0, b = 0;
from_hsv((float)millis() / 5000.0f, 1.0f, 0.5f + sinf(millis() / 100.0f / 3.14159f) * 0.5f, r, g, b);
pico_display.set_led(r, g, b);
led.set_rgb(r, g, b);
pico_display.set_pen(255, 255, 255);

View File

@ -5,8 +5,6 @@ add_subdirectory(breakout_encoder)
add_subdirectory(breakout_ioexpander)
add_subdirectory(breakout_ltr559)
add_subdirectory(breakout_colourlcd160x80)
add_subdirectory(breakout_colourlcd240x240)
add_subdirectory(breakout_roundlcd)
add_subdirectory(breakout_rgbmatrix5x5)
add_subdirectory(breakout_matrix11x7)
add_subdirectory(breakout_mics6814)
@ -19,6 +17,7 @@ add_subdirectory(breakout_sgp30)
add_subdirectory(breakout_as7262)
add_subdirectory(breakout_msa301)
add_subdirectory(breakout_bh1745)
add_subdirectory(generic_st7789)
add_subdirectory(pico_graphics)
add_subdirectory(pico_display)
add_subdirectory(pico_display_2)

View File

@ -3,24 +3,24 @@
namespace pimoroni {
BreakoutColourLCD240x240::BreakoutColourLCD240x240(uint16_t *buf)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf) {
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf,
PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) {
__fb = buf;
}
BreakoutColourLCD240x240::BreakoutColourLCD240x240(uint16_t *buf, spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint miso, uint bl)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, spi, cs, dc, sck, mosi, miso, bl) {
uint cs, uint dc, uint sck, uint mosi, uint bl)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf, spi, cs, dc, sck, mosi, bl) {
__fb = buf;
}
BreakoutColourLCD240x240::BreakoutColourLCD240x240(uint16_t *buf, BG_SPI_SLOT slot)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, slot) {
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf,
PIMORONI_SPI_DEFAULT_INSTANCE, screen.get_slot_cs(slot), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, screen.get_slot_bl(slot)) {
__fb = buf;
}
void BreakoutColourLCD240x240::init() {
// initialise the screen
screen.init();
}
spi_inst_t* BreakoutColourLCD240x240::get_spi() const {

View File

@ -29,7 +29,7 @@ namespace pimoroni {
public:
BreakoutColourLCD240x240(uint16_t *buf);
BreakoutColourLCD240x240(uint16_t *buf, spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint miso = PIN_UNUSED, uint bl = PIN_UNUSED);
uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED);
BreakoutColourLCD240x240(uint16_t *buf, BG_SPI_SLOT slot);

View File

@ -3,23 +3,24 @@
namespace pimoroni {
BreakoutRoundLCD::BreakoutRoundLCD(uint16_t *buf)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf) {
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, true, buf,
PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) {
__fb = buf;
}
BreakoutRoundLCD::BreakoutRoundLCD(uint16_t *buf, spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint miso, uint bl)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, spi, cs, dc, sck, mosi, miso, bl) {
uint cs, uint dc, uint sck, uint mosi, uint bl)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, true, buf, spi, cs, dc, sck, mosi, bl) {
__fb = buf;
}
BreakoutRoundLCD::BreakoutRoundLCD(uint16_t *buf, BG_SPI_SLOT slot) : PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, slot) {
BreakoutRoundLCD::BreakoutRoundLCD(uint16_t *buf, BG_SPI_SLOT slot)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, true, buf,
PIMORONI_SPI_DEFAULT_INSTANCE, screen.get_slot_cs(slot), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, screen.get_slot_bl(slot)) {
__fb = buf;
}
void BreakoutRoundLCD::init() {
// initialise the screen
screen.init(true, true);
}
spi_inst_t* BreakoutRoundLCD::get_spi() const {

View File

@ -31,7 +31,7 @@ namespace pimoroni {
public:
BreakoutRoundLCD(uint16_t *buf);
BreakoutRoundLCD(uint16_t *buf, spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint miso = PIN_UNUSED, uint bl = PIN_UNUSED);
uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED);
BreakoutRoundLCD(uint16_t *buf, BG_SPI_SLOT slot);

View File

@ -0,0 +1 @@
include(generic_st7789.cmake)

View File

@ -0,0 +1,106 @@
# Genereic ST7789 - Pico Display Pack & Pico Display Pack 2.0" and 240x240 Square & Round LCD Breakouts <!-- omit in toc -->
Our Pico Display Packs offers a vibrant 1.14" (240x135) or 2.0" (320x240) IPS LCD screen for your Raspberry Pi Pico it also includes four switches and and an RGB LED!
We've included helper functions to handle every aspect of drawing to the screen and interfacing with the buttons and LED. See the [function reference](#function-reference) for details.
- [Example Program](#example-program)
- [Function Reference](#function-reference)
- [PicoGraphics](#picographics)
- [set_backlight](#set_backlight)
- [update](#update)
## Example Program
The following example sets up Pico Display, displays some basic demo text and graphics and will illuminate the RGB LED green if the A button is pressed.
```c++
#include "pico_display.hpp"
#include "generic_st7789.hpp"
#include "rgbled.hpp"
#include "button.hpp"
using namespace pimoroni;
const bool ROTATE_180 = false;
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
// Swap WIDTH and HEIGHT to rotate 90 degrees
ST7789Generic pico_display(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, buffer);
// RGB LED controller
RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B);
// Buttons
Button button_a(PicoDisplay::A);
Button button_b(PicoDisplay::B);
Button button_x(PicoDisplay::X);
Button button_y(PicoDisplay::Y);
int main() {
pico_display.configure_display(ROTATE_180);
// set the backlight to a value between 0 and 255
// the backlight is driven via PWM and is gamma corrected by our
// library to give a gorgeous linear brightness range.
pico_display.set_backlight(100);
while(true) {
// detect if the A button is pressed (could be A, B, X, or Y)
if(button_a.raw()) {
// make the led glow green
// parameters are red, green, blue all between 0 and 255
// these are also gamma corrected
led.set_rgb(0, 255, 0);
}
// set the colour of the pen
// parameters are red, green, blue all between 0 and 255
pico_display.set_pen(30, 40, 50);
// fill the screen with the current pen colour
pico_display.clear();
// draw a box to put some text in
pico_display.set_pen(10, 20, 30);
Rect text_rect(10, 10, 150, 150);
pico_display.rectangle(text_rect);
// write some text inside the box with 10 pixels of margin
// automatically word wrapping
text_rect.deflate(10);
pico_display.set_pen(110, 120, 130);
pico_display.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w);
// now we've done our drawing let's update the screen
pico_display.update();
}
}
```
## Function Reference
### PicoGraphics
The generic ST7789 driver uses our Pico Graphics library to draw graphics and text. For more information [read the Pico Graphics function reference.](../pico_graphics/README.md#function-reference).
You will also need to use the RGBLED library to drive the RGB LED, and the Button library for the four buttons.
### set_backlight
Set the display backlight from 0-255.
```c++
pico_display.set_backlight(brightness);
```
Uses hardware PWM to dim the display backlight, dimming values are gamma-corrected to provide smooth brightness transitions across the full range of intensity. This may result in some low values mapping as "off."
### update
To display your changes on Pico Display's screen you need to call `update`:
```c++
pico_display.update();
```

View File

@ -0,0 +1,11 @@
set(LIB_NAME generic_st7789)
add_library(${LIB_NAME} INTERFACE)
target_sources(${LIB_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
)
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma st7789 pico_graphics)

View File

@ -0,0 +1,47 @@
#include <math.h>
#include <string.h>
#include "generic_st7789.hpp"
namespace pimoroni {
spi_inst_t* ST7789Generic::get_spi() const {
return st7789.get_spi();
}
int ST7789Generic::get_cs() const {
return st7789.get_cs();
}
int ST7789Generic::get_dc() const {
return st7789.get_dc();
}
int ST7789Generic::get_sck() const {
return st7789.get_sck();
}
int ST7789Generic::get_mosi() const {
return st7789.get_mosi();
}
int ST7789Generic::get_bl() const {
return st7789.get_bl();
}
void ST7789Generic::update() {
st7789.update();
}
void ST7789Generic::flip() {
st7789.configure_display(true);
}
void ST7789Generic::set_backlight(uint8_t brightness) {
st7789.set_backlight(brightness);
}
void ST7789Generic::configure_display(bool rotate180) {
st7789.configure_display(rotate180);
}
}

View File

@ -0,0 +1,49 @@
#pragma once
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
namespace pimoroni {
class ST7789Generic : public PicoGraphics {
private:
ST7789 st7789;
public:
ST7789Generic(uint16_t width, uint16_t height, bool round=false, uint16_t *frame_buffer=nullptr) :
PicoGraphics(width, height, frame_buffer),
st7789(width, height, round, frame_buffer, PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) {
this->frame_buffer = st7789.frame_buffer;
this->st7789.init();
};
ST7789Generic(uint16_t width, uint16_t height, bool round, uint16_t *frame_buffer, BG_SPI_SLOT slot) :
PicoGraphics(width, height, frame_buffer),
st7789(width, height, round, frame_buffer, PIMORONI_SPI_DEFAULT_INSTANCE, st7789.get_slot_cs(slot), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, st7789.get_slot_bl(slot)) {
this->frame_buffer = st7789.frame_buffer;
this->st7789.init();
};
ST7789Generic(uint16_t width, uint16_t height, bool round, uint16_t *frame_buffer,
spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED) :
PicoGraphics(width, height, frame_buffer),
st7789(width, height, round, frame_buffer, spi, cs, dc, sck, mosi, bl) {
this->frame_buffer = st7789.frame_buffer;
this->st7789.init();
};
spi_inst_t* get_spi() const;
int get_cs() const;
int get_dc() const;
int get_sck() const;
int get_mosi() const;
int get_bl() const;
void update();
[[deprecated("Use configure_display(true) instead.")]] void flip();
void set_backlight(uint8_t brightness);
void configure_display(bool rotate180);
};
}

View File

@ -19,49 +19,60 @@ The following example sets up Pico Display, displays some basic demo text and gr
```c++
#include "pico_display.hpp"
using namespace pimoroni;
#include "generic_st7789.hpp"
#include "rgbled.hpp"
#include "button.hpp"
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
PicoDisplay pico_display(buffer);
// Swap WIDTH and HEIGHT to rotate 90 degrees
ST7789Generic display(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, buffer);
// Create an RGB LED
RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B);
// And each button
Button button_a(PicoDisplay::A);
Button button_b(PicoDisplay::B);
Button button_x(PicoDisplay::X);
Button button_y(PicoDisplay::Y);
int main() {
pico_display.init();
// set the backlight to a value between 0 and 255
// the backlight is driven via PWM and is gamma corrected by our
// library to give a gorgeous linear brightness range.
pico_display.set_backlight(100);
display.set_backlight(100);
while(true) {
// detect if the A button is pressed (could be A, B, X, or Y)
if(pico_display.is_pressed(pico_display.A)) {
if(button_a.raw(display.A)) {
// make the led glow green
// parameters are red, green, blue all between 0 and 255
// these are also gamma corrected
pico_display.set_led(0, 255, 0);
led.set_rgb(0, 255, 0);
}
// set the colour of the pen
// parameters are red, green, blue all between 0 and 255
pico_display.set_pen(30, 40, 50);
display.set_pen(30, 40, 50);
// fill the screen with the current pen colour
pico_display.clear();
display.clear();
// draw a box to put some text in
pico_display.set_pen(10, 20, 30);
display.set_pen(10, 20, 30);
Rect text_rect(10, 10, 150, 150);
pico_display.rectangle(text_rect);
display.rectangle(text_rect);
// write some text inside the box with 10 pixels of margin
// automatically word wrapping
text_rect.deflate(10);
pico_display.set_pen(110, 120, 130);
pico_display.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w);
display.set_pen(110, 120, 130);
display.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w);
// now we've done our drawing let's update the screen
pico_display.update();
display.update();
}
}
```
@ -72,52 +83,32 @@ int main() {
Pico Display uses our Pico Graphics library to draw graphics and text. For more information [read the Pico Graphics function reference.](../pico_graphics/README.md#function-reference).
### init
### configure_display
Sets up Pico Display. `init` must be called before any other functions since it configures the required PWM and GPIO:
Configures an ST7789 display. Done by default, but you can use this to set 180 degree rotation like so:
```c++
pico_display.init();
display.configure_display(true);
```
### flip
Deprecated: calls `configure_display(true);`
### set_backlight
Set the display backlight from 0-255.
```c++
pico_display.set_backlight(brightness);
display.set_backlight(brightness);
```
Uses hardware PWM to dim the display backlight, dimming values are gamma-corrected to provide smooth brightness transitions across the full range of intensity. This may result in some low values mapping as "off."
### set_led
Sets the RGB LED on Pico Display with an RGB triplet:
```c++
pico_display.set_led(r, g, b);
```
Uses hardware PWM to drive the LED. Values are automatically gamma-corrected to provide smooth brightness transitions and low values may map as "off."
### is_pressed
Reads the GPIO pin connected to one of Pico Display's buttons, returning a `bool` - `true` if it's pressed and `false` if it is released.
```c++
pico_display.is_pressed(button);
```
The button vaule should be a `uint8_t` denoting a pin, and constants `A`, `B`, `X` and `Y` are supplied to make it easier. e:
```c++
bool is_a_button_pressed = pico_display.is_pressed(PicoDisplay::A)
```
### update
To display your changes on Pico Display's screen you need to call `update`:
```c++
pico_display.update();
```
display.update();
```

View File

@ -6,19 +6,17 @@
#include "pico_display.hpp"
const uint8_t LED_R = 6;
const uint8_t LED_G = 7;
const uint8_t LED_B = 8;
namespace pimoroni {
PicoDisplay::PicoDisplay(uint16_t *buf)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, BG_SPI_FRONT) {
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf,
PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) {
__fb = buf;
}
PicoDisplay::PicoDisplay(uint16_t *buf, int width, int height)
: PicoGraphics(width, height, buf), screen(width, height, buf, BG_SPI_FRONT) {
: PicoGraphics(width, height, buf), screen(width, height, false, buf,
PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) {
__fb = buf;
}
@ -47,9 +45,6 @@ namespace pimoroni {
gpio_set_function(B, GPIO_FUNC_SIO); gpio_set_dir(B, GPIO_IN); gpio_pull_up(B);
gpio_set_function(X, GPIO_FUNC_SIO); gpio_set_dir(X, GPIO_IN); gpio_pull_up(X);
gpio_set_function(Y, GPIO_FUNC_SIO); gpio_set_dir(Y, GPIO_IN); gpio_pull_up(Y);
// initialise the screen
screen.init();
}
void PicoDisplay::update() {

View File

@ -11,10 +11,13 @@ namespace pimoroni {
static const int HEIGHT = 135;
static const int PORTRAIT_WIDTH = 135;
static const int PORTRAIT_HEIGHT = 240;
static const uint8_t A = 12;
static const uint8_t A = 12;
static const uint8_t B = 13;
static const uint8_t X = 14;
static const uint8_t Y = 15;
static const uint8_t LED_R = 6;
static const uint8_t LED_G = 7;
static const uint8_t LED_B = 8;
uint16_t *__fb;
private:

View File

@ -6,19 +6,17 @@
#include "pico_display_2.hpp"
const uint8_t LED_R = 6;
const uint8_t LED_G = 7;
const uint8_t LED_B = 8;
namespace pimoroni {
PicoDisplay2::PicoDisplay2(uint16_t *buf)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, BG_SPI_FRONT) {
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, false, buf,
PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) {
__fb = buf;
}
PicoDisplay2::PicoDisplay2(uint16_t *buf, int width, int height)
: PicoGraphics(width, height, buf), screen(width, height, buf, BG_SPI_FRONT) {
: PicoGraphics(width, height, buf), screen(width, height, false, buf,
PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, SPI_BG_FRONT_PWM) {
__fb = buf;
}
@ -47,9 +45,6 @@ namespace pimoroni {
gpio_set_function(B, GPIO_FUNC_SIO); gpio_set_dir(B, GPIO_IN); gpio_pull_up(B);
gpio_set_function(X, GPIO_FUNC_SIO); gpio_set_dir(X, GPIO_IN); gpio_pull_up(X);
gpio_set_function(Y, GPIO_FUNC_SIO); gpio_set_dir(Y, GPIO_IN); gpio_pull_up(Y);
// initialise the screen
screen.init(true, false);
}
void PicoDisplay2::update() {

View File

@ -11,10 +11,13 @@ namespace pimoroni {
static const int HEIGHT = 240;
static const int PORTRAIT_WIDTH = 240;
static const int PORTRAIT_HEIGHT = 320;
static const uint8_t A = 12;
static const uint8_t A = 12;
static const uint8_t B = 13;
static const uint8_t X = 14;
static const uint8_t Y = 15;
static const uint8_t LED_R = 6;
static const uint8_t LED_G = 7;
static const uint8_t LED_B = 8;
uint16_t *__fb;
private:

View File

@ -15,8 +15,9 @@ const uint8_t MOTOR2P = 11;
namespace pimoroni {
PicoExplorer::PicoExplorer(uint16_t *buf)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, PICO_EXPLORER_ONBOARD) {
__fb = buf;
: PicoGraphics(WIDTH, HEIGHT, buf),
screen(WIDTH, HEIGHT, false, buf, PIMORONI_SPI_DEFAULT_INSTANCE, screen.get_slot_cs(PICO_EXPLORER_ONBOARD), SPI_DEFAULT_MISO, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, screen.get_slot_bl(PICO_EXPLORER_ONBOARD)) {
__fb = buf;
}
void PicoExplorer::init() {
@ -48,9 +49,6 @@ namespace pimoroni {
pwm_init(pwm_gpio_to_slice_num(MOTOR2P), &motor_pwm_cfg, true);
gpio_set_function(MOTOR2P, GPIO_FUNC_PWM);
// initialise the screen
screen.init();
}
void PicoExplorer::update() {

View File

@ -1,12 +1,10 @@
import time
import random
from breakout_colourlcd240x240 import BreakoutColourLCD240x240
from st7789 import ST7789
width = BreakoutColourLCD240x240.WIDTH
height = BreakoutColourLCD240x240.HEIGHT
WIDTH, HEIGHT = 240, 240
display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565)
display = BreakoutColourLCD240x240(display_buffer)
display = ST7789(WIDTH, HEIGHT, round=False)
display.set_backlight(1.0)
@ -27,8 +25,8 @@ for i in range(0, 100):
r = random.randint(0, 10) + 3
balls.append(
Ball(
random.randint(r, r + (width - 2 * r)),
random.randint(r, r + (height - 2 * r)),
random.randint(r, r + (WIDTH - 2 * r)),
random.randint(r, r + (HEIGHT - 2 * r)),
r,
(14 - r) / 2,
(14 - r) / 2,
@ -44,9 +42,9 @@ while True:
ball.x += ball.dx
ball.y += ball.dy
xmax = width - ball.r
xmax = WIDTH - ball.r
xmin = ball.r
ymax = height - ball.r
ymax = HEIGHT - ball.r
ymin = ball.r
if ball.x < xmin or ball.x > xmax:

View File

@ -1,16 +1,14 @@
import time
import math
from breakout_roundlcd import BreakoutRoundLCD
from st7789 import ST7789
width = BreakoutRoundLCD.WIDTH
height = BreakoutRoundLCD.HEIGHT
WIDTH, HEIGHT = 240, 240
display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565)
display = BreakoutRoundLCD(display_buffer)
display = ST7789(WIDTH, HEIGHT, round=True)
display.set_backlight(1.0)
RADIUS = width // 2
RADIUS = WIDTH // 2
def hsv_to_rgb(h, s, v):

View File

@ -1,7 +1,13 @@
import picodisplay as display # Comment this line out to use PicoDisplay2
# import picodisplay2 as display # Uncomment this line to use PicoDisplay2
import st7789
import qrcode
# Set the display resolution
# in most cases you can swap WIDTH weith HEIGHT for portrait mode
WIDTH, HEIGHT = 240, 135 # Pico Display
# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0
display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False)
def measure_qr_code(size, code):
w, h = code.get_size()
@ -20,22 +26,17 @@ def draw_qr_code(ox, oy, size, code):
display.rectangle(ox + x * module_size, oy + y * module_size, module_size, module_size)
width = display.get_width()
height = display.get_height()
code = qrcode.QRCode()
code.set_text("shop.pimoroni.com")
display.init(bytearray(width * height * 2))
display.set_pen(128, 128, 128)
display.clear()
display.set_pen(0, 0, 0)
size, module_size = measure_qr_code(height, code)
left = int((width // 2) - (size // 2))
top = int((height // 2) - (size // 2))
draw_qr_code(left, top, height, code)
size, module_size = measure_qr_code(HEIGHT, code)
left = int((WIDTH // 2) - (size // 2))
top = int((HEIGHT // 2) - (size // 2))
draw_qr_code(left, top, HEIGHT, code)
display.update()

View File

@ -1,15 +1,24 @@
# This example shows you a simple, non-interrupt way of reading Pico Display's buttons with a loop that checks to see if buttons are pressed.
import picodisplay as display # Comment this line out to use PicoDisplay2
# import picodisplay2 as display # Uncomment this line to use PicoDisplay2
import st7789
import utime
from pimoroni import Button
# Initialise display with a bytearray display buffer
buf = bytearray(display.get_width() * display.get_height() * 2)
display.init(buf)
# Set the display resolution
# in most cases you can swap WIDTH weith HEIGHT for portrait mode
WIDTH, HEIGHT = 240, 135 # Pico Display
# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0
display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False)
display.set_backlight(0.5)
button_a = Button(12)
button_b = Button(13)
button_x = Button(14)
button_y = Button(15)
# sets up a handy function we can call to clear the screen
def clear():
display.set_pen(0, 0, 0)
@ -18,28 +27,28 @@ def clear():
while True:
if display.is_pressed(display.BUTTON_A): # if a button press is detected then...
if button_a.read(): # if a button press is detected then...
clear() # clear to black
display.set_pen(255, 255, 255) # change the pen colour
display.text("Button A pressed", 10, 10, 240, 4) # display some text on the screen
display.update() # update the display
utime.sleep(1) # pause for a sec
clear() # clear to black again
elif display.is_pressed(display.BUTTON_B):
elif button_b.read():
clear()
display.set_pen(0, 255, 255)
display.text("Button B pressed", 10, 10, 240, 4)
display.update()
utime.sleep(1)
clear()
elif display.is_pressed(display.BUTTON_X):
elif button_x.read():
clear()
display.set_pen(255, 0, 255)
display.text("Button X pressed", 10, 10, 240, 4)
display.update()
utime.sleep(1)
clear()
elif display.is_pressed(display.BUTTON_Y):
elif button_y.read():
clear()
display.set_pen(255, 255, 0)
display.text("Button Y pressed", 10, 10, 240, 4)

View File

@ -1,13 +1,12 @@
import time
import random
import picodisplay as display # Comment this line out to use PicoDisplay2
# import picodisplay2 as display # Uncomment this line to use PicoDisplay2
import st7789
width = display.get_width()
height = display.get_height()
# Set the display resolution, in most cases you can flip these for portrait mode
WIDTH, HEIGHT = 240, 135 # Pico Display
# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0
display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565)
display.init(display_buffer)
display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False)
display.set_backlight(1.0)
@ -28,8 +27,8 @@ for i in range(0, 100):
r = random.randint(0, 10) + 3
balls.append(
Ball(
random.randint(r, r + (width - 2 * r)),
random.randint(r, r + (height - 2 * r)),
random.randint(r, r + (WIDTH - 2 * r)),
random.randint(r, r + (HEIGHT - 2 * r)),
r,
(14 - r) / 2,
(14 - r) / 2,
@ -45,9 +44,9 @@ while True:
ball.x += ball.dx
ball.y += ball.dy
xmax = width - ball.r
xmax = WIDTH - ball.r
xmin = ball.r
ymax = height - ball.r
ymax = HEIGHT - ball.r
ymin = ball.r
if ball.x < xmin or ball.x > xmax:

View File

@ -1,14 +1,19 @@
# This example borrows a CircuitPython hsv_to_rgb function to cycle through some rainbows on Pico Display's screen and RGB LED . If you're into rainbows, HSV (Hue, Saturation, Value) is very useful!
import utime
import picodisplay as display # Comment this line out to use PicoDisplay2
# import picodisplay2 as display # Uncomment this line to use PicoDisplay2
import st7789
from pimoroni import RGBLED
# Set up and initialise Pico Display
buf = bytearray(display.get_width() * display.get_height() * 2)
display.init(buf)
# Set the display resolution
# in most cases you can swap WIDTH weith HEIGHT for portrait mode
WIDTH, HEIGHT = 240, 135 # Pico Display
# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0
display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False)
display.set_backlight(0.8)
led = RGBLED(6, 7, 8)
# From CPython Lib/colorsys.py
def hsv_to_rgb(h, s, v):
@ -39,7 +44,7 @@ h = 0
while True:
h += 1
r, g, b = [int(255 * c) for c in hsv_to_rgb(h / 360.0, 1.0, 1.0)] # rainbow magic
display.set_led(r, g, b) # Set LED to a converted HSV value
led.set_rgb(r, g, b) # Set LED to a converted HSV value
display.set_pen(r, g, b) # Set pen to a converted HSV value
display.clear() # Fill the screen with the colour
display.set_pen(0, 0, 0) # Set pen to black

View File

@ -3,17 +3,21 @@
import machine
import utime
import gc
# Pico Display boilerplate
import picodisplay as display # Comment this line out to use PicoDisplay2
# import picodisplay2 as display # Uncomment this line to use PicoDisplay2
import st7789
from pimoroni import RGBLED
width = display.get_width()
height = display.get_height()
gc.collect()
display_buffer = bytearray(width * height * 2)
display.init(display_buffer)
# Set the display resolution
# in most cases you can swap WIDTH weith HEIGHT for portrait mode
WIDTH, HEIGHT = 135, 240 # Pico Display
# WIDTH, HEIGHT = 320, 240 # Pico Display 2.0
display = st7789.ST7789(WIDTH, HEIGHT, rotate180=False)
# Set the display backlight to 50%
display.set_backlight(0.5)
led = RGBLED(6, 7, 8)
# reads from Pico's temp sensor and converts it into a more manageable number
sensor_temp = machine.ADC(4)
@ -22,9 +26,6 @@ temp_min = 10
temp_max = 30
bar_width = 5
# Set the display backlight to 50%
display.set_backlight(0.5)
temperatures = []
colors = [(0, 0, 255), (0, 255, 0), (255, 255, 0), (255, 0, 0)]
@ -62,7 +63,7 @@ while True:
temperatures.append(temperature)
# shifts the temperatures history to the left by one sample
if len(temperatures) > width // bar_width:
if len(temperatures) > WIDTH // bar_width:
temperatures.pop(0)
i = 0
@ -71,14 +72,14 @@ while True:
display.set_pen(*temperature_to_color(t))
# draws the reading as a tall, thin rectangle
display.rectangle(i, height - (round(t) * 4), bar_width, height)
display.rectangle(i, HEIGHT - (round(t) * 4), bar_width, HEIGHT)
# the next tall thin rectangle needs to be drawn
# "bar_width" (default: 5) pixels to the right of the last one
i += bar_width
# heck lets also set the LED to match
display.set_led(*temperature_to_color(temperature))
led.set_rgb(*temperature_to_color(temperature))
# draws a white background for the text
display.set_pen(255, 255, 255)

View File

@ -120,7 +120,7 @@ mp_obj_t BreakoutColourLCD240x240_make_new(const mp_obj_type_t *type, size_t n_a
spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1;
self->breakout = new BreakoutColourLCD240x240((uint16_t *)bufinfo.buf, spi,
args[ARG_cs].u_int, args[ARG_dc].u_int, sck, mosi, PIN_UNUSED, args[ARG_bl].u_int);
args[ARG_cs].u_int, args[ARG_dc].u_int, sck, mosi, args[ARG_bl].u_int);
}
self->breakout->init();

View File

@ -120,7 +120,7 @@ mp_obj_t BreakoutRoundLCD_make_new(const mp_obj_type_t *type, size_t n_args, siz
spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1;
self->breakout = new BreakoutRoundLCD((uint16_t *)bufinfo.buf, spi,
args[ARG_cs].u_int, args[ARG_dc].u_int, sck, mosi, PIN_UNUSED, args[ARG_bl].u_int);
args[ARG_cs].u_int, args[ARG_dc].u_int, sck, mosi, args[ARG_bl].u_int);
}
self->breakout->init();

View File

@ -1,6 +1,6 @@
add_library(usermod_hub75 INTERFACE)
target_sources(usermod_pico_display INTERFACE
target_sources(usermod_hub75 INTERFACE
${CMAKE_CURRENT_LIST_DIR}/hub75.c
${CMAKE_CURRENT_LIST_DIR}/hub75.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/hub75/hub75.cpp

View File

@ -12,7 +12,6 @@ include(breakout_ioexpander/micropython)
include(breakout_ltr559/micropython)
include(breakout_colourlcd160x80/micropython)
include(breakout_as7262/micropython)
include(breakout_roundlcd/micropython)
include(breakout_rgbmatrix5x5/micropython)
include(breakout_matrix11x7/micropython)
include(breakout_msa301/micropython)
@ -22,7 +21,6 @@ include(breakout_potentiometer/micropython)
include(breakout_rtc/micropython)
include(breakout_trackball/micropython)
include(breakout_sgp30/micropython)
include(breakout_colourlcd240x240/micropython)
include(breakout_bh1745/micropython)
include(breakout_bme68x/micropython)
include(breakout_bme280/micropython)
@ -34,8 +32,6 @@ include(breakout_vl53l5cx/micropython)
include(pico_scroll/micropython)
include(pico_rgb_keypad/micropython)
include(pico_unicorn/micropython)
include(pico_display/micropython)
include(pico_display_2/micropython)
include(pico_explorer/micropython)
include(pico_wireless/micropython)
@ -50,4 +46,6 @@ include(motor/micropython)
include(ulab/code/micropython)
include(qrcode/micropython/micropython)
include(st7789/micropython)
include(modules_py/modules_py)

View File

@ -1,14 +1,5 @@
#include "pico_display.h"
/***** Constants *****/
enum buttons
{
BUTTON_A = 0,
BUTTON_B,
BUTTON_X,
BUTTON_Y,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// picodisplay Module
@ -61,10 +52,13 @@ STATIC const mp_map_elem_t picodisplay_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&picodisplay_circle_obj) },
{ MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&picodisplay_character_obj) },
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&picodisplay_text_obj) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(BUTTON_A) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(BUTTON_B) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(BUTTON_X) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(BUTTON_Y) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(12) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(13) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(14) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(15) },
{ MP_ROM_QSTR(MP_QSTR_LED_R), MP_ROM_INT(6) },
{ MP_ROM_QSTR(MP_QSTR_LED_G), MP_ROM_INT(7) },
{ MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_INT(8) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_picodisplay_globals, picodisplay_globals_table);

View File

@ -96,36 +96,11 @@ mp_obj_t picodisplay_set_led(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj) {
}
mp_obj_t picodisplay_is_pressed(mp_obj_t button_obj) {
bool buttonPressed = false;
if(display != nullptr) {
int buttonID = mp_obj_get_int(button_obj);
switch(buttonID) {
case 0:
buttonPressed = display->is_pressed(PicoDisplay::A);
break;
case 1:
buttonPressed = display->is_pressed(PicoDisplay::B);
break;
case 2:
buttonPressed = display->is_pressed(PicoDisplay::X);
break;
case 3:
buttonPressed = display->is_pressed(PicoDisplay::Y);
break;
default:
mp_raise_ValueError("button not valid. Expected 0 to 3");
break;
}
return display->is_pressed(mp_obj_get_int(button_obj)) ? mp_const_true : mp_const_false;
}
else
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
return buttonPressed ? mp_const_true : mp_const_false;
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
return mp_const_none;
}
mp_obj_t picodisplay_set_pen(mp_uint_t n_args, const mp_obj_t *args) {

View File

@ -1,14 +1,5 @@
#include "pico_display_2.h"
/***** Constants *****/
enum buttons
{
BUTTON_A = 0,
BUTTON_B,
BUTTON_X,
BUTTON_Y,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// picodisplay2 Module
@ -61,10 +52,13 @@ STATIC const mp_map_elem_t picodisplay2_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&picodisplay2_circle_obj) },
{ MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&picodisplay2_character_obj) },
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&picodisplay2_text_obj) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(BUTTON_A) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(BUTTON_B) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(BUTTON_X) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(BUTTON_Y) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(12) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(13) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(14) },
{ MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(15) },
{ MP_ROM_QSTR(MP_QSTR_LED_R), MP_ROM_INT(6) },
{ MP_ROM_QSTR(MP_QSTR_LED_G), MP_ROM_INT(7) },
{ MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_INT(8) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_picodisplay2_globals, picodisplay2_globals_table);

View File

@ -96,36 +96,11 @@ mp_obj_t picodisplay2_set_led(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj) {
}
mp_obj_t picodisplay2_is_pressed(mp_obj_t button_obj) {
bool buttonPressed = false;
if(display2 != nullptr) {
int buttonID = mp_obj_get_int(button_obj);
switch(buttonID) {
case 0:
buttonPressed = display2->is_pressed(PicoDisplay2::A);
break;
case 1:
buttonPressed = display2->is_pressed(PicoDisplay2::B);
break;
case 2:
buttonPressed = display2->is_pressed(PicoDisplay2::X);
break;
case 3:
buttonPressed = display2->is_pressed(PicoDisplay2::Y);
break;
default:
mp_raise_ValueError("button not valid. Expected 0 to 3");
break;
}
return display2->is_pressed(mp_obj_get_int(button_obj)) ? mp_const_true : mp_const_false;
}
else
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
return buttonPressed ? mp_const_true : mp_const_false;
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
return mp_const_none;
}
mp_obj_t picodisplay2_set_pen(mp_uint_t n_args, const mp_obj_t *args) {

View File

@ -0,0 +1,22 @@
set(MOD_NAME st7789)
string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER)
add_library(usermod_${MOD_NAME} INTERFACE)
target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/generic_st7789/generic_st7789.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../drivers/st7789/st7789.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/types.cpp
)
target_include_directories(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_definitions(usermod_${MOD_NAME} INTERFACE
-DMODULE_${MOD_NAME_UPPER}_ENABLED=1
)
target_link_libraries(usermod INTERFACE usermod_${MOD_NAME})

View File

@ -0,0 +1,62 @@
#include "st7789.h"
MP_DEFINE_CONST_FUN_OBJ_1(GenericST7789_update_obj, GenericST7789_update);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_set_backlight_obj, 1, GenericST7789_set_backlight);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_set_pen_obj, 1, GenericST7789_set_pen);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_create_pen_obj, 1, GenericST7789_create_pen);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_set_clip_obj, 1, GenericST7789_set_clip);
MP_DEFINE_CONST_FUN_OBJ_1(GenericST7789_remove_clip_obj, GenericST7789_remove_clip);
MP_DEFINE_CONST_FUN_OBJ_1(GenericST7789_clear_obj, GenericST7789_clear);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_pixel_obj, 1, GenericST7789_pixel);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_pixel_span_obj, 1, GenericST7789_pixel_span);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_rectangle_obj, 1, GenericST7789_rectangle);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_circle_obj, 1, GenericST7789_circle);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_character_obj, 1, GenericST7789_character);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_text_obj, 1, GenericST7789_text);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_polygon_obj, 2, GenericST7789_polygon);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_triangle_obj, 1, GenericST7789_triangle);
MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_line_obj, 1, GenericST7789_line);
STATIC const mp_rom_map_elem_t GenericST7789_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&GenericST7789_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&GenericST7789_set_backlight_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&GenericST7789_set_pen_obj) },
{ MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&GenericST7789_create_pen_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&GenericST7789_set_clip_obj) },
{ MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&GenericST7789_remove_clip_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&GenericST7789_clear_obj) },
{ MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&GenericST7789_pixel_obj) },
{ MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&GenericST7789_pixel_span_obj) },
{ MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&GenericST7789_rectangle_obj) },
{ MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&GenericST7789_circle_obj) },
{ MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&GenericST7789_character_obj) },
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&GenericST7789_text_obj) },
{ MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&GenericST7789_polygon_obj) },
{ MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&GenericST7789_triangle_obj) },
{ MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&GenericST7789_line_obj) },
};
STATIC MP_DEFINE_CONST_DICT(GenericST7789_locals_dict, GenericST7789_locals_dict_table);
/***** Class Definition *****/
const mp_obj_type_t GenericST7789_type = {
{ &mp_type_type },
.name = MP_QSTR_st7789,
.print = GenericST7789_print,
.make_new = GenericST7789_make_new,
.locals_dict = (mp_obj_dict_t*)&GenericST7789_locals_dict,
};
/***** Module Globals *****/
STATIC const mp_map_elem_t st7789_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_st7789) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_ST7789), (mp_obj_t)&GenericST7789_type },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_st7789_globals, st7789_globals_table);
/***** Module Definition *****/
const mp_obj_module_t st7789_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_st7789_globals,
};
MP_REGISTER_MODULE(MP_QSTR_st7789, st7789_user_cmodule, MODULE_ST7789_ENABLED);

View File

@ -0,0 +1,559 @@
#include "libraries/generic_st7789/generic_st7789.hpp"
#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o))
#define IS_VALID_PERIPH(spi, pin) ((((pin) & 8) >> 3) == (spi))
#define IS_VALID_SCK(spi, pin) (((pin) & 3) == 2 && IS_VALID_PERIPH(spi, pin))
#define IS_VALID_MOSI(spi, pin) (((pin) & 3) == 3 && IS_VALID_PERIPH(spi, pin))
#define IS_VALID_MISO(spi, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(spi, pin))
using namespace pimoroni;
extern "C" {
#include "st7789.h"
/***** Variables Struct *****/
typedef struct _GenericST7789_obj_t {
mp_obj_base_t base;
ST7789Generic *st7789;
uint16_t *buffer;
} GenericST7789_obj_t;
/***** Print *****/
void GenericST7789_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind; //Unused input parameter
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t);
mp_print_str(print, "ST7789(");
mp_print_str(print, "spi = ");
mp_obj_print_helper(print, mp_obj_new_int((self->st7789->get_spi() == spi0) ? 0 : 1), PRINT_REPR);
mp_print_str(print, ", cs = ");
mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_cs()), PRINT_REPR);
mp_print_str(print, ", dc = ");
mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_dc()), PRINT_REPR);
mp_print_str(print, ", sck = ");
mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_sck()), PRINT_REPR);
mp_print_str(print, ", mosi = ");
mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_mosi()), PRINT_REPR);
mp_print_str(print, ", bl = ");
mp_obj_print_helper(print, mp_obj_new_int(self->st7789->get_bl()), PRINT_REPR);
mp_print_str(print, ")");
}
/***** Constructor *****/
mp_obj_t GenericST7789_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
GenericST7789_obj_t *self = nullptr;
enum { ARG_width, ARG_height, ARG_round, ARG_rotate180, ARG_slot, ARG_buffer, ARG_spi, ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_bl };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_round, MP_ARG_OBJ, {.u_obj = mp_const_false} },
{ MP_QSTR_rotate180, MP_ARG_OBJ, {.u_obj = mp_const_false} },
{ MP_QSTR_slot, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_cs, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_CS} },
{ MP_QSTR_dc, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MISO} },
{ MP_QSTR_sck, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_SCK} },
{ MP_QSTR_mosi, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MOSI} },
{ MP_QSTR_bl, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_PWM} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
self = m_new_obj(GenericST7789_obj_t);
self->base.type = &GenericST7789_type;
bool rotate180 = args[ARG_rotate180].u_obj == mp_const_true;
bool round = args[ARG_round].u_obj == mp_const_true;
int width = args[ARG_width].u_int;
int height = args[ARG_height].u_int;
if (args[ARG_buffer].u_obj != mp_const_none) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
self->buffer = (uint16_t *)bufinfo.buf;
if(bufinfo.len < (size_t)(width * height * 2)) {
mp_raise_ValueError("Supplied buffer is too small!");
}
} else {
self->buffer = m_new(uint16_t, width * height);
}
if(args[ARG_slot].u_int != -1) {
BG_SPI_SLOT slot = (BG_SPI_SLOT)args[ARG_slot].u_int;
self->st7789 = new ST7789Generic(width, height, round, self->buffer, slot);
if (rotate180) {
self->st7789->configure_display(true);
}
} else {
// Get SPI bus.
int spi_id = args[ARG_spi].u_int;
int sck = args[ARG_sck].u_int;
int mosi = args[ARG_mosi].u_int;
int dc = args[ARG_dc].u_int;
int cs = args[ARG_cs].u_int;
int bl = args[ARG_bl].u_int;
if(spi_id == -1) {
spi_id = (sck >> 3) & 0b1; // If no spi specified, choose the one for the given SCK pin
}
if(spi_id < 0 || spi_id > 1) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id);
}
if(!IS_VALID_SCK(spi_id, sck)) {
mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin"));
}
if(!IS_VALID_MOSI(spi_id, mosi)) {
mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin"));
}
spi_inst_t *spi = (spi_id == 0) ? spi0 : spi1;
self->st7789 = new ST7789Generic(width, height, round, self->buffer,
spi, cs, dc, sck, mosi, bl);
if (rotate180) {
self->st7789->configure_display(true);
}
}
return MP_OBJ_FROM_PTR(self);
}
/***** Methods *****/
mp_obj_t GenericST7789_update(mp_obj_t self_in) {
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t);
self->st7789->update();
return mp_const_none;
}
mp_obj_t GenericST7789_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_brightness };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_brightness, MP_ARG_REQUIRED | MP_ARG_OBJ },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
float brightness = mp_obj_get_float(args[ARG_brightness].u_obj);
if(brightness < 0 || brightness > 1.0f)
mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0");
else
self->st7789->set_backlight((uint8_t)(brightness * 255.0f));
return mp_const_none;
}
mp_obj_t GenericST7789_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
if(n_args <= 2) {
enum { ARG_self, ARG_pen };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_pen, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int pen = args[ARG_pen].u_int;
if(pen < 0 || pen > 0xffff)
mp_raise_ValueError("p is not a valid pen.");
else
self->st7789->set_pen(pen);
}
else {
enum { ARG_self, ARG_r, ARG_g, ARG_b };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int r = args[ARG_r].u_int;
int g = args[ARG_g].u_int;
int b = args[ARG_b].u_int;
if(r < 0 || r > 255)
mp_raise_ValueError("r out of range. Expected 0 to 255");
else if(g < 0 || g > 255)
mp_raise_ValueError("g out of range. Expected 0 to 255");
else if(b < 0 || b > 255)
mp_raise_ValueError("b out of range. Expected 0 to 255");
else
self->st7789->set_pen(r, g, b);
}
return mp_const_none;
}
mp_obj_t GenericST7789_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
int pen = 0;
enum { ARG_self, ARG_r, ARG_g, ARG_b };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int r = args[ARG_r].u_int;
int g = args[ARG_g].u_int;
int b = args[ARG_b].u_int;
if(r < 0 || r > 255)
mp_raise_ValueError("r out of range. Expected 0 to 255");
else if(g < 0 || g > 255)
mp_raise_ValueError("g out of range. Expected 0 to 255");
else if(b < 0 || b > 255)
mp_raise_ValueError("b out of range. Expected 0 to 255");
else
pen = self->st7789->create_pen(r, g, b);
return mp_obj_new_int(pen);
}
mp_obj_t GenericST7789_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int w = args[ARG_w].u_int;
int h = args[ARG_h].u_int;
Rect r(x, y, w, h);
self->st7789->set_clip(r);
return mp_const_none;
}
mp_obj_t GenericST7789_remove_clip(mp_obj_t self_in) {
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t);
self->st7789->remove_clip();
return mp_const_none;
}
mp_obj_t GenericST7789_clear(mp_obj_t self_in) {
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t);
self->st7789->clear();
return mp_const_none;
}
mp_obj_t GenericST7789_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
Point p(x, y);
self->st7789->pixel(p);
return mp_const_none;
}
mp_obj_t GenericST7789_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_l };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_l, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int l = args[ARG_l].u_int;
Point p(x, y);
self->st7789->pixel_span(p, l);
return mp_const_none;
}
mp_obj_t GenericST7789_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int w = args[ARG_w].u_int;
int h = args[ARG_h].u_int;
Rect r(x, y, w, h);
self->st7789->rectangle(r);
return mp_const_none;
}
mp_obj_t GenericST7789_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_r };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int r = args[ARG_r].u_int;
Point p(x, y);
self->st7789->circle(p, r);
return mp_const_none;
}
mp_obj_t GenericST7789_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_char, ARG_x, ARG_y, ARG_scale };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int c = mp_obj_get_int(args[ARG_char].u_obj);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int scale = args[ARG_scale].u_int;
self->st7789->character((char)c, Point(x, y), scale);
return mp_const_none;
}
mp_obj_t GenericST7789_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_wordwrap, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
mp_obj_t text_obj = args[ARG_text].u_obj;
if(mp_obj_is_str_or_bytes(text_obj)) {
GET_STR_DATA_LEN(text_obj, str, str_len);
std::string t((const char*)str);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int wrap = args[ARG_wrap].u_int;
int scale = args[ARG_scale].u_int;
self->st7789->text(t, Point(x, y), wrap, scale);
}
else if(mp_obj_is_float(text_obj)) {
mp_raise_TypeError("can't convert 'float' object to str implicitly");
}
else if(mp_obj_is_int(text_obj)) {
mp_raise_TypeError("can't convert 'int' object to str implicitly");
}
else if(mp_obj_is_bool(text_obj)) {
mp_raise_TypeError("can't convert 'bool' object to str implicitly");
}
else {
mp_raise_TypeError("can't convert object to str implicitly");
}
return mp_const_none;
}
mp_obj_t GenericST7789_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
size_t num_tuples = n_args - 1;
const mp_obj_t *tuples = pos_args + 1;
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], GenericST7789_obj_t);
// Check if there is only one argument, which might be a list
if(n_args == 2) {
if(mp_obj_is_type(pos_args[1], &mp_type_list)) {
mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t);
if(points->len > 0) {
num_tuples = points->len;
tuples = points->items;
}
else {
mp_raise_ValueError("poly(): cannot provide an empty list");
}
}
else {
mp_raise_TypeError("poly(): can't convert object to list");
}
}
if(num_tuples > 0) {
std::vector<Point> points;
for(size_t i = 0; i < num_tuples; i++) {
mp_obj_t obj = tuples[i];
if(!mp_obj_is_type(obj, &mp_type_tuple)) {
mp_raise_ValueError("poly(): can't convert object to tuple");
}
else {
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t);
if(tuple->len != 2) {
mp_raise_ValueError("poly(): tuple must only contain two numbers");
}
points.push_back(Point(
mp_obj_get_int(tuple->items[0]),
mp_obj_get_int(tuple->items[1]))
);
}
}
self->st7789->polygon(points);
}
return mp_const_none;
}
mp_obj_t GenericST7789_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_x3, ARG_y3 };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_x3, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y3, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int x1 = args[ARG_x1].u_int;
int y1 = args[ARG_y1].u_int;
int x2 = args[ARG_x1].u_int;
int y2 = args[ARG_y1].u_int;
int x3 = args[ARG_x1].u_int;
int y3 = args[ARG_y1].u_int;
Point p1(x1, y1);
Point p2(x2, y2);
Point p3(x3, y3);
self->st7789->triangle(p1, p2, p3);
return mp_const_none;
}
mp_obj_t GenericST7789_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2 };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t);
int x1 = args[ARG_x1].u_int;
int y1 = args[ARG_y1].u_int;
int x2 = args[ARG_x1].u_int;
int y2 = args[ARG_y1].u_int;
Point p1(x1, y1);
Point p2(x2, y2);
self->st7789->line(p1, p2);
return mp_const_none;
}
}

View File

@ -0,0 +1,27 @@
// Include MicroPython API.
#include "py/runtime.h"
#include "py/objstr.h"
/***** Extern of Class Definition *****/
extern const mp_obj_type_t GenericST7789_type;
/***** Extern of Class Methods *****/
extern void GenericST7789_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
extern mp_obj_t GenericST7789_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
extern mp_obj_t GenericST7789_update(mp_obj_t self_in);
extern mp_obj_t GenericST7789_set_backlight(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_set_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_set_clip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_remove_clip(mp_obj_t self_in);
extern mp_obj_t GenericST7789_clear(mp_obj_t self_in);
extern mp_obj_t GenericST7789_pixel(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_pixel_span(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_triangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t GenericST7789_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

View File

@ -10,6 +10,10 @@ HEADER_I2C_PINS = {"sda": 20, "scl": 21}
NORMAL_DIR = 0x00
REVERSED_DIR = 0x01
BREAKOUT_GARDEN_SPI_SLOT_FRONT = 0
BREAKOUT_GARDEN_SPI_SLOT_BACK = 1
PICO_EXPLORER_SPI_ONBOARD = 2
class Analog:
def __init__(self, pin, amplifier_gain=1, resistor=0, offset=0):