kopia lustrzana https://github.com/pimoroni/pimoroni-pico
				
				
				
			Exposed support for GPIO pins on encoder wheel
							rodzic
							
								
									8966cbf348
								
							
						
					
					
						commit
						653090c89e
					
				| 
						 | 
				
			
			@ -523,13 +523,35 @@ namespace pimoroni {
 | 
			
		|||
    return divider_good;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void IOExpander::set_pwm_period(uint16_t value, bool load) {
 | 
			
		||||
  void IOExpander::set_pwm_period(uint16_t value, bool load, bool wait_for_load) {
 | 
			
		||||
    value &= 0xffff;
 | 
			
		||||
    i2c->reg_write_uint8(address, reg::PWMPL, (uint8_t)(value & 0xff));
 | 
			
		||||
    i2c->reg_write_uint8(address, reg::PWMPH, (uint8_t)(value >> 8));
 | 
			
		||||
 | 
			
		||||
    if(load)
 | 
			
		||||
      pwm_load();
 | 
			
		||||
      pwm_load(wait_for_load);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint16_t IOExpander::set_pwm_frequency(float frequency, bool load, bool wait_for_load) {
 | 
			
		||||
    uint32_t period = (uint32_t)(CLOCK_FREQ / frequency);
 | 
			
		||||
    if (period / 128 > MAX_PERIOD) {
 | 
			
		||||
      return MAX_PERIOD;
 | 
			
		||||
    }
 | 
			
		||||
    if (period < 2) {
 | 
			
		||||
      return 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t divider = 1;
 | 
			
		||||
    while ((period > MAX_PERIOD) && (divider < MAX_DIVIDER)) {
 | 
			
		||||
      period >>= 1;
 | 
			
		||||
      divider <<= 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    period = MIN(period, MAX_PERIOD); // Should be unnecessary because of earlier raised errors, but kept in case
 | 
			
		||||
    set_pwm_control(divider);
 | 
			
		||||
    set_pwm_period((uint16_t)(period - 1), load, wait_for_load);
 | 
			
		||||
 | 
			
		||||
    return (uint16_t)period;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t IOExpander::get_mode(uint8_t pin) {
 | 
			
		||||
| 
						 | 
				
			
			@ -701,7 +723,7 @@ namespace pimoroni {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void IOExpander::output(uint8_t pin, uint16_t value, bool load) {
 | 
			
		||||
  void IOExpander::output(uint8_t pin, uint16_t value, bool load, bool wait_for_load) {
 | 
			
		||||
    if(pin < 1 || pin > NUM_PINS) {
 | 
			
		||||
      printf("Pin should be in range 1-14.");
 | 
			
		||||
      return;
 | 
			
		||||
| 
						 | 
				
			
			@ -717,7 +739,7 @@ namespace pimoroni {
 | 
			
		|||
      i2c->reg_write_uint8(address, io_pin.reg_pwml, (uint8_t)(value & 0xff));
 | 
			
		||||
      i2c->reg_write_uint8(address, io_pin.reg_pwmh, (uint8_t)(value >> 8));
 | 
			
		||||
      if(load)
 | 
			
		||||
        pwm_load();
 | 
			
		||||
        pwm_load(wait_for_load);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      if(value == LOW) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,9 @@ namespace pimoroni {
 | 
			
		|||
    static const uint8_t PIN_MODE_ADC = 0b01010;  // ADC, Input-only (high-impedance)
 | 
			
		||||
 | 
			
		||||
    static const uint32_t RESET_TIMEOUT_MS = 1000;
 | 
			
		||||
    static const uint32_t CLOCK_FREQ = 24000000;
 | 
			
		||||
    static const uint32_t MAX_PERIOD = (1 << 16) - 1;
 | 
			
		||||
    static const uint32_t MAX_DIVIDER = (1 << 7);
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    static const uint8_t DEFAULT_I2C_ADDRESS = 0x18;
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +208,8 @@ namespace pimoroni {
 | 
			
		|||
    void pwm_clear(bool wait_for_clear = true);
 | 
			
		||||
    bool pwm_clearing();
 | 
			
		||||
    bool set_pwm_control(uint8_t divider);
 | 
			
		||||
    void set_pwm_period(uint16_t value, bool load = true);
 | 
			
		||||
    void set_pwm_period(uint16_t value, bool load = true, bool wait_for_load = true);
 | 
			
		||||
    uint16_t set_pwm_frequency(float frequency, bool load = true, bool wait_for_load = true);
 | 
			
		||||
 | 
			
		||||
    uint8_t get_mode(uint8_t pin);
 | 
			
		||||
    void set_mode(uint8_t pin, uint8_t mode, bool schmitt_trigger = false, bool invert = false);
 | 
			
		||||
| 
						 | 
				
			
			@ -213,7 +217,7 @@ namespace pimoroni {
 | 
			
		|||
    int16_t input(uint8_t pin, uint32_t adc_timeout = 1);
 | 
			
		||||
    float input_as_voltage(uint8_t pin, uint32_t adc_timeout = 1);
 | 
			
		||||
 | 
			
		||||
    void output(uint8_t pin, uint16_t value, bool load = true);
 | 
			
		||||
    void output(uint8_t pin, uint16_t value, bool load = true, bool wait_for_load = true);
 | 
			
		||||
 | 
			
		||||
    void setup_rotary_encoder(uint8_t channel, uint8_t pin_a, uint8_t pin_b, uint8_t pin_c = 0, bool count_microsteps = false);
 | 
			
		||||
    int16_t read_rotary_encoder(uint8_t channel);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,6 @@ add_subdirectory(chase_game)
 | 
			
		|||
add_subdirectory(clock)
 | 
			
		||||
add_subdirectory(colour_picker)
 | 
			
		||||
add_subdirectory(encoder)
 | 
			
		||||
#add_subdirectory(gpio_pwm)
 | 
			
		||||
add_subdirectory(gpio_pwm)
 | 
			
		||||
add_subdirectory(led_rainbow)
 | 
			
		||||
add_subdirectory(stop_watch)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
set(OUTPUT_NAME encoderwheel_gpio_pwm)
 | 
			
		||||
add_executable(${OUTPUT_NAME} gpio_pwm.cpp)
 | 
			
		||||
 | 
			
		||||
# Pull in pico libraries that we need
 | 
			
		||||
target_link_libraries(${OUTPUT_NAME}
 | 
			
		||||
  pico_stdlib
 | 
			
		||||
  breakout_encoder_wheel
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# enable usb output
 | 
			
		||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
 | 
			
		||||
 | 
			
		||||
pico_add_extra_outputs(${OUTPUT_NAME})
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
#include <math.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "pimoroni_i2c.hpp"
 | 
			
		||||
#include "breakout_encoder_wheel.hpp"
 | 
			
		||||
#include "time.h"
 | 
			
		||||
 | 
			
		||||
using namespace pimoroni;
 | 
			
		||||
using namespace encoderwheel;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Output a sine wave PWM sequence on the Encoder Wheel's side GPIO pins.
 | 
			
		||||
 | 
			
		||||
Press the centre button to stop the program.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Constants
 | 
			
		||||
constexpr float SPEED = 5.0f;         // The speed that the LEDs will cycle at
 | 
			
		||||
const uint UPDATES = 50;              // How many times the LEDs will be updated per second
 | 
			
		||||
const uint UPDATE_RATE_US = 1000000 / UPDATES;
 | 
			
		||||
constexpr float FREQUENCY = 1000.0f;  // The frequency to run the PWM at
 | 
			
		||||
 | 
			
		||||
// Create a new BreakoutEncoderWheel
 | 
			
		||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
 | 
			
		||||
BreakoutEncoderWheel wheel(&i2c);
 | 
			
		||||
 | 
			
		||||
// Variables
 | 
			
		||||
float offset = 0.0f;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int main() {
 | 
			
		||||
  stdio_init_all();
 | 
			
		||||
 | 
			
		||||
  // Attempt to initialise the encoder wheel
 | 
			
		||||
  if(wheel.init()) {
 | 
			
		||||
 | 
			
		||||
    // Set the PWM frequency for the GPIOs
 | 
			
		||||
    uint16_t period = wheel.gpio_pwm_frequency(FREQUENCY);
 | 
			
		||||
 | 
			
		||||
    // Set the GPIO pins to PWM outputs
 | 
			
		||||
    for(int i = 0; i < NUM_GPIOS; i++) {
 | 
			
		||||
      wheel.gpio_pin_mode(GPIOS[i], IOExpander::PIN_PWM);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Loop forever
 | 
			
		||||
    while(!wheel.pressed(CENTRE)) {
 | 
			
		||||
      // Record the start time of this loop
 | 
			
		||||
      absolute_time_t start_time = get_absolute_time();
 | 
			
		||||
 | 
			
		||||
      offset += SPEED / 1000.0f;
 | 
			
		||||
 | 
			
		||||
      // Update all the PWMs
 | 
			
		||||
      for(int i = 0; i < NUM_GPIOS; i++) {
 | 
			
		||||
        float angle = (((float)i / NUM_GPIOS) + offset) * M_PI;
 | 
			
		||||
        uint16_t duty = (uint16_t)(((sinf(angle) / 2.0f) + 0.5f) * period);
 | 
			
		||||
 | 
			
		||||
        // Set the GPIO pin to the new duty cycle, but do not load it yet
 | 
			
		||||
        wheel.gpio_pin_value(GPIOS[i], duty, false);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Have all the PWMs load at once
 | 
			
		||||
      wheel.gpio_pwm_load();
 | 
			
		||||
 | 
			
		||||
      // Sleep until the next update, accounting for how long the above operations took to perform
 | 
			
		||||
      sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Turn off the PWM outputs
 | 
			
		||||
    for(int i = 0; i < NUM_GPIOS; i++) {
 | 
			
		||||
      wheel.gpio_pin_value(GPIOS[i], 0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -190,32 +190,37 @@ namespace encoderwheel {
 | 
			
		|||
    led_ring.update();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int BreakoutEncoderWheel::gpio_pin_mode(int gpio) {
 | 
			
		||||
    return 0; // TODO
 | 
			
		||||
  uint8_t BreakoutEncoderWheel::gpio_pin_mode(uint8_t gpio) {
 | 
			
		||||
    assert(gpio < GP7 || gpio > GP9);
 | 
			
		||||
    return ioe.get_mode(gpio);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void BreakoutEncoderWheel::gpio_pin_mode(int gpio, int mode) {
 | 
			
		||||
 | 
			
		||||
  void BreakoutEncoderWheel::gpio_pin_mode(uint8_t gpio, uint8_t mode) {
 | 
			
		||||
    assert(gpio < GP7 || gpio > GP9);
 | 
			
		||||
    ioe.set_mode(gpio, mode);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int BreakoutEncoderWheel::gpio_pin_value(int gpio) {
 | 
			
		||||
    return 0; // TODO
 | 
			
		||||
  int16_t BreakoutEncoderWheel::gpio_pin_value(uint8_t gpio) {
 | 
			
		||||
    assert(gpio < GP7 || gpio > GP9);
 | 
			
		||||
    return ioe.input(gpio);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float BreakoutEncoderWheel::gpio_pin_value_as_voltage(int gpio) {
 | 
			
		||||
    return 0; // TODO
 | 
			
		||||
  float BreakoutEncoderWheel::gpio_pin_value_as_voltage(uint8_t gpio) {
 | 
			
		||||
    assert(gpio < GP7 || gpio > GP9);
 | 
			
		||||
    return ioe.input_as_voltage(gpio);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void BreakoutEncoderWheel::gpio_pin_value(int gpio, int value, bool load, bool wait_for_load) {
 | 
			
		||||
 | 
			
		||||
  void BreakoutEncoderWheel::gpio_pin_value(uint8_t gpio, uint16_t value, bool load, bool wait_for_load) {
 | 
			
		||||
    assert(gpio < GP7 || gpio > GP9);
 | 
			
		||||
    ioe.output(gpio, value, load, wait_for_load);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void BreakoutEncoderWheel::gpio_pwm_load(bool wait_for_load) {
 | 
			
		||||
 | 
			
		||||
    ioe.pwm_load(wait_for_load);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int BreakoutEncoderWheel::gpio_pwm_frequency(float frequency, bool load, bool wait_for_load) {
 | 
			
		||||
    return 0; // TODO
 | 
			
		||||
    return ioe.set_pwm_frequency(frequency, load, wait_for_load);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void BreakoutEncoderWheel::take_encoder_reading() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,17 +41,17 @@ namespace encoderwheel {
 | 
			
		|||
    static const uint32_t DEFAULT_TIMEOUT     = 1;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    static const uint8_t ENC_CHANNEL    = 1;
 | 
			
		||||
    static const uint8_t ENC_TERM_A     = 3;
 | 
			
		||||
    static const uint8_t ENC_TERM_B     = 12;
 | 
			
		||||
    static const uint8_t ENC_COUNTS_PER_REV = 24;
 | 
			
		||||
    static const uint8_t ENC_COUNT_DIVIDER = 2;
 | 
			
		||||
    static const uint8_t ENC_CHANNEL          = 1;
 | 
			
		||||
    static const uint8_t ENC_TERM_A           = 3;
 | 
			
		||||
    static const uint8_t ENC_TERM_B           = 12;
 | 
			
		||||
    static const uint8_t ENC_COUNTS_PER_REV   = 24;
 | 
			
		||||
    static const uint8_t ENC_COUNT_DIVIDER    = 2;
 | 
			
		||||
 | 
			
		||||
    static const uint8_t SW_UP          = 13;
 | 
			
		||||
    static const uint8_t SW_DOWN        = 4;
 | 
			
		||||
    static const uint8_t SW_LEFT        = 11;
 | 
			
		||||
    static const uint8_t SW_RIGHT       = 2;
 | 
			
		||||
    static const uint8_t SW_CENTRE      = 1;
 | 
			
		||||
    static const uint8_t SW_UP                = 13;
 | 
			
		||||
    static const uint8_t SW_DOWN              = 4;
 | 
			
		||||
    static const uint8_t SW_LEFT              = 11;
 | 
			
		||||
    static const uint8_t SW_RIGHT             = 2;
 | 
			
		||||
    static const uint8_t SW_CENTRE            = 1;
 | 
			
		||||
 | 
			
		||||
    // This wonderful lookup table maps the LEDs on the encoder wheel
 | 
			
		||||
    // from their 3x24 (remember, they're RGB) configuration to
 | 
			
		||||
| 
						 | 
				
			
			@ -146,12 +146,12 @@ namespace encoderwheel {
 | 
			
		|||
    void clear();
 | 
			
		||||
    void show();
 | 
			
		||||
 | 
			
		||||
    int gpio_pin_mode(int gpio);
 | 
			
		||||
    void gpio_pin_mode(int gpio, int mode);
 | 
			
		||||
    int gpio_pin_value(int gpio);
 | 
			
		||||
    float gpio_pin_value_as_voltage(int gpio);
 | 
			
		||||
    void gpio_pin_value(int gpio, int value, bool load = true, bool wait_for_load = false);
 | 
			
		||||
    void gpio_pwm_load(bool wait_for_load = false);
 | 
			
		||||
    uint8_t gpio_pin_mode(uint8_t gpio);
 | 
			
		||||
    void gpio_pin_mode(uint8_t gpio, uint8_t mode);
 | 
			
		||||
    int16_t gpio_pin_value(uint8_t gpio);
 | 
			
		||||
    float gpio_pin_value_as_voltage(uint8_t gpio);
 | 
			
		||||
    void gpio_pin_value(uint8_t gpio, uint16_t value, bool load = true, bool wait_for_load = false);
 | 
			
		||||
    void gpio_pwm_load(bool wait_for_load = true);
 | 
			
		||||
    int gpio_pwm_frequency(float frequency, bool load = true, bool wait_for_load = false);
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue