Skip to content

Commit

Permalink
Merge pull request #774 from pimoroni/breakout_encoder_wheel
Browse files Browse the repository at this point in the history
Support for RGB Encoder Wheel Breakout
  • Loading branch information
Gadgetoid committed May 12, 2023
2 parents ec205fb + d00185d commit 8648196
Show file tree
Hide file tree
Showing 47 changed files with 3,727 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
* MICS6814 - Gas Sensor - https://shop.pimoroni.com/products/mics6814-gas-sensor-breakout
* RGB Potentiometer - https://shop.pimoroni.com/products/rgb-potentiometer-breakout
* RGB Encoder - https://shop.pimoroni.com/products/rgb-encoder-breakout
* RGB Encoder Wheel - https://shop.pimoroni.com/products/rgb-encoder-wheel-breakout
* IO Expander - https://shop.pimoroni.com/products/io-expander
* RV3028 - Real-Time Clock (RTC) - https://shop.pimoroni.com/products/rv3028-real-time-clock-rtc-breakout
* ST7735 - 0.96" LCD - https://shop.pimoroni.com/products/0-96-spi-colour-lcd-160x80-breakout
Expand Down
84 changes: 69 additions & 15 deletions drivers/ioexpander/ioexpander.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,21 @@ namespace pimoroni {
Pin::adc(1, 7, 0)}
{}

bool IOExpander::init(bool skipChipIdCheck) {
bool succeeded = true;
bool IOExpander::init(bool skipChipIdCheck, bool perform_reset) {
if(!skipChipIdCheck) {
uint16_t chip_id = get_chip_id();
if(chip_id != CHIP_ID) {
if(debug) {
printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID);
}
return false;
}
}

// Reset the chip if requested, to put it into a known state
if(perform_reset && !reset()) {
return false;
}

if(interrupt != PIN_UNUSED) {
gpio_set_function(interrupt, GPIO_FUNC_SIO);
Expand All @@ -331,17 +344,36 @@ namespace pimoroni {
enable_interrupt_out(true);
}

if(!skipChipIdCheck) {
uint16_t chip_id = get_chip_id();
if(chip_id != CHIP_ID) {
if(debug) {
printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID);
}
succeeded = false;
return true;
}

uint8_t IOExpander::check_reset() {
uint8_t user_flash_reg = reg::USER_FLASH;
uint8_t value;
if(i2c_write_blocking(i2c->get_i2c(), address, &user_flash_reg, 1, false) == PICO_ERROR_GENERIC) {
return 0x00;
}
if(i2c_read_blocking(i2c->get_i2c(), address, (uint8_t *)&value, sizeof(uint8_t), false) == PICO_ERROR_GENERIC) {
return 0x00;
}

return value;
}

bool IOExpander::reset() {
uint32_t start_time = millis();
set_bits(reg::CTRL, ctrl_mask::RESET);
// Wait for a register to read its initialised value
while(check_reset() != 0x78) {
sleep_ms(1);
if(millis() - start_time >= RESET_TIMEOUT_MS) {
if(debug)
printf("Timed out waiting for Reset!");
return false;
}
}

return succeeded;
return true;
}

i2c_inst_t* IOExpander::get_i2c() const {
Expand Down Expand Up @@ -370,7 +402,7 @@ namespace pimoroni {

void IOExpander::set_address(uint8_t address) {
set_bit(reg::CTRL, 4);
i2c->reg_write_uint8(address, reg::ADDR, address);
i2c->reg_write_uint8(this->address, reg::ADDR, address);
this->address = address;
sleep_ms(250); //TODO Handle addr change IOError better
//wait_for_flash()
Expand Down Expand Up @@ -491,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) {
Expand Down Expand Up @@ -669,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;
Expand All @@ -685,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) {
Expand Down
18 changes: 15 additions & 3 deletions drivers/ioexpander/ioexpander.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace pimoroni {
static const uint8_t PIN_MODE_PWM = 0b00101; // PWM, Output, Push-Pull mode
static const uint8_t PIN_MODE_ADC = 0b01010; // ADC, Input-only (high-impedance)

static const uint32_t RESET_TIMEOUT_MS = 1000;

public:
static const uint8_t DEFAULT_I2C_ADDRESS = 0x18;

Expand All @@ -45,6 +47,10 @@ namespace pimoroni {
static const uint16_t LOW = 0;
static const uint16_t HIGH = 1;

static const uint32_t CLOCK_FREQ = 24000000;
static const uint32_t MAX_PERIOD = (1 << 16) - 1;
static const uint32_t MAX_DIVIDER = (1 << 7);


//--------------------------------------------------
// Subclasses
Expand Down Expand Up @@ -171,7 +177,12 @@ namespace pimoroni {
// Methods
//--------------------------------------------------
public:
bool init(bool skip_chip_id_check = false);
bool init(bool skip_chip_id_check = false, bool perform_reset = false);

private:
uint8_t check_reset();
public:
bool reset();

// For print access in micropython
i2c_inst_t* get_i2c() const;
Expand All @@ -198,15 +209,16 @@ 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);

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);
Expand Down
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_subdirectory(breakout_dotmatrix)
add_subdirectory(breakout_encoder)
add_subdirectory(breakout_encoder_wheel)
add_subdirectory(breakout_ioexpander)
add_subdirectory(breakout_ltr559)
add_subdirectory(breakout_colourlcd160x80)
Expand Down
9 changes: 9 additions & 0 deletions examples/breakout_encoder_wheel/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
add_subdirectory(buttons)
add_subdirectory(chase_game)
add_subdirectory(clock)
add_subdirectory(colour_picker)
add_subdirectory(encoder)
add_subdirectory(gpio_pwm)
add_subdirectory(interrupt)
add_subdirectory(led_rainbow)
add_subdirectory(stop_watch)
77 changes: 77 additions & 0 deletions examples/breakout_encoder_wheel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# RGB Encoder Wheel Breakout Examples (C++) <!-- omit in toc -->

- [Function Examples](#function-examples)
- [Buttons](#buttons)
- [Encoder](#encoder)
- [Interrupt](#interrupt)
- [LED Examples](#led-examples)
- [LED Rainbow](#led-rainbow)
- [Clock](#clock)
- [Interactive Examples](#interactive-examples)
- [Colour Picker](#colour-picker)
- [Stop Watch](#stop-watch)
- [Chase Game](#chase-game)
- [GPIO Examples](#gpio-examples)
- [GPIO PWM](#gpio-pwm)


## Function Examples

### Buttons
[buttons/buttons.cpp](buttons/buttons.cpp)

A demonstration of reading the 5 buttons on Encoder Wheel.


### Encoder
[encoder/encoder.cpp](encoder/encoder.cpp)

A demonstration of reading the rotary dial of the Encoder Wheel breakout.


### Interrupt
[interrupt/interrupt.cpp](interrupt/interrupt.cpp)

How to read the buttons and rotary dial of the Encoder Wheel breakout, only when an interrupt occurs.


## LED Examples

### LED Rainbow
[led_rainbow/led_rainbow.cpp](led_rainbow/led_rainbow.cpp)

Displays a rotating rainbow pattern on Encoder Wheel's LED ring.


### Clock
[clock/clock.cpp](clock/clock.cpp)

Displays a 12 hour clock on Encoder Wheel's LED ring, getting time from the system.


## Interactive Examples

### Colour Picker
[colour_picker/colour_picker.cpp](colour_picker/colour_picker.cpp)

Create a colour wheel on the Encoder Wheel's LED ring, and use all functions of the wheel to interact with it.


### Stop Watch
[stop_watch/stop_watch.cpp](stop_watch/stop_watch.cpp)

Display a circular stop-watch on the Encoder Wheel's LED ring.


### Chase Game
[chase_game/chase_game.cpp](chase_game/chase_game.cpp)

A simple alignment game. Use Encoder Wheel's rotary dial to align the coloured band to the white goal. The closer to the goal, the greener your coloured band will be. When you reach the goal, the goal will move to a new random position.


## GPIO Examples

### GPIO PWM
[gpio_pwm/gpio_pwm.cpp](gpio_pwm/gpio_pwm.cpp)

Output a sine wave PWM sequence on the Encoder Wheel's side GPIO pins.
13 changes: 13 additions & 0 deletions examples/breakout_encoder_wheel/buttons/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(OUTPUT_NAME encoderwheel_buttons)
add_executable(${OUTPUT_NAME} buttons.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})
Loading

0 comments on commit 8648196

Please sign in to comment.