|
| 1 | +#include <string.h> |
| 2 | +#include <math.h> |
| 3 | +#include <functional> |
| 4 | + |
| 5 | +#include "hardware/pwm.h" |
| 6 | +#include "hardware/watchdog.h" |
| 7 | + |
| 8 | +#include "inky_frame_7.hpp" |
| 9 | + |
| 10 | +namespace pimoroni { |
| 11 | + void gpio_configure(uint gpio, bool dir, bool value = false) { |
| 12 | + gpio_set_function(gpio, GPIO_FUNC_SIO); gpio_set_dir(gpio, dir); gpio_put(gpio, value); |
| 13 | + } |
| 14 | + |
| 15 | + void gpio_configure_pwm(uint gpio) { |
| 16 | + pwm_config cfg = pwm_get_default_config(); |
| 17 | + pwm_set_wrap(pwm_gpio_to_slice_num(gpio), 65535); |
| 18 | + pwm_init(pwm_gpio_to_slice_num(gpio), &cfg, true); |
| 19 | + gpio_set_function(gpio, GPIO_FUNC_PWM); |
| 20 | + } |
| 21 | + |
| 22 | + void InkyFrame::init() { |
| 23 | + // keep the pico awake by holding vsys_en high |
| 24 | + gpio_set_function(HOLD_VSYS_EN, GPIO_FUNC_SIO); |
| 25 | + gpio_set_dir(HOLD_VSYS_EN, GPIO_OUT); |
| 26 | + gpio_put(HOLD_VSYS_EN, true); |
| 27 | + |
| 28 | + // setup the shift register |
| 29 | + gpio_configure(SR_CLOCK, GPIO_OUT, true); |
| 30 | + gpio_configure(SR_LATCH, GPIO_OUT, true); |
| 31 | + gpio_configure(SR_OUT, GPIO_IN); |
| 32 | + |
| 33 | + // determine wake up event |
| 34 | + if(read_shift_register_bit(BUTTON_A)) {_wake_up_event = BUTTON_A_EVENT;} |
| 35 | + if(read_shift_register_bit(BUTTON_B)) {_wake_up_event = BUTTON_B_EVENT;} |
| 36 | + if(read_shift_register_bit(BUTTON_C)) {_wake_up_event = BUTTON_C_EVENT;} |
| 37 | + if(read_shift_register_bit(BUTTON_D)) {_wake_up_event = BUTTON_D_EVENT;} |
| 38 | + if(read_shift_register_bit(BUTTON_E)) {_wake_up_event = BUTTON_E_EVENT;} |
| 39 | + if(read_shift_register_bit(RTC_ALARM)) {_wake_up_event = RTC_ALARM_EVENT;} |
| 40 | + if(read_shift_register_bit(EXTERNAL_TRIGGER)) {_wake_up_event = EXTERNAL_TRIGGER_EVENT;} |
| 41 | + // there are other reasons a wake event can occur: connect power via usb, |
| 42 | + // connect a battery, or press the reset button. these cannot be |
| 43 | + // disambiguated so we don't attempt to report them |
| 44 | + |
| 45 | + // Disable display update busy wait, we'll handle it ourselves |
| 46 | + inky73.set_blocking(false); |
| 47 | + |
| 48 | + // initialise the rtc |
| 49 | + rtc.init(); |
| 50 | + |
| 51 | + // setup led pwm |
| 52 | + gpio_configure_pwm(LED_A); |
| 53 | + gpio_configure_pwm(LED_B); |
| 54 | + gpio_configure_pwm(LED_C); |
| 55 | + gpio_configure_pwm(LED_D); |
| 56 | + gpio_configure_pwm(LED_E); |
| 57 | + gpio_configure_pwm(LED_ACTIVITY); |
| 58 | + gpio_configure_pwm(LED_CONNECTION); |
| 59 | + } |
| 60 | + |
| 61 | + bool InkyFrame::is_busy() { |
| 62 | + // check busy flag on shift register |
| 63 | + bool busy = !read_shift_register_bit(Flags::EINK_BUSY); |
| 64 | + return busy; |
| 65 | + } |
| 66 | + |
| 67 | + void InkyFrame::update(bool blocking) { |
| 68 | + while(is_busy()) { |
| 69 | + tight_loop_contents(); |
| 70 | + } |
| 71 | + inky73.update((PicoGraphics_PenInky7 *)this); |
| 72 | + while(is_busy()) { |
| 73 | + tight_loop_contents(); |
| 74 | + } |
| 75 | + inky73.power_off(); |
| 76 | + } |
| 77 | + |
| 78 | + bool InkyFrame::pressed(Button button) { |
| 79 | + return read_shift_register_bit(button); |
| 80 | + } |
| 81 | + |
| 82 | + // set the LED brightness by generating a gamma corrected target value for |
| 83 | + // the 16-bit pwm channel. brightness values are from 0 to 100. |
| 84 | + void InkyFrame::led(LED led, uint8_t brightness) { |
| 85 | + uint16_t value = |
| 86 | + (uint16_t)(pow((float)(brightness) / 100.0f, 2.8) * 65535.0f + 0.5f); |
| 87 | + pwm_set_gpio_level(led, value); |
| 88 | + } |
| 89 | + |
| 90 | + uint8_t InkyFrame::read_shift_register() { |
| 91 | + gpio_put(SR_LATCH, false); sleep_us(1); |
| 92 | + gpio_put(SR_LATCH, true); sleep_us(1); |
| 93 | + |
| 94 | + uint8_t result = 0; |
| 95 | + uint8_t bits = 8; |
| 96 | + while(bits--) { |
| 97 | + result <<= 1; |
| 98 | + result |= gpio_get(SR_OUT) ? 1 : 0; |
| 99 | + |
| 100 | + gpio_put(SR_CLOCK, false); sleep_us(1); |
| 101 | + gpio_put(SR_CLOCK, true); sleep_us(1); |
| 102 | + } |
| 103 | + |
| 104 | + return result; |
| 105 | + } |
| 106 | + |
| 107 | + bool InkyFrame::read_shift_register_bit(uint8_t index) { |
| 108 | + return read_shift_register() & (1U << index); |
| 109 | + } |
| 110 | + |
| 111 | + void InkyFrame::sleep(int wake_in_minutes) { |
| 112 | + if(wake_in_minutes != -1) { |
| 113 | + // set an alarm to wake inky up in wake_in_minutes - the maximum sleep |
| 114 | + // is 255 minutes or around 4.5 hours which is the longest timer the RTC |
| 115 | + // supports, to sleep any longer we need to specify a date and time to |
| 116 | + // wake up |
| 117 | + rtc.set_timer(wake_in_minutes, PCF85063A::TIMER_TICK_1_OVER_60HZ); |
| 118 | + rtc.enable_timer_interrupt(true, false); |
| 119 | + } |
| 120 | + |
| 121 | + // release the vsys hold pin so that inky can go to sleep |
| 122 | + gpio_put(HOLD_VSYS_EN, false); |
| 123 | + while(true){}; |
| 124 | + } |
| 125 | + |
| 126 | + void InkyFrame::sleep_until(int second, int minute, int hour, int day) { |
| 127 | + if(second != -1 || minute != -1 || hour != -1 || day != -1) { |
| 128 | + // set an alarm to wake inky up at the specified time and day |
| 129 | + rtc.set_alarm(second, minute, hour, day); |
| 130 | + rtc.enable_alarm_interrupt(true); |
| 131 | + } |
| 132 | + |
| 133 | + // release the vsys hold pin so that inky can go to sleep |
| 134 | + gpio_put(HOLD_VSYS_EN, false); |
| 135 | + } |
| 136 | + |
| 137 | + // Display a portion of an image (icon sheet) at dx, dy |
| 138 | + void InkyFrame::icon(const uint8_t *data, int sheet_width, int icon_size, int index, int dx, int dy) { |
| 139 | + image(data, sheet_width, icon_size * index, 0, icon_size, icon_size, dx, dy); |
| 140 | + } |
| 141 | + |
| 142 | + // Display an image that fills the screen (286*128) |
| 143 | + void InkyFrame::image(const uint8_t* data) { |
| 144 | + image(data, width, 0, 0, width, height, 0, 0); |
| 145 | + } |
| 146 | + |
| 147 | + // Display an image smaller than the screen (sw*sh) at dx, dy |
| 148 | + void InkyFrame::image(const uint8_t *data, int w, int h, int x, int y) { |
| 149 | + image(data, w, 0, 0, w, h, x, y); |
| 150 | + } |
| 151 | + |
| 152 | + void InkyFrame::image(const uint8_t *data, int stride, int sx, int sy, int dw, int dh, int dx, int dy) { |
| 153 | + for(auto y = 0; y < dh; y++) { |
| 154 | + for(auto x = 0; x < dw; x++) { |
| 155 | + |
| 156 | + uint32_t o = ((y + sy) * (stride / 2)) + ((x + sx) / 2); |
| 157 | + uint8_t d = ((x + sx) & 0b1) ? data[o] >> 4 : data[o] & 0xf; |
| 158 | + |
| 159 | + // draw the pixel |
| 160 | + set_pen(d); |
| 161 | + pixel({dx + x, dy + y}); |
| 162 | + } |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | +} |
0 commit comments