Merge pull request #373 from pimoroni/experimental/8bitfb

ST7789 / PicoGraphics rewrite - Support for 4-bit, 8-bit and 16-bit framebuffers and more.
pull/395/head v1.19.0
Philip Howard 2022-06-17 15:59:56 +01:00 zatwierdzone przez GitHub
commit f101ffdb4d
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
249 zmienionych plików z 10808 dodań i 7073 usunięć

Wyświetl plik

@ -7,7 +7,7 @@ on:
types: [created]
env:
MICROPYTHON_VERSION: v1.18
MICROPYTHON_VERSION: v1.19
BOARD_TYPE: PICO
# MicroPython version will be contained in github.event.release.tag_name for releases
RELEASE_FILE: pimoroni-badger2040-${{github.event.release.tag_name || github.sha}}-micropython.uf2

Wyświetl plik

@ -7,7 +7,7 @@ on:
types: [created]
env:
MICROPYTHON_VERSION: v1.18
MICROPYTHON_VERSION: v1.19
jobs:
deps:
@ -66,6 +66,8 @@ jobs:
board: PIMORONI_PICOLIPO_4MB
- name: picolipo_16mb
board: PIMORONI_PICOLIPO_16MB
- name: tufty2040
board: PIMORONI_TUFTY2040
env:
# MicroPython version will be contained in github.event.release.tag_name for releases
@ -107,6 +109,12 @@ jobs:
with:
submodules: true
path: pimoroni-pico-${{ github.sha }}
- name: "HACK: MicroPython Board Fixups"
shell: bash
working-directory: micropython/ports/rp2
run: |
../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board/board-fixup.sh ${{matrix.name}} ${{matrix.board}} ../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board
- name: Configure MicroPython
shell: bash

Wyświetl plik

@ -1 +1,2 @@
include(pimoroni_i2c.cmake)
include(pimoroni_bus.cmake)

Wyświetl plik

@ -1,11 +1,11 @@
set(LIB_NAME breakout_roundlcd)
set(LIB_NAME pimoroni_bus)
add_library(${LIB_NAME} INTERFACE)
target_sources(${LIB_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
${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 st7789 pico_graphics)
target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hardware_spi)

Wyświetl plik

@ -0,0 +1,15 @@
#include "pimoroni_bus.hpp"
namespace pimoroni {
SPIPins get_spi_pins(BG_SPI_SLOT slot) {
switch(slot) {
case PICO_EXPLORER_ONBOARD:
return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_DC, PIN_UNUSED};
case BG_SPI_FRONT:
return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_DC, SPI_BG_FRONT_PWM};
case BG_SPI_BACK:
return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_BACK_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_DC, SPI_BG_BACK_PWM};
}
return {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_DC, SPI_BG_FRONT_PWM};
};
}

Wyświetl plik

@ -0,0 +1,27 @@
#pragma once
#include "pimoroni_common.hpp"
#include "hardware/gpio.h"
#include "hardware/spi.h"
namespace pimoroni {
struct SPIPins {
spi_inst_t *spi;
uint cs;
uint sck;
uint mosi;
uint miso;
uint dc;
uint bl;
};
struct ParallelPins {
uint cs;
uint dc;
uint wr_sck;
uint rd_sck;
uint d0;
uint bl;
};
SPIPins get_spi_pins(BG_SPI_SLOT slot);
}

Wyświetl plik

@ -32,6 +32,7 @@ namespace pimoroni {
// SPI
static const unsigned int SPI_DEFAULT_MOSI = 19;
static const unsigned int SPI_DEFAULT_MISO = 16;
static const unsigned int SPI_DEFAULT_DC = 16;
static const unsigned int SPI_DEFAULT_SCK = 18;
static const unsigned int SPI_BG_FRONT_PWM = 20;
@ -53,6 +54,13 @@ namespace pimoroni {
INTERSTATE_75,
SERVO_2040
};
enum Rotation {
ROTATE_0 = 0,
ROTATE_90 = 90,
ROTATE_180 = 180,
ROTATE_270 = 270
};
enum Polarity {
ACTIVE_LOW = 0,
@ -100,8 +108,8 @@ namespace pimoroni {
uint8_t enable;
};
pin_pair() : first(0), second(0) {}
pin_pair(uint8_t first, uint8_t second) : first(first), second(second) {}
constexpr pin_pair() : first(0), second(0) {}
constexpr pin_pair(uint8_t first, uint8_t second) : first(first), second(second) {}
};
struct bool_pair {

Wyświetl plik

@ -1,37 +1,39 @@
add_subdirectory(analog)
add_subdirectory(analogmux)
add_subdirectory(esp32spi)
add_subdirectory(ioexpander)
add_subdirectory(ltp305)
add_subdirectory(ltr559)
add_subdirectory(pmw3901)
add_subdirectory(sgp30)
add_subdirectory(st7735)
add_subdirectory(st7789)
add_subdirectory(msa301)
add_subdirectory(rv3028)
add_subdirectory(trackball)
add_subdirectory(vl53l1x)
add_subdirectory(is31fl3731)
add_subdirectory(fatfs)
add_subdirectory(sdcard)
add_subdirectory(as7262)
add_subdirectory(bh1745)
add_subdirectory(bme68x)
add_subdirectory(bmp280)
add_subdirectory(bme280)
add_subdirectory(button)
add_subdirectory(pid)
add_subdirectory(plasma)
add_subdirectory(rgbled)
add_subdirectory(icp10125)
add_subdirectory(scd4x)
add_subdirectory(hub75)
add_subdirectory(uc8151)
add_subdirectory(pwm)
add_subdirectory(servo)
add_subdirectory(encoder)
add_subdirectory(motor)
add_subdirectory(vl53l5cx)
add_subdirectory(pcf85063a)
add_subdirectory(pms5003)
add_subdirectory(analog)
add_subdirectory(analogmux)
add_subdirectory(esp32spi)
add_subdirectory(ioexpander)
add_subdirectory(ltp305)
add_subdirectory(ltr559)
add_subdirectory(pmw3901)
add_subdirectory(sgp30)
add_subdirectory(st7735)
add_subdirectory(st7789)
add_subdirectory(msa301)
add_subdirectory(rv3028)
add_subdirectory(trackball)
add_subdirectory(vl53l1x)
add_subdirectory(is31fl3731)
add_subdirectory(fatfs)
add_subdirectory(sdcard)
add_subdirectory(as7262)
add_subdirectory(bh1745)
add_subdirectory(bme68x)
add_subdirectory(bmp280)
add_subdirectory(bme280)
add_subdirectory(button)
add_subdirectory(pid)
add_subdirectory(plasma)
add_subdirectory(rgbled)
add_subdirectory(icp10125)
add_subdirectory(scd4x)
add_subdirectory(hub75)
add_subdirectory(uc8151)
add_subdirectory(pwm)
add_subdirectory(servo)
add_subdirectory(encoder)
add_subdirectory(motor)
add_subdirectory(vl53l5cx)
add_subdirectory(pcf85063a)
add_subdirectory(pms5003)
add_subdirectory(sh1107)

Wyświetl plik

@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/sh1107.cmake)

Wyświetl plik

@ -0,0 +1,58 @@
# ST7789 Display Driver for Pimoroni LCDs <!-- omit in toc -->
The ST7789 driver supports both Parallel and Serial (SPI) ST7789 displays and is intended for use with:
* Pico Display
* Pico Display 2.0
* Tufty 2040
* Pico Explorer
* 240x240 Round & Square SPI LCD Breakouts
## Setup
Construct an instance of the ST7789 driver with either Parallel or SPI pins.
Parallel:
```c++
ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, {
Tufty2040::LCD_CS, // Chip-Select
Tufty2040::LCD_DC, // Data-Command
Tufty2040::LCD_WR, // Write
Tufty2040::LCD_RD, // Read
Tufty2040::LCD_D0, // Data 0 (start of a bank of 8 pins)
Tufty2040::BACKLIGHT // Backlight
});
```
SPI:
```c++
ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, false, {
PIMORONI_SPI_DEFAULT_INSTANCE, // SPI instance
SPI_BG_FRONT_CS, // Chip-select
SPI_DEFAULT_SCK, // SPI Clock
SPI_DEFAULT_MOSI, // SPI Out
PIN_UNUSED, // SPI In
SPI_DEFAULT_DC, // SPI Data/Command
PIN_UNUSED // Backlight
});
```
## Reference
### Update
ST7789's `update` accepts an instance of `PicoGraphics` in any colour mode:
```c++
st7789.update(&graphics);
```
### Set Backlight
If a backlight pin has been configured, you can set the backlight from 0 to 255:
```c++
st7789.set_backlight(128)
```

Wyświetl plik

@ -0,0 +1,12 @@
set(DRIVER_NAME sh1107)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(sh1107 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_i2c pico_graphics)

Wyświetl plik

@ -0,0 +1,49 @@
#include "sh1107.hpp"
#include <cstdlib>
#include <math.h>
#include <string.h>
namespace pimoroni {
void SH1107::update(PicoGraphics *graphics) {
if(graphics->pen_type == PicoGraphics::PEN_1BIT) { // Display buffer is screen native
uint8_t *p = (uint8_t *)graphics->frame_buffer;
uint framebuffer_size = PicoGraphics_Pen1Bit::buffer_size(width, height);
uint page_size = framebuffer_size / 16;
uint8_t temp[framebuffer_size] = {0};
uint8_t *ptemp = temp;
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
uint bo = 7 - (x & 0b111);
uint8_t color = p[(x / 8) + (y * width / 8)] & (1U << bo);
if(color) {
temp[x + (y / 8 ) * width] |= 1 << (y % 8);
}else{
temp[x + (y / 8 ) * width] &= ~(1 << (y % 8));
}
}
}
uint8_t buf[page_size + 1];
for(int i = 0; i < 16; i++) {
i2c.reg_write_uint8(0x3c, 0, 0xb0 + i);
i2c.reg_write_uint8(0x3c, 0, 0x00);
i2c.reg_write_uint8(0x3c, 0, 0x10);
memcpy(buf + 1, ptemp, page_size);
buf[0] = 0x40;
i2c.write_blocking(0x3c, buf, page_size + 1, false);
ptemp += page_size;
}
}
}
}

Wyświetl plik

@ -0,0 +1,85 @@
#pragma once
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_i2c.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include <algorithm>
namespace pimoroni {
class SH1107 : public DisplayDriver {
I2C &i2c;
public:
bool round;
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
public:
// Parallel init
SH1107(uint16_t width, uint16_t height, I2C &i2c) :
DisplayDriver(width, height, ROTATE_0),
i2c(i2c) {
i2c.reg_write_uint8(0x3c, 0, 0xae);
i2c.reg_write_uint8(0x3c, 0, 0x20); // set memory addressing mode
i2c.reg_write_uint8(0x3c, 0, 0x00);
i2c.reg_write_uint8(0x3c, 0, 0xb0); // set page start address
i2c.reg_write_uint8(0x3c, 0, 0xc0); // mirror vertically (for australian market)
i2c.reg_write_uint8(0x3c, 0, 0x00);
i2c.reg_write_uint8(0x3c, 0, 0x10);
i2c.reg_write_uint8(0x3c, 0, 0x40);
i2c.reg_write_uint8(0x3c, 0, 0xa0); // mirror horizontally
i2c.reg_write_uint8(0x3c, 0, 0xa6); // no inversion
i2c.reg_write_uint8(0x3c, 0, 0xff); // ??????!
i2c.reg_write_uint8(0x3c, 0, 0x3f); // confusion intensifies..
i2c.reg_write_uint8(0x3c, 0, 0xa4);
i2c.reg_write_uint8(0x3c, 0, 0xd3); // set display offset
i2c.reg_write_uint8(0x3c, 0, 0x00);
i2c.reg_write_uint8(0x3c, 0, 0xd5); // set display clock divide
i2c.reg_write_uint8(0x3c, 0, 0xf0);
i2c.reg_write_uint8(0x3c, 0, 0xd9); // set precharge period
i2c.reg_write_uint8(0x3c, 0, 0x22);
i2c.reg_write_uint8(0x3c, 0, 0xda); // set com pins hardware configuration
i2c.reg_write_uint8(0x3c, 0, 0x12);
i2c.reg_write_uint8(0x3c, 0, 0xdb); // set vcomh
i2c.reg_write_uint8(0x3c, 0, 0x20);
i2c.reg_write_uint8(0x3c, 0, 0x8d); // set dc-dc enable
i2c.reg_write_uint8(0x3c, 0, 0x14);
i2c.reg_write_uint8(0x3c, 0, 0xaf); // turn display on
}
void update(PicoGraphics *graphics) override;
private:
void common_init();
void command(uint8_t command, size_t len = 0, const char *data = NULL);
};
}

Wyświetl plik

@ -7,4 +7,4 @@ target_sources(${DRIVER_NAME} INTERFACE
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma)
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm hardware_dma)

Wyświetl plik

@ -67,7 +67,6 @@ namespace pimoroni {
};
void ST7735::init(bool auto_init_sequence) {
// configure spi interface and pins
spi_init(spi, spi_baud);
gpio_set_function(dc, GPIO_FUNC_SIO);
@ -76,21 +75,9 @@ namespace pimoroni {
gpio_set_function(cs, GPIO_FUNC_SIO);
gpio_set_dir(cs, GPIO_OUT);
gpio_set_function(sck, GPIO_FUNC_SPI);
gpio_set_function(sck, GPIO_FUNC_SPI);
gpio_set_function(mosi, GPIO_FUNC_SPI);
if(miso != PIN_UNUSED) {
gpio_set_function(miso, GPIO_FUNC_SPI);
}
// 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 a backlight pin is provided then set it up for
// pwm control
if(bl != PIN_UNUSED) {
@ -98,6 +85,7 @@ namespace pimoroni {
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
}
// if auto_init_sequence then send initialisation sequence
@ -155,30 +143,10 @@ namespace pimoroni {
command(reg::DISPON);
sleep_ms(100);
}
}
spi_inst_t* ST7735::get_spi() const {
return spi;
}
int ST7735::get_cs() const {
return cs;
}
int ST7735::get_dc() const {
return dc;
}
int ST7735::get_sck() const {
return sck;
}
int ST7735::get_mosi() const {
return mosi;
}
int ST7735::get_bl() const {
return bl;
if(bl != PIN_UNUSED) {
set_backlight(255); // Turn backlight on now surprises have passed
}
}
void ST7735::command(uint8_t command, size_t len, const char *data) {
@ -195,8 +163,21 @@ namespace pimoroni {
gpio_put(cs, 1);
}
void ST7735::update(bool dont_block) {
ST7735::command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)frame_buffer);
// Native 16-bit framebuffer update
void ST7735::update(PicoGraphics *graphics) {
if(graphics->pen_type == PicoGraphics::PEN_RGB565) {
command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer);
} else {
command(reg::RAMWR);
gpio_put(dc, 1); // data mode
gpio_put(cs, 0);
graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) {
spi_write_blocking(spi, (const uint8_t*)data, length);
});
gpio_put(cs, 1);
}
}
void ST7735::set_backlight(uint8_t brightness) {

Wyświetl plik

@ -2,21 +2,16 @@
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "../../common/pimoroni_common.hpp"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
namespace pimoroni {
class ST7735 {
class ST7735 : public DisplayDriver {
//--------------------------------------------------
// Constants
//--------------------------------------------------
public:
static const uint8_t DEFAULT_CS_PIN = 17;
static const uint8_t DEFAULT_DC_PIN = 16;
static const uint8_t DEFAULT_SCK_PIN = 18;
static const uint8_t DEFAULT_MOSI_PIN = 19;
static const uint8_t DEFAULT_BL_PIN = 20;
private:
static const uint8_t ROWS = 162;
static const uint8_t COLS = 132;
@ -25,83 +20,44 @@ namespace pimoroni {
// Variables
//--------------------------------------------------
private:
// screen properties
uint16_t width;
uint16_t height;
uint16_t row_stride;
public:
// frame buffer where pixel data is stored
uint16_t *frame_buffer;
private:
spi_inst_t *spi = spi0;
uint32_t dma_channel;
// interface pins with our standard defaults where appropriate
uint cs = DEFAULT_CS_PIN;
uint dc = DEFAULT_DC_PIN;
uint sck = DEFAULT_SCK_PIN;
uint mosi = DEFAULT_MOSI_PIN;
uint miso = PIN_UNUSED; // we generally don't use this pin
uint bl = DEFAULT_BL_PIN;
uint vsync = PIN_UNUSED; // only available on some products
uint cs;
uint dc;
uint sck;
uint mosi;
uint bl;
uint32_t spi_baud = 30 * 1024 * 1024;
uint8_t offset_cols = 0;
uint8_t offset_rows = 0;
//--------------------------------------------------
// Constructors/Destructor
//--------------------------------------------------
public:
ST7735(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: // Don't read too much into this, the case is just here to avoid a compile error
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;
ST7735(uint16_t width, uint16_t height, SPIPins pins) :
DisplayDriver(width, height, ROTATE_0),
spi(pins.spi), cs(pins.cs), dc(pins.dc), sck(pins.sck), mosi(pins.mosi), bl(pins.bl) {
init();
}
}
ST7735(uint16_t width, uint16_t height, uint16_t *frame_buffer) :
width(width), height(height), frame_buffer(frame_buffer) {}
ST7735(uint16_t width, uint16_t height, uint16_t *frame_buffer,
spi_inst_t *spi,
uint8_t cs, uint8_t dc, uint8_t sck, uint8_t mosi, uint8_t miso = -1, uint8_t bl = -1) :
width(width), height(height), frame_buffer(frame_buffer),
spi(spi), cs(cs), dc(dc), sck(sck), mosi(mosi), miso(miso), bl(bl) {}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void init(bool auto_init_sequence = true);
void update(PicoGraphics *graphics) override;
void set_backlight(uint8_t brightness) override;
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;
private:
void init(bool auto_init_sequence = true);
void command(uint8_t command, size_t len = 0, const char *data = NULL);
void update(bool dont_block = false);
void set_backlight(uint8_t brightness);
};
}

Wyświetl plik

@ -0,0 +1,58 @@
# ST7789 Display Driver for Pimoroni LCDs <!-- omit in toc -->
The ST7789 driver supports both Parallel and Serial (SPI) ST7789 displays and is intended for use with:
* Pico Display
* Pico Display 2.0
* Tufty 2040
* Pico Explorer
* 240x240 Round & Square SPI LCD Breakouts
## Setup
Construct an instance of the ST7789 driver with either Parallel or SPI pins.
Parallel:
```c++
ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, {
Tufty2040::LCD_CS, // Chip-Select
Tufty2040::LCD_DC, // Data-Command
Tufty2040::LCD_WR, // Write
Tufty2040::LCD_RD, // Read
Tufty2040::LCD_D0, // Data 0 (start of a bank of 8 pins)
Tufty2040::BACKLIGHT // Backlight
});
```
SPI:
```c++
ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, false, {
PIMORONI_SPI_DEFAULT_INSTANCE, // SPI instance
SPI_BG_FRONT_CS, // Chip-select
SPI_DEFAULT_SCK, // SPI Clock
SPI_DEFAULT_MOSI, // SPI Out
PIN_UNUSED, // SPI In
SPI_DEFAULT_DC, // SPI Data/Command
PIN_UNUSED // Backlight
});
```
## Reference
### Update
ST7789's `update` accepts an instance of `PicoGraphics` in any colour mode:
```c++
st7789.update(&graphics);
```
### Set Backlight
If a backlight pin has been configured, you can set the backlight from 0 to 255:
```c++
st7789.set_backlight(128)
```

Wyświetl plik

@ -4,9 +4,11 @@ add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
pico_generate_pio_header(${DRIVER_NAME} ${CMAKE_CURRENT_LIST_DIR}/st7789_parallel.pio)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(st7789 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma)
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm hardware_pio hardware_dma pico_graphics)

Wyświetl plik

@ -13,7 +13,7 @@ namespace pimoroni {
COL_ORDER = 0b01000000,
SWAP_XY = 0b00100000, // AKA "MV"
SCAN_ORDER = 0b00010000,
RGB = 0b00001000,
RGB_BGR = 0b00001000,
HORIZ_ORDER = 0b00000100
};
@ -46,7 +46,23 @@ namespace pimoroni {
PWMFRSEL = 0xCC
};
void ST7789::init() {
void ST7789::common_init() {
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);
// 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
}
command(reg::SWRESET);
sleep_ms(150);
@ -70,8 +86,7 @@ namespace pimoroni {
command(reg::GMCTRN1, 14, "\xD0\x04\x0C\x11\x13\x2C\x3F\x44\x51\x2F\x1F\x1F\x20\x23");
}
if((width == 320 && height == 240)
|| (width == 240 && height == 320)) {
if(width == 320 && height == 240) {
command(reg::GCTRL, 1, "\x35");
command(reg::VCOMS, 1, "\x1f");
command(0xd6, 1, "\xa1"); // ???
@ -85,30 +100,78 @@ namespace pimoroni {
sleep_ms(100);
configure_display(false);
configure_display(rotation);
if(bl != PIN_UNUSED) {
update(); // Send the new buffer to the display to clear any previous content
//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
}
}
void ST7789::configure_display(bool rotate180) {
void ST7789::cleanup() {
if(spi) return; // SPI mode needs no tear down
if(dma_channel_is_claimed(parallel_dma)) {
dma_channel_abort(parallel_dma);
dma_channel_unclaim(parallel_dma);
}
if(pio_sm_is_claimed(parallel_pio, parallel_sm)) {
pio_sm_set_enabled(parallel_pio, parallel_sm, false);
pio_sm_drain_tx_fifo(parallel_pio, parallel_sm);
pio_sm_unclaim(parallel_pio, parallel_sm);
}
}
void ST7789::configure_display(Rotation rotate) {
bool rotate180 = rotate == ROTATE_180 || rotate == ROTATE_90;
if(rotate == ROTATE_90 || rotate == ROTATE_270) {
std::swap(width, height);
}
// 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;
} else {
raset[0] = rotate180 ? 80 : 0;
raset[1] = rotate180 ? 329 : 239;
int row_offset = round ? 40 : 80;
int col_offset = 0;
switch(rotate) {
case ROTATE_90:
if (!round) row_offset = 0;
caset[0] = row_offset;
caset[1] = width + row_offset - 1;
raset[0] = col_offset;
raset[1] = width + col_offset - 1;
madctl = MADCTL::HORIZ_ORDER | MADCTL::COL_ORDER | MADCTL::SWAP_XY;
break;
case ROTATE_180:
caset[0] = col_offset;
caset[1] = width + col_offset - 1;
raset[0] = row_offset;
raset[1] = width + row_offset - 1;
madctl = MADCTL::HORIZ_ORDER | MADCTL::COL_ORDER | MADCTL::ROW_ORDER;
break;
case ROTATE_270:
caset[0] = row_offset;
caset[1] = width + row_offset - 1;
raset[0] = col_offset;
raset[1] = width + col_offset - 1;
madctl = MADCTL::ROW_ORDER | MADCTL::SWAP_XY;
break;
default: // ROTATE_0 (and for any smart-alec who tries to rotate 45 degrees or something...)
if (!round) row_offset = 0;
caset[0] = col_offset;
caset[1] = width + col_offset - 1;
raset[0] = row_offset;
raset[1] = width + row_offset - 1;
madctl = MADCTL::HORIZ_ORDER;
break;
}
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
madctl |= MADCTL::HORIZ_ORDER;
}
// Pico Display
@ -160,46 +223,99 @@ namespace pimoroni {
command(reg::MADCTL, 1, (char *)&madctl);
}
spi_inst_t* ST7789::get_spi() const {
return spi;
void ST7789::write_blocking_parallel_dma(const uint8_t *src, size_t len) {
while (dma_channel_is_busy(parallel_dma))
;
dma_channel_set_trans_count(parallel_dma, len, false);
dma_channel_set_read_addr(parallel_dma, src, true);
}
uint ST7789::get_cs() const {
return cs;
}
void ST7789::write_blocking_parallel(const uint8_t *src, size_t len) {
const uint8_t *p = src;
while(len--) {
// Does not byte align correctly
//pio_sm_put_blocking(parallel_pio, parallel_sm, *p);
while (pio_sm_is_tx_fifo_full(parallel_pio, parallel_sm))
;
*(volatile uint8_t*)&parallel_pio->txf[parallel_sm] = *p;
p++;
}
uint ST7789::get_dc() const {
return dc;
}
uint ST7789::get_sck() const {
return sck;
}
uint ST7789::get_mosi() const {
return mosi;
}
uint ST7789::get_bl() const {
return bl;
uint32_t sm_stall_mask = 1u << (parallel_sm + PIO_FDEBUG_TXSTALL_LSB);
parallel_pio->fdebug = sm_stall_mask;
while (!(parallel_pio->fdebug & sm_stall_mask))
;
/*uint32_t mask = 0xff << d0;
while(len--) {
gpio_put(wr_sck, false);
uint8_t v = *src++;
gpio_put_masked(mask, v << d0);
//asm("nop;");
gpio_put(wr_sck, true);
asm("nop;");
}*/
}
void ST7789::command(uint8_t command, size_t len, const char *data) {
gpio_put(cs, 0);
gpio_put(dc, 0); // command mode
spi_write_blocking(spi, &command, 1);
gpio_put(cs, 0);
if(spi) {
spi_write_blocking(spi, &command, 1);
} else {
write_blocking_parallel(&command, 1);
}
if(data) {
gpio_put(dc, 1); // data mode
spi_write_blocking(spi, (const uint8_t*)data, len);
if(spi) {
spi_write_blocking(spi, (const uint8_t*)data, len);
} else {
write_blocking_parallel((const uint8_t*)data, len);
}
}
gpio_put(cs, 1);
}
void ST7789::update(PicoGraphics *graphics) {
uint8_t cmd = reg::RAMWR;
void ST7789::update() {
command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)frame_buffer);
if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native
command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer);
} else if(spi) { // SPI Bus
gpio_put(dc, 0); // command mode
gpio_put(cs, 0);
spi_write_blocking(spi, &cmd, 1);
gpio_put(dc, 1); // data mode
graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) {
spi_write_blocking(spi, (const uint8_t*)data, length);
});
gpio_put(cs, 1);
} else { // Parallel Bus
gpio_put(dc, 0); // command mode
gpio_put(cs, 0);
write_blocking_parallel(&cmd, 1);
gpio_put(dc, 1); // data mode
int scanline = 0;
graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this, scanline](void *data, size_t length) mutable {
write_blocking_parallel_dma((const uint8_t*)data, length);
// Stall on the last scanline since "data" goes out of scope and is lost
scanline++;
if(scanline == height) {
while (dma_channel_is_busy(parallel_dma))
;
}
});
gpio_put(cs, 1);
}
}
void ST7789::set_backlight(uint8_t brightness) {
@ -209,8 +325,4 @@ namespace pimoroni {
uint16_t value = (uint16_t)(pow((float)(brightness) / 255.0f, gamma) * 65535.0f + 0.5f);
pwm_set_gpio_level(bl, value);
}
void ST7789::flip(){
configure_display(true);
}
}

Wyświetl plik

@ -1,32 +1,46 @@
#pragma once
#include "hardware/spi.h"
#include "hardware/dma.h"
#include "hardware/gpio.h"
#include "hardware/pio.h"
#include "hardware/pwm.h"
#include "../../common/pimoroni_common.hpp"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "st7789_parallel.pio.h"
#include <algorithm>
namespace pimoroni {
class ST7789 {
class ST7789 : public DisplayDriver {
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
public:
bool round;
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
// screen properties
uint16_t width;
uint16_t height;
bool round;
// interface pins with our standard defaults where appropriate
uint cs;
uint dc;
uint sck;
uint mosi;
uint wr_sck;
uint rd_sck = PIN_UNUSED;
uint d0;
uint bl;
uint vsync = PIN_UNUSED; // only available on some products
uint parallel_sm;
PIO parallel_pio;
uint parallel_offset;
uint parallel_dma;
// The ST7789 requires 16 ns between SPI rising edges.
// 16 ns = 62,500,000 Hz
@ -34,87 +48,83 @@ namespace pimoroni {
public:
// frame buffer where pixel data is stored
uint16_t *frame_buffer;
// Parallel init
ST7789(uint16_t width, uint16_t height, Rotation rotation, ParallelPins pins) :
DisplayDriver(width, height, rotation),
spi(nullptr), round(false),
cs(pins.cs), dc(pins.dc), wr_sck(pins.wr_sck), rd_sck(pins.rd_sck), d0(pins.d0), bl(pins.bl) {
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 bl = PIN_UNUSED) :
spi(spi),
width(width), height(height), round(round),
cs(cs), dc(dc), sck(sck), mosi(mosi), bl(bl), frame_buffer(frame_buffer) {
parallel_pio = pio1;
parallel_sm = pio_claim_unused_sm(parallel_pio, true);
parallel_offset = pio_add_program(parallel_pio, &st7789_parallel_program);
//gpio_init(wr_sck);
//gpio_set_dir(wr_sck, GPIO_OUT);
//gpio_set_function(wr_sck, GPIO_FUNC_SIO);
pio_gpio_init(parallel_pio, wr_sck);
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
}
gpio_set_function(rd_sck, GPIO_FUNC_SIO);
gpio_set_dir(rd_sck, GPIO_OUT);
for(auto i = 0u; i < 8; i++) {
//gpio_set_function(d0 + i, GPIO_FUNC_SIO);
//gpio_set_dir(d0 + i, GPIO_OUT);
//gpio_init(d0 + 0); gpio_set_dir(d0 + i, GPIO_OUT);
pio_gpio_init(parallel_pio, d0 + i);
}
pio_sm_set_consecutive_pindirs(parallel_pio, parallel_sm, d0, 8, true);
pio_sm_set_consecutive_pindirs(parallel_pio, parallel_sm, wr_sck, 1, true);
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void init();
void configure_display(bool rotate180);
pio_sm_config c = st7789_parallel_program_get_default_config(parallel_offset);
spi_inst_t* get_spi() const;
uint get_cs() const;
uint get_dc() const;
uint get_sck() const;
uint get_mosi() const;
uint get_bl() const;
sm_config_set_out_pins(&c, d0, 8);
sm_config_set_sideset_pins(&c, wr_sck);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
sm_config_set_out_shift(&c, false, true, 8);
sm_config_set_clkdiv(&c, 4);
pio_sm_init(parallel_pio, parallel_sm, parallel_offset, &c);
pio_sm_set_enabled(parallel_pio, parallel_sm, true);
parallel_dma = dma_claim_unused_channel(true);
dma_channel_config config = dma_channel_get_default_config(parallel_dma);
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
channel_config_set_bswap(&config, false);
channel_config_set_dreq(&config, pio_get_dreq(parallel_pio, parallel_sm, true));
dma_channel_configure(parallel_dma, &config, &parallel_pio->txf[parallel_sm], NULL, 0, false);
gpio_put(rd_sck, 1);
common_init();
}
// Serial init
ST7789(uint16_t width, uint16_t height, Rotation rotation, bool round, SPIPins pins) :
DisplayDriver(width, height, rotation),
spi(pins.spi), round(round),
cs(pins.cs), dc(pins.dc), wr_sck(pins.sck), d0(pins.mosi), bl(pins.bl) {
// configure spi interface and pins
spi_init(spi, SPI_BAUD);
gpio_set_function(wr_sck, GPIO_FUNC_SPI);
gpio_set_function(d0, GPIO_FUNC_SPI);
common_init();
}
void cleanup() override;
void update(PicoGraphics *graphics) override;
void set_backlight(uint8_t brightness) override;
private:
void common_init();
void configure_display(Rotation rotate);
void write_blocking_parallel_dma(const uint8_t *src, size_t len);
void write_blocking_parallel(const uint8_t *src, size_t len);
void command(uint8_t command, size_t len = 0, const char *data = NULL);
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;
};
};
}

Wyświetl plik

@ -0,0 +1,7 @@
.program st7789_parallel
.side_set 1
.wrap_target
out pins, 8 side 0
nop side 1
.wrap

Wyświetl plik

@ -1,50 +1,52 @@
add_subdirectory(breakout_dotmatrix)
add_subdirectory(breakout_encoder)
add_subdirectory(breakout_ioexpander)
add_subdirectory(breakout_ltr559)
add_subdirectory(breakout_colourlcd160x80)
add_subdirectory(breakout_roundlcd)
add_subdirectory(breakout_rgbmatrix5x5)
add_subdirectory(breakout_matrix11x7)
add_subdirectory(breakout_mics6814)
add_subdirectory(breakout_pmw3901)
add_subdirectory(breakout_potentiometer)
add_subdirectory(breakout_rtc)
add_subdirectory(breakout_trackball)
add_subdirectory(breakout_sgp30)
add_subdirectory(breakout_colourlcd240x240)
add_subdirectory(breakout_msa301)
add_subdirectory(breakout_bme688)
add_subdirectory(breakout_bmp280)
add_subdirectory(breakout_bme280)
add_subdirectory(breakout_as7262)
add_subdirectory(breakout_bh1745)
add_subdirectory(breakout_icp10125)
add_subdirectory(breakout_scd41)
add_subdirectory(breakout_vl53l5cx)
add_subdirectory(breakout_pms5003)
add_subdirectory(pico_display)
add_subdirectory(pico_display_2)
add_subdirectory(pico_unicorn)
add_subdirectory(pico_unicorn_plasma)
add_subdirectory(pico_scroll)
add_subdirectory(pico_enc_explorer)
add_subdirectory(pico_explorer)
add_subdirectory(pico_pot_explorer)
add_subdirectory(pico_explorer_encoder)
add_subdirectory(pico_motor_shim)
add_subdirectory(pico_rgb_keypad)
add_subdirectory(pico_rtc_display)
add_subdirectory(pico_rtc)
add_subdirectory(pico_tof_display)
add_subdirectory(pico_trackball_display)
add_subdirectory(pico_audio)
add_subdirectory(pico_wireless)
add_subdirectory(plasma2040)
add_subdirectory(badger2040)
add_subdirectory(interstate75)
add_subdirectory(servo2040)
add_subdirectory(motor2040)
add_subdirectory(encoder)
add_subdirectory(breakout_dotmatrix)
add_subdirectory(breakout_encoder)
add_subdirectory(breakout_ioexpander)
add_subdirectory(breakout_ltr559)
add_subdirectory(breakout_colourlcd160x80)
add_subdirectory(breakout_roundlcd)
add_subdirectory(breakout_rgbmatrix5x5)
add_subdirectory(breakout_matrix11x7)
add_subdirectory(breakout_mics6814)
add_subdirectory(breakout_pmw3901)
add_subdirectory(breakout_potentiometer)
add_subdirectory(breakout_rtc)
add_subdirectory(breakout_trackball)
add_subdirectory(breakout_sgp30)
add_subdirectory(breakout_colourlcd240x240)
add_subdirectory(breakout_msa301)
add_subdirectory(breakout_bme688)
add_subdirectory(breakout_bmp280)
add_subdirectory(breakout_bme280)
add_subdirectory(breakout_as7262)
add_subdirectory(breakout_bh1745)
add_subdirectory(breakout_icp10125)
add_subdirectory(breakout_scd41)
add_subdirectory(breakout_vl53l5cx)
add_subdirectory(breakout_pms5003)
add_subdirectory(breakout_oled_128x128)
add_subdirectory(pico_display)
add_subdirectory(pico_display_2)
add_subdirectory(pico_unicorn)
add_subdirectory(pico_unicorn_plasma)
add_subdirectory(pico_scroll)
add_subdirectory(pico_enc_explorer)
add_subdirectory(pico_explorer)
add_subdirectory(pico_pot_explorer)
add_subdirectory(pico_explorer_encoder)
add_subdirectory(pico_motor_shim)
add_subdirectory(pico_rgb_keypad)
add_subdirectory(pico_rtc_display)
add_subdirectory(pico_rtc)
add_subdirectory(pico_tof_display)
add_subdirectory(pico_trackball_display)
add_subdirectory(pico_audio)
add_subdirectory(pico_wireless)
add_subdirectory(plasma2040)
add_subdirectory(badger2040)
add_subdirectory(tufty2040)
add_subdirectory(interstate75)
add_subdirectory(servo2040)
add_subdirectory(motor2040)
add_subdirectory(encoder)

Wyświetl plik

@ -10,7 +10,7 @@ pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_enable_stdio_uart(${OUTPUT_NAME} 1)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_as7262 pico_explorer)
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_as7262 pico_explorer pico_graphics st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -4,29 +4,33 @@
#include "breakout_as7262.hpp"
#include "pico_explorer.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
constexpr float INTEGRATION_TIME = 10.0f;
I2C i2c(BOARD::PICO_EXPLORER);
BreakoutAS7262 as7262(&i2c);
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_explorer(buffer);
uint8_t bar_width = PicoExplorer::WIDTH / 6;
uint8_t bar_height = PicoExplorer::HEIGHT;
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
void draw_bar(float scale, uint16_t channel) {
static uint8_t bar_width = st7789.width / 6;
static uint8_t bar_height = st7789.height;
int16_t bar_top = bar_height - (bar_height * scale);
bar_top = std::max((int16_t)0, bar_top);
int16_t current_bar_height = bar_height - bar_top;
pico_explorer.rectangle(Rect(channel * bar_width, bar_top, bar_width, current_bar_height - 1));
graphics.rectangle(Rect(channel * bar_width, bar_top, bar_width, current_bar_height - 1));
}
int main() {
stdio_init_all();
pico_explorer.init();
as7262.init();
uint8_t dev_type = as7262.device_type();
@ -43,9 +47,17 @@ int main() {
as7262.set_indicator_current(AS7262::indicator_current::ma4);
as7262.set_leds(true, true);
Pen BLACK = graphics.create_pen(0, 0, 0);
Pen RED = graphics.create_pen(255, 0, 0);
Pen ORANGE = graphics.create_pen(255, 128, 0);
Pen YELLOW = graphics.create_pen(255, 255, 0);
Pen GREEN = graphics.create_pen(0, 255, 0);
Pen BLUE = graphics.create_pen(0, 0, 255);
Pen VIOLET = graphics.create_pen(255, 0, 255);
while(true) {
pico_explorer.set_pen(0, 0, 0);
pico_explorer.clear();
graphics.set_pen(BLACK);
graphics.clear();
AS7262::reading reading = as7262.read();
printf("R: %f O: %f Y: %f G: %f B: %f V: %f \n",
@ -64,34 +76,34 @@ int main() {
if(reading.blue > m) m = reading.blue;
if(reading.violet > m) m = reading.violet;
pico_explorer.set_pen(0, 0, 0);
pico_explorer.clear();
graphics.set_pen(BLACK);
graphics.clear();
// Red
pico_explorer.set_pen(255, 0, 0);
graphics.set_pen(RED);
draw_bar(reading.red / m, 0);
// Orange
pico_explorer.set_pen(255, 128, 0);
graphics.set_pen(ORANGE);
draw_bar(reading.orange / m, 1);
// Yellow
pico_explorer.set_pen(255, 255, 0);
graphics.set_pen(YELLOW);
draw_bar(reading.yellow / m, 2);
// Green
pico_explorer.set_pen(0, 255, 0);
graphics.set_pen(GREEN);
draw_bar(reading.green / m, 3);
// Blue
pico_explorer.set_pen(0, 0, 255);
graphics.set_pen(BLUE);
draw_bar(reading.blue / m, 4);
// Violet
pico_explorer.set_pen(255, 0, 255);
graphics.set_pen(VIOLET);
draw_bar(reading.violet / m, 5);
pico_explorer.update();
st7789.update(&graphics);
sleep_ms(INTEGRATION_TIME);
}

Wyświetl plik

@ -2,11 +2,11 @@ set(OUTPUT_NAME colourlcd160x80_demo)
add_executable(
${OUTPUT_NAME}
demo.cpp
colorlcd_160x80_demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_colourlcd160x80)
target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_graphics st7735 hardware_spi)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,59 @@
#include <math.h>
#include <vector>
#include "drivers/st7735/st7735.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
ST7735 st7735(160, 80, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenP8 graphics(st7735.width, st7735.height, nullptr);
int main() {
st7735.set_backlight(255);
struct pt {
float x;
float y;
uint8_t r;
float dx;
float dy;
Pen pen;
};
std::vector<pt> shapes;
for(int i = 0; i < 1000; i++) {
pt shape;
shape.x = rand() % graphics.bounds.w;
shape.y = rand() % graphics.bounds.h;
shape.r = (rand() % 10) + 3;
shape.dx = float(rand() % 255) / 128.0f;
shape.dy = float(rand() % 255) / 128.0f;
shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
uint8_t bg = graphics.create_pen(120, 40, 60);
while(true) {
graphics.set_pen(bg);
graphics.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < 0) shape.dx *= -1;
if(shape.x >= graphics.bounds.w) shape.dx *= -1;
if(shape.y < 0) shape.dy *= -1;
if(shape.y >= graphics.bounds.h) shape.dy *= -1;
graphics.set_pen(shape.pen);
graphics.circle(Point(shape.x, shape.y), shape.r);
}
// update screen
st7735.update(&graphics);
}
return 0;
}

Wyświetl plik

@ -1,57 +0,0 @@
#include <math.h>
#include <vector>
#include "breakout_colourlcd160x80.hpp"
using namespace pimoroni;
uint16_t buffer[BreakoutColourLCD160x80::WIDTH * BreakoutColourLCD160x80::HEIGHT];
BreakoutColourLCD160x80 lcd(buffer);
int main() {
lcd.init();
lcd.set_backlight(255);
struct pt {
float x;
float y;
uint8_t r;
float dx;
float dy;
uint16_t pen;
};
std::vector<pt> shapes;
for(int i = 0; i < 1000; i++) {
pt shape;
shape.x = rand() % lcd.bounds.w;
shape.y = rand() % lcd.bounds.h;
shape.r = (rand() % 10) + 3;
shape.dx = float(rand() % 255) / 128.0f;
shape.dy = float(rand() % 255) / 128.0f;
shape.pen = lcd.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
while(true) {
lcd.set_pen(120, 40, 60);
lcd.clear();
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;
lcd.set_pen(shape.pen);
lcd.circle(Point(shape.x, shape.y), shape.r);
}
// update screen
lcd.update();
}
return 0;
}

Wyświetl plik

@ -2,11 +2,11 @@ set(OUTPUT_NAME colourlcd240x240_demo)
add_executable(
${OUTPUT_NAME}
demo.cpp
colorlcd_240x240_demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib generic_st7789)
target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_graphics st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,68 @@
#include <math.h>
#include <vector>
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
const int WIDTH = 240;
const int HEIGHT = 240;
ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
int main() {
st7789.set_backlight(255);
struct pt {
float x;
float y;
uint8_t r;
float dx;
float dy;
Pen pen;
};
std::vector<pt> shapes;
for(int i = 0; i < 100; i++) {
pt shape;
shape.r = (rand() % 10) + 3;
shape.x = rand() % (graphics.bounds.w - (shape.r * 2));
shape.y = rand() % (graphics.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 = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
Pen BG = graphics.create_pen(120, 40, 60);
Pen WHITE = graphics.create_pen(255, 255, 255);
while(true) {
graphics.set_pen(BG);
graphics.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < shape.r) shape.dx *= -1;
if(shape.x >= graphics.bounds.w - shape.r) shape.dx *= -1;
if(shape.y < shape.r) shape.dy *= -1;
if(shape.y >= graphics.bounds.h - shape.r) shape.dy *= -1;
graphics.set_pen(shape.pen);
graphics.circle(Point(shape.x, shape.y), shape.r);
}
graphics.set_pen(WHITE);
graphics.text("Hello World", Point(0, 0), 240);
// update screen
st7789.update(&graphics);
}
return 0;
}

Wyświetl plik

@ -1,64 +0,0 @@
#include <math.h>
#include <vector>
#include "generic_st7789.hpp"
using namespace pimoroni;
const int WIDTH = 240;
const int HEIGHT = 240;
ST7789Generic lcd(WIDTH, HEIGHT, false, nullptr, BG_SPI_FRONT);
int main() {
//lcd.configure_display(false);
lcd.set_backlight(255);
struct pt {
float x;
float y;
uint8_t r;
float dx;
float dy;
uint16_t pen;
};
std::vector<pt> shapes;
for(int i = 0; i < 100; i++) {
pt shape;
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);
shapes.push_back(shape);
}
while(true) {
lcd.set_pen(120, 40, 60);
lcd.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
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();
}
return 0;
}

Wyświetl plik

@ -0,0 +1,12 @@
set(OUTPUT_NAME oled_128x128_demo)
add_executable(
${OUTPUT_NAME}
oled_128x128_demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_graphics sh1107)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,52 @@
#include <string.h>
#include <math.h>
#include <vector>
#include <cstdlib>
#include "drivers/sh1107/sh1107.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "time.h"
using namespace pimoroni;
const int WIDTH = 128;
const int HEIGHT = 128;
I2C i2c(4, 5);
SH1107 sh1107(WIDTH, HEIGHT, i2c);
PicoGraphics_Pen1Bit graphics(sh1107.width, sh1107.height, nullptr);
int main() {
uint i = 0;
while(true) {
// for(int y = 0; y < 128; y++) {
// for(int x = 0; x < 128; x++) {
// graphics.set_pen(((x + y + i) & 0b1111) > 8 ? 1 : 0);
// graphics.set_pixel({x, y});
// }
// }
graphics.set_pen(1);
graphics.clear();
float s = (sin(i / 10.0f) * 1.0f) + 1.5f;
float a = (cos(i / 10.0f) * 10.0f);
graphics.set_font("gothic");
graphics.set_pen(0);
int w = graphics.measure_text("PIRATES!", s);
graphics.text("PIRATES!", {64 - w / 2, 60}, 0, s, a);
sh1107.update(&graphics);
//sleep_ms(10);
i++;
}
}

Wyświetl plik

@ -2,11 +2,11 @@ set(OUTPUT_NAME roundlcd_demo)
add_executable(
${OUTPUT_NAME}
demo.cpp
roundlcd_demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib generic_st7789)
target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_graphics st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -3,7 +3,8 @@
#include <vector>
#include <cstdlib>
#include "generic_st7789.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "time.h"
// Place a 1.3 Round SPI LCD in the *front* slot of breakout garden.
@ -14,7 +15,8 @@ using namespace pimoroni;
const int WIDTH = 240;
const int HEIGHT = 240;
ST7789Generic display(WIDTH, HEIGHT, true, nullptr, BG_SPI_FRONT);
ST7789 st7789(WIDTH, HEIGHT, ROTATE_0, true, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
constexpr float RADIUS = WIDTH / 2;
@ -37,23 +39,26 @@ Pen from_hsv(float h, float s, float v) {
case 5: r = v; g = p; b = q; break;
}
return display.create_pen(r, g, b);
return graphics.create_pen(r, g, b);
}
int main() {
display.set_backlight(255);
st7789.set_backlight(255);
uint32_t steps = 70;
float angle_step = 0.5f;
Pen BLACK = graphics.create_pen(0, 0, 0);
Pen WHITE = graphics.create_pen(255, 255, 255);
while(1) {
absolute_time_t at = get_absolute_time();
uint64_t t = to_us_since_boot(at) / 100000;
float angle = (t % 360) * M_PI / 180.0f;
display.set_pen(0, 0, 0);
display.clear();
display.set_pen(255, 255, 255);
graphics.set_pen(BLACK);
graphics.clear();
graphics.set_pen(WHITE);
for(auto step = 0u; step < steps; step++) {
auto distance = RADIUS / steps * step;
@ -68,11 +73,11 @@ int main() {
auto p = from_hsv((t / 10.0f) + distance / 120.0f, 1.0, 1.0);
display.set_pen(p);
display.circle(Point(x, y), l);
graphics.set_pen(p);
graphics.circle(Point(x, y), l);
}
display.update();
st7789.update(&graphics);
sleep_ms(10);
}
}

Wyświetl plik

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

Wyświetl plik

@ -4,22 +4,20 @@
#include <cstdlib>
#include "pico_display.hpp"
#include "generic_st7789.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "rgbled.hpp"
using namespace pimoroni;
const bool ROTATE_180 = false;
// Swap WIDTH and HEIGHT to rotate 90 degrees
ST7789Generic pico_display(PicoDisplay::WIDTH, PicoDisplay::HEIGHT);
ST7789 st7789(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B);
int main() {
pico_display.configure_display(ROTATE_180);
pico_display.set_backlight(100);
st7789.set_backlight(100);
struct pt {
float x;
@ -38,25 +36,30 @@ int main() {
shape.r = (rand() % 10) + 3;
shape.dx = float(rand() % 255) / 128.0f;
shape.dy = float(rand() % 255) / 128.0f;
shape.pen = pico_display.create_pen(rand() % 255, rand() % 255, rand() % 255);
shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
uint32_t i = 0;
Pen BG = graphics.create_pen(120, 40, 60);
Pen YELLOW = graphics.create_pen(255, 255, 0);
Pen TEAL = graphics.create_pen(0, 255, 255);
Pen WHITE = graphics.create_pen(255, 255, 255);
while(true) {
pico_display.set_pen(120, 40, 60);
pico_display.clear();
graphics.set_pen(BG);
graphics.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < 0) shape.dx *= -1;
if(shape.x >= pico_display.bounds.w) shape.dx *= -1;
if(shape.x >= graphics.bounds.w) shape.dx *= -1;
if(shape.y < 0) shape.dy *= -1;
if(shape.y >= pico_display.bounds.h) shape.dy *= -1;
if(shape.y >= graphics.bounds.h) shape.dy *= -1;
pico_display.set_pen(shape.pen);
pico_display.circle(Point(shape.x, shape.y), shape.r);
graphics.set_pen(shape.pen);
graphics.circle(Point(shape.x, shape.y), shape.r);
}
float led_step = fmod(i / 20.0f, M_PI * 2.0f);
@ -72,17 +75,17 @@ int main() {
poly.push_back(Point(50, 85));
poly.push_back(Point(30, 45));
pico_display.set_pen(255, 255, 0);
graphics.set_pen(YELLOW);
//pico_display.pixel(Point(0, 0));
pico_display.polygon(poly);
graphics.polygon(poly);
pico_display.set_pen(0, 255, 255);
pico_display.triangle(Point(50, 50), Point(130, 80), Point(80, 110));
graphics.set_pen(TEAL);
graphics.triangle(Point(50, 50), Point(130, 80), Point(80, 110));
pico_display.set_pen(255, 255, 255);
pico_display.line(Point(50, 50), Point(120, 80));
pico_display.line(Point(20, 20), Point(120, 20));
pico_display.line(Point(20, 20), Point(20, 120));
graphics.set_pen(WHITE);
graphics.line(Point(50, 50), Point(120, 80));
graphics.line(Point(20, 20), Point(120, 20));
graphics.line(Point(20, 20), Point(20, 120));
for(int r = 0; r < 30; r++) {
for(int j = 0; j < 10; j++) {
@ -91,12 +94,12 @@ int main() {
rads += (float(j) / 100.0f);
float cx = sin(rads) * 300.0f;
float cy = cos(rads) * 300.0f;
pico_display.line(Point(120, 67), Point(cx + 120, cy + 67));
graphics.line(Point(120, 67), Point(cx + 120, cy + 67));
}
}
// update screen
pico_display.update();
st7789.update(&graphics);
sleep_ms(1000 / 60);
i++;
}

Wyświetl plik

@ -2,11 +2,11 @@ set(OUTPUT_NAME pico_display2_demo)
add_executable(
${OUTPUT_NAME}
demo.cpp
pico_display_2_demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button pico_display_2 generic_st7789)
target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button pico_display_2 st7789 pico_graphics)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -3,17 +3,16 @@
#include <vector>
#include <cstdlib>
#include "pico_display_2.hpp"
#include "generic_st7789.hpp"
#include "libraries/pico_display_2/pico_display_2.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "rgbled.hpp"
#include "button.hpp"
using namespace pimoroni;
const bool ROTATE_180 = false;
// Swap WIDTH and HEIGHT to rotate 90 degrees
ST7789Generic pico_display(240, 240);
ST7789 st7789(320, 240, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
RGBLED led(PicoDisplay2::LED_R, PicoDisplay2::LED_G, PicoDisplay2::LED_B);
@ -43,8 +42,7 @@ void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
}
int main() {
pico_display.configure_display(ROTATE_180);
pico_display.set_backlight(255);
st7789.set_backlight(255);
struct pt {
float x;
@ -58,17 +56,20 @@ int main() {
std::vector<pt> shapes;
for(int i = 0; i < 100; i++) {
pt shape;
shape.x = rand() % pico_display.bounds.w;
shape.y = rand() % pico_display.bounds.h;
shape.x = rand() % graphics.bounds.w;
shape.y = rand() % graphics.bounds.h;
shape.r = (rand() % 10) + 3;
shape.dx = float(rand() % 255) / 64.0f;
shape.dy = float(rand() % 255) / 64.0f;
shape.pen = pico_display.create_pen(rand() % 255, rand() % 255, rand() % 255);
shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
Point text_location(0, 0);
Pen BG = graphics.create_pen(120, 40, 60);
Pen WHITE = graphics.create_pen(255, 255, 255);
while(true) {
if(button_a.raw()) text_location.x -= 1;
if(button_b.raw()) text_location.x += 1;
@ -76,8 +77,8 @@ int main() {
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();
graphics.set_pen(BG);
graphics.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
@ -86,21 +87,21 @@ int main() {
shape.dx *= -1;
shape.x = shape.r;
}
if((shape.x + shape.r) >= pico_display.bounds.w) {
if((shape.x + shape.r) >= graphics.bounds.w) {
shape.dx *= -1;
shape.x = pico_display.bounds.w - shape.r;
shape.x = graphics.bounds.w - shape.r;
}
if((shape.y - shape.r) < 0) {
shape.dy *= -1;
shape.y = shape.r;
}
if((shape.y + shape.r) >= pico_display.bounds.h) {
if((shape.y + shape.r) >= graphics.bounds.h) {
shape.dy *= -1;
shape.y = pico_display.bounds.h - shape.r;
shape.y = graphics.bounds.h - shape.r;
}
pico_display.set_pen(shape.pen);
pico_display.circle(Point(shape.x, shape.y), shape.r);
graphics.set_pen(shape.pen);
graphics.circle(Point(shape.x, shape.y), shape.r);
}
@ -112,11 +113,11 @@ int main() {
led.set_rgb(r, g, b);
pico_display.set_pen(255, 255, 255);
pico_display.text("Hello World", text_location, 320);
graphics.set_pen(WHITE);
graphics.text("Hello World", text_location, 320);
// update screen
pico_display.update();
st7789.update(&graphics);
}
return 0;

Wyświetl plik

@ -2,11 +2,11 @@ set(OUTPUT_NAME encoder_explorer)
add_executable(
${OUTPUT_NAME}
demo.cpp
pico_enc_explorer.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_encoder pico_explorer)
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_encoder pico_explorer pico_graphics st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -6,11 +6,18 @@
#include "pico_explorer.hpp"
#include "breakout_encoder.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_explorer(buffer);
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
Pen BLACK = graphics.create_pen(0, 0, 0);
Pen RED = graphics.create_pen(255, 0, 0);
Pen GREEN = graphics.create_pen(0, 255, 0);
Pen BLUE = graphics.create_pen(0, 0, 255);
static const uint8_t STEPS_PER_REV = 24;
@ -44,47 +51,49 @@ void count_changed(int16_t count) {
from_hsv(h, 1.0f, 1.0f, r, g, b);
enc.set_led(r, g, b);
pico_explorer.set_pen(0, 0, 0);
pico_explorer.clear();
graphics.set_pen(BLACK);
graphics.clear();
{
pico_explorer.set_pen(255, 0, 0);
graphics.set_pen(RED);
std::ostringstream ss;
ss << "R = ";
ss << (int)r;
std::string s(ss.str());
pico_explorer.text(s, Point(10, 10), 220, 6);
graphics.text(s, Point(10, 10), 220, 6);
}
{
pico_explorer.set_pen(0, 255, 0);
graphics.set_pen(GREEN);
std::ostringstream ss;
ss << "G = ";
ss << (int)g;
std::string s(ss.str());
pico_explorer.text(s, Point(10, 70), 220, 6);
graphics.text(s, Point(10, 70), 220, 6);
}
{
pico_explorer.set_pen(0, 0, 255);
graphics.set_pen(BLUE);
std::ostringstream ss;
ss << "B = ";
ss << (int)b;
std::string s(ss.str());
pico_explorer.text(s, Point(10, 130), 220, 6);
graphics.text(s, Point(10, 130), 220, 6);
}
{
pico_explorer.set_pen(r, g, b);
// Shouldn't really use create_pen in-line.
// In default (RGB332) palette mode this will lookup the nearest 8-bit colour
graphics.set_pen(graphics.create_pen(r, g, b));
std::ostringstream ss;
ss << "#";
ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)r;
ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)g;
ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)b;
std::string s(ss.str());
pico_explorer.text(s, Point(10, 190), 220, 5);
graphics.text(s, Point(10, 190), 220, 5);
}
pico_explorer.update();
st7789.update(&graphics);
}
int main() {
@ -93,9 +102,6 @@ int main() {
stdio_init_all();
pico_explorer.init();
pico_explorer.update();
int16_t count = 0;
if(enc.init()) {
printf("Encoder found...\n");

Wyświetl plik

@ -1,21 +1,21 @@
add_executable(
explorer
demo.cpp
pico_explorer_demo
pico_explorer_demo.cpp
)
add_resource(explorer fox.tga)
add_resource(pico_explorer_demo fox.tga)
# Pull in pico libraries that we need
target_link_libraries(explorer pico_stdlib pico_explorer msa301)
target_link_libraries(pico_explorer_demo pico_stdlib pico_explorer msa301 pico_graphics st7789 button motor analog)
# create map/bin/hex file etc.
pico_add_extra_outputs(explorer)
pico_add_extra_outputs(pico_explorer_demo)
add_executable(
text_demo
text_demo.cpp
)
target_link_libraries(text_demo pico_stdlib pico_explorer msa301)
target_link_libraries(text_demo pico_stdlib pico_explorer msa301 pico_graphics st7789)
pico_add_extra_outputs(text_demo)

Wyświetl plik

@ -1,222 +0,0 @@
#include <string.h>
#include <math.h>
#include <vector>
#include <cstdlib>
#include "pico_explorer.hpp"
#include "msa301.hpp"
using namespace pimoroni;
extern unsigned char _binary_fox_tga_start[];
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_explorer(buffer);
MSA301 msa301;
uint8_t arrow[] = {
0b00010000,
0b00110000,
0b01110000,
0b11111111,
0b11111111,
0b01110000,
0b00110000,
0b00010000
};
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_explorer.init();
msa301.init();
struct pt {
float x;
float y;
uint8_t r;
float dx;
float dy;
uint16_t pen;
};
std::vector<pt> shapes;
for(int i = 0; i < 100; i++) {
pt shape;
shape.x = rand() % 240;
shape.y = rand() % 135;
shape.r = (rand() % 10) + 3;
shape.dx = float(rand() % 255) / 128.0f;
shape.dy = float(rand() % 255) / 128.0f;
shape.pen = pico_explorer.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
pico_explorer.set_audio_pin(pico_explorer.GP0);
uint32_t i = 0;
while(true) {
pico_explorer.set_pen(120, 40, 60);
pico_explorer.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < 0) shape.dx *= -1;
if(shape.x >= pico_explorer.bounds.w) shape.dx *= -1;
if(shape.y < 0) shape.dy *= -1;
if(shape.y >= pico_explorer.bounds.h) shape.dy *= -1;
pico_explorer.set_pen(shape.pen);
pico_explorer.circle(Point(shape.x, shape.y), shape.r);
}
float rv = pico_explorer.get_adc(pico_explorer.ADC0);
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(Point(rv * 140 + 50, 110), 20);
pico_explorer.set_pen(rv * 255, 0, 0);
pico_explorer.circle(Point(rv * 140 + 50, 110), 15);
float gv = pico_explorer.get_adc(pico_explorer.ADC1);
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(Point(gv * 140 + 50, 160), 20);
pico_explorer.set_pen(0, gv * 255, 0);
pico_explorer.circle(Point(gv * 140 + 50, 160), 15);
float bv = pico_explorer.get_adc(pico_explorer.ADC2);
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(Point(bv * 140 + 50, 210), 20);
pico_explorer.set_pen(0, 0, bv * 255);
pico_explorer.circle(Point(bv * 140 + 50, 210), 15);
pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, bv);
pico_explorer.set_motor(pico_explorer.MOTOR2, pico_explorer.FORWARD, rv);
pico_explorer.set_tone(440, 0.5);
if(pico_explorer.is_pressed(pico_explorer.A)) {
pico_explorer.set_pen(255, 255, 255);
pico_explorer.character('A', Point(120, 180), 5);
}
if(pico_explorer.is_pressed(pico_explorer.B)) {
pico_explorer.set_pen(255, 255, 255);
pico_explorer.character('B', Point(120, 180), 5);
}
if(pico_explorer.is_pressed(pico_explorer.X)) {
pico_explorer.set_pen(255, 255, 255);
pico_explorer.character('X', Point(120, 180), 5);
}
if(pico_explorer.is_pressed(pico_explorer.Y)) {
pico_explorer.set_pen(255, 255, 255);
pico_explorer.character('Y', Point(120, 180), 5);
}
float tyoff = cos(i / 20.0f) * 50.0f - 50.0f;
Rect text_box(10, 10, 150, 150);
pico_explorer.set_pen(55, 65, 75);
pico_explorer.rectangle(text_box);
text_box.deflate(10);
pico_explorer.set_clip(text_box);
pico_explorer.set_pen(255, 255, 255);
pico_explorer.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", Point(text_box.x, text_box.y + tyoff), 100);
float xoff = sin(i / 20.0f) * 50.0f;
xoff += 120 - (81 / 2);
float yoff = cos(i / 20.0f) * 50.0f;
yoff += 120 - (68 / 2);
for(int y = 0; y < 68; y++) {
// uint16_t *dest = pico_explorer.frame_buffer + (y * 240);
uint8_t *src = _binary_fox_tga_start + 18 + (y * 81 * 3);
for(int x = 0; x < 81; x++) {
uint8_t b = *src++;
uint8_t g = *src++;
uint8_t r = *src++;
pico_explorer.set_pen(r, g, b);
pico_explorer.pixel(Point(x + xoff, 68 - y + yoff));
}
}
pico_explorer.remove_clip();
pico_explorer.set_pen(255, 255, 255);
pico_explorer.text("x: " + std::to_string(int(msa301.get_axis(msa301.X, 16) * 100)), Point(10, 190), 100);
pico_explorer.text("y: " + std::to_string(int(msa301.get_axis(msa301.Y, 16) * 100)), Point(10, 205), 100);
pico_explorer.text("z: " + std::to_string(int(msa301.get_axis(msa301.Z, 16) * 100)), Point(10, 220), 100);
uint16_t xpos = (msa301.get_axis(msa301.X, 16) * 120) + 120;
uint16_t ypos = (msa301.get_axis(msa301.Z, 16) * 120) + 120;
pico_explorer.set_pen(255, 255, 255);
pico_explorer.circle(Point(xpos, ypos), 20);
pico_explorer.set_pen(255, 0, 255);
pico_explorer.circle(Point(xpos, ypos), 15);
/*
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_explorer.update();
i++;
}
return 0;
}

Wyświetl plik

@ -0,0 +1,215 @@
#include <string.h>
#include <math.h>
#include <vector>
#include <cstdlib>
#include "pico_explorer.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "button.hpp"
#include "motor.hpp"
#include "msa301.hpp"
#include "analog.hpp"
using namespace pimoroni;
using namespace motor;
extern unsigned char _binary_fox_tga_start[];
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
// Buttons
Button button_a(PicoExplorer::A);
Button button_b(PicoExplorer::B);
Button button_x(PicoExplorer::X);
Button button_y(PicoExplorer::Y);
Motor motor1(PicoExplorer::MOTOR1_PINS);
Motor motor2(PicoExplorer::MOTOR2_PINS);
Analog adc0(PicoExplorer::ADC0_PIN);
Analog adc1(PicoExplorer::ADC1_PIN);
Analog adc2(PicoExplorer::ADC2_PIN);
MSA301 msa301;
uint8_t arrow[] = {
0b00010000,
0b00110000,
0b01110000,
0b11111111,
0b11111111,
0b01110000,
0b00110000,
0b00010000
};
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() {
msa301.init();
motor1.init();
motor2.init();
struct pt {
float x;
float y;
uint8_t r;
float dx;
float dy;
uint16_t pen;
};
std::vector<pt> shapes;
for(int i = 0; i < 100; i++) {
pt shape;
shape.x = rand() % 240;
shape.y = rand() % 135;
shape.r = (rand() % 10) + 3;
shape.dx = float(rand() % 255) / 128.0f;
shape.dy = float(rand() % 255) / 128.0f;
shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
Pen BG = graphics.create_pen(120, 40, 60);
Pen WHITE = graphics.create_pen(255, 255, 255);
Pen BOX = graphics.create_pen(55, 65, 75);
Pen PURPLE = graphics.create_pen(255, 0, 255);
uint32_t i = 0;
while(true) {
graphics.set_pen(BG);
graphics.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < 0) shape.dx *= -1;
if(shape.x >= graphics.bounds.w) shape.dx *= -1;
if(shape.y < 0) shape.dy *= -1;
if(shape.y >= graphics.bounds.h) shape.dy *= -1;
graphics.set_pen(shape.pen);
graphics.circle(Point(shape.x, shape.y), shape.r);
}
float rv = adc0.read_voltage() / 3.3f;
graphics.set_pen(WHITE);
graphics.circle(Point(rv * 140 + 50, 110), 20);
graphics.set_pen(graphics.create_pen(rv * 255, 0, 0));
graphics.circle(Point(rv * 140 + 50, 110), 15);
float gv = adc1.read_voltage() / 3.3f;
graphics.set_pen(WHITE);
graphics.circle(Point(gv * 140 + 50, 160), 20);
graphics.set_pen(graphics.create_pen(0, gv * 255, 0));
graphics.circle(Point(gv * 140 + 50, 160), 15);
float bv = adc2.read_voltage() / 3.3f;
graphics.set_pen(WHITE);
graphics.circle(Point(bv * 140 + 50, 210), 20);
graphics.set_pen(graphics.create_pen(0, 0, bv * 255));
graphics.circle(Point(bv * 140 + 50, 210), 15);
motor1.speed(bv);
motor2.speed(rv);
// TODO make this work
// display.set_tone(440, 0.5);
if(button_a.read()) {
graphics.set_pen(WHITE);
graphics.character('A', Point(120, 180), 5);
}
if(button_b.read()) {
graphics.set_pen(WHITE);
graphics.character('B', Point(120, 180), 5);
}
if(button_x.read()) {
graphics.set_pen(WHITE);
graphics.character('X', Point(120, 180), 5);
}
if(button_y.read()) {
graphics.set_pen(WHITE);
graphics.character('Y', Point(120, 180), 5);
}
float tyoff = cos(i / 20.0f) * 50.0f - 50.0f;
Rect text_box(10, 10, 150, 150);
graphics.set_pen(BOX);
graphics.rectangle(text_box);
text_box.deflate(10);
graphics.set_clip(text_box);
graphics.set_pen(WHITE);
graphics.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", Point(text_box.x, text_box.y + tyoff), 100);
float xoff = sin(i / 20.0f) * 50.0f;
xoff += 120 - (81 / 2);
float yoff = cos(i / 20.0f) * 50.0f;
yoff += 120 - (68 / 2);
for(int y = 0; y < 68; y++) {
uint8_t *src = _binary_fox_tga_start + 18 + (y * 81 * 3);
for(int x = 0; x < 81; x++) {
uint8_t b = *src++;
uint8_t g = *src++;
uint8_t r = *src++;
graphics.set_pen(graphics.create_pen(r, g, b));
graphics.pixel(Point(x + xoff, 68 - y + yoff));
}
}
graphics.remove_clip();
graphics.set_pen(WHITE);
graphics.text("x: " + std::to_string(int(msa301.get_axis(msa301.X, 16) * 100)), Point(10, 190), 100);
graphics.text("y: " + std::to_string(int(msa301.get_axis(msa301.Y, 16) * 100)), Point(10, 205), 100);
graphics.text("z: " + std::to_string(int(msa301.get_axis(msa301.Z, 16) * 100)), Point(10, 220), 100);
uint16_t xpos = (msa301.get_axis(msa301.X, 16) * 120) + 120;
uint16_t ypos = (msa301.get_axis(msa301.Z, 16) * 120) + 120;
graphics.set_pen(WHITE);
graphics.circle(Point(xpos, ypos), 20);
graphics.set_pen(PURPLE);
graphics.circle(Point(xpos, ypos), 15);
// update screen
st7789.update(&graphics);
i++;
}
return 0;
}

Wyświetl plik

@ -4,6 +4,9 @@
#include <cstdlib>
#include "pico_explorer.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "font6_data.hpp"
#include "font8_data.hpp"
#include "msa301.hpp"
@ -12,29 +15,32 @@ using namespace pimoroni;
extern unsigned char _binary_fox_tga_start[];
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_explorer(buffer);
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
MSA301 msa301;
int main() {
pico_explorer.init();
pico_explorer.set_font(&font8);
graphics.set_font(&font8);
msa301.init();
Pen BG = graphics.create_pen(120, 40, 60);
Pen WHITE = graphics.create_pen(255, 255, 255);
uint32_t i = 0;
while(true) {
pico_explorer.set_pen(120, 40, 60);
pico_explorer.clear();
graphics.set_pen(BG);
graphics.clear();
pico_explorer.set_pen(255, 255, 255);
pico_explorer.set_font(&font6);
pico_explorer.text("6x6: The quick, brown fox jumps over the lazy dog! UPPER. lower.", Point(10, 10), 220);
pico_explorer.text("0123456789 !$%^&*()", Point(10, 70), 220);
pico_explorer.set_font(&font8);
pico_explorer.text("6x8: The quick, brown fox jumps over the lazy dog! UPPER. lower.", Point(10, 120), 220);
pico_explorer.text("0123456789 !$%^&*()", Point(10, 180), 220);
graphics.set_pen(WHITE);
graphics.set_font(&font6);
graphics.text("6x6: The quick, brown fox jumps over the lazy dog! UPPER. lower.", Point(10, 10), 220);
graphics.text("0123456789 !$%^&*()", Point(10, 70), 220);
graphics.set_font(&font8);
graphics.text("6x8: The quick, brown fox jumps over the lazy dog! UPPER. lower.", Point(10, 120), 220);
graphics.text("0123456789 !$%^&*()", Point(10, 180), 220);
pico_explorer.update();
st7789.update(&graphics);
i++;
}

Wyświetl plik

@ -1,12 +1,12 @@
add_executable(
explorerencoder
demo.cpp
pico_explorer_encoder
pico_explorer_encoder.cpp
)
pico_generate_pio_header(explorerencoder ${CMAKE_CURRENT_LIST_DIR}/quadrature_out.pio)
pico_generate_pio_header(pico_explorer_encoder ${CMAKE_CURRENT_LIST_DIR}/quadrature_out.pio)
# Pull in pico libraries that we need
target_link_libraries(explorerencoder pico_stdlib pico_explorer encoder)
target_link_libraries(pico_explorer_encoder pico_stdlib pico_explorer encoder button motor pico_graphics st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(explorerencoder)
pico_add_extra_outputs(pico_explorer_encoder)

Wyświetl plik

@ -4,6 +4,9 @@
#include "pico/stdlib.h"
#include "encoder.hpp"
#include "quadrature_out.pio.h"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "button.hpp"
/*
An interactive demo of how rotary encoders work.
@ -31,6 +34,7 @@ be used by jumping GP0 to GP6 and GP1 to GP7.
using namespace pimoroni;
using namespace encoder;
using namespace motor;
//--------------------------------------------------
// Constants
@ -90,8 +94,15 @@ enum DrawState {
//--------------------------------------------------
// Variables
//--------------------------------------------------
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_explorer(buffer);
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
Button button_a(PicoExplorer::A);
Button button_b(PicoExplorer::B);
Button button_x(PicoExplorer::X);
Button button_y(PicoExplorer::Y);
Motor motor1(PicoExplorer::MOTOR1_PINS);
Encoder enc(pio0, 0, ENCODER_PINS, ENCODER_COMMON_PIN, NORMAL_DIR, COUNTS_PER_REV, COUNT_MICROSTEPS, FREQ_DIVIDER);
@ -166,13 +177,13 @@ uint32_t draw_plot(Point p1, Point p2, volatile bool (&readings)[READINGS_SIZE],
switch(draw_state) {
case DRAW_TRANSITION:
for(uint8_t y = p1.y; y < p2.y; y++)
pico_explorer.pixel(Point(x + p1.x, y));
graphics.pixel(Point(x + p1.x, y));
break;
case DRAW_HIGH:
pico_explorer.pixel(Point(x + p1.x, p1.y));
graphics.pixel(Point(x + p1.x, p1.y));
break;
case DRAW_LOW:
pico_explorer.pixel(Point(x + p1.x, p2.y - 1));
graphics.pixel(Point(x + p1.x, p2.y - 1));
break;
}
}
@ -215,11 +226,7 @@ void setup() {
gpio_pull_down(ENCODER_SWITCH_PIN);
}
pico_explorer.init();
pico_explorer.set_pen(0);
pico_explorer.clear();
pico_explorer.update();
motor1.init();
enc.init();
bool_pair state = enc.state();
@ -243,6 +250,7 @@ void setup() {
// MAIN
////////////////////////////////////////////////////////////////////////////////////////////////////
int main() {
Pen WHITE = graphics.create_pen(255, 255, 255);
// Perform the main setup for the demo
setup();
@ -273,18 +281,18 @@ int main() {
Encoder::Capture capture = enc.capture();
// Spin Motor 1 either clockwise or counterclockwise depending on if B or Y are pressed
if(pico_explorer.is_pressed(PicoExplorer::B) && !pico_explorer.is_pressed(PicoExplorer::Y)) {
pico_explorer.set_motor(PicoExplorer::MOTOR1, PicoExplorer::FORWARD, 1.0f);
if(button_b.read() && !button_y.read()) {
motor1.speed(1.0f); // Full forward
}
else if(pico_explorer.is_pressed(PicoExplorer::Y) && !pico_explorer.is_pressed(PicoExplorer::B)) {
pico_explorer.set_motor(PicoExplorer::MOTOR1, PicoExplorer::REVERSE, 0.2f);
else if(button_y.read() && !button_b.read()) {
motor1.speed(-0.2f); // Reverse
}
else {
pico_explorer.set_motor(PicoExplorer::MOTOR1, PicoExplorer::STOP);
motor1.speed(0.0f); // Stop
}
// If A has been pressed, zoom the view out to a min of x1
if(pico_explorer.is_pressed(PicoExplorer::A)) {
if(button_a.read()) {
if(!button_latch_a) {
button_latch_a = true;
current_zoom_level = std::max(current_zoom_level / 2, 1);
@ -295,7 +303,7 @@ int main() {
}
// If X has been pressed, zoom the view in to the max of x512
if(pico_explorer.is_pressed(PicoExplorer::X)) {
if(button_x.read()) {
if(!button_latch_x) {
button_latch_x = true;
current_zoom_level = std::min(current_zoom_level * 2, 512);
@ -308,16 +316,16 @@ int main() {
//--------------------------------------------------
// Draw the encoder readings to the screen as a signal plot
pico_explorer.set_pen(0, 0, 0);
pico_explorer.clear();
graphics.set_pen(graphics.create_pen(0, 0, 0));
graphics.clear();
drawing_to_screen = true;
pico_explorer.set_pen(255, 255, 0);
graphics.set_pen(graphics.create_pen(255, 255, 0));
uint32_t local_pos = next_reading_index;
uint32_t alignment_offset = draw_plot(Point(0, 10), Point(PicoExplorer::WIDTH, 10 + 50), enc_a_readings, local_pos, current_zoom_level > EDGE_ALIGN_ABOVE_ZOOM);
pico_explorer.set_pen(0, 255, 255);
graphics.set_pen(graphics.create_pen(0, 255, 255));
draw_plot(Point(0, 80), Point(PicoExplorer::WIDTH, 80 + 50), enc_b_readings, (local_pos + (READINGS_SIZE - alignment_offset)) % READINGS_SIZE, false);
// Copy values that may have been stored in the scratch buffers, back into the main buffers
@ -333,49 +341,49 @@ int main() {
drawing_to_screen = false;
next_scratch_index = 0;
pico_explorer.set_pen(255, 255, 255);
pico_explorer.character('A', Point(5, 10 + 15), 3);
pico_explorer.character('B', Point(5, 80 + 15), 3);
graphics.set_pen(WHITE);
graphics.character('A', Point(5, 10 + 15), 3);
graphics.character('B', Point(5, 80 + 15), 3);
if(current_zoom_level < 10)
pico_explorer.text("x" + std::to_string(current_zoom_level), Point(220, 62), 200, 2);
graphics.text("x" + std::to_string(current_zoom_level), Point(220, 62), 200, 2);
else if(current_zoom_level < 100)
pico_explorer.text("x" + std::to_string(current_zoom_level), Point(210, 62), 200, 2);
graphics.text("x" + std::to_string(current_zoom_level), Point(210, 62), 200, 2);
else
pico_explorer.text("x" + std::to_string(current_zoom_level), Point(200, 62), 200, 2);
graphics.text("x" + std::to_string(current_zoom_level), Point(200, 62), 200, 2);
//--------------------------------------------------
// Write out the count, frequency and rpm of the encoder
pico_explorer.set_pen(8, 8, 8);
pico_explorer.rectangle(Rect(0, 140, PicoExplorer::WIDTH, PicoExplorer::HEIGHT - 140));
graphics.set_pen(graphics.create_pen(8, 8, 8));
graphics.rectangle(Rect(0, 140, PicoExplorer::WIDTH, PicoExplorer::HEIGHT - 140));
pico_explorer.set_pen(64, 64, 64);
pico_explorer.rectangle(Rect(0, 140, PicoExplorer::WIDTH, 2));
graphics.set_pen(graphics.create_pen(64, 64, 64));
graphics.rectangle(Rect(0, 140, PicoExplorer::WIDTH, 2));
{
std::stringstream sstream;
sstream << capture.count();
pico_explorer.set_pen(255, 255, 255); pico_explorer.text("Count:", Point(10, 150), 200, 3);
pico_explorer.set_pen(255, 128, 255); pico_explorer.text(sstream.str(), Point(110, 150), 200, 3);
graphics.set_pen(WHITE); graphics.text("Count:", Point(10, 150), 200, 3);
graphics.set_pen(graphics.create_pen(255, 128, 255)); graphics.text(sstream.str(), Point(110, 150), 200, 3);
}
{
std::stringstream sstream;
sstream << std::fixed << std::setprecision(1) << capture.frequency() << "hz";
pico_explorer.set_pen(255, 255, 255); pico_explorer.text("Freq: ", Point(10, 180), 220, 3);
pico_explorer.set_pen(128, 255, 255); pico_explorer.text(sstream.str(), Point(90, 180), 220, 3);
graphics.set_pen(WHITE); graphics.text("Freq: ", Point(10, 180), 220, 3);
graphics.set_pen(graphics.create_pen(128, 255, 255)); graphics.text(sstream.str(), Point(90, 180), 220, 3);
}
{
std::stringstream sstream;
sstream << std::fixed << std::setprecision(1) << capture.revolutions_per_minute();
pico_explorer.set_pen(255, 255, 255); pico_explorer.text("RPM: ", Point(10, 210), 220, 3);
pico_explorer.set_pen(255, 255, 128); pico_explorer.text(sstream.str(), Point(80, 210), 220, 3);
graphics.set_pen(WHITE); graphics.text("RPM: ", Point(10, 210), 220, 3);
graphics.set_pen(graphics.create_pen(255, 255, 128)); graphics.text(sstream.str(), Point(80, 210), 220, 3);
}
pico_explorer.update(); // Refresh the screen
st7789.update(&graphics); // Refresh the screen
gpio_put(PICO_DEFAULT_LED_PIN, false); // Show the screen refresh has ended
}
}

Wyświetl plik

@ -2,11 +2,11 @@ set(OUTPUT_NAME potentiometer_explorer)
add_executable(
${OUTPUT_NAME}
demo.cpp
pico_pot_explorer.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_potentiometer pico_explorer)
target_link_libraries(${OUTPUT_NAME} pico_stdlib breakout_potentiometer pico_explorer pico_graphics st7789)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -7,11 +7,18 @@
#include "common/pimoroni_i2c.hpp"
#include "pico_explorer.hpp"
#include "breakout_potentiometer.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_explorer(buffer);
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
Pen BLACK = graphics.create_pen(0, 0, 0);
Pen RED = graphics.create_pen(255, 0, 0);
Pen GREEN = graphics.create_pen(0, 255, 0);
Pen BLUE = graphics.create_pen(0, 0, 255);
I2C i2c(PICO_EXPLORER);
BreakoutPotentiometer pot(&i2c);
@ -43,9 +50,6 @@ int main() {
stdio_init_all();
pico_explorer.init();
pico_explorer.update();
printf("Starting...\n");
if(pot.init()) {
@ -62,47 +66,49 @@ int main() {
from_hsv(percent, 1.0f, 1.0f, r, g, b);
pot.set_led(r, g, b);
pico_explorer.set_pen(0, 0, 0);
pico_explorer.clear();
graphics.set_pen(BLACK);
graphics.clear();
{
pico_explorer.set_pen(255, 0, 0);
graphics.set_pen(RED);
std::ostringstream ss;
ss << "R = ";
ss << (int)r;
std::string s(ss.str());
pico_explorer.text(s, Point(10, 10), 220, 6);
graphics.text(s, Point(10, 10), 220, 6);
}
{
pico_explorer.set_pen(0, 255, 0);
graphics.set_pen(GREEN);
std::ostringstream ss;
ss << "G = ";
ss << (int)g;
std::string s(ss.str());
pico_explorer.text(s, Point(10, 70), 220, 6);
graphics.text(s, Point(10, 70), 220, 6);
}
{
pico_explorer.set_pen(0, 0, 255);
graphics.set_pen(BLUE);
std::ostringstream ss;
ss << "B = ";
ss << (int)b;
std::string s(ss.str());
pico_explorer.text(s, Point(10, 130), 220, 6);
graphics.text(s, Point(10, 130), 220, 6);
}
{
pico_explorer.set_pen(r, g, b);
// Shouldn't really use create_pen in-line.
// In default (RGB332) palette mode this will lookup the nearest 8-bit colour
graphics.set_pen(graphics.create_pen(r, g, b));
std::ostringstream ss;
ss << "#";
ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)r;
ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)g;
ss << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (int)b;
std::string s(ss.str());
pico_explorer.text(s, Point(10, 190), 220, 5);
graphics.text(s, Point(10, 190), 220, 5);
}
pico_explorer.update();
st7789.update(&graphics);
}
}
else {

Wyświetl plik

@ -2,11 +2,11 @@ set(OUTPUT_NAME rtc_display)
add_executable(
${OUTPUT_NAME}
demo.cpp
pico_rtc_display.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_explorer pico_display breakout_rtc)
target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_explorer pico_display breakout_rtc pico_graphics st7789 button rgbled)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -17,7 +17,7 @@
// To use PicoExplorer rather than PicoDisplay, uncomment the following line
// #define USE_PICO_EXPLORER 1
// This:
// - Includes pico_explorer.hpp rather than pico_display.hpp
// - Includes pico_explorer.hpp rather than display.hpp
// - Replaces all PicoDisplay references with PicoExplorer
// - Leaves out the .set_led() calls in flash_led()
#ifdef USE_PICO_EXPLORER
@ -27,6 +27,11 @@
#endif
#include "breakout_rtc.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "drivers/button/button.hpp"
#include "drivers/rgbled/rgbled.hpp"
#define MODE_DISP_CLOCK 0
#define MODE_DISP_TIMER 1
#define MODE_SET_TIMER 2
@ -36,17 +41,29 @@
using namespace pimoroni;
#ifdef USE_PICO_EXPLORER
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_display(buffer);
uint16_t screen_width = PicoExplorer::WIDTH;
uint16_t screen_height = PicoExplorer::HEIGHT;
Button button_a(PicoExplorer::A);
Button button_b(PicoExplorer::B);
Button button_x(PicoExplorer::X);
Button button_y(PicoExplorer::Y);
#else
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
PicoDisplay pico_display(buffer);
uint16_t screen_width = PicoDisplay::WIDTH;
uint16_t screen_height = PicoDisplay::HEIGHT;
Button button_a(PicoDisplay::A);
Button button_b(PicoDisplay::B);
Button button_x(PicoDisplay::X);
Button button_y(PicoDisplay::Y);
RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B);
#endif
ST7789 st7789(screen_width, screen_height, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
BreakoutRTC rtc;
#define LOW_COUNT_MOD 40
@ -69,17 +86,20 @@ void flash_led(uint32_t curr_count) {
#ifndef USE_PICO_EXPLORER
if((curr_count % FLASH_MOD) < (FLASH_MOD / 2)) {
// value less than half modded number - LED off
pico_display.set_led(0, 0, 0);
led.set_rgb(0, 0, 0);
}
else {
// value more than half modded number - LED on
pico_display.set_led(128, 128, 128);
led.set_rgb(128, 128, 128);
}
#endif
}
int main() {
pico_display.init();
Pen WHITE = graphics.create_pen(255, 255, 255);
Pen BG = graphics.create_pen(55, 65, 75);
Pen RED = graphics.create_pen(255, 0, 0);
Pen GREEN = graphics.create_pen(0, 255, 0);
rtc.init();
// rtc.setup(false);
@ -112,7 +132,7 @@ int main() {
while(true) {
if(a_pressed == 0 && pico_display.is_pressed(pico_display.A)) {
if(a_pressed == 0 && button_a.read()) {
a_pressed = 1;
if(display_mode == MODE_DISP_CLOCK) {
// We were displaying clock = set up timer
@ -134,11 +154,11 @@ int main() {
display_mode = MODE_SET_TIMER;
}
}
else if(a_pressed >= 1 && !pico_display.is_pressed(pico_display.A)) {
else if(a_pressed >= 1 && !button_a.read()) {
a_pressed = 0;
}
if(b_pressed == 0 && pico_display.is_pressed(pico_display.B)) {
if(b_pressed == 0 && button_b.read()) {
b_pressed = 1;
if((display_mode == MODE_DISP_TIMER)
|| (display_mode == MODE_SET_TIMER)) {
@ -150,117 +170,117 @@ int main() {
timer_count = DEFAULT_TIMER_COUNT;
}
}
else if(b_pressed >= 1 && !pico_display.is_pressed(pico_display.B)) {
else if(b_pressed >= 1 && !button_b.read()) {
b_pressed = 0;
}
if(x_pressed == 0 && pico_display.is_pressed(pico_display.X)) {
if(x_pressed == 0 && button_x.read()) {
x_pressed = 1;
if(display_mode == MODE_SET_TIMER) {
// Setting timer - Increment count
timer_count++;
}
}
else if(x_pressed >= 1 && pico_display.is_pressed(pico_display.X)) {
else if(x_pressed >= 1 && button_x.read()) {
// Button still pressed - check if has reached repeat count
if(repeat_count_reached(x_pressed++)) {
timer_count++;
}
}
else if(x_pressed >= 1 && !pico_display.is_pressed(pico_display.X)) {
else if(x_pressed >= 1 && !button_x.read()) {
x_pressed = 0;
}
if(y_pressed == 0 && pico_display.is_pressed(pico_display.Y)) {
if(y_pressed == 0 && button_y.read()) {
y_pressed = 1;
if(display_mode == MODE_SET_TIMER) {
// Setting timer - Decrement count
if (timer_count >= 1) timer_count--;
}
}
else if(y_pressed >= 1 && pico_display.is_pressed(pico_display.Y)) {
else if(y_pressed >= 1 && button_y.read()) {
// Button still pressed - check if has reached repeat count
if(repeat_count_reached(y_pressed++)) {
if(timer_count >= 1)
timer_count--;
}
}
else if(y_pressed >= 1 && !pico_display.is_pressed(pico_display.Y)) {
else if(y_pressed >= 1 && !button_y.read()) {
y_pressed = 0;
}
Rect text_box(5, 5, screen_width-10, screen_height-10);
pico_display.set_pen(55, 65, 75);
pico_display.rectangle(text_box);
graphics.set_pen(BG);
graphics.rectangle(text_box);
// text_box.deflate(10);
pico_display.set_clip(text_box);
pico_display.set_pen(255, 255, 255);
graphics.set_clip(text_box);
graphics.set_pen(WHITE);
switch(display_mode) {
case MODE_DISP_CLOCK:
// Show the clock face
flash_led(0);
if(rtc.update_time()) {
pico_display.text("Set Timer",
graphics.text("Set Timer",
Point(text_box.x, text_box.y+2), 230, 1);
pico_display.set_pen(0, 255, 0);
pico_display.text(rtc.string_date(),
graphics.set_pen(GREEN);
graphics.text(rtc.string_date(),
Point(text_box.x, text_box.y+20), 230, 4);
pico_display.set_pen(255, 0, 0);
pico_display.text(rtc.string_time(),
graphics.set_pen(RED);
graphics.text(rtc.string_time(),
Point(text_box.x, text_box.y+60), 230, 6);
pico_display.set_pen(255, 255, 255);
pico_display.text("Clock",
graphics.set_pen(WHITE);
graphics.text("Clock",
Point(text_box.x, text_box.y+screen_height-20), 230, 1);
}
else {
sprintf(buf, "Time: rtc.updateTime() ret err");
pico_display.text(buf,
graphics.text(buf,
Point(text_box.x, text_box.y), 30, 2);
}
break;
case MODE_DISP_TIMER:
pico_display.text("Set Timer",
graphics.text("Set Timer",
Point(text_box.x, text_box.y+2), 230, 1);
if(rtc.read_timer_interrupt_flag()) {
// Go periodic time interupt - say loop ended
pico_display.set_pen(255, 0, 0);
graphics.set_pen(RED);
sprintf(buf, "%s", "Timer complete");
pico_display.text(buf,
graphics.text(buf,
Point(text_box.x, text_box.y+30), 230, 4);
pico_display.set_pen(255, 255, 255);
graphics.set_pen(WHITE);
flash_led(i);
}
else {
sprintf(buf, "%s %d", "Timer running", rtc.get_timer_count());
pico_display.text(buf,
graphics.text(buf,
Point(text_box.x, text_box.y+30), 230, 3);
}
pico_display.text("Clock",
graphics.text("Clock",
Point(text_box.x, text_box.y+screen_height-20), 230, 1);
break;
case MODE_SET_TIMER:
flash_led(0);
pico_display.text("Run Timer",
graphics.text("Run Timer",
Point(text_box.x, text_box.y+2), 230, 1);
pico_display.text("+ Time",
graphics.text("+ Time",
Point(text_box.x+screen_width-42, text_box.y+2), 230, 1);
sprintf(buf, "Time %d secs", timer_count);
pico_display.text(buf,
graphics.text(buf,
Point(text_box.x, text_box.y+30), 230, 3);
pico_display.text("Clock",
graphics.text("Clock",
Point(text_box.x, text_box.y+screen_height-20), 230, 1);
pico_display.text("- Time",
graphics.text("- Time",
Point(text_box.x+screen_width-42,
text_box.y+screen_height-20), 230, 1);
break;
}
pico_display.remove_clip();
graphics.remove_clip();
// update screen
pico_display.update();
st7789.update(&graphics);
i++;
}

Wyświetl plik

@ -1,10 +1,10 @@
add_executable(
tof_display
demo.cpp
pico_tof_display.cpp
)
# Pull in pico libraries that we need
target_link_libraries(tof_display pico_stdlib pico_explorer pico_display vl53l1x)
target_link_libraries(tof_display pico_stdlib pico_explorer pico_display vl53l1x pico_graphics st7789 rgbled button)
pico_enable_stdio_uart(tof_display 1)

Wyświetl plik

@ -24,62 +24,22 @@
#include "pico_display.hpp"
#endif
#include "vl53l1x.hpp"
#include "drivers/button/button.hpp"
#include "drivers/rgbled/rgbled.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
class AutoRepeat {
public:
AutoRepeat(uint32_t repeat_time=200, uint32_t hold_time=1000) {
this->repeat_time = repeat_time;
this->hold_time = hold_time;
}
bool next(uint32_t time, bool state) {
bool changed = state != last_state;
last_state = state;
if(changed) {
if(state) {
pressed_time = time;
pressed = true;
last_time = time;
return true;
}
else {
pressed_time = 0;
pressed = false;
last_time = 0;
}
}
// Shortcut for no auto-repeat
if(repeat_time == 0) return false;
if(pressed) {
uint32_t repeat_rate = repeat_time;
if(hold_time > 0 && time - pressed_time > hold_time) {
repeat_rate /= 3;
}
if(time - last_time > repeat_rate) {
last_time = time;
return true;
}
}
return false;
}
private:
uint32_t repeat_time;
uint32_t hold_time;
bool pressed = false;
bool last_state = false;
uint32_t pressed_time = 0;
uint32_t last_time = 0;
};
#ifdef USE_PICO_EXPLORER
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_display(buffer);
uint16_t screen_width = PicoExplorer::WIDTH;
uint16_t screen_height = PicoExplorer::HEIGHT;
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
Button button_a(PicoExplorer::A);
Button button_b(PicoExplorer::B);
Button button_x(PicoExplorer::X);
Button button_y(PicoExplorer::Y);
uint16_t disptext_reminder_size = 2;
uint16_t disptext_b_reminder_xoff = 5;
uint16_t disptext_b_reminder_yoff = 210;
@ -94,10 +54,15 @@ uint16_t disptext_dist_xoff = 10;
uint16_t disptext_dist_yoff = 90;
uint16_t disptext_dist_size = 6;
#else
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
PicoDisplay pico_display(buffer);
uint16_t screen_width = PicoDisplay::WIDTH;
uint16_t screen_height = PicoDisplay::HEIGHT;
ST7789 st7789(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
RGBLED rgbled(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B);
Button button_a(PicoDisplay::A);
Button button_b(PicoDisplay::B);
Button button_x(PicoDisplay::X);
Button button_y(PicoDisplay::Y);
uint16_t disptext_reminder_size = 2;
uint16_t disptext_b_reminder_xoff = 2;
uint16_t disptext_b_reminder_yoff = 110;
@ -113,6 +78,11 @@ uint16_t disptext_dist_yoff = 45;
uint16_t disptext_dist_size = 4;
#endif
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
uint16_t screen_width = graphics.bounds.w;
uint16_t screen_height = graphics.bounds.h;
#define MM_TO_INCH 25.4
VL53L1X vl53l1x;
@ -124,11 +94,6 @@ const char mode_to_text[4][7] = {
"Long"
};
AutoRepeat ar_button_a;
AutoRepeat ar_button_b;
AutoRepeat ar_button_x;
AutoRepeat ar_button_y;
#define FLASH_MOD 20
void flash_led(uint32_t curr_count) {
// Flash the LED based on the current loop counter
@ -136,10 +101,10 @@ void flash_led(uint32_t curr_count) {
#ifndef USE_PICO_EXPLORER
if ((curr_count % FLASH_MOD) < (FLASH_MOD / 2)) {
// value less than half modded number - LED off
pico_display.set_led(0, 0, 0);
rgbled.set_rgb(0, 0, 0);
} else {
// value more than half modded number - LED on
pico_display.set_led(128, 128, 128);
rgbled.set_rgb(128, 128, 128);
}
#endif
}
@ -148,8 +113,6 @@ int main() {
bool vl53_present = false;
uint16_t vl53_mode = 1;
pico_display.init();
vl53_present = vl53l1x.init();
uint32_t i = 0;
@ -169,11 +132,15 @@ int main() {
// Whether the display is being held
bool dist_held = false;
Pen WHITE = graphics.create_pen(255, 255, 255);
Pen REDDISH = graphics.create_pen(255, 64, 64);
Pen BG = graphics.create_pen(55, 65, 75);
while(true) {
// bool a_pressed = ar_button_a.next(i, pico_display.is_pressed(pico_display.A));
bool b_pressed = ar_button_b.next(i, pico_display.is_pressed(pico_display.B));
bool x_pressed = ar_button_x.next(i, pico_display.is_pressed(pico_display.X));
bool y_pressed = ar_button_y.next(i, pico_display.is_pressed(pico_display.Y));
// bool a_pressed = button_a.read();
bool b_pressed = button_b.read();
bool x_pressed = button_x.read();
bool y_pressed = button_y.read();
if (b_pressed) {
dist_held = !dist_held;
@ -192,30 +159,30 @@ int main() {
}
Rect text_box(5, 5, screen_width-10, screen_height-10);
pico_display.set_pen(55, 65, 75);
pico_display.rectangle(text_box);
graphics.set_pen(BG);
graphics.rectangle(text_box);
// text_box.deflate(10);
pico_display.set_clip(text_box);
pico_display.set_pen(255, 255, 255);
graphics.set_clip(text_box);
graphics.set_pen(WHITE);
// Show the current distance
flash_led(0);
if (vl53_present) {
pico_display.text("Units",
graphics.text("Units",
Point(text_box.x+disptext_x_reminder_xoff,
text_box.y+disptext_x_reminder_yoff), 230, disptext_reminder_size);
pico_display.text("+Mode",
graphics.text("+Mode",
Point(text_box.x+disptext_y_reminder_xoff,
text_box.y+disptext_y_reminder_yoff), 230, disptext_reminder_size);
if(dist_held) {
pico_display.set_pen(255, 64, 64);
graphics.set_pen(REDDISH);
}
pico_display.text("Hold",
graphics.text("Hold",
Point(text_box.x+disptext_b_reminder_xoff,
text_box.y+disptext_b_reminder_yoff), 230, disptext_reminder_size);
pico_display.set_pen(255, 255, 255);
graphics.set_pen(WHITE);
sprintf(buf, "Mode: %s", mode_to_text[vl53_mode]);
pico_display.text(buf,
graphics.text(buf,
Point(text_box.x+disptext_mode_xoff,
text_box.y+disptext_mode_yoff), 230, disptext_mode_size);
@ -228,19 +195,19 @@ int main() {
sprintf(buf, "%dft %.1fin", ft,
((float)dist/MM_TO_INCH)-ft*12.0);
}
pico_display.text(buf,
graphics.text(buf,
Point(text_box.x+disptext_dist_xoff,
text_box.y+disptext_dist_yoff), 120, disptext_dist_size);
} else {
pico_display.text("VL53L1X Missing",
graphics.text("VL53L1X Missing",
Point(text_box.x+disptext_dist_xoff,
text_box.y+disptext_dist_yoff), 230, disptext_dist_size);
}
pico_display.remove_clip();
graphics.remove_clip();
// update screen
pico_display.update();
st7789.update(&graphics);
i++;
}

Wyświetl plik

@ -2,11 +2,11 @@ set(OUTPUT_NAME trackball_display)
add_executable(
${OUTPUT_NAME}
demo.cpp
pico_trackball_display.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_explorer pico_display breakout_trackball)
target_link_libraries(${OUTPUT_NAME} pico_stdlib pico_explorer pico_display breakout_trackball pico_graphics st7789)
pico_enable_stdio_uart(${OUTPUT_NAME} 1)

Wyświetl plik

@ -16,7 +16,7 @@
// To use PicoExplorer rather than PicoDisplay, uncomment the following line
#define USE_PICO_EXPLORER 1
// This:
// - Includes pico_explorer.hpp rather than pico_display.hpp
// - Includes pico_explorer.hpp rather than display.hpp
// - Replaces all PicoDisplay references with PicoExplorer
#ifdef USE_PICO_EXPLORER
#include "pico_explorer.hpp"
@ -25,6 +25,9 @@
#endif
#include "breakout_trackball.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
using namespace pimoroni;
struct TrackballColour {
@ -35,16 +38,16 @@ struct TrackballColour {
};
#ifdef USE_PICO_EXPLORER
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_display(buffer);
const uint16_t screen_width = PicoExplorer::WIDTH;
const uint16_t screen_height = PicoExplorer::HEIGHT;
#else
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
PicoDisplay pico_display(buffer);
const uint16_t screen_width = PicoDisplay::WIDTH;
const uint16_t screen_height = PicoDisplay::HEIGHT;
#endif
ST7789 st7789(screen_width, screen_height, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
const Point screen_centre(screen_width / 2, screen_height / 2);
const uint16_t circle_radius = std::min(screen_centre.x, screen_centre.y) / 4;
const float ring_radius_mult = 0.7f;
@ -71,7 +74,6 @@ bool centre_circle_state = false;
int main() {
int16_t x = screen_centre.x;
int16_t y = screen_centre.y;
pico_display.init();
trackball.init();
@ -84,52 +86,57 @@ int main() {
positions[i] = pos;
}
Pen WHITE = graphics.create_pen(255, 255, 255);
Pen BLACK = graphics.create_pen(0, 0, 0);
Pen LIGHT_GREY = graphics.create_pen(212, 212, 212);
Pen MID_GREY = graphics.create_pen(128, 128, 128);
while(true) {
Trackball::State state = trackball.read();
x = std::min(std::max(x - state.left + state.right, 0), (int)screen_width);
y = std::min(std::max(y - state.up + state.down, 0), (int)screen_height);
Point cursor_pos(x, y);
pico_display.set_pen(0, 0, 0);
pico_display.clear();
graphics.set_pen(BLACK);
graphics.clear();
//Draw a set of circles in a ring around the screen centre
for(uint8_t i = 0; i < NUM_CIRCLES; i++) {
TrackballColour col = colour_circles[i];
if(circle_states[i]) {
pico_display.set_pen(col.r, col.g, col.b);
pico_display.circle(positions[i], circle_radius + circle_border);
pico_display.set_pen(col.r >> 1, col.g >> 1, col.b >> 1);
pico_display.circle(positions[i], circle_radius);
graphics.set_pen(graphics.create_pen(col.r, col.g, col.b));
graphics.circle(positions[i], circle_radius + circle_border);
graphics.set_pen(graphics.create_pen(col.r >> 1, col.g >> 1, col.b >> 1));
graphics.circle(positions[i], circle_radius);
}
else {
pico_display.set_pen(col.r >> 1, col.g >> 1, col.b >> 1);
pico_display.circle(positions[i], circle_radius + circle_border);
pico_display.set_pen(col.r, col.g, col.b);
pico_display.circle(positions[i], circle_radius);
graphics.set_pen(graphics.create_pen(col.r >> 1, col.g >> 1, col.b >> 1));
graphics.circle(positions[i], circle_radius + circle_border);
graphics.set_pen(graphics.create_pen(col.r, col.g, col.b));
graphics.circle(positions[i], circle_radius);
}
}
//Draw a centre circle
if(centre_circle_state) {
pico_display.set_pen(255, 255, 255);
pico_display.circle(screen_centre, circle_radius + circle_border);
pico_display.set_pen(128, 128, 128);
pico_display.circle(screen_centre, circle_radius);
graphics.set_pen(WHITE);
graphics.circle(screen_centre, circle_radius + circle_border);
graphics.set_pen(MID_GREY);
graphics.circle(screen_centre, circle_radius);
}
else {
pico_display.set_pen(128, 128, 128);
pico_display.circle(screen_centre, circle_radius + circle_border);
pico_display.set_pen(255, 255, 255);
pico_display.circle(screen_centre, circle_radius);
graphics.set_pen(MID_GREY);
graphics.circle(screen_centre, circle_radius + circle_border);
graphics.set_pen(WHITE);
graphics.circle(screen_centre, circle_radius);
}
//Draw the cursor
pico_display.set_pen(0, 0, 0);
pico_display.circle(cursor_pos, cursor_radius + cursor_border);
pico_display.set_pen(212, 212, 212);
pico_display.circle(cursor_pos, cursor_radius);
graphics.set_pen(BLACK);
graphics.circle(cursor_pos, cursor_radius + cursor_border);
graphics.set_pen(LIGHT_GREY);
graphics.circle(cursor_pos, cursor_radius);
int16_t x_diff = cursor_pos.x - screen_centre.x;
int16_t y_diff = cursor_pos.y - screen_centre.y;
@ -161,7 +168,7 @@ int main() {
}
// update screen
pico_display.update();
st7789.update(&graphics);
}
return 0;

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,15 @@
set(OUTPUT_NAME tufty2040_drawing)
add_executable(${OUTPUT_NAME} tufty2040_drawing.cpp)
target_link_libraries(${OUTPUT_NAME}
tufty2040
hardware_spi
pico_graphics
st7789
button
)
# enable usb output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,138 @@
#include "pico/stdlib.h"
#include <stdio.h>
#include <cstring>
#include <string>
#include <algorithm>
#include "pico/time.h"
#include "pico/platform.h"
#include "common/pimoroni_common.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "tufty2040.hpp"
#include "button.hpp"
using namespace pimoroni;
Tufty2040 tufty;
ST7789 st7789(
Tufty2040::WIDTH,
Tufty2040::HEIGHT,
ROTATE_0,
ParallelPins{
Tufty2040::LCD_CS,
Tufty2040::LCD_DC,
Tufty2040::LCD_WR,
Tufty2040::LCD_RD,
Tufty2040::LCD_D0,
Tufty2040::BACKLIGHT
}
);
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
Button button_a(Tufty2040::A);
Button button_b(Tufty2040::B);
Button button_c(Tufty2040::C);
Button button_up(Tufty2040::UP);
Button button_down(Tufty2040::DOWN);
uint32_t time() {
absolute_time_t t = get_absolute_time();
return to_ms_since_boot(t);
}
// 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
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
float i = floor(h * 6.0f);
float f = h * 6.0f - i;
v *= 255.0f;
uint8_t p = v * (1.0f - s);
uint8_t q = v * (1.0f - f * s);
uint8_t t = v * (1.0f - (1.0f - f) * s);
switch (int(i) % 6) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
}
int main() {
st7789.set_backlight(255);
Pen WHITE = graphics.create_pen(255, 255, 255);
Pen BG = graphics.create_pen(120, 40, 60);
struct pt {
float x;
float y;
uint8_t r;
float dx;
float dy;
uint16_t pen;
};
std::vector<pt> shapes;
for(int i = 0; i < 100; i++) {
pt shape;
shape.x = rand() % graphics.bounds.w;
shape.y = rand() % graphics.bounds.h;
shape.r = (rand() % 10) + 3;
shape.dx = float(rand() % 255) / 64.0f;
shape.dy = float(rand() % 255) / 64.0f;
shape.pen = graphics.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
Point text_location(0, 0);
uint8_t i = 0;
while(true) {
graphics.set_pen(BG);
graphics.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if((shape.x - shape.r) < 0) {
shape.dx *= -1;
shape.x = shape.r;
}
if((shape.x + shape.r) >= graphics.bounds.w) {
shape.dx *= -1;
shape.x = graphics.bounds.w - shape.r;
}
if((shape.y - shape.r) < 0) {
shape.dy *= -1;
shape.y = shape.r;
}
if((shape.y + shape.r) >= graphics.bounds.h) {
shape.dy *= -1;
shape.y = graphics.bounds.h - shape.r;
}
graphics.set_pen(shape.pen);
graphics.circle(Point(shape.x, shape.y), shape.r);
}
graphics.set_pen(WHITE);
graphics.text("Hello World", text_location, 320);
// update screen
st7789.update(&graphics);
i+=10;
tufty.led(i);
}
return 0;
}

Wyświetl plik

@ -4,7 +4,6 @@ add_subdirectory(breakout_dotmatrix)
add_subdirectory(breakout_encoder)
add_subdirectory(breakout_ioexpander)
add_subdirectory(breakout_ltr559)
add_subdirectory(breakout_colourlcd160x80)
add_subdirectory(breakout_rgbmatrix5x5)
add_subdirectory(breakout_matrix11x7)
add_subdirectory(breakout_mics6814)
@ -17,7 +16,6 @@ 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)
@ -29,6 +27,7 @@ add_subdirectory(pico_rgb_keypad)
add_subdirectory(pico_wireless)
add_subdirectory(plasma2040)
add_subdirectory(badger2040)
add_subdirectory(tufty2040)
add_subdirectory(servo2040)
add_subdirectory(motor2040)
add_subdirectory(adcfft)

Wyświetl plik

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

Wyświetl plik

@ -1,11 +0,0 @@
set(LIB_NAME breakout_colourlcd160x80)
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 st7735 pico_graphics hardware_spi)

Wyświetl plik

@ -1,58 +0,0 @@
#include "breakout_colourlcd160x80.hpp"
namespace pimoroni {
BreakoutColourLCD160x80::BreakoutColourLCD160x80(uint16_t *buf)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf) {
__fb = buf;
}
BreakoutColourLCD160x80::BreakoutColourLCD160x80(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) {
__fb = buf;
}
BreakoutColourLCD160x80::BreakoutColourLCD160x80(uint16_t *buf, BG_SPI_SLOT slot)
: PicoGraphics(WIDTH, HEIGHT, buf), screen(WIDTH, HEIGHT, buf, slot) {
__fb = buf;
}
void BreakoutColourLCD160x80::init() {
// initialise the screen
screen.init();
}
spi_inst_t* BreakoutColourLCD160x80::get_spi() const {
return screen.get_spi();
}
int BreakoutColourLCD160x80::get_cs() const {
return screen.get_cs();
}
int BreakoutColourLCD160x80::get_dc() const {
return screen.get_dc();
}
int BreakoutColourLCD160x80::get_sck() const {
return screen.get_sck();
}
int BreakoutColourLCD160x80::get_mosi() const {
return screen.get_mosi();
}
int BreakoutColourLCD160x80::get_bl() const {
return screen.get_bl();
}
void BreakoutColourLCD160x80::update() {
screen.update();
}
void BreakoutColourLCD160x80::set_backlight(uint8_t brightness) {
screen.set_backlight(brightness);
}
}

Wyświetl plik

@ -1,53 +0,0 @@
#pragma once
#include "drivers/st7735/st7735.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "common/pimoroni_common.hpp"
namespace pimoroni {
class BreakoutColourLCD160x80 : public PicoGraphics {
//--------------------------------------------------
// Constants
//--------------------------------------------------
public:
static const int WIDTH = 160;
static const int HEIGHT = 80;
//--------------------------------------------------
// Variables
//--------------------------------------------------
public:
uint16_t *__fb;
private:
ST7735 screen;
//--------------------------------------------------
// Constructors/Destructor
//--------------------------------------------------
public:
BreakoutColourLCD160x80(uint16_t *buf);
BreakoutColourLCD160x80(uint16_t *buf, spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint miso = PIN_UNUSED, uint bl = PIN_UNUSED);
BreakoutColourLCD160x80(uint16_t *buf, BG_SPI_SLOT slot);
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void 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();
void set_backlight(uint8_t brightness);
};
}

Wyświetl plik

@ -1,11 +0,0 @@
set(LIB_NAME breakout_colourlcd240x240)
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 st7789 pico_graphics)

Wyświetl plik

@ -1,14 +0,0 @@
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7789/st7789.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
set(LIB_NAME breakout_colourlcd240x240)
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 st7789 pico_graphics)

Wyświetl plik

@ -1,58 +0,0 @@
#include "breakout_colourlcd240x240.hpp"
namespace pimoroni {
BreakoutColourLCD240x240::BreakoutColourLCD240x240(uint16_t *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 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, 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() {
}
spi_inst_t* BreakoutColourLCD240x240::get_spi() const {
return screen.get_spi();
}
int BreakoutColourLCD240x240::get_cs() const {
return screen.get_cs();
}
int BreakoutColourLCD240x240::get_dc() const {
return screen.get_dc();
}
int BreakoutColourLCD240x240::get_sck() const {
return screen.get_sck();
}
int BreakoutColourLCD240x240::get_mosi() const {
return screen.get_mosi();
}
int BreakoutColourLCD240x240::get_bl() const {
return screen.get_bl();
}
void BreakoutColourLCD240x240::update() {
screen.update();
}
void BreakoutColourLCD240x240::set_backlight(uint8_t brightness) {
screen.set_backlight(brightness);
}
}

Wyświetl plik

@ -1,53 +0,0 @@
#pragma once
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "common/pimoroni_common.hpp"
namespace pimoroni {
class BreakoutColourLCD240x240 : public PicoGraphics {
//--------------------------------------------------
// Constants
//--------------------------------------------------
public:
static const int WIDTH = 240;
static const int HEIGHT = 240;
//--------------------------------------------------
// Variables
//--------------------------------------------------
public:
uint16_t *__fb;
private:
ST7789 screen;
//--------------------------------------------------
// Constructors/Destructor
//--------------------------------------------------
public:
BreakoutColourLCD240x240(uint16_t *buf);
BreakoutColourLCD240x240(uint16_t *buf, spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED);
BreakoutColourLCD240x240(uint16_t *buf, BG_SPI_SLOT slot);
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void 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();
void set_backlight(uint8_t brightness);
};
}

Wyświetl plik

@ -1,14 +0,0 @@
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7789/st7789.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
set(LIB_NAME breakout_roundlcd)
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 st7789 pico_graphics)

Wyświetl plik

@ -1,58 +0,0 @@
#include "breakout_roundlcd.hpp"
namespace pimoroni {
BreakoutRoundLCD::BreakoutRoundLCD(uint16_t *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 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, 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() {
}
spi_inst_t* BreakoutRoundLCD::get_spi() const {
return screen.get_spi();
}
int BreakoutRoundLCD::get_cs() const {
return screen.get_cs();
}
int BreakoutRoundLCD::get_dc() const {
return screen.get_dc();
}
int BreakoutRoundLCD::get_sck() const {
return screen.get_sck();
}
int BreakoutRoundLCD::get_mosi() const {
return screen.get_mosi();
}
int BreakoutRoundLCD::get_bl() const {
return screen.get_bl();
}
void BreakoutRoundLCD::update() {
screen.update();
}
void BreakoutRoundLCD::set_backlight(uint8_t brightness) {
screen.set_backlight(brightness);
}
}

Wyświetl plik

@ -1,55 +0,0 @@
#pragma once
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
namespace pimoroni {
class BreakoutRoundLCD : public PicoGraphics {
//--------------------------------------------------
// Constants
//--------------------------------------------------
public:
static const int WIDTH = 240;
static const int HEIGHT = 240;
static const uint8_t PIN_UNUSED = UINT8_MAX;
//--------------------------------------------------
// Variables
//--------------------------------------------------
public:
uint16_t *__fb;
private:
ST7789 screen;
//--------------------------------------------------
// Constructors/Destructor
//--------------------------------------------------
public:
BreakoutRoundLCD(uint16_t *buf);
BreakoutRoundLCD(uint16_t *buf, spi_inst_t *spi,
uint cs, uint dc, uint sck, uint mosi, uint bl = PIN_UNUSED);
BreakoutRoundLCD(uint16_t *buf, BG_SPI_SLOT slot);
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
void 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();
void set_backlight(uint8_t brightness);
};
}

Wyświetl plik

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

Wyświetl plik

@ -1,106 +0,0 @@
# 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();
```

Wyświetl plik

@ -1,47 +0,0 @@
#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);
}
}

Wyświetl plik

@ -1,49 +0,0 @@
#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);
};
}

Wyświetl plik

@ -0,0 +1,203 @@
//
// JPEG Decoder
//
// written by Larry Bank
// bitbank@pobox.com
// Arduino port started 8/2/2020
// Original JPEG code written 26+ years ago :)
// The goal of this code is to decode baseline JPEG images
// using no more than 18K of RAM (if sent directly to an LCD display)
//
// Copyright 2020 BitBank Software, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
//
#include "JPEGDEC.h"
// forward references
JPEG_STATIC int JPEGInit(JPEGIMAGE *pJPEG);
JPEG_STATIC int JPEGParseInfo(JPEGIMAGE *pPage, int bExtractThumb);
JPEG_STATIC void JPEGGetMoreData(JPEGIMAGE *pPage);
JPEG_STATIC int DecodeJPEG(JPEGIMAGE *pImage);
// Include the C code which does the actual work
#include "jpeg.inl"
void JPEGDEC::setPixelType(int iType)
{
if (iType >= 0 && iType < INVALID_PIXEL_TYPE)
_jpeg.ucPixelType = (uint8_t)iType;
else
_jpeg.iError = JPEG_INVALID_PARAMETER;
} /* setPixelType() */
void JPEGDEC::setMaxOutputSize(int iMaxMCUs)
{
if (iMaxMCUs < 1)
iMaxMCUs = 1; // don't allow invalid value
_jpeg.iMaxMCUs = iMaxMCUs;
} /* setMaxOutputSize() */
//
// Memory initialization
//
int JPEGDEC::openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw)
{
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.ucMemType = JPEG_MEM_RAM;
_jpeg.pfnRead = readRAM;
_jpeg.pfnSeek = seekMem;
_jpeg.pfnDraw = pfnDraw;
_jpeg.pfnOpen = NULL;
_jpeg.pfnClose = NULL;
_jpeg.JPEGFile.iSize = iDataSize;
_jpeg.JPEGFile.pData = pData;
_jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start
return JPEGInit(&_jpeg);
} /* openRAM() */
int JPEGDEC::openFLASH(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw)
{
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.ucMemType = JPEG_MEM_FLASH;
_jpeg.pfnRead = readFLASH;
_jpeg.pfnSeek = seekMem;
_jpeg.pfnDraw = pfnDraw;
_jpeg.pfnOpen = NULL;
_jpeg.pfnClose = NULL;
_jpeg.JPEGFile.iSize = iDataSize;
_jpeg.JPEGFile.pData = pData;
_jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start
return JPEGInit(&_jpeg);
} /* openRAM() */
int JPEGDEC::getOrientation()
{
return (int)_jpeg.ucOrientation;
} /* getOrientation() */
int JPEGDEC::getLastError()
{
return _jpeg.iError;
} /* getLastError() */
int JPEGDEC::getWidth()
{
return _jpeg.iWidth;
} /* getWidth() */
int JPEGDEC::getHeight()
{
return _jpeg.iHeight;
} /* getHeight() */
int JPEGDEC::hasThumb()
{
return (int)_jpeg.ucHasThumb;
} /* hasThumb() */
int JPEGDEC::getThumbWidth()
{
return _jpeg.iThumbWidth;
} /* getThumbWidth() */
int JPEGDEC::getThumbHeight()
{
return _jpeg.iThumbHeight;
} /* getThumbHeight() */
int JPEGDEC::getBpp()
{
return (int)_jpeg.ucBpp;
} /* getBpp() */
int JPEGDEC::getSubSample()
{
return (int)_jpeg.ucSubSample;
} /* getSubSample() */
//
// File (SD/MMC) based initialization
//
int JPEGDEC::open(const char *szFilename, JPEG_OPEN_CALLBACK *pfnOpen, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw)
{
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.pfnRead = pfnRead;
_jpeg.pfnSeek = pfnSeek;
_jpeg.pfnDraw = pfnDraw;
_jpeg.pfnOpen = pfnOpen;
_jpeg.pfnClose = pfnClose;
_jpeg.iMaxMCUs = 1000; // set to an unnaturally high value to start
_jpeg.JPEGFile.fHandle = (*pfnOpen)(szFilename, &_jpeg.JPEGFile.iSize);
if (_jpeg.JPEGFile.fHandle == NULL)
return 0;
return JPEGInit(&_jpeg);
} /* open() */
#ifdef FS_H
static int32_t FileRead(JPEGFILE *handle, uint8_t *buffer, int32_t length)
{
return ((File *)(handle->fHandle))->read(buffer, length);
}
static int32_t FileSeek(JPEGFILE *handle, int32_t position)
{
return ((File *)(handle->fHandle))->seek(position);
}
static void FileClose(void *handle)
{
((File *)handle)->close();
}
int JPEGDEC::open(File &file, JPEG_DRAW_CALLBACK *pfnDraw)
{
if (!file) return 0;
memset(&_jpeg, 0, sizeof(JPEGIMAGE));
_jpeg.pfnRead = FileRead;
_jpeg.pfnSeek = FileSeek;
_jpeg.pfnClose = FileClose;
_jpeg.pfnDraw = pfnDraw;
_jpeg.iMaxMCUs = 1000;
_jpeg.JPEGFile.fHandle = &file;
_jpeg.JPEGFile.iSize = file.size();
return JPEGInit(&_jpeg);
}
#endif // FS_H
void JPEGDEC::close()
{
if (_jpeg.pfnClose)
(*_jpeg.pfnClose)(_jpeg.JPEGFile.fHandle);
} /* close() */
//
// Decode the image
// returns:
// 1 = good result
// 0 = error
//
int JPEGDEC::decode(int x, int y, int iOptions)
{
_jpeg.iXOffset = x;
_jpeg.iYOffset = y;
_jpeg.iOptions = iOptions;
_jpeg.pDitherBuffer = nullptr;
return DecodeJPEG(&_jpeg);
} /* decode() */
// TODO PR these tweaks to https://github.com/bitbank2/JPEGDEC
int JPEGDEC::decodeDither(int x, int y, uint8_t *pDither, int iOptions)
{
_jpeg.iXOffset = x;
_jpeg.iYOffset = y;
_jpeg.iOptions = iOptions;
_jpeg.pDitherBuffer = pDither;
return DecodeJPEG(&_jpeg);
}

Wyświetl plik

@ -0,0 +1,262 @@
//
// Copyright 2020 BitBank Software, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
//
#ifndef __JPEGDEC__
#define __JPEGDEC__
#if defined( __MACH__ ) || defined( __LINUX__ ) || defined( __MCUXPRESSO ) || defined( PICO_BUILD )
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#else
#include <Arduino.h>
#if !defined(HAL_ESP32_HAL_H_) && defined(__has_include) && __has_include(<FS.h>)
#include <FS.h>
#endif
#endif
#ifndef PROGMEM
#define memcpy_P memcpy
#define PROGMEM
#endif
//
// JPEG Decoder
// Written by Larry Bank
// Copyright (c) 2020 BitBank Software, Inc.
//
// Designed to decode baseline JPEG images (8 or 24-bpp)
// using less than 22K of RAM
//
/* Defines and variables */
#define FILE_HIGHWATER 1536
#define JPEG_FILE_BUF_SIZE 2048
#define HUFF_TABLEN 273
#define HUFF11SIZE (1<<11)
#define DC_TABLE_SIZE 1024
#define DCTSIZE 64
#define MAX_MCU_COUNT 6
#define MAX_COMPS_IN_SCAN 4
#define MAX_BUFFERED_PIXELS 2048
// Decoder options
#define JPEG_AUTO_ROTATE 1
#define JPEG_SCALE_HALF 2
#define JPEG_SCALE_QUARTER 4
#define JPEG_SCALE_EIGHTH 8
#define JPEG_LE_PIXELS 16
#define JPEG_EXIF_THUMBNAIL 32
#define JPEG_LUMA_ONLY 64
#define MCU0 (DCTSIZE * 0)
#define MCU1 (DCTSIZE * 1)
#define MCU2 (DCTSIZE * 2)
#define MCU3 (DCTSIZE * 3)
#define MCU4 (DCTSIZE * 4)
#define MCU5 (DCTSIZE * 5)
// Pixel types (defaults to little endian RGB565)
enum {
RGB565_LITTLE_ENDIAN = 0,
RGB565_BIG_ENDIAN,
EIGHT_BIT_GRAYSCALE,
FOUR_BIT_DITHERED,
TWO_BIT_DITHERED,
ONE_BIT_DITHERED,
INVALID_PIXEL_TYPE
};
enum {
JPEG_MEM_RAM=0,
JPEG_MEM_FLASH
};
// Error codes returned by getLastError()
enum {
JPEG_SUCCESS = 0,
JPEG_INVALID_PARAMETER,
JPEG_DECODE_ERROR,
JPEG_UNSUPPORTED_FEATURE,
JPEG_INVALID_FILE
};
typedef struct buffered_bits
{
unsigned char *pBuf; // buffer pointer
uint32_t ulBits; // buffered bits
uint32_t ulBitOff; // current bit offset
} BUFFERED_BITS;
typedef struct jpeg_file_tag
{
int32_t iPos; // current file position
int32_t iSize; // file size
uint8_t *pData; // memory file pointer
void * fHandle; // class pointer to File/SdFat or whatever you want
} JPEGFILE;
typedef struct jpeg_draw_tag
{
int x, y; // upper left corner of current MCU
int iWidth, iHeight; // size of this MCU
int iBpp; // bit depth of the pixels (8 or 16)
uint16_t *pPixels; // 16-bit pixels
} JPEGDRAW;
// Callback function prototypes
typedef int32_t (JPEG_READ_CALLBACK)(JPEGFILE *pFile, uint8_t *pBuf, int32_t iLen);
typedef int32_t (JPEG_SEEK_CALLBACK)(JPEGFILE *pFile, int32_t iPosition);
typedef int (JPEG_DRAW_CALLBACK)(JPEGDRAW *pDraw);
typedef void * (JPEG_OPEN_CALLBACK)(const char *szFilename, int32_t *pFileSize);
typedef void (JPEG_CLOSE_CALLBACK)(void *pHandle);
/* JPEG color component info */
typedef struct _jpegcompinfo
{
// These values are fixed over the whole image
// For compression, they must be supplied by the user interface
// for decompression, they are read from the SOF marker.
unsigned char component_needed; /* do we need the value of this component? */
unsigned char component_id; /* identifier for this component (0..255) */
unsigned char component_index; /* its index in SOF or cinfo->comp_info[] */
//unsigned char h_samp_factor; /* horizontal sampling factor (1..4) */
//unsigned char v_samp_factor; /* vertical sampling factor (1..4) */
unsigned char quant_tbl_no; /* quantization table selector (0..3) */
// These values may vary between scans
// For compression, they must be supplied by the user interface
// for decompression, they are read from the SOS marker.
unsigned char dc_tbl_no; /* DC entropy table selector (0..3) */
unsigned char ac_tbl_no; /* AC entropy table selector (0..3) */
// These values are computed during compression or decompression startup
//int true_comp_width; /* component's image width in samples */
//int true_comp_height; /* component's image height in samples */
// the above are the logical dimensions of the downsampled image
// These values are computed before starting a scan of the component
//int MCU_width; /* number of blocks per MCU, horizontally */
//int MCU_height; /* number of blocks per MCU, vertically */
//int MCU_blocks; /* MCU_width * MCU_height */
//int downsampled_width; /* image width in samples, after expansion */
//int downsampled_height; /* image height in samples, after expansion */
// the above are the true_comp_xxx values rounded up to multiples of
// the MCU dimensions; these are the working dimensions of the array
// as it is passed through the DCT or IDCT step. NOTE: these values
// differ depending on whether the component is interleaved or not!!
// This flag is used only for decompression. In cases where some of the
// components will be ignored (eg grayscale output from YCbCr image),
// we can skip IDCT etc. computations for the unused components.
} JPEGCOMPINFO;
//
// our private structure to hold a JPEG image decode state
//
typedef struct jpeg_image_tag
{
int iWidth, iHeight; // image size
int iThumbWidth, iThumbHeight; // thumbnail size (if present)
int iThumbData; // offset to image data
int iXOffset, iYOffset; // placement on the display
uint8_t ucBpp, ucSubSample, ucHuffTableUsed;
uint8_t ucMode, ucOrientation, ucHasThumb, b11Bit;
uint8_t ucComponentsInScan, cApproxBitsLow, cApproxBitsHigh;
uint8_t iScanStart, iScanEnd, ucFF, ucNumComponents;
uint8_t ucACTable, ucDCTable, ucMaxACCol, ucMaxACRow;
uint8_t ucMemType, ucPixelType;
int iEXIF; // Offset to EXIF 'TIFF' file
int iError;
int iOptions;
int iVLCOff; // current VLC data offset
int iVLCSize; // current quantity of data in the VLC buffer
int iResInterval, iResCount; // restart interval
int iMaxMCUs; // max MCUs of pixels per JPEGDraw call
JPEG_READ_CALLBACK *pfnRead;
JPEG_SEEK_CALLBACK *pfnSeek;
JPEG_DRAW_CALLBACK *pfnDraw;
JPEG_OPEN_CALLBACK *pfnOpen;
JPEG_CLOSE_CALLBACK *pfnClose;
JPEGCOMPINFO JPCI[MAX_COMPS_IN_SCAN]; /* Max color components */
JPEGFILE JPEGFile;
BUFFERED_BITS bb;
uint8_t *pDitherBuffer; // provided externally to do Floyd-Steinberg dithering
uint16_t usPixels[MAX_BUFFERED_PIXELS];
int16_t sMCUs[DCTSIZE * MAX_MCU_COUNT]; // 4:2:0 needs 6 DCT blocks per MCU
int16_t sQuantTable[DCTSIZE*4]; // quantization tables
uint8_t ucFileBuf[JPEG_FILE_BUF_SIZE]; // holds temp data and pixel stack
uint8_t ucHuffDC[DC_TABLE_SIZE * 2]; // up to 2 'short' tables
uint16_t usHuffAC[HUFF11SIZE * 2];
} JPEGIMAGE;
#ifdef __cplusplus
#if defined(__has_include) && __has_include(<FS.h>)
#include "FS.h"
#endif
#define JPEG_STATIC static
//
// The JPEGDEC class wraps portable C code which does the actual work
//
class JPEGDEC
{
public:
int openRAM(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw);
int openFLASH(uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw);
int open(const char *szFilename, JPEG_OPEN_CALLBACK *pfnOpen, JPEG_CLOSE_CALLBACK *pfnClose, JPEG_READ_CALLBACK *pfnRead, JPEG_SEEK_CALLBACK *pfnSeek, JPEG_DRAW_CALLBACK *pfnDraw);
#ifdef FS_H
int open(File &file, JPEG_DRAW_CALLBACK *pfnDraw);
#endif
void close();
int decode(int x, int y, int iOptions);
int decodeDither(int x, int y, uint8_t *pDither, int iOptions);
int getOrientation();
int getWidth();
int getHeight();
int getBpp();
int getSubSample();
int hasThumb();
int getThumbWidth();
int getThumbHeight();
int getLastError();
void setPixelType(int iType); // defaults to little endian
void setMaxOutputSize(int iMaxMCUs);
private:
JPEGIMAGE _jpeg;
};
#else
#define JPEG_STATIC
int JPEG_openRAM(JPEGIMAGE *pJPEG, uint8_t *pData, int iDataSize, JPEG_DRAW_CALLBACK *pfnDraw);
int JPEG_openFile(JPEGIMAGE *pJPEG, const char *szFilename, JPEG_DRAW_CALLBACK *pfnDraw);
int JPEG_getWidth(JPEGIMAGE *pJPEG);
int JPEG_getHeight(JPEGIMAGE *pJPEG);
int JPEG_decode(JPEGIMAGE *pJPEG, int x, int y, int iOptions);
int JPEG_decodeDither(JPEGIMAGE *pJPEG, uint8_t *pDither, int iOptions);
void JPEG_close(JPEGIMAGE *pJPEG);
int JPEG_getLastError(JPEGIMAGE *pJPEG);
int JPEG_getOrientation(JPEGIMAGE *pJPEG);
int JPEG_getBpp(JPEGIMAGE *pJPEG);
int JPEG_getSubSample(JPEGIMAGE *pJPEG);
int JPEG_hasThumb(JPEGIMAGE *pJPEG);
int JPEG_getThumbWidth(JPEGIMAGE *pJPEG);
int JPEG_getThumbHeight(JPEGIMAGE *pJPEG);
int JPEG_getLastError(JPEGIMAGE *pJPEG);
void JPEG_setPixelType(JPEGIMAGE *pJPEG, int iType); // defaults to little endian
void JPEG_setMaxOutputSize(JPEGIMAGE *pJPEG, int iMaxMCUs);
#endif // __cplusplus
// Due to unaligned memory causing an exception, we have to do these macros the slow way
#define INTELSHORT(p) ((*p) + (*(p+1)<<8))
#define INTELLONG(p) ((*p) + (*(p+1)<<8) + (*(p+2)<<16) + (*(p+3)<<24))
#define MOTOSHORT(p) (((*(p))<<8) + (*(p+1)))
#define MOTOLONG(p) (((*p)<<24) + ((*(p+1))<<16) + ((*(p+2))<<8) + (*(p+3)))
// Must be a 32-bit target processor
#define REGISTER_WIDTH 32
#endif // __JPEGDEC__

Plik diff jest za duży Load Diff

Wyświetl plik

@ -7,11 +7,7 @@ We've included helper functions to handle every aspect of drawing to the screen
- [Example Program](#example-program)
- [Function Reference](#function-reference)
- [PicoGraphics](#picographics)
- [init](#init)
- [set_backlight](#set_backlight)
- [set_led](#set_led)
- [is_pressed](#is_pressed)
- [update](#update)
- [ST7789](#st7789)
## Example Program
@ -19,16 +15,18 @@ The following example sets up Pico Display, displays some basic demo text and gr
```c++
#include "pico_display.hpp"
#include "generic_st7789.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "rgbled.hpp"
#include "button.hpp"
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
// Display driver
ST7789 st7789(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
// Swap WIDTH and HEIGHT to rotate 90 degrees
ST7789Generic display(PicoDisplay::WIDTH, PicoDisplay::HEIGHT, buffer);
// Graphics library - in RGB332 mode you get 256 colours and optional dithering for ~32K RAM.
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
// Create an RGB LED
// RGB LED
RGBLED led(PicoDisplay::LED_R, PicoDisplay::LED_G, PicoDisplay::LED_B);
// And each button
@ -42,7 +40,7 @@ int main() {
// 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.
display.set_backlight(100);
st7789.set_backlight(100);
while(true) {
// detect if the A button is pressed (could be A, B, X, or Y)
@ -55,24 +53,24 @@ int main() {
// set the colour of the pen
// parameters are red, green, blue all between 0 and 255
display.set_pen(30, 40, 50);
graphics.set_pen(30, 40, 50);
// fill the screen with the current pen colour
display.clear();
graphics.clear();
// draw a box to put some text in
display.set_pen(10, 20, 30);
graphics.set_pen(10, 20, 30);
Rect text_rect(10, 10, 150, 150);
display.rectangle(text_rect);
graphics.rectangle(text_rect);
// write some text inside the box with 10 pixels of margin
// automatically word wrapping
text_rect.deflate(10);
display.set_pen(110, 120, 130);
display.text("This is a message", Point(text_rect.x, text_rect.y), text_rect.w);
graphics.set_pen(110, 120, 130);
graphics.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
display.update();
st7789.update(&graphics);
}
}
```
@ -83,32 +81,6 @@ 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).
### configure_display
### ST7789
Configures an ST7789 display. Done by default, but you can use this to set 180 degree rotation like so:
```c++
display.configure_display(true);
```
### flip
Deprecated: calls `configure_display(true);`
### set_backlight
Set the display backlight from 0-255.
```c++
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++
display.update();
```
Pico Display uses the ST7789 display driver to handle the LCD. For more information [read the ST7789 README.](../../drivers/st7789/README.md).

Wyświetl plik

@ -1,6 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7789/st7789.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
add_library(pico_display INTERFACE)
target_sources(pico_display INTERFACE
@ -10,4 +7,4 @@ target_sources(pico_display INTERFACE
target_include_directories(pico_display INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(pico_display INTERFACE pico_stdlib hardware_spi hardware_pwm hardware_dma st7789 pico_graphics)
target_link_libraries(pico_display INTERFACE pico_stdlib)

Wyświetl plik

@ -1,85 +1 @@
#include <math.h>
#include <string.h>
#include "hardware/gpio.h" // Workaround SDK bug - https://github.com/raspberrypi/pico-sdk/issues/3
#include "hardware/pwm.h"
#include "pico_display.hpp"
namespace pimoroni {
PicoDisplay::PicoDisplay(uint16_t *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;
}
PicoDisplay::PicoDisplay(uint16_t *buf, int width, int height)
: 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;
}
void PicoDisplay::init() {
// setup the rgb led for pwm control
pwm_config cfg = pwm_get_default_config();
pwm_config_set_output_polarity(&cfg, true, true);
// red
pwm_set_wrap(pwm_gpio_to_slice_num(LED_R), 65535);
pwm_init(pwm_gpio_to_slice_num(LED_R), &cfg, true);
gpio_set_function(LED_R, GPIO_FUNC_PWM);
// green
pwm_set_wrap(pwm_gpio_to_slice_num(LED_G), 65535);
pwm_init(pwm_gpio_to_slice_num(LED_G), &cfg, true);
gpio_set_function(LED_G, GPIO_FUNC_PWM);
// blue
pwm_set_wrap(pwm_gpio_to_slice_num(LED_B), 65535);
pwm_init(pwm_gpio_to_slice_num(LED_B), &cfg, true);
gpio_set_function(LED_B, GPIO_FUNC_PWM);
// setup button inputs
gpio_set_function(A, GPIO_FUNC_SIO); gpio_set_dir(A, GPIO_IN); gpio_pull_up(A);
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);
}
void PicoDisplay::update() {
screen.update();
}
void PicoDisplay::set_backlight(uint8_t brightness) {
screen.set_backlight(brightness);
}
void PicoDisplay::set_led(uint8_t r, uint8_t g, uint8_t b) {
// gamma correct the provided 0-255 brightness value onto a
// 0-65535 range for the pwm counter
static const float gamma = 2.8;
uint16_t value;
// red
value = (uint16_t)(pow((float)(r) / 255.0f, gamma) * 65535.0f + 0.5f);
pwm_set_gpio_level(LED_R, value);
// green
value = (uint16_t)(pow((float)(g) / 255.0f, gamma) * 65535.0f + 0.5f);
pwm_set_gpio_level(LED_G, value);
// blue
value = (uint16_t)(pow((float)(b) / 255.0f, gamma) * 65535.0f + 0.5f);
pwm_set_gpio_level(LED_B, value);
}
bool PicoDisplay::is_pressed(uint8_t button) {
return !gpio_get(button);
}
void PicoDisplay::flip() {
screen.flip();
}
}
#include "pico_display.hpp"

Wyświetl plik

@ -1,16 +1,12 @@
#pragma once
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "pico/stdlib.h"
namespace pimoroni {
class PicoDisplay : public PicoGraphics {
class PicoDisplay {
public:
static const int WIDTH = 240;
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 B = 13;
static const uint8_t X = 14;
@ -18,21 +14,5 @@ namespace pimoroni {
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:
ST7789 screen;
public:
PicoDisplay(uint16_t *buf);
PicoDisplay(uint16_t *buf, int width, int height);
void init();
void update();
void set_backlight(uint8_t brightness);
void set_led(uint8_t r, uint8_t g, uint8_t b);
bool is_pressed(uint8_t button);
void flip();
};
}

Wyświetl plik

@ -2,16 +2,10 @@
Our Pico Display Pack offers a vibrant 1.14" (240x135) 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)
- [init](#init)
- [set_backlight](#set_backlight)
- [set_led](#set_led)
- [is_pressed](#is_pressed)
- [update](#update)
- [ST7789](#st7789)
## Example Program
@ -19,49 +13,62 @@ The following example sets up Pico Display, displays some basic demo text and gr
```c++
#include "pico_display_2.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "rgbled.hpp"
#include "button.hpp"
using namespace pimoroni;
// Display driver
ST7789 st7789(PicoDisplay2::WIDTH, PicoDisplay2::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
uint16_t buffer[PicoDisplay2::WIDTH * PicoDisplay2::HEIGHT];
PicoDisplay2 pico_display(buffer);
// Graphics library - in RGB332 mode you get 256 colours and optional dithering for 75K RAM.
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
// RGB LED
RGBLED led(PicoDisplay2::LED_R, PicoDisplay2::LED_G, PicoDisplay2::LED_B);
// And each button
Button button_a(PicoDisplay2::A);
Button button_b(PicoDisplay2::B);
Button button_x(PicoDisplay2::X);
Button button_y(PicoDisplay2::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);
st7789.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);
graphics.set_pen(30, 40, 50);
// fill the screen with the current pen colour
pico_display.clear();
graphics.clear();
// draw a box to put some text in
pico_display.set_pen(10, 20, 30);
graphics.set_pen(10, 20, 30);
Rect text_rect(10, 10, 150, 150);
pico_display.rectangle(text_rect);
graphics.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);
graphics.set_pen(110, 120, 130);
graphics.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();
st7789.update(&graphics);
}
}
```
@ -72,52 +79,6 @@ 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
### ST7789
Sets up Pico Display. `init` must be called before any other functions since it configures the required PWM and GPIO:
```c++
pico_display.init();
```
### 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."
### 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(PicoDisplay2::A)
```
### update
To display your changes on Pico Display's screen you need to call `update`:
```c++
pico_display.update();
```
Pico Display uses the ST7789 display driver to handle the LCD. For more information [read the ST7789 README.](../../drivers/st7789/README.md).

Wyświetl plik

@ -1,14 +1,10 @@
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7789/st7789.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
add_library(pico_display_2 INTERFACE)
set(LIB_NAME pico_display_2)
add_library(${LIB_NAME} INTERFACE)
target_sources(${LIB_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
target_sources(pico_display_2 INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_display_2.cpp
)
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(pico_display_2 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)
target_link_libraries(pico_display_2 INTERFACE pico_stdlib)

Wyświetl plik

@ -1,85 +1 @@
#include <math.h>
#include <string.h>
#include "hardware/gpio.h" // Workaround SDK bug - https://github.com/raspberrypi/pico-sdk/issues/3
#include "hardware/pwm.h"
#include "pico_display_2.hpp"
namespace pimoroni {
PicoDisplay2::PicoDisplay2(uint16_t *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;
}
PicoDisplay2::PicoDisplay2(uint16_t *buf, int width, int height)
: 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;
}
void PicoDisplay2::init() {
// setup the rgb led for pwm control
pwm_config cfg = pwm_get_default_config();
pwm_config_set_output_polarity(&cfg, true, true);
// red
pwm_set_wrap(pwm_gpio_to_slice_num(LED_R), 65535);
pwm_init(pwm_gpio_to_slice_num(LED_R), &cfg, true);
gpio_set_function(LED_R, GPIO_FUNC_PWM);
// green
pwm_set_wrap(pwm_gpio_to_slice_num(LED_G), 65535);
pwm_init(pwm_gpio_to_slice_num(LED_G), &cfg, true);
gpio_set_function(LED_G, GPIO_FUNC_PWM);
// blue
pwm_set_wrap(pwm_gpio_to_slice_num(LED_B), 65535);
pwm_init(pwm_gpio_to_slice_num(LED_B), &cfg, true);
gpio_set_function(LED_B, GPIO_FUNC_PWM);
// setup button inputs
gpio_set_function(A, GPIO_FUNC_SIO); gpio_set_dir(A, GPIO_IN); gpio_pull_up(A);
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);
}
void PicoDisplay2::update() {
screen.update();
}
void PicoDisplay2::set_backlight(uint8_t brightness) {
screen.set_backlight(brightness);
}
void PicoDisplay2::set_led(uint8_t r, uint8_t g, uint8_t b) {
// gamma correct the provided 0-255 brightness value onto a
// 0-65535 range for the pwm counter
static const float gamma = 2.8;
uint16_t value;
// red
value = (uint16_t)(pow((float)(r) / 255.0f, gamma) * 65535.0f + 0.5f);
pwm_set_gpio_level(LED_R, value);
// green
value = (uint16_t)(pow((float)(g) / 255.0f, gamma) * 65535.0f + 0.5f);
pwm_set_gpio_level(LED_G, value);
// blue
value = (uint16_t)(pow((float)(b) / 255.0f, gamma) * 65535.0f + 0.5f);
pwm_set_gpio_level(LED_B, value);
}
bool PicoDisplay2::is_pressed(uint8_t button) {
return !gpio_get(button);
}
void PicoDisplay2::flip() {
screen.flip();
}
}
#include "pico_display_2.hpp"

Wyświetl plik

@ -1,16 +1,12 @@
#pragma once
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "pico/stdlib.h"
namespace pimoroni {
class PicoDisplay2 : public PicoGraphics {
class PicoDisplay2 {
public:
static const int WIDTH = 320;
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 B = 13;
static const uint8_t X = 14;
@ -18,21 +14,5 @@ namespace pimoroni {
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:
ST7789 screen;
public:
PicoDisplay2(uint16_t *buf);
PicoDisplay2(uint16_t *buf, int width, int height);
void init();
void update();
void set_backlight(uint8_t brightness);
void set_led(uint8_t r, uint8_t g, uint8_t b);
bool is_pressed(uint8_t button);
void flip();
};
}

Wyświetl plik

@ -11,35 +11,59 @@ We've included helper functions to handle every aspect of drawing to the screen
- [Buttons](#buttons)
- [ADC Channels](#adc-channels)
- [GPIO](#gpio)
- [Motors](#motors)
- [Functions](#functions)
- [init](#init)
- [set_motor](#set_motor)
- [get_adc](#get_adc)
- [set_audio_pin](#set_audio_pin)
- [set_tone](#set_tone)
- [is_pressed](#is_pressed)
- [update](#update)
- [Motors](#motors-1)
- [Analog](#analog)
- [Buzzer](#buzzer)
- [Buttons](#buttons-1)
- [ST7789](#st7789)
## Example Program
The following example sets up Pico Explorer, displays some basic demo text and graphics and will illuminate the RGB LED green if the A button is pressed.
The following example shows how to set up all of Pico Explorers functions:
```c++
#include "pico_explorer.hpp"
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "button.hpp"
#include "motor.hpp"
#include "analog.hpp"
using namespace pimoroni;
uint16_t buffer[PicoExplorer::WIDTH * PicoExplorer::HEIGHT];
PicoExplorer pico_explorer(buffer);
// Display driver
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
// Graphics library - in RGB332 mode you get 256 colours and optional dithering for ~56K RAM.
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
// Buttons
Button button_a(PicoExplorer::A);
Button button_b(PicoExplorer::B);
Button button_x(PicoExplorer::X);
Button button_y(PicoExplorer::Y);
// Motors
Motor motor1(PicoExplorer::MOTOR1_PINS);
Motor motor2(PicoExplorer::MOTOR2_PINS);
// Analog
Analog adc0(PicoExplorer::ADC0_PIN);
Analog adc1(PicoExplorer::ADC1_PIN);
Analog adc2(PicoExplorer::ADC2_PIN);
int main() {
pico_explorer.init();
motor1.init();
motor2.init();
while(true) {
// now we've done our drawing let's update the screen
pico_explorer.update();
}
while(true) {
// update screen
st7789.update(&graphics);
}
}
```
@ -55,132 +79,129 @@ Please note that the backlight on Pico Explorer is not dimmable (we needed the p
#### Buttons
The four buttons, A, B, X and Y have corresponding constants set to their respective GPIO pins. For example:
The four buttons, A, B, X and Y have corresponding constants set to their respective GPIO pins.
```c++
bool a_is_pressed = pico_explorer.is_pressed(pico_explorer.A);
PicoExplorer::A = 12;
PicoExplorer::B = 13;
PicoExplorer::X = 14;
PicoExplorer::Y = 15;
```
#### ADC Channels
The three ADC channels are defined as `ADC0`, `ADC1` and `ADC2`, and should be used with `get_adc`, eg:
The three ADC channels are defined as `ADC0_PIN`, `ADC1_PIN` and `ADC2_PIN`, and should be used with an instance of the `Analog` class:
```c++
float adc0_reading = pico_explorer.get_adc(pico_explorer.ADC0);
PicoExplorer::ADC0_PIN = 26;
PicoExplorer::ADC1_PIN = 27;
PicoExplorer::ADC2_PIN = 28;
```
#### GPIO
The 8 general purpose IO pins on the lower Pico Explorer are GP0 through GP7, we've created constants for you to identify them easily. You should use Pico SDK's `gpio_get` to read a pin, eg:
The 8 general purpose IO pins on the lower Pico Explorer are GP0 through GP7, we've created constants for you to identify them easily.
```c++
PicoExplorer::GP0 = 0;
PicoExplorer::GP1 = 1;
PicoExplorer::GP2 = 2;
PicoExplorer::GP3 = 3;
PicoExplorer::GP4 = 4;
PicoExplorer::GP5 = 5;
PicoExplorer::GP6 = 6;
PicoExplorer::GP7 = 7;
```
You should use Pico SDK's `gpio_get` to read a pin, eg:
```c++
bool pin_state = gpio_get(pico_explorer.GP0);
```
#### Motors
The two motor channels are defined as pin pairs, and should be used with an instance of the `Motor` class:
```c++
PicoExplorer::MOTOR1_PINS = {9, 8};
PicoExplorer::MOTOR2_PINS = {11, 10};
```
### Functions
#### init
#### Motors
Sets up Pico Explorer. `init` must be called before any other functions since it configures the required PWM and GPIO:
Pico Explorer uses our `Motor` library to drive motors. Motors are driven by PWM via an onboard DRV8833. Constants are provided for both motors, so setup is as easy as:
```c++
pico_explorer.init();
#include "motor.hpp"
using namespace motor;
Motor motor1(PicoExplorer::MOTOR1_PINS);
Motor motor2(PicoExplorer::MOTOR2_PINS);
```
#### set_motor
You should also init your motors to set up the required GPIO/PWM on their pins:
```c++
void PicoExplorer::set_motor(uint8_t channel, uint8_t action, float speed);
motor1.init();
motor2.init();
```
Motors are driven by PWM via an onboard DRV8833, `set_motor` will set the PWM values for the corresponding channel.
Channel should be one of `MOTOR1` or `MOTOR2`.
Action should be `FORWARD`, `REVERSE` or `STOP`.
Speed should be given as a float between `0.0` and `1.0`, eg:
Speed should be given as a float between `-1.0` and `1.0`, eg:
```c++
pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, 0.5f);
pico_explorer.set_motor(pico_explorer.MOTOR2, pico_explorer.REVERSE, 0.5f);
motor1.speed(1.0) // Full-speed forward
motor1.speed(-1.0) // Full-speed backward
```
And to stop the motor:
```c++
pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.STOP);
pico_explorer.set_motor(pico_explorer.MOTOR2, pico_explorer.STOP);
motor1.stop()
```
#### get_adc
#### Analog
```c++
float get_adc(uint8_t channel);
```
Pico Explorer's ADC channels are connected to Pico's ADC-capable pins 26, 27 and 28 which correspond to channels 0, 1 and 2 respectively. eg:
Pico Explorer's ADC channels are connected to Pico's ADC-capable pins 26, 27 and 28 which correspond to channels 0, 1 and 2 respectively.
Constants are supplied for these in the PicoExplorer library, so you can create an Analog instance for each:
```c++
float reading = pico_explorer.get_adc(pico_explorer.ADC0);
#include "analog.hpp"
Analog adc0(PicoExplorer::ADC0_PIN);
Analog adc1(PicoExplorer::ADC1_PIN);
Analog adc2(PicoExplorer::ADC2_PIN);
```
Will perform a 12-bit ADC read and return the result as a float scaled from `0.0f` to `1.0f`. This value can be plugged directly into a motor, eg:
And read a voltage with:
```c++
float reading = pico_explorer.get_adc(pico_explorer.ADC0);
pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, reading);
adc0.read_voltage();
```
#### set_audio_pin
#### Buzzer
Note: You must bridge the pin you use over to the `AUDIO` pin on the Pico Explorer header in order to drive the onboard Piezo.
TODO document buzzer
#### Buttons
```c++
void set_audio_pin(uint8_t p);
#include "button.hpp"
Button button_a(PicoExplorer::A);
Button button_b(PicoExplorer::B);
Button button_x(PicoExplorer::X);
Button button_y(PicoExplorer::Y);
```
`set_audio_pin` configures the PIN that Pico Explorer uses for audio output. It should be one of `GP0` through `GP7`, eg:
### ST7789
```c++
pico_explorer.set_audio_pin(pico_explorer.GP0);
```
Note: You must bridge this pin over to the `AUDIO` pin on the Pico Explorer header in order to drive the onboard Piezo, eg:
#### set_tone
```c++
void set_tone(uint16_t frequency, float duty = 0.2f);
```
`set_tone` will play an audio tone out of your chosen audio pin, if you have bridged that pin to "AUDIO" on the Pico Explorer header then it will sound the onboard Piezo.
```c++
uint16_t frequency = 440;
pico_explorer.set_tone(frequency);
```
#### is_pressed
```c++
bool is_pressed(uint8_t button);
```
Reads the GPIO pin connected to one of Pico Explorer's buttons, returning a `bool` - `true` if it's pressed and `false` if it is released.
```c++
pico_explorer.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_explorer.is_pressed(PicoDisplay::A)
```
#### update
To display your changes on Pico Explorer's screen you need to call `update`:
```c++
pico_explorer.update();
```
Pico Explorer uses the ST7789 display driver to handle the LCD. For more information [read the ST7789 README.](../../drivers/st7789/README.md).

Wyświetl plik

@ -1,117 +1 @@
#include <math.h>
#include <string.h>
#include "hardware/gpio.h"
#include "hardware/pwm.h"
#include "hardware/adc.h"
#include "pico_explorer.hpp"
const uint8_t MOTOR1N = 8;
const uint8_t MOTOR1P = 9;
const uint8_t MOTOR2N = 10;
const uint8_t MOTOR2P = 11;
namespace pimoroni {
PicoExplorer::PicoExplorer(uint16_t *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() {
// setup button inputs
gpio_set_function(A, GPIO_FUNC_SIO); gpio_set_dir(A, GPIO_IN); gpio_pull_up(A);
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);
// setup ADC channels
adc_init();
const uint8_t ADC_BASE_PIN = 26;
adc_gpio_init(ADC0 + ADC_BASE_PIN);
adc_gpio_init(ADC1 + ADC_BASE_PIN);
adc_gpio_init(ADC2 + ADC_BASE_PIN);
// setup motor pins
pwm_config motor_pwm_cfg = pwm_get_default_config();
pwm_config_set_wrap(&motor_pwm_cfg, 5500);
pwm_init(pwm_gpio_to_slice_num(MOTOR1N), &motor_pwm_cfg, true);
gpio_set_function(MOTOR1N, GPIO_FUNC_PWM);
pwm_init(pwm_gpio_to_slice_num(MOTOR1P), &motor_pwm_cfg, true);
gpio_set_function(MOTOR1P, GPIO_FUNC_PWM);
pwm_init(pwm_gpio_to_slice_num(MOTOR2N), &motor_pwm_cfg, true);
gpio_set_function(MOTOR2N, GPIO_FUNC_PWM);
pwm_init(pwm_gpio_to_slice_num(MOTOR2P), &motor_pwm_cfg, true);
gpio_set_function(MOTOR2P, GPIO_FUNC_PWM);
}
void PicoExplorer::update() {
screen.update();
}
bool PicoExplorer::is_pressed(uint8_t button) {
return !gpio_get(button);
}
float PicoExplorer::get_adc(uint8_t channel) {
adc_select_input(channel);
// scale raw 12-bit adc value to 0 .. 1 float
float result = float(adc_read()) / (1 << 12);
// clamp result to 0 .. 1
result = std::min(1.0f, std::max(0.0f, result));
return result;
}
void PicoExplorer::set_motor(uint8_t channel, uint8_t action, float speed) {
uint8_t p = channel == MOTOR1 ? MOTOR1P : MOTOR2P;
uint8_t n = channel == MOTOR1 ? MOTOR1N : MOTOR2N;
switch(action) {
case FORWARD: {
pwm_set_gpio_level(n, (1 - speed) * 5500);
pwm_set_gpio_level(p, 5500);
break;
}
case REVERSE: {
pwm_set_gpio_level(n, 5500);
pwm_set_gpio_level(p, (1 - speed) * 5500);
break;
}
case STOP: {
pwm_set_gpio_level(p, 5500);
pwm_set_gpio_level(n, 5500);
break;
}
}
}
void PicoExplorer::set_audio_pin(uint pin) {
pwm_config tone_pwm_cfg = pwm_get_default_config();
// calculate the pwm wrap value for this frequency
// first we set the clock divider to give us exactly
// ten thousand cycles per second
pwm_config_set_clkdiv(&tone_pwm_cfg, 255);
pwm_init(pwm_gpio_to_slice_num(pin), &tone_pwm_cfg, true);
gpio_set_function(pin, GPIO_FUNC_PWM);
audio_pin = pin;
}
void PicoExplorer::set_tone(uint16_t frequency, float duty) {
// output a square wave, so 50% duty cycle
if(audio_pin != -1) {
uint16_t pwm_wrap = 490196 / frequency;
pwm_set_wrap(audio_pin, pwm_wrap);
pwm_set_gpio_level(audio_pin, pwm_wrap * duty);
}
}
}
#include "pico_explorer.hpp"

Wyświetl plik

@ -1,11 +1,10 @@
#pragma once
#include "drivers/st7789/st7789.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "drivers/motor/motor.hpp"
namespace pimoroni {
class PicoExplorer : public PicoGraphics {
class PicoExplorer {
public:
static const int WIDTH = 240;
static const int HEIGHT = 240;
@ -18,6 +17,13 @@ namespace pimoroni {
static const uint8_t ADC1 = 1;
static const uint8_t ADC2 = 2;
static const uint ADC0_PIN = 26;
static const uint ADC1_PIN = 27;
static const uint ADC2_PIN = 28;
static constexpr pin_pair MOTOR1_PINS{9, 8};
static constexpr pin_pair MOTOR2_PINS{11, 10};
static const uint8_t MOTOR1 = 0;
static const uint8_t MOTOR2 = 1;
@ -33,25 +39,6 @@ namespace pimoroni {
static const uint GP5 = 5;
static const uint GP6 = 6;
static const uint GP7 = 7;
uint16_t *__fb;
private:
ST7789 screen;
int8_t audio_pin = -1;
public:
PicoExplorer(uint16_t *buf);
void init();
void update();
bool is_pressed(uint8_t button);
float get_adc(uint8_t channel);
void set_motor(uint8_t channel, uint8_t action, float speed = 0.0f);
void set_audio_pin(uint pin);
void set_tone(uint16_t frequency, float duty = 0.2f);
};
}

Wyświetl plik

@ -1,9 +1,12 @@
# Pico Graphics <!-- omit in toc -->
Pico Graphics is a tiny graphics library for 16-bit RGB565 displays.
Pico Graphics is a tiny graphics library supporting a number of underlying buffer formats including 8-bit paletted (256 colour), 8-bit RGB332 (256 colour), 16-bit RGB565 (65K colour) and 4-bit packed (8 colour).
It supports drawing text, primitive and individual pixels and includes basic types such as `rect` and `point` brimming with methods to help you develop games and applications.
- [Overview](#overview)
- [Pen Types](#pen-types)
- [Creating A Pico Graphics Instance](#creating-a-pico-graphics-instance)
- [Function Reference](#function-reference)
- [Types](#types)
- [rect](#rect)
@ -14,11 +17,13 @@ It supports drawing text, primitive and individual pixels and includes basic typ
- [rect.inflate & rect.deflate](#rectinflate--rectdeflate)
- [point](#point)
- [point.clamp](#pointclamp)
- [operators](#operators)
- [Pens & Clipping](#pens--clipping)
- [set_pen](#set_pen)
- [create_pen](#create_pen)
- [set_clip & remove_clip](#set_clip--remove_clip)
- [Palette](#palette)
- [update_pen](#update_pen)
- [reset_pen](#reset_pen)
- [Pixels](#pixels)
- [pixel](#pixel)
- [pixel_span](#pixel_span)
@ -28,6 +33,45 @@ It supports drawing text, primitive and individual pixels and includes basic typ
- [Text](#text)
- [Change Font](#change-font)
## Overview
Pico Graphics comes in multiple flavours depending on which underlying buffer type you wish to work with.
Your buffer doesn't have to be native to your display. For example a 16-bit ST7789 display can work with P4, P8, RGB332 and RGB565 buffers, with palette lookups handled for you on the fly.
### Pen Types
* `P4` - 4-bit packed, with an 8 colour palette. This is commonly used for 7/8-colour e-ink displays or driving large displays with few colours.
* `P8` - 8-bit, with a 256 colour palette. Great balance of memory usage versus available colours. You can replace palette entries on the fly.
* `RGB332` - 8-bit, with a fixed 256 colour RGB332 palette. Great for quickly porting an RGB565 app to use less RAM. Limits your colour choices, but is easier to grok.
* `RGB565` - 16-bit, 65K "True Colour." Great for rainbows, gradients and images but comes at the cost of RAM!
### Creating A Pico Graphics Instance
To create a Pico Graphics instance to draw into, you should construct an instance of the Pen type class you want to use:
```c++
PicoGraphics_PenP4 graphics(WITH, HEIGHT, nullptr);
PicoGraphics_PenP8 graphics(WITH, HEIGHT, nullptr);
PicoGraphics_PenRGB332 graphics(WITH, HEIGHT, nullptr);
PicoGraphics_PenRGB565 graphics(WITH, HEIGHT, nullptr);
```
To draw something to a display you should create a display driver instance, eg:
```c++
ST7789 st7789(PicoExplorer::WIDTH, PicoExplorer::HEIGHT, ROTATE_0, false, get_spi_pins(BG_SPI_FRONT));
```
And then send it the Pico Graphics instance to draw:
```c++
st7789.update(&graphics);
```
The driver will check your graphics type and act accordingly.
## Function Reference
### Types
@ -123,9 +167,10 @@ Would deflate our `box` to start at `11,11` and be 8x8 pixels in size.
Since `rectangle` *always* draws a filled rectangle, this can be useful to add an outline of your desired thickness:
```c++
WHITE = screen.create_pen(255, 255, 255);
rect box(10, 10, 100, 100);
box.inflate(1); // Inflate our box by 1px on all sides
screen.set_pen(255, 255, 255); // White outline
screen.set_pen(WHITE); // White outline
screen.rectangle(box);
box.deflate(1); // Return to our original box size
screen.set_pen(0, 0, 0); /// Black fill
@ -146,37 +191,31 @@ A point can be clamped within the confines of a `rect`. This is useful for keepi
```c++
point cursor(10, 1000); // A point, far outside the bounds of our screen
cursor.clamp(screen.bounds)); // Clamp to the screen
cursor.clamp(screen.bounds); // Clamp to the screen
```
##### operators
TODO
### Pens & Clipping
#### set_pen
In order to draw anything with Pico Graphics you must first set the pen to your desired colour, there are two ways to do this:
In order to draw anything with Pico Graphics you must first set the pen to your desired palette colour:
```c++
void PicoGraphics::set_pen(uint8_t r, uint8_t g, uint8_t b);
void PicoGraphics::set_pen(uint16_t p);
void PicoGraphics::set_pen(uint8_t p);
```
The former uses 8-bit R, G and B values which are clipped to 5, 6 and 5 bits respectively to form a 16-bit colour. Internally it uses `create_pen`.
This value represents an index into the internal colour palette, which has 256 entries and defaults to RGB332 giving an approximation of all RGB888 colours.
The latter takes a 16-bit colour directly and is a great way to save a few cycles if you're working with a constant palette of colours.
#### create_pen
```c++
uint16_t PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b);
int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b);
```
Create pen takes R, G and B values, clamps them to 5, 6 and 5 bits respectively and joins them into a `uint16_t` pen that represents a single 16-bit colour.
By default create pen takes R, G and B values, clamps them to 3, 3 and 2 bits respectively and returns an index in the RGB332 palette.
Creating your pens up front and storing them as `uint16_t` can speed up switching colours.
You must create pens before using them with `set_pen()` which accepts only a palette index.
#### set_clip & remove_clip
@ -189,6 +228,29 @@ void PicoGraphics::remove_clip();
`remove_clip` sets the surface clipping rectangle back to the surface `bounds`.
### Palette
By default Pico Graphics uses an `RGB332` palette and clamps all pens to their `RGB332` values so it can give you an approximate colour for every `RGB888` value you request. If you don't want to think about colours and palettes you can leave it as is.
Alternatively `set_palette_mode()` lets you switch into an RGB565 `USER` palette which gives you up to 256 16-bit colours of your choice.
#### update_pen
```c++
int PicoGraphics::update_pen(uint8_t index, uint8_t r, uint8_t g, uint8_t b);
```
Modify a palette entry to the given RGB colour (or nearest supported equivilent.)
#### reset_pen
```c++
void PicoGraphics::reset_pen(uint8_t index);
```
Return a palette entry to its default value. Usually black and marked unused.
### Pixels
#### pixel

Wyświetl plik

@ -1,8 +1,13 @@
add_library(pico_graphics
${CMAKE_CURRENT_LIST_DIR}/types.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_1bit.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_p4.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_p8.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb332.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb565.cpp
)
target_include_directories(pico_graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(pico_graphics bitmap_fonts)
target_link_libraries(pico_graphics bitmap_fonts hershey_fonts pico_stdlib)

Wyświetl plik

@ -1,21 +1,46 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics::PicoGraphics(uint16_t width, uint16_t height, uint16_t *frame_buffer)
: frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) {
set_font(&font6);
};
int PicoGraphics::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {return -1;};
int PicoGraphics::reset_pen(uint8_t i) {return -1;};
int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) {return -1;};
void PicoGraphics::set_pixel_dither(const Point &p, const RGB &c) {};
void PicoGraphics::set_pixel_dither(const Point &p, const RGB565 &c) {};
void PicoGraphics::scanline_convert(PenType type, conversion_callback_func callback) {};
void PicoGraphics::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) {};
void PicoGraphics::set_dimensions(int width, int height) {
bounds = clip = {0, 0, width, height};
}
void PicoGraphics::set_framebuffer(void *frame_buffer) {
this->frame_buffer = frame_buffer;
}
void PicoGraphics::set_font(const bitmap::font_t *font){
this->font = font;
this->bitmap_font = font;
this->hershey_font = nullptr;
}
void PicoGraphics::set_pen(uint8_t r, uint8_t g, uint8_t b) {
pen = create_pen(r, g, b);
void PicoGraphics::set_font(const hershey::font_t *font){
this->bitmap_font = nullptr;
this->hershey_font = font;
}
void PicoGraphics::set_pen(Pen p) {
pen = p;
void PicoGraphics::set_font(std::string name){
if (name == "bitmap6") {
set_font(&font6);
} else if (name == "bitmap8") {
set_font(&font8);
} else if (name == "bitmap14_outline") {
set_font(&font14_outline);
} else {
// check that font exists and assign it
if(hershey::fonts.find(name) != hershey::fonts.end()) {
set_font(hershey::fonts[name]);
}
}
}
void PicoGraphics::set_clip(const Rect &r) {
@ -25,26 +50,14 @@ namespace pimoroni {
void PicoGraphics::remove_clip() {
clip = bounds;
}
Pen* PicoGraphics::ptr(const Rect &r) {
return frame_buffer + r.x + r.y * bounds.w;
}
Pen* PicoGraphics::ptr(const Point &p) {
return frame_buffer + p.x + p.y * bounds.w;
}
Pen* PicoGraphics::ptr(int32_t x, int32_t y) {
return frame_buffer + x + y * bounds.w;
}
void PicoGraphics::clear() {
rectangle(clip);
}
void PicoGraphics::pixel(const Point &p) {
if(!clip.contains(p)) return;
*ptr(p) = pen;
set_pixel(p);
}
void PicoGraphics::pixel_span(const Point &p, int32_t l) {
@ -57,10 +70,8 @@ namespace pimoroni {
if(clipped.x < clip.x) {l += clipped.x - clip.x; clipped.x = clip.x;}
if(clipped.x + l >= clip.x + clip.w) {l = clip.x + clip.w - clipped.x;}
Pen *dest = ptr(clipped);
while(l--) {
*dest++ = pen;
}
Point dest(clipped.x, clipped.y);
set_pixel_span(dest, l);
}
void PicoGraphics::rectangle(const Rect &r) {
@ -69,15 +80,12 @@ namespace pimoroni {
if(clipped.empty()) return;
Pen *dest = ptr(clipped);
Point dest(clipped.x, clipped.y);
while(clipped.h--) {
// draw span of pixels for this row
for(int32_t i = 0; i < clipped.w; i++) {
*dest++ = pen;
}
set_pixel_span(dest, clipped.w);
// move to next scanline
dest += bounds.w - clipped.w;
dest.y++;
}
}
@ -109,16 +117,42 @@ namespace pimoroni {
}
}
void PicoGraphics::character(const char c, const Point &p, uint8_t scale) {
bitmap::character(font, [this](int32_t x, int32_t y, int32_t w, int32_t h){
rectangle(Rect(x, y, w, h));
}, c, p.x, p.y, scale);
void PicoGraphics::character(const char c, const Point &p, float s, float a) {
if (bitmap_font) {
bitmap::character(bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) {
rectangle(Rect(x, y, w, h));
}, c, p.x, p.y, std::max(1.0f, s));
return;
}
if (hershey_font) {
hershey::glyph(hershey_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
line(Point(x1, y1), Point(x2, y2));
}, c, p.x, p.y, s, a);
return;
}
}
void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale) {
bitmap::text(font, [this](int32_t x, int32_t y, int32_t w, int32_t h){
rectangle(Rect(x, y, w, h));
}, t, p.x, p.y, wrap, scale);
void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, float s, float a, uint8_t letter_spacing) {
if (bitmap_font) {
bitmap::text(bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) {
rectangle(Rect(x, y, w, h));
}, t, p.x, p.y, wrap, std::max(1.0f, s), letter_spacing);
return;
}
if (hershey_font) {
hershey::text(hershey_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
line(Point(x1, y1), Point(x2, y2));
}, t, p.x, p.y, s, a);
return;
}
}
int32_t PicoGraphics::measure_text(const std::string &t, float s, uint8_t letter_spacing) {
if (bitmap_font) return bitmap::measure_text(bitmap_font, t, std::max(1.0f, s), letter_spacing);
if (hershey_font) return hershey::measure_text(hershey_font, t, s);
return 0;
}
int32_t orient2d(Point p1, Point p2, Point p3) {
@ -171,13 +205,13 @@ namespace pimoroni {
int32_t w1 = w1row;
int32_t w2 = w2row;
Pen *dest = ptr(triangle_bounds.x, triangle_bounds.y + y);
Point dest = Point(triangle_bounds.x, triangle_bounds.y + y);
for (int32_t x = 0; x < triangle_bounds.w; x++) {
if ((w0 | w1 | w2) >= 0) {
*dest = pen;
set_pixel(dest);
}
dest++;
dest.x++;
w0 += a12;
w1 += a20;
@ -239,24 +273,29 @@ namespace pimoroni {
void PicoGraphics::line(Point p1, Point p2) {
// fast horizontal line
if(p1.y == p2.y) {
int32_t start = std::max(clip.x, std::min(p1.x, p2.x));
int32_t end = std::min(clip.x + clip.w, std::max(p1.x, p2.x));
p1 = p1.clamp(clip);
p2 = p2.clamp(clip);
int32_t start = std::min(p1.x, p2.x);
int32_t end = std::max(p1.x, p2.x);
pixel_span(Point(start, p1.y), end - start);
return;
}
// fast vertical line
if(p1.x == p2.x) {
int32_t start = std::max(clip.y, std::min(p1.y, p2.y));
int32_t length = std::min(clip.y + clip.h, std::max(p1.y, p2.y)) - start;
Pen *dest = ptr(p1.x, start);
p1 = p1.clamp(clip);
p2 = p2.clamp(clip);
int32_t start = std::min(p1.y, p2.y);
int32_t length = std::max(p1.y, p2.y) - start;
Point dest(p1.x, start);
while(length--) {
*dest = pen;
dest += bounds.w;
set_pixel(dest);
dest.y++;
}
return;
}
// general purpose line
// lines are either "shallow" or "steep" based on whether the x delta
// is greater than the y delta
@ -271,7 +310,8 @@ namespace pimoroni {
int32_t x = p1.x;
int32_t y = p1.y << 16;
while(s--) {
pixel(Point(x, y >> 16));
Point p(x, y >> 16);
if(clip.contains(p)) set_pixel(p);
y += sy;
x += sx;
}
@ -283,7 +323,8 @@ namespace pimoroni {
int32_t y = p1.y;
int32_t x = p1.x << 16;
while(s--) {
pixel(Point(x >> 16, y));
Point p(x >> 16, y);
if(clip.contains(p)) set_pixel(p);
y += sy;
x += sx;
}

Wyświetl plik

@ -1,16 +1,90 @@
#pragma once
#include <string>
#include <array>
#include <cstdint>
#include <algorithm>
#include <vector>
#include <functional>
#include "libraries/hershey_fonts/hershey_fonts.hpp"
#include "libraries/bitmap_fonts/bitmap_fonts.hpp"
#include "libraries/bitmap_fonts/font6_data.hpp"
#include "libraries/bitmap_fonts/font8_data.hpp"
#include "libraries/bitmap_fonts/font14_outline_data.hpp"
// a tiny little graphics library for our Pico products
// supports only 16-bit (565) RGB framebuffers
#include "common/pimoroni_common.hpp"
// A tiny graphics library for our Pico products
// supports:
// - 16-bit (565) RGB
// - 8-bit (332) RGB
// - 8-bit with 16-bit 256 entry palette
// - 4-bit with 16-bit 8 entry palette
namespace pimoroni {
typedef uint8_t RGB332;
typedef uint16_t RGB565;
struct RGB {
int16_t r, g, b;
typedef uint16_t Pen;
constexpr RGB() : r(0), g(0), b(0) {}
constexpr RGB(RGB332 c) :
r((c & 0b11100000) >> 0),
g((c & 0b00011100) << 3),
b((c & 0b00000011) << 6) {}
constexpr RGB(RGB565 c) :
r((__builtin_bswap16(c) & 0b1111100000000000) >> 8),
g((__builtin_bswap16(c) & 0b0000011111100000) >> 3),
b((__builtin_bswap16(c) & 0b0000000000011111) << 3) {}
constexpr RGB(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {}
constexpr RGB operator+ (const RGB& c) const {return RGB(r + c.r, g + c.g, b + c.b);}
constexpr RGB& operator+=(const RGB& c) {r += c.r; g += c.g; b += c.b; return *this;}
constexpr RGB operator- (const RGB& c) const {return RGB(r - c.r, g - c.g, b - c.b);}
// a rough approximation of how bright a colour is used to compare the
// relative brightness of two colours
int luminance() const {
// weights based on https://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/
return r * 21 + g * 72 + b * 7;
}
// a relatively low cost approximation of how "different" two colours are
// perceived which avoids expensive colour space conversions.
// described in detail at https://www.compuphase.com/cmetric.htm
int distance(const RGB& c) const {
int rmean = (r + c.r) / 2;
int rx = r - c.r;
int gx = g - c.g;
int bx = b - c.b;
return abs((int)(
(((512 + rmean) * rx * rx) >> 8) + 4 * gx * gx + (((767 - rmean) * bx * bx) >> 8)
));
}
int closest(const RGB *palette, size_t len) const {
int d = INT_MAX, m = -1;
for(size_t i = 0; i < len; i++) {
int dc = distance(palette[i]);
if(dc < d) {m = i; d = dc;}
}
return m;
}
constexpr RGB565 to_rgb565() {
uint16_t p = ((r & 0b11111000) << 8) |
((g & 0b11111100) << 3) |
((b & 0b11111000) >> 3);
return __builtin_bswap16(p);
}
constexpr RGB565 to_rgb332() {
return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6);
}
};
typedef int Pen;
struct Rect;
@ -22,10 +96,18 @@ namespace pimoroni {
inline Point& operator-= (const Point &a) { x -= a.x; y -= a.y; return *this; }
inline Point& operator+= (const Point &a) { x += a.x; y += a.y; return *this; }
inline Point& operator/= (const int32_t a) { x /= a; y /= a; return *this; }
Point clamp(const Rect &r) const;
};
inline bool operator== (const Point &lhs, const Point &rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; }
inline bool operator!= (const Point &lhs, const Point &rhs) { return !(lhs == rhs); }
inline Point operator- (Point lhs, const Point &rhs) { lhs -= rhs; return lhs; }
inline Point operator- (const Point &rhs) { return Point(-rhs.x, -rhs.y); }
inline Point operator+ (Point lhs, const Point &rhs) { lhs += rhs; return lhs; }
inline Point operator/ (Point lhs, const int32_t a) { lhs /= a; return lhs; }
struct Rect {
int32_t x = 0, y = 0, w = 0, h = 0;
@ -43,48 +125,251 @@ namespace pimoroni {
void deflate(int32_t v);
};
static const RGB565 rgb332_to_rgb565_lut[256] = {
0x0000, 0x0800, 0x1000, 0x1800, 0x0001, 0x0801, 0x1001, 0x1801, 0x0002, 0x0802, 0x1002, 0x1802, 0x0003, 0x0803, 0x1003, 0x1803,
0x0004, 0x0804, 0x1004, 0x1804, 0x0005, 0x0805, 0x1005, 0x1805, 0x0006, 0x0806, 0x1006, 0x1806, 0x0007, 0x0807, 0x1007, 0x1807,
0x0020, 0x0820, 0x1020, 0x1820, 0x0021, 0x0821, 0x1021, 0x1821, 0x0022, 0x0822, 0x1022, 0x1822, 0x0023, 0x0823, 0x1023, 0x1823,
0x0024, 0x0824, 0x1024, 0x1824, 0x0025, 0x0825, 0x1025, 0x1825, 0x0026, 0x0826, 0x1026, 0x1826, 0x0027, 0x0827, 0x1027, 0x1827,
0x0040, 0x0840, 0x1040, 0x1840, 0x0041, 0x0841, 0x1041, 0x1841, 0x0042, 0x0842, 0x1042, 0x1842, 0x0043, 0x0843, 0x1043, 0x1843,
0x0044, 0x0844, 0x1044, 0x1844, 0x0045, 0x0845, 0x1045, 0x1845, 0x0046, 0x0846, 0x1046, 0x1846, 0x0047, 0x0847, 0x1047, 0x1847,
0x0060, 0x0860, 0x1060, 0x1860, 0x0061, 0x0861, 0x1061, 0x1861, 0x0062, 0x0862, 0x1062, 0x1862, 0x0063, 0x0863, 0x1063, 0x1863,
0x0064, 0x0864, 0x1064, 0x1864, 0x0065, 0x0865, 0x1065, 0x1865, 0x0066, 0x0866, 0x1066, 0x1866, 0x0067, 0x0867, 0x1067, 0x1867,
0x0080, 0x0880, 0x1080, 0x1880, 0x0081, 0x0881, 0x1081, 0x1881, 0x0082, 0x0882, 0x1082, 0x1882, 0x0083, 0x0883, 0x1083, 0x1883,
0x0084, 0x0884, 0x1084, 0x1884, 0x0085, 0x0885, 0x1085, 0x1885, 0x0086, 0x0886, 0x1086, 0x1886, 0x0087, 0x0887, 0x1087, 0x1887,
0x00a0, 0x08a0, 0x10a0, 0x18a0, 0x00a1, 0x08a1, 0x10a1, 0x18a1, 0x00a2, 0x08a2, 0x10a2, 0x18a2, 0x00a3, 0x08a3, 0x10a3, 0x18a3,
0x00a4, 0x08a4, 0x10a4, 0x18a4, 0x00a5, 0x08a5, 0x10a5, 0x18a5, 0x00a6, 0x08a6, 0x10a6, 0x18a6, 0x00a7, 0x08a7, 0x10a7, 0x18a7,
0x00c0, 0x08c0, 0x10c0, 0x18c0, 0x00c1, 0x08c1, 0x10c1, 0x18c1, 0x00c2, 0x08c2, 0x10c2, 0x18c2, 0x00c3, 0x08c3, 0x10c3, 0x18c3,
0x00c4, 0x08c4, 0x10c4, 0x18c4, 0x00c5, 0x08c5, 0x10c5, 0x18c5, 0x00c6, 0x08c6, 0x10c6, 0x18c6, 0x00c7, 0x08c7, 0x10c7, 0x18c7,
0x00e0, 0x08e0, 0x10e0, 0x18e0, 0x00e1, 0x08e1, 0x10e1, 0x18e1, 0x00e2, 0x08e2, 0x10e2, 0x18e2, 0x00e3, 0x08e3, 0x10e3, 0x18e3,
0x00e4, 0x08e4, 0x10e4, 0x18e4, 0x00e5, 0x08e5, 0x10e5, 0x18e5, 0x00e6, 0x08e6, 0x10e6, 0x18e6, 0x00e7, 0x08e7, 0x10e7, 0x18e7,
};
class PicoGraphics {
public:
uint16_t *frame_buffer;
Rect bounds;
Rect clip;
Pen pen;
const bitmap::font_t *font;
public:
PicoGraphics(uint16_t width, uint16_t height, uint16_t *frame_buffer);
void set_font(const bitmap::font_t *font);
void set_pen(uint8_t r, uint8_t g, uint8_t b);
void set_pen(Pen p);
constexpr Pen create_pen(uint8_t r, uint8_t g, uint8_t b) {
uint16_t p = ((r & 0b11111000) << 8) |
((g & 0b11111100) << 3) |
((b & 0b11111000) >> 3);
return __builtin_bswap16(p);
enum PenType {
PEN_1BIT,
PEN_P2,
PEN_P4,
PEN_P8,
PEN_RGB332,
PEN_RGB565
};
void *frame_buffer;
PenType pen_type;
Rect bounds;
Rect clip;
typedef std::function<void(void *data, size_t length)> conversion_callback_func;
//typedef std::function<void(int y)> scanline_interrupt_func;
//scanline_interrupt_func scanline_interrupt = nullptr;
const bitmap::font_t *bitmap_font;
const hershey::font_t *hershey_font;
static constexpr RGB332 rgb_to_rgb332(uint8_t r, uint8_t g, uint8_t b) {
return RGB(r, g, b).to_rgb332();
}
static constexpr RGB565 rgb332_to_rgb565(RGB332 c) {
uint16_t p = ((c & 0b11100000) << 8) |
((c & 0b00011100) << 6) |
((c & 0b00000011) << 3);
return __builtin_bswap16(p);
}
static constexpr RGB565 rgb565_to_rgb332(RGB565 c) {
c = __builtin_bswap16(c);
return ((c & 0b1110000000000000) >> 8) |
((c & 0b0000011100000000) >> 6) |
((c & 0b0000000000011000) >> 3);
}
static constexpr RGB565 rgb_to_rgb565(uint8_t r, uint8_t g, uint8_t b) {
return RGB(r, g, b).to_rgb565();
}
static constexpr RGB rgb332_to_rgb(RGB332 c) {
return RGB((RGB332)c);
};
static constexpr RGB rgb565_to_rgb(RGB565 c) {
return RGB((RGB565)c);
};
PicoGraphics(uint16_t width, uint16_t height, void *frame_buffer)
: frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) {
set_font(&font6);
};
virtual void set_pen(uint c) = 0;
virtual void set_pen(uint8_t r, uint8_t g, uint8_t b) = 0;
virtual void set_pixel(const Point &p) = 0;
virtual void set_pixel_span(const Point &p, uint l) = 0;
virtual int create_pen(uint8_t r, uint8_t g, uint8_t b);
virtual int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b);
virtual int reset_pen(uint8_t i);
virtual void set_pixel_dither(const Point &p, const RGB &c);
virtual void set_pixel_dither(const Point &p, const RGB565 &c);
virtual void scanline_convert(PenType type, conversion_callback_func callback);
virtual void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent);
void set_font(const bitmap::font_t *font);
void set_font(const hershey::font_t *font);
void set_font(std::string font);
void set_dimensions(int width, int height);
void set_framebuffer(void *frame_buffer);
void *get_data();
void get_data(PenType type, uint y, void *row_buf);
void set_clip(const Rect &r);
void remove_clip();
Pen* ptr(const Point &p);
Pen* ptr(const Rect &r);
Pen* ptr(int32_t x, int32_t y);
void clear();
void pixel(const Point &p);
void pixel_span(const Point &p, int32_t l);
void rectangle(const Rect &r);
void circle(const Point &p, int32_t r);
void character(const char c, const Point &p, uint8_t scale = 2);
void text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale = 2);
void character(const char c, const Point &p, float s = 2.0f, float a = 0.0f);
void text(const std::string &t, const Point &p, int32_t wrap, float s = 2.0f, float a = 0.0f, uint8_t letter_spacing = 1);
int32_t measure_text(const std::string &t, float s = 2.0f, uint8_t letter_spacing = 1);
void polygon(const std::vector<Point> &points);
void triangle(Point p1, Point p2, Point p3);
void line(Point p1, Point p2);
};
class PicoGraphics_Pen1Bit : public PicoGraphics {
public:
uint8_t color;
PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
static size_t buffer_size(uint w, uint h) {
return w * h / 8;
}
};
class PicoGraphics_PenP4 : public PicoGraphics {
public:
static const uint palette_size = 16;
uint8_t color;
RGB palette[palette_size];
bool used[palette_size];
const uint pattern[16] = // dither pattern
{0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
std::array<std::array<uint8_t, 16>, 512> candidate_cache;
bool cache_built = false;
std::array<uint8_t, 16> candidates;
PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
int reset_pen(uint8_t i) override;
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates);
void set_pixel_dither(const Point &p, const RGB &c) override;
void scanline_convert(PenType type, conversion_callback_func callback) override;
static size_t buffer_size(uint w, uint h) {
return w * h / 2;
}
};
class PicoGraphics_PenP8 : public PicoGraphics {
public:
static const uint palette_size = 256;
uint8_t color;
RGB palette[palette_size];
bool used[palette_size];
const uint pattern[16] = // dither pattern
{0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
std::array<std::array<uint8_t, 16>, 512> candidate_cache;
bool cache_built = false;
std::array<uint8_t, 16> candidates;
PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
int reset_pen(uint8_t i) override;
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates);
void set_pixel_dither(const Point &p, const RGB &c) override;
void scanline_convert(PenType type, conversion_callback_func callback) override;
static size_t buffer_size(uint w, uint h) {
return w * h;
}
};
class PicoGraphics_PenRGB332 : public PicoGraphics {
public:
RGB332 color;
PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
void set_pixel_dither(const Point &p, const RGB &c) override;
void set_pixel_dither(const Point &p, const RGB565 &c) override;
void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) override;
void scanline_convert(PenType type, conversion_callback_func callback) override;
static size_t buffer_size(uint w, uint h) {
return w * h;
}
};
class PicoGraphics_PenRGB565 : public PicoGraphics {
public:
RGB src_color;
RGB565 color;
PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
static size_t buffer_size(uint w, uint h) {
return w * h * sizeof(RGB565);
}
};
class DisplayDriver {
public:
uint16_t width;
uint16_t height;
Rotation rotation;
DisplayDriver(uint16_t width, uint16_t height, Rotation rotation)
: width(width), height(height), rotation(rotation) {};
virtual void update(PicoGraphics *display) {};
virtual void set_backlight(uint8_t brightness) {};
virtual void cleanup() {};
};
}

Wyświetl plik

@ -0,0 +1,60 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_Pen1Bit::PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
this->pen_type = PEN_1BIT;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
}
void PicoGraphics_Pen1Bit::set_pen(uint c) {
color = c != 0 ? 1 : 0;
}
void PicoGraphics_Pen1Bit::set_pen(uint8_t r, uint8_t g, uint8_t b) {
color = r != 0 || g != 0 || b != 0 ? 1 : 0;
}
void PicoGraphics_Pen1Bit::set_pixel(const Point &p) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
uint8_t *f = &buf[(p.x / 8) + (p.y * bounds.w / 8)];
uint bo = 7 - (p.x & 0b111);
// forceably clear the bit
*f &= ~(1U << bo);
// set pixel
*f |= (color << bo);
}
void PicoGraphics_Pen1Bit::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
uint8_t *f = &buf[(p.x / 8) + (p.y * bounds.w / 8)];
uint bo = 7 - (p.x & 0b111);
// TODO: this could trivially be sped up by processing single bits only at
// the start and the end of the span and writing full bytes (8 pixels at
// a time) in the middle portion of the span. would only be more efficient
// for longer spans (probably around 20 pixels or more)
while(l--) {
// forceably clear the bit and then set to the correct value
*f &= ~(1U << bo);
*f |= (color << bo);
if(bo == 0) { // last bit of this byte?
// move to next byte in framebuffer and reset the bit offset
f++; bo = 8;
}
bo--;
}
}
}

Wyświetl plik

@ -0,0 +1,154 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenP4::PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
this->pen_type = PEN_P4;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
for(auto i = 0u; i < palette_size; i++) {
palette[i] = {
uint8_t(i << 4),
uint8_t(i << 4),
uint8_t(i << 4)
};
used[i] = false;
}
cache_built = false;
}
void PicoGraphics_PenP4::set_pen(uint c) {
color = c & 0xf;
}
void PicoGraphics_PenP4::set_pen(uint8_t r, uint8_t g, uint8_t b) {
int pen = RGB(r, g, b).closest(palette, palette_size);
if(pen != -1) color = pen;
}
int PicoGraphics_PenP4::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {
i &= 0xf;
used[i] = true;
palette[i] = {r, g, b};
cache_built = false;
return i;
}
int PicoGraphics_PenP4::create_pen(uint8_t r, uint8_t g, uint8_t b) {
// Create a colour and place it in the palette if there's space
for(auto i = 0u; i < palette_size; i++) {
if(!used[i]) {
palette[i] = {r, g, b};
used[i] = true;
cache_built = false;
return i;
}
}
return -1;
}
int PicoGraphics_PenP4::reset_pen(uint8_t i) {
palette[i] = {0, 0, 0};
used[i] = false;
cache_built = false;
return i;
}
void PicoGraphics_PenP4::set_pixel(const Point &p) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
uint8_t *f = &buf[(p.x / 2) + (p.y * bounds.w / 2)];
uint8_t o = (~p.x & 0b1) * 4; // bit offset within byte
uint8_t m = ~(0b1111 << o); // bit mask for byte
uint8_t b = color << o; // bit value shifted to position
*f &= m; // clear bits
*f |= b; // set value
}
void PicoGraphics_PenP4::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
uint8_t *f = &buf[(p.x / 2) + (p.y * bounds.w / 2)];
// doubled up color value, so the color is stored in both nibbles
uint8_t cc = color | (color << 4);
// handle the first pixel if not byte aligned
if(p.x & 0b1) {*f &= 0b11110000; *f |= (cc & 0b00001111); f++; l--;}
// write any double nibble pixels
while(l > 1) {*f++ = cc; l -= 2;}
// handle the last pixel if not byte aligned
if(l) {*f &= 0b00001111; *f |= (cc & 0b11110000);}
}
void PicoGraphics_PenP4::get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates) {
RGB error;
for(size_t i = 0; i < candidates.size(); i++) {
candidates[i] = (col + error).closest(palette, len);
error += (col - palette[candidates[i]]);
}
// sort by a rough approximation of luminance, this ensures that neighbouring
// pixels in the dither matrix are at extreme opposites of luminence
// giving a more balanced output
std::sort(candidates.begin(), candidates.end(), [palette](int a, int b) {
return palette[a].luminance() > palette[b].luminance();
});
}
void PicoGraphics_PenP4::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
if(!cache_built) {
for(uint i = 0; i < 512; i++) {
RGB cache_col((i & 0x1C0) >> 1, (i & 0x38) << 2, (i & 0x7) << 5);
get_dither_candidates(cache_col, palette, palette_size, candidate_cache[i]);
}
cache_built = true;
}
uint cache_key = ((c.r & 0xE0) << 1) | ((c.g & 0xE0) >> 2) | ((c.b & 0xE0) >> 5);
//get_dither_candidates(c, palette, 256, candidates);
// find the pattern coordinate offset
uint pattern_index = (p.x & 0b11) | ((p.y & 0b11) << 2);
// set the pixel
//color = candidates[pattern[pattern_index]];
color = candidate_cache[cache_key][pattern[pattern_index]];
set_pixel(p);
}
void PicoGraphics_PenP4::scanline_convert(PenType type, conversion_callback_func callback) {
if(type == PEN_RGB565) {
// Cache the RGB888 palette as RGB565
RGB565 cache[palette_size];
for(auto i = 0u; i < palette_size; i++) {
cache[i] = palette[i].to_rgb565();
}
// Treat our void* frame_buffer as uint8_t
uint8_t *src = (uint8_t *)frame_buffer;
// Allocate a per-row temporary buffer
uint16_t row_buf[bounds.w];
for(auto y = 0; y < bounds.h; y++) {
/*if(scanline_interrupt != nullptr) {
scanline_interrupt(y);
// Cache the RGB888 palette as RGB565
for(auto i = 0u; i < 16; i++) {
cache[i] = palette[i].to_rgb565();
}
}*/
for(auto x = 0; x < bounds.w; x++) {
uint8_t c = src[(bounds.w * y / 2) + (x / 2)];
uint8_t o = (~x & 0b1) * 4; // bit offset within byte
uint8_t b = (c >> o) & 0xf; // bit value shifted to position
row_buf[x] = cache[b];
}
// Callback to the driver with the row data
callback(row_buf, bounds.w * sizeof(RGB565));
}
}
}
}

Wyświetl plik

@ -0,0 +1,123 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenP8::PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
this->pen_type = PEN_P8;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
for(auto i = 0u; i < palette_size; i++) {
palette[i] = {uint8_t(i), uint8_t(i), uint8_t(i)};
used[i] = false;
}
cache_built = false;
}
void PicoGraphics_PenP8::set_pen(uint c) {
color = c;
}
void PicoGraphics_PenP8::set_pen(uint8_t r, uint8_t g, uint8_t b) {
int pen = RGB(r, g, b).closest(palette, 16);
if(pen != -1) color = pen;
}
int PicoGraphics_PenP8::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {
i &= 0xff;
used[i] = true;
palette[i] = {r, g, b};
cache_built = false;
return i;
}
int PicoGraphics_PenP8::create_pen(uint8_t r, uint8_t g, uint8_t b) {
// Create a colour and place it in the palette if there's space
for(auto i = 0u; i < palette_size; i++) {
if(!used[i]) {
palette[i] = {r, g, b};
used[i] = true;
cache_built = false;
return i;
}
}
return -1;
}
int PicoGraphics_PenP8::reset_pen(uint8_t i) {
palette[i] = {0, 0, 0};
used[i] = false;
cache_built = false;
return i;
}
void PicoGraphics_PenP8::set_pixel(const Point &p) {
uint8_t *buf = (uint8_t *)frame_buffer;
buf[p.y * bounds.w + p.x] = color;
}
void PicoGraphics_PenP8::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
buf = &buf[p.y * bounds.w + p.x];
while(l--) {
*buf++ = color;
}
}
void PicoGraphics_PenP8::get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates) {
RGB error;
for(size_t i = 0; i < candidates.size(); i++) {
candidates[i] = (col + error).closest(palette, len);
error += (col - palette[candidates[i]]);
}
// sort by a rough approximation of luminance, this ensures that neighbouring
// pixels in the dither matrix are at extreme opposites of luminence
// giving a more balanced output
std::sort(candidates.begin(), candidates.end(), [palette](int a, int b) {
return palette[a].luminance() > palette[b].luminance();
});
}
void PicoGraphics_PenP8::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
if(!cache_built) {
for(uint i = 0; i < 512; i++) {
RGB cache_col((i & 0x1C0) >> 1, (i & 0x38) << 2, (i & 0x7) << 5);
get_dither_candidates(cache_col, palette, palette_size, candidate_cache[i]);
}
cache_built = true;
}
uint cache_key = ((c.r & 0xE0) << 1) | ((c.g & 0xE0) >> 2) | ((c.b & 0xE0) >> 5);
//get_dither_candidates(c, palette, 256, candidates);
// find the pattern coordinate offset
uint pattern_index = (p.x & 0b11) | ((p.y & 0b11) << 2);
// set the pixel
//color = candidates[pattern[pattern_index]];
color = candidate_cache[cache_key][pattern[pattern_index]];
set_pixel(p);
}
void PicoGraphics_PenP8::scanline_convert(PenType type, conversion_callback_func callback) {
if(type == PEN_RGB565) {
// Cache the RGB888 palette as RGB565
RGB565 cache[palette_size];
for(auto i = 0u; i < palette_size; i++) {
cache[i] = palette[i].to_rgb565();
}
// Treat our void* frame_buffer as uint8_t
uint8_t *src = (uint8_t *)frame_buffer;
// Allocate a per-row temporary buffer
uint16_t row_buf[bounds.w];
for(auto y = 0; y < bounds.h; y++) {
for(auto x = 0; x < bounds.w; x++) {
row_buf[x] = cache[src[bounds.w * y + x]];
}
// Callback to the driver with the row data
callback(row_buf, bounds.w * sizeof(RGB565));
}
}
}
}

Wyświetl plik

@ -0,0 +1,134 @@
#include "pico_graphics.hpp"
#include <string.h>
namespace pimoroni {
PicoGraphics_PenRGB332::PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
this->pen_type = PEN_RGB332;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
}
void PicoGraphics_PenRGB332::set_pen(uint c) {
color = c;
}
void PicoGraphics_PenRGB332::set_pen(uint8_t r, uint8_t g, uint8_t b) {
color = rgb_to_rgb332(r, g, b);
}
int PicoGraphics_PenRGB332::create_pen(uint8_t r, uint8_t g, uint8_t b) {
return rgb_to_rgb332(r, g, b);
}
void PicoGraphics_PenRGB332::set_pixel(const Point &p) {
uint8_t *buf = (uint8_t *)frame_buffer;
buf[p.y * bounds.w + p.x] = color;
}
void PicoGraphics_PenRGB332::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
buf = &buf[p.y * bounds.w + p.x];
while(l--) {
*buf++ = color;
}
}
void PicoGraphics_PenRGB332::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
static uint8_t _odm[16] = {
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
};
uint8_t _dmv = _odm[(p.x & 0b11) | ((p.y & 0b11) << 2)];
uint8_t red = c.r & 0b11000000; // Two bits red
uint8_t red_r = c.r & 0b111111; // Remaining six bits red
red_r >>= 2; // Discard down to four bit
uint8_t grn = (c.g & 0b11000000) >> 3; // Two bits green
uint8_t grn_r = c.g & 0b111111; // Remaining six bits green
grn_r >>= 2; // Discard down to four bit
uint8_t blu = (c.b & 0b10000000) >> 6; // One bit blue
uint8_t blu_r = c.b & 0b1111111; // Remaining seven bits green
blu_r >>= 3; // Discard down to four bit
color = red | grn | blu;
if(red_r > _dmv) color |= 0b00100000;
if(grn_r > _dmv) color |= 0b00000100;
if(blu_r > _dmv) color |= 0b00000001;
set_pixel(p);
}
void PicoGraphics_PenRGB332::set_pixel_dither(const Point &p, const RGB565 &c) {
if(!bounds.contains(p)) return;
RGB565 cs = __builtin_bswap16(c);
static uint8_t _odm[16] = {
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
};
uint8_t _dmv = _odm[(p.x & 0b11) | ((p.y & 0b11) << 2)];
// RRRRRGGGGGGBBBBB
uint8_t red = (cs & 0b1100000000000000) >> 8; // Two bits grn
uint8_t red_r = (cs & 0b0011100000000000) >> 10; // Four bits cmp
uint8_t grn = (cs & 0b0000011000000000) >> 6; // Two bit grn
uint8_t grn_r = (cs & 0b0000000111100000) >> 5; // Four bit cmp
uint8_t blu = (cs & 0b0000000000010000) >> 3; // Two bit blu
uint8_t blu_r = (cs & 0b0000000000001111); // Four bit cmp
color = red | grn | blu;
// RRRGGGBB
if(red_r > _dmv) color |= 0b00100000;
if(grn_r > _dmv) color |= 0b00000100;
if(blu_r > _dmv) color |= 0b00000001;
set_pixel(p);
}
void PicoGraphics_PenRGB332::scanline_convert(PenType type, conversion_callback_func callback) {
if(type == PEN_RGB565) {
// Treat our void* frame_buffer as uint8_t
uint8_t *src = (uint8_t *)frame_buffer;
// Allocate a per-row temporary buffer
uint16_t row_buf[bounds.w];
for(auto y = 0; y < bounds.h; y++) {
for(auto x = 0; x < bounds.w; x++) {
row_buf[x] = rgb332_to_rgb565_lut[*src];
src++;
}
// Callback to the driver with the row data
callback(row_buf, bounds.w * sizeof(RGB565));
}
}
}
void PicoGraphics_PenRGB332::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) {
//int sprite_x = (sprite & 0x0f) << 3;
//int sprite_y = (sprite & 0xf0) >> 1;
Point s {
sprite.x << 3,
sprite.y << 3
};
RGB332 *ptr = (RGB332 *)data;
Point o = {0, 0};
for(o.y = 0; o.y < 8 * scale; o.y++) {
Point so = {
0,
o.y / scale
};
for(o.x = 0; o.x < 8 * scale; o.x++) {
so.x = o.x / scale;
color = ptr[(s.y + so.y) * 128 + (s.x + so.x)];
if(color != transparent) pixel(dest + o);
}
}
}
}

Wyświetl plik

@ -0,0 +1,34 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenRGB565::PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
this->pen_type = PEN_RGB565;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
}
void PicoGraphics_PenRGB565::set_pen(uint c) {
color = c;
}
void PicoGraphics_PenRGB565::set_pen(uint8_t r, uint8_t g, uint8_t b) {
src_color = {r, g, b};
color = src_color.to_rgb565();
}
int PicoGraphics_PenRGB565::create_pen(uint8_t r, uint8_t g, uint8_t b) {
return RGB(r, g, b).to_rgb565();
}
void PicoGraphics_PenRGB565::set_pixel(const Point &p) {
uint16_t *buf = (uint16_t *)frame_buffer;
buf[p.y * bounds.w + p.x] = color;
}
void PicoGraphics_PenRGB565::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint16_t *buf = (uint16_t *)frame_buffer;
buf = &buf[p.y * bounds.w + p.x];
while(l--) {
*buf++ = color;
}
}
}

Wyświetl plik

@ -12,20 +12,6 @@ namespace pimoroni {
);
}
Point operator- (Point lhs, const Point &rhs) {
lhs -= rhs;
return lhs;
}
Point operator- (const Point &rhs) {
return Point(-rhs.x, -rhs.y);
}
Point operator+ (Point lhs, const Point &rhs) {
lhs += rhs;
return lhs;
}
bool Rect::empty() const {
return w <= 0 || h <= 0;
}

Wyświetl plik

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

Wyświetl plik

@ -1,11 +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)
set(LIB_NAME tufty2040)
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_pwm)

Wyświetl plik

@ -0,0 +1,7 @@
#include <math.h>
#include <string.h>
#include "tufty2040.hpp"
namespace pimoroni {
}

Some files were not shown because too many files have changed in this diff Show More