#include #include #include #include "hardware/pwm.h" #include "hardware/watchdog.h" #include "inky_frame.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 uc8159.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(); } uc8159.update((PicoGraphics_PenP4 *)this); while(is_busy()) { tight_loop_contents(); } uc8159.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}); } } } }