168 lines
5.5 KiB
C++
168 lines
5.5 KiB
C++
#include <string.h>
|
|
#include <math.h>
|
|
#include <functional>
|
|
|
|
#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});
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|