diff --git a/examples/inky_frame/inky_frame_jpeg_image.cmake b/examples/inky_frame/inky_frame_jpeg_image.cmake index e456f4ae..1afd7f0d 100644 --- a/examples/inky_frame/inky_frame_jpeg_image.cmake +++ b/examples/inky_frame/inky_frame_jpeg_image.cmake @@ -1,14 +1,28 @@ -set(OUTPUT_NAME inky_frame_jpeg_image) - add_executable( - ${OUTPUT_NAME} + inky_frame_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_jpeg_image pico_stdlib jpegdec inky_frame 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_jpeg_image 1) # create map/bin/hex file etc. -pico_add_extra_outputs(${OUTPUT_NAME}) +pico_add_extra_outputs(inky_frame_jpeg_image) + + +add_executable( + inky_frame_7_jpeg_image + inky_frame_jpeg_image.cpp +) + +# Pull in pico libraries that we need +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(inky_frame_7_jpeg_image 1) + +# create map/bin/hex file etc. +pico_add_extra_outputs(inky_frame_7_jpeg_image) + +target_compile_definitions(inky_frame_7_jpeg_image PUBLIC INKY_FRAME_7) diff --git a/examples/inky_frame/inky_frame_jpeg_image.cpp b/examples/inky_frame/inky_frame_jpeg_image.cpp index b9c235b6..2efea15a 100644 --- a/examples/inky_frame/inky_frame_jpeg_image.cpp +++ b/examples/inky_frame/inky_frame_jpeg_image.cpp @@ -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; @@ -169,7 +173,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(); diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index b98955da..317ba021 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -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) diff --git a/libraries/inky_frame_7/CMakeLists.txt b/libraries/inky_frame_7/CMakeLists.txt new file mode 100644 index 00000000..e5e87596 --- /dev/null +++ b/libraries/inky_frame_7/CMakeLists.txt @@ -0,0 +1 @@ +include(inky_frame_7.cmake) \ No newline at end of file diff --git a/libraries/inky_frame_7/inky_frame_7.cmake b/libraries/inky_frame_7/inky_frame_7.cmake new file mode 100644 index 00000000..e06cde18 --- /dev/null +++ b/libraries/inky_frame_7/inky_frame_7.cmake @@ -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) \ No newline at end of file diff --git a/libraries/inky_frame_7/inky_frame_7.cpp b/libraries/inky_frame_7/inky_frame_7.cpp new file mode 100644 index 00000000..b22af2d8 --- /dev/null +++ b/libraries/inky_frame_7/inky_frame_7.cpp @@ -0,0 +1,166 @@ +#include +#include +#include + +#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}); + } + } + } + +} diff --git a/libraries/inky_frame_7/inky_frame_7.hpp b/libraries/inky_frame_7/inky_frame_7.hpp new file mode 100644 index 00000000..2b743f7d --- /dev/null +++ b/libraries/inky_frame_7/inky_frame_7.hpp @@ -0,0 +1,135 @@ +#pragma once + +#include + +#include "drivers/inky73/inky73.hpp" +#include "drivers/psram_display/psram_display.hpp" +#include "drivers/pcf85063a/pcf85063a.hpp" +#include "drivers/fatfs/ff.h" + +#include "libraries/pico_graphics/pico_graphics.hpp" + +namespace pimoroni { + + class InkyFrame : public PicoGraphics_PenInky7 { + public: + enum Button : uint8_t { + BUTTON_A = 0, + BUTTON_B = 1, + BUTTON_C = 2, + BUTTON_D = 3, + BUTTON_E = 4 + }; + + enum LED : uint8_t { + LED_ACTIVITY = 6, + LED_CONNECTION = 7, + LED_A = 11, + LED_B = 12, + LED_C = 13, + LED_D = 14, + LED_E = 15 + }; + + enum Flags : uint8_t { + RTC_ALARM = 5, + EXTERNAL_TRIGGER = 6, + EINK_BUSY = 7 + }; + + enum WakeUpEvent : uint8_t { + UNKNOWN_EVENT = 0, + BUTTON_A_EVENT = 1, + BUTTON_B_EVENT = 2, + BUTTON_C_EVENT = 3, + BUTTON_D_EVENT = 4, + BUTTON_E_EVENT = 5, + RTC_ALARM_EVENT = 6, + EXTERNAL_TRIGGER_EVENT = 7, + }; + + enum Pen : uint8_t { + BLACK = 0, + WHITE = 1, + GREEN = 2, + BLUE = 3, + RED = 4, + YELLOW = 5, + ORANGE = 6, + CLEAN = 7, + TAUPE = 7 + }; + + protected: + WakeUpEvent _wake_up_event = UNKNOWN_EVENT; + + enum Pin { + HOLD_VSYS_EN = 2, + I2C_INT = 3, + I2C_SDA = 4, + I2C_SCL = 5, + SR_CLOCK = 8, + SR_LATCH = 9, + SR_OUT = 10, + MISO = 16, + EINK_CS = 17, + CLK = 18, + MOSI = 19, + SD_DAT0 = 19, + SD_DAT1 = 20, + SD_DAT2 = 21, + SD_DAT3 = 22, + SD_CS = 22, + ADC0 = 26, + EINK_RESET = 27, + EINK_DC = 28 + }; + + public: + PSRamDisplay ramDisplay; + Inky73 inky73; + I2C i2c; + PCF85063A rtc; + + int width; + int height; + + // Default 7.3" constructor + InkyFrame() : InkyFrame(800, 480) {}; + + // 600x448 for 5.7" + // 640x400 for 4.0" + InkyFrame(int width, int height) : + ramDisplay(width, height), + PicoGraphics_PenInky7(width, height, ramDisplay), + inky73(width, height), + i2c(4, 5), + rtc(&i2c), + width(width), + height(height) { + } + + void init(); + + // wake/sleep management + void sleep(int wake_in_minutes = -1); + void sleep_until(int second = -1, int minute = -1, int hour = -1, int day = -1); + WakeUpEvent get_wake_up_event() {return _wake_up_event;} + + // screen management + void update(bool blocking=false); + static bool is_busy(); + + // state + bool pressed(Button button); + static uint8_t read_shift_register(); + static bool read_shift_register_bit(uint8_t index); + void led(LED led, uint8_t brightness); + + void icon(const uint8_t *data, int sheet_width, int icon_size, int index, int dx, int dy); + void image(const uint8_t* data); + void image(const uint8_t *data, int w, int h, int x, int y); + void image(const uint8_t *data, int stride, int sx, int sy, int dw, int dh, int dx, int dy); + }; + +}