pimoroni-pico/libraries/inky_frame/inky_frame.cpp

168 lines
5.5 KiB
C++
Raw Normal View History

2022-07-04 11:35:32 +01:00
#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});
}
}
}
}