kopia lustrzana https://github.com/pimoroni/pimoroni-pico
				
				
				
			
		
			
				
	
	
		
			168 wiersze
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			168 wiersze
		
	
	
		
			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});
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
}
 |