kopia lustrzana https://github.com/pimoroni/pimoroni-pico
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
commit
f101ffdb4d
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
include(pimoroni_i2c.cmake)
|
||||
include(pimoroni_bus.cmake)
|
|
@ -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)
|
|
@ -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};
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(${CMAKE_CURRENT_LIST_DIR}/sh1107.cmake)
|
|
@ -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)
|
||||
```
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
```
|
|
@ -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)
|
||||
|
|
|
@ -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*)¶llel_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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, ¶llel_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;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.program st7789_parallel
|
||||
.side_set 1
|
||||
|
||||
.wrap_target
|
||||
out pins, 8 side 0
|
||||
nop side 1
|
||||
.wrap
|
|
@ -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)
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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})
|
|
@ -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++;
|
||||
}
|
||||
|
||||
}
|
|
@ -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})
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
|
@ -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++;
|
||||
}
|
|
@ -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})
|
|
@ -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;
|
|
@ -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})
|
||||
|
|
|
@ -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");
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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})
|
||||
|
|
|
@ -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 {
|
|
@ -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})
|
||||
|
|
|
@ -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++;
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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++;
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -0,0 +1 @@
|
|||
include(tufty2040_drawing.cmake)
|
|
@ -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})
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
include(breakout_colourlcd160x80.cmake)
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
include(generic_st7789.cmake)
|
|
@ -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();
|
||||
```
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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).
|
|
@ -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)
|
|
@ -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"
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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).
|
|
@ -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)
|
|
@ -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"
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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).
|
|
@ -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"
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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--;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(tufty2040.cmake)
|
|
@ -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)
|
|
@ -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
Ładowanie…
Reference in New Issue