Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inky 7.3: C++ library and JPEG example compatibility. #682

Merged
merged 2 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions examples/inky_frame/inky_frame_day_planner.cmake
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
set(OUTPUT_NAME inky_frame_day_planner)
# Inky Frame 5.7"
add_executable(
inky_frame_day_planner
inky_frame_day_planner.cpp
)

# Pull in pico libraries that we need
target_link_libraries(inky_frame_day_planner pico_stdlib inky_frame hardware_pwm hardware_spi hardware_i2c hardware_rtc fatfs sdcard pico_graphics)

pico_enable_stdio_usb(inky_frame_day_planner 1)

# create map/bin/hex file etc.
pico_add_extra_outputs(inky_frame_day_planner)

# Inky Frame 7.3"
add_executable(
${OUTPUT_NAME}
inky_frame_7_day_planner
inky_frame_day_planner.cpp
)

# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib inky_frame hardware_pwm hardware_spi hardware_i2c hardware_rtc fatfs sdcard pico_graphics)
target_link_libraries(inky_frame_7_day_planner pico_stdlib inky_frame_7 hardware_pwm hardware_spi hardware_i2c hardware_rtc fatfs sdcard pico_graphics)

pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_enable_stdio_usb(inky_frame_7_day_planner 1)

# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})
pico_add_extra_outputs(inky_frame_7_day_planner)

target_compile_definitions(inky_frame_7_day_planner PUBLIC INKY_FRAME_7)

4 changes: 4 additions & 0 deletions examples/inky_frame/inky_frame_day_planner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
#include <stdio.h>
#include "pico/stdlib.h"

#ifdef INKY_FRAME_7
#include "libraries/inky_frame_7/inky_frame_7.hpp"
#else
#include "libraries/inky_frame/inky_frame.hpp"
#endif

using namespace pimoroni;

Expand Down
24 changes: 19 additions & 5 deletions examples/inky_frame/inky_frame_jpeg_image.cmake
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
set(OUTPUT_NAME inky_frame_jpeg_image)
add_executable(
inky_frame_jpeg_image
inky_frame_jpeg_image.cpp
)

# Pull in pico libraries that we need
target_link_libraries(inky_frame_jpeg_image pico_stdlib jpegdec inky_frame fatfs hardware_pwm hardware_spi hardware_i2c hardware_rtc fatfs sdcard pico_graphics)

pico_enable_stdio_usb(inky_frame_jpeg_image 1)

# create map/bin/hex file etc.
pico_add_extra_outputs(inky_frame_jpeg_image)


add_executable(
${OUTPUT_NAME}
inky_frame_7_jpeg_image
inky_frame_jpeg_image.cpp
)

# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib jpegdec inky_frame fatfs hardware_pwm hardware_spi hardware_i2c hardware_rtc fatfs sdcard pico_graphics)
target_link_libraries(inky_frame_7_jpeg_image pico_stdlib jpegdec inky_frame_7 fatfs hardware_pwm hardware_spi hardware_i2c hardware_rtc fatfs sdcard pico_graphics)

pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_enable_stdio_usb(inky_frame_7_jpeg_image 1)

# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})
pico_add_extra_outputs(inky_frame_7_jpeg_image)

target_compile_definitions(inky_frame_7_jpeg_image PUBLIC INKY_FRAME_7)
41 changes: 38 additions & 3 deletions examples/inky_frame/inky_frame_jpeg_image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

#include "JPEGDEC.h"

#ifdef INKY_FRAME_7
#include "libraries/inky_frame_7/inky_frame_7.hpp"
#else
#include "libraries/inky_frame/inky_frame.hpp"
#endif

using namespace pimoroni;

Expand Down Expand Up @@ -65,6 +69,36 @@ int jpegdec_draw_callback(JPEGDRAW *draw) {
return 1; // continue drawing
}

// Draw to the nearest colour instead of dithering
int jpegdec_draw_posterize_callback(JPEGDRAW *draw) {
uint16_t *p = draw->pPixels;

int xo = jpeg_decode_options.x;
int yo = jpeg_decode_options.y;

for(int y = 0; y < draw->iHeight; y++) {
for(int x = 0; x < draw->iWidth; x++) {
int sx = ((draw->x + x + xo) * jpeg_decode_options.w) / jpeg.getWidth();
int sy = ((draw->y + y + yo) * jpeg_decode_options.h) / jpeg.getHeight();

if(xo + sx > 0 && xo + sx < inky.bounds.w && yo + sy > 0 && yo + sy < inky.bounds.h) {
int closest = RGB(RGB565(*p)).closest(inky.palette, inky.palette_size);
if (closest != -1) {
inky.set_pen(closest);
inky.set_pixel({xo + sx, yo + sy});
} else {
inky.set_pen(0);
inky.set_pixel({xo + sx, yo + sy});
}
}

p++;
}
}

return 1; // continue drawing
}

void draw_jpeg(std::string filename, int x, int y, int w, int h) {

// TODO: this is a horrible way to do it but we need to pass some parameters
Expand All @@ -81,7 +115,8 @@ void draw_jpeg(std::string filename, int x, int y, int w, int h) {
jpegdec_close_callback,
jpegdec_read_callback,
jpegdec_seek_callback,
jpegdec_draw_callback);
jpegdec_draw_callback // Try jpegdec_draw_posterize_callback
);

jpeg.setPixelType(RGB565_BIG_ENDIAN);

Expand Down Expand Up @@ -130,7 +165,7 @@ int main() {
}; // Wait for debugger
}

filename = "butterfly-600x448.jpg";
filename = "shutterstock_172537049.jpg";

//inky.led(InkyFrame::LED_E, 255);
//sleep_ms(1000);
Expand Down Expand Up @@ -169,7 +204,7 @@ int main() {
printf("done!\n");

printf("Displaying file: %s\n", filename.c_str());
draw_jpeg(filename, 0, 0, 600, 448);
draw_jpeg(filename, 0, 0, inky.width, inky.height);
printf("done!\n");

inky.update();
Expand Down
1 change: 1 addition & 0 deletions libraries/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ add_subdirectory(inventor2040w)
add_subdirectory(adcfft)
add_subdirectory(jpegdec)
add_subdirectory(inky_frame)
add_subdirectory(inky_frame_7)
add_subdirectory(galactic_unicorn)
add_subdirectory(gfx_pack)
add_subdirectory(interstate75)
Expand Down
1 change: 1 addition & 0 deletions libraries/inky_frame_7/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include(inky_frame_7.cmake)
13 changes: 13 additions & 0 deletions libraries/inky_frame_7/inky_frame_7.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(LIB_NAME inky_frame_7)
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 hardware_i2c pico_graphics hardware_spi hardware_pwm bitmap_fonts hershey_fonts pico_stdlib sdcard fatfs pcf85063a psram_display inky73 jpegdec)

target_compile_options(${LIB_NAME} INTERFACE -Wno-error=reorder)
166 changes: 166 additions & 0 deletions libraries/inky_frame_7/inky_frame_7.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include <string.h>
#include <math.h>
#include <functional>

#include "hardware/pwm.h"
#include "hardware/watchdog.h"

#include "inky_frame_7.hpp"

namespace pimoroni {
void gpio_configure(uint gpio, bool dir, bool value = false) {
gpio_set_function(gpio, GPIO_FUNC_SIO); gpio_set_dir(gpio, dir); gpio_put(gpio, value);
}

void gpio_configure_pwm(uint gpio) {
pwm_config cfg = pwm_get_default_config();
pwm_set_wrap(pwm_gpio_to_slice_num(gpio), 65535);
pwm_init(pwm_gpio_to_slice_num(gpio), &cfg, true);
gpio_set_function(gpio, GPIO_FUNC_PWM);
}

void InkyFrame::init() {
// keep the pico awake by holding vsys_en high
gpio_set_function(HOLD_VSYS_EN, GPIO_FUNC_SIO);
gpio_set_dir(HOLD_VSYS_EN, GPIO_OUT);
gpio_put(HOLD_VSYS_EN, true);

// setup the shift register
gpio_configure(SR_CLOCK, GPIO_OUT, true);
gpio_configure(SR_LATCH, GPIO_OUT, true);
gpio_configure(SR_OUT, GPIO_IN);

// determine wake up event
if(read_shift_register_bit(BUTTON_A)) {_wake_up_event = BUTTON_A_EVENT;}
if(read_shift_register_bit(BUTTON_B)) {_wake_up_event = BUTTON_B_EVENT;}
if(read_shift_register_bit(BUTTON_C)) {_wake_up_event = BUTTON_C_EVENT;}
if(read_shift_register_bit(BUTTON_D)) {_wake_up_event = BUTTON_D_EVENT;}
if(read_shift_register_bit(BUTTON_E)) {_wake_up_event = BUTTON_E_EVENT;}
if(read_shift_register_bit(RTC_ALARM)) {_wake_up_event = RTC_ALARM_EVENT;}
if(read_shift_register_bit(EXTERNAL_TRIGGER)) {_wake_up_event = EXTERNAL_TRIGGER_EVENT;}
// there are other reasons a wake event can occur: connect power via usb,
// connect a battery, or press the reset button. these cannot be
// disambiguated so we don't attempt to report them

// Disable display update busy wait, we'll handle it ourselves
inky73.set_blocking(false);

// initialise the rtc
rtc.init();

// setup led pwm
gpio_configure_pwm(LED_A);
gpio_configure_pwm(LED_B);
gpio_configure_pwm(LED_C);
gpio_configure_pwm(LED_D);
gpio_configure_pwm(LED_E);
gpio_configure_pwm(LED_ACTIVITY);
gpio_configure_pwm(LED_CONNECTION);
}

bool InkyFrame::is_busy() {
// check busy flag on shift register
bool busy = !read_shift_register_bit(Flags::EINK_BUSY);
return busy;
}

void InkyFrame::update(bool blocking) {
while(is_busy()) {
tight_loop_contents();
}
inky73.update((PicoGraphics_PenInky7 *)this);
while(is_busy()) {
tight_loop_contents();
}
inky73.power_off();
}

bool InkyFrame::pressed(Button button) {
return read_shift_register_bit(button);
}

// set the LED brightness by generating a gamma corrected target value for
// the 16-bit pwm channel. brightness values are from 0 to 100.
void InkyFrame::led(LED led, uint8_t brightness) {
uint16_t value =
(uint16_t)(pow((float)(brightness) / 100.0f, 2.8) * 65535.0f + 0.5f);
pwm_set_gpio_level(led, value);
}

uint8_t InkyFrame::read_shift_register() {
gpio_put(SR_LATCH, false); sleep_us(1);
gpio_put(SR_LATCH, true); sleep_us(1);

uint8_t result = 0;
uint8_t bits = 8;
while(bits--) {
result <<= 1;
result |= gpio_get(SR_OUT) ? 1 : 0;

gpio_put(SR_CLOCK, false); sleep_us(1);
gpio_put(SR_CLOCK, true); sleep_us(1);
}

return result;
}

bool InkyFrame::read_shift_register_bit(uint8_t index) {
return read_shift_register() & (1U << index);
}

void InkyFrame::sleep(int wake_in_minutes) {
if(wake_in_minutes != -1) {
// set an alarm to wake inky up in wake_in_minutes - the maximum sleep
// is 255 minutes or around 4.5 hours which is the longest timer the RTC
// supports, to sleep any longer we need to specify a date and time to
// wake up
rtc.set_timer(wake_in_minutes, PCF85063A::TIMER_TICK_1_OVER_60HZ);
rtc.enable_timer_interrupt(true, false);
}

// release the vsys hold pin so that inky can go to sleep
gpio_put(HOLD_VSYS_EN, false);
while(true){};
}

void InkyFrame::sleep_until(int second, int minute, int hour, int day) {
if(second != -1 || minute != -1 || hour != -1 || day != -1) {
// set an alarm to wake inky up at the specified time and day
rtc.set_alarm(second, minute, hour, day);
rtc.enable_alarm_interrupt(true);
}

// release the vsys hold pin so that inky can go to sleep
gpio_put(HOLD_VSYS_EN, false);
}

// Display a portion of an image (icon sheet) at dx, dy
void InkyFrame::icon(const uint8_t *data, int sheet_width, int icon_size, int index, int dx, int dy) {
image(data, sheet_width, icon_size * index, 0, icon_size, icon_size, dx, dy);
}

// Display an image that fills the screen (286*128)
void InkyFrame::image(const uint8_t* data) {
image(data, width, 0, 0, width, height, 0, 0);
}

// Display an image smaller than the screen (sw*sh) at dx, dy
void InkyFrame::image(const uint8_t *data, int w, int h, int x, int y) {
image(data, w, 0, 0, w, h, x, y);
}

void InkyFrame::image(const uint8_t *data, int stride, int sx, int sy, int dw, int dh, int dx, int dy) {
for(auto y = 0; y < dh; y++) {
for(auto x = 0; x < dw; x++) {

uint32_t o = ((y + sy) * (stride / 2)) + ((x + sx) / 2);
uint8_t d = ((x + sx) & 0b1) ? data[o] >> 4 : data[o] & 0xf;

// draw the pixel
set_pen(d);
pixel({dx + x, dy + y});
}
}
}

}
Loading