Skip to content

Commit 8648196

Browse files
authored
Merge pull request #774 from pimoroni/breakout_encoder_wheel
Support for RGB Encoder Wheel Breakout
2 parents ec205fb + d00185d commit 8648196

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3727
-18
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
116116
* MICS6814 - Gas Sensor - https://shop.pimoroni.com/products/mics6814-gas-sensor-breakout
117117
* RGB Potentiometer - https://shop.pimoroni.com/products/rgb-potentiometer-breakout
118118
* RGB Encoder - https://shop.pimoroni.com/products/rgb-encoder-breakout
119+
* RGB Encoder Wheel - https://shop.pimoroni.com/products/rgb-encoder-wheel-breakout
119120
* IO Expander - https://shop.pimoroni.com/products/io-expander
120121
* RV3028 - Real-Time Clock (RTC) - https://shop.pimoroni.com/products/rv3028-real-time-clock-rtc-breakout
121122
* ST7735 - 0.96" LCD - https://shop.pimoroni.com/products/0-96-spi-colour-lcd-160x80-breakout

drivers/ioexpander/ioexpander.cpp

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,21 @@ namespace pimoroni {
320320
Pin::adc(1, 7, 0)}
321321
{}
322322

323-
bool IOExpander::init(bool skipChipIdCheck) {
324-
bool succeeded = true;
323+
bool IOExpander::init(bool skipChipIdCheck, bool perform_reset) {
324+
if(!skipChipIdCheck) {
325+
uint16_t chip_id = get_chip_id();
326+
if(chip_id != CHIP_ID) {
327+
if(debug) {
328+
printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID);
329+
}
330+
return false;
331+
}
332+
}
333+
334+
// Reset the chip if requested, to put it into a known state
335+
if(perform_reset && !reset()) {
336+
return false;
337+
}
325338

326339
if(interrupt != PIN_UNUSED) {
327340
gpio_set_function(interrupt, GPIO_FUNC_SIO);
@@ -331,17 +344,36 @@ namespace pimoroni {
331344
enable_interrupt_out(true);
332345
}
333346

334-
if(!skipChipIdCheck) {
335-
uint16_t chip_id = get_chip_id();
336-
if(chip_id != CHIP_ID) {
337-
if(debug) {
338-
printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID);
339-
}
340-
succeeded = false;
347+
return true;
348+
}
349+
350+
uint8_t IOExpander::check_reset() {
351+
uint8_t user_flash_reg = reg::USER_FLASH;
352+
uint8_t value;
353+
if(i2c_write_blocking(i2c->get_i2c(), address, &user_flash_reg, 1, false) == PICO_ERROR_GENERIC) {
354+
return 0x00;
355+
}
356+
if(i2c_read_blocking(i2c->get_i2c(), address, (uint8_t *)&value, sizeof(uint8_t), false) == PICO_ERROR_GENERIC) {
357+
return 0x00;
358+
}
359+
360+
return value;
361+
}
362+
363+
bool IOExpander::reset() {
364+
uint32_t start_time = millis();
365+
set_bits(reg::CTRL, ctrl_mask::RESET);
366+
// Wait for a register to read its initialised value
367+
while(check_reset() != 0x78) {
368+
sleep_ms(1);
369+
if(millis() - start_time >= RESET_TIMEOUT_MS) {
370+
if(debug)
371+
printf("Timed out waiting for Reset!");
372+
return false;
341373
}
342374
}
343375

344-
return succeeded;
376+
return true;
345377
}
346378

347379
i2c_inst_t* IOExpander::get_i2c() const {
@@ -370,7 +402,7 @@ namespace pimoroni {
370402

371403
void IOExpander::set_address(uint8_t address) {
372404
set_bit(reg::CTRL, 4);
373-
i2c->reg_write_uint8(address, reg::ADDR, address);
405+
i2c->reg_write_uint8(this->address, reg::ADDR, address);
374406
this->address = address;
375407
sleep_ms(250); //TODO Handle addr change IOError better
376408
//wait_for_flash()
@@ -491,13 +523,35 @@ namespace pimoroni {
491523
return divider_good;
492524
}
493525

494-
void IOExpander::set_pwm_period(uint16_t value, bool load) {
526+
void IOExpander::set_pwm_period(uint16_t value, bool load, bool wait_for_load) {
495527
value &= 0xffff;
496528
i2c->reg_write_uint8(address, reg::PWMPL, (uint8_t)(value & 0xff));
497529
i2c->reg_write_uint8(address, reg::PWMPH, (uint8_t)(value >> 8));
498530

499531
if(load)
500-
pwm_load();
532+
pwm_load(wait_for_load);
533+
}
534+
535+
uint16_t IOExpander::set_pwm_frequency(float frequency, bool load, bool wait_for_load) {
536+
uint32_t period = (uint32_t)(CLOCK_FREQ / frequency);
537+
if (period / 128 > MAX_PERIOD) {
538+
return MAX_PERIOD;
539+
}
540+
if (period < 2) {
541+
return 2;
542+
}
543+
544+
uint8_t divider = 1;
545+
while ((period > MAX_PERIOD) && (divider < MAX_DIVIDER)) {
546+
period >>= 1;
547+
divider <<= 1;
548+
}
549+
550+
period = MIN(period, MAX_PERIOD); // Should be unnecessary because of earlier raised errors, but kept in case
551+
set_pwm_control(divider);
552+
set_pwm_period((uint16_t)(period - 1), load, wait_for_load);
553+
554+
return (uint16_t)period;
501555
}
502556

503557
uint8_t IOExpander::get_mode(uint8_t pin) {
@@ -669,7 +723,7 @@ namespace pimoroni {
669723
}
670724
}
671725

672-
void IOExpander::output(uint8_t pin, uint16_t value, bool load) {
726+
void IOExpander::output(uint8_t pin, uint16_t value, bool load, bool wait_for_load) {
673727
if(pin < 1 || pin > NUM_PINS) {
674728
printf("Pin should be in range 1-14.");
675729
return;
@@ -685,7 +739,7 @@ namespace pimoroni {
685739
i2c->reg_write_uint8(address, io_pin.reg_pwml, (uint8_t)(value & 0xff));
686740
i2c->reg_write_uint8(address, io_pin.reg_pwmh, (uint8_t)(value >> 8));
687741
if(load)
688-
pwm_load();
742+
pwm_load(wait_for_load);
689743
}
690744
else {
691745
if(value == LOW) {

drivers/ioexpander/ioexpander.hpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ namespace pimoroni {
2626
static const uint8_t PIN_MODE_PWM = 0b00101; // PWM, Output, Push-Pull mode
2727
static const uint8_t PIN_MODE_ADC = 0b01010; // ADC, Input-only (high-impedance)
2828

29+
static const uint32_t RESET_TIMEOUT_MS = 1000;
30+
2931
public:
3032
static const uint8_t DEFAULT_I2C_ADDRESS = 0x18;
3133

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

50+
static const uint32_t CLOCK_FREQ = 24000000;
51+
static const uint32_t MAX_PERIOD = (1 << 16) - 1;
52+
static const uint32_t MAX_DIVIDER = (1 << 7);
53+
4854

4955
//--------------------------------------------------
5056
// Subclasses
@@ -171,7 +177,12 @@ namespace pimoroni {
171177
// Methods
172178
//--------------------------------------------------
173179
public:
174-
bool init(bool skip_chip_id_check = false);
180+
bool init(bool skip_chip_id_check = false, bool perform_reset = false);
181+
182+
private:
183+
uint8_t check_reset();
184+
public:
185+
bool reset();
175186

176187
// For print access in micropython
177188
i2c_inst_t* get_i2c() const;
@@ -198,15 +209,16 @@ namespace pimoroni {
198209
void pwm_clear(bool wait_for_clear = true);
199210
bool pwm_clearing();
200211
bool set_pwm_control(uint8_t divider);
201-
void set_pwm_period(uint16_t value, bool load = true);
212+
void set_pwm_period(uint16_t value, bool load = true, bool wait_for_load = true);
213+
uint16_t set_pwm_frequency(float frequency, bool load = true, bool wait_for_load = true);
202214

203215
uint8_t get_mode(uint8_t pin);
204216
void set_mode(uint8_t pin, uint8_t mode, bool schmitt_trigger = false, bool invert = false);
205217

206218
int16_t input(uint8_t pin, uint32_t adc_timeout = 1);
207219
float input_as_voltage(uint8_t pin, uint32_t adc_timeout = 1);
208220

209-
void output(uint8_t pin, uint16_t value, bool load = true);
221+
void output(uint8_t pin, uint16_t value, bool load = true, bool wait_for_load = true);
210222

211223
void setup_rotary_encoder(uint8_t channel, uint8_t pin_a, uint8_t pin_b, uint8_t pin_c = 0, bool count_microsteps = false);
212224
int16_t read_rotary_encoder(uint8_t channel);

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_subdirectory(breakout_dotmatrix)
22
add_subdirectory(breakout_encoder)
3+
add_subdirectory(breakout_encoder_wheel)
34
add_subdirectory(breakout_ioexpander)
45
add_subdirectory(breakout_ltr559)
56
add_subdirectory(breakout_colourlcd160x80)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
add_subdirectory(buttons)
2+
add_subdirectory(chase_game)
3+
add_subdirectory(clock)
4+
add_subdirectory(colour_picker)
5+
add_subdirectory(encoder)
6+
add_subdirectory(gpio_pwm)
7+
add_subdirectory(interrupt)
8+
add_subdirectory(led_rainbow)
9+
add_subdirectory(stop_watch)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# RGB Encoder Wheel Breakout Examples (C++) <!-- omit in toc -->
2+
3+
- [Function Examples](#function-examples)
4+
- [Buttons](#buttons)
5+
- [Encoder](#encoder)
6+
- [Interrupt](#interrupt)
7+
- [LED Examples](#led-examples)
8+
- [LED Rainbow](#led-rainbow)
9+
- [Clock](#clock)
10+
- [Interactive Examples](#interactive-examples)
11+
- [Colour Picker](#colour-picker)
12+
- [Stop Watch](#stop-watch)
13+
- [Chase Game](#chase-game)
14+
- [GPIO Examples](#gpio-examples)
15+
- [GPIO PWM](#gpio-pwm)
16+
17+
18+
## Function Examples
19+
20+
### Buttons
21+
[buttons/buttons.cpp](buttons/buttons.cpp)
22+
23+
A demonstration of reading the 5 buttons on Encoder Wheel.
24+
25+
26+
### Encoder
27+
[encoder/encoder.cpp](encoder/encoder.cpp)
28+
29+
A demonstration of reading the rotary dial of the Encoder Wheel breakout.
30+
31+
32+
### Interrupt
33+
[interrupt/interrupt.cpp](interrupt/interrupt.cpp)
34+
35+
How to read the buttons and rotary dial of the Encoder Wheel breakout, only when an interrupt occurs.
36+
37+
38+
## LED Examples
39+
40+
### LED Rainbow
41+
[led_rainbow/led_rainbow.cpp](led_rainbow/led_rainbow.cpp)
42+
43+
Displays a rotating rainbow pattern on Encoder Wheel's LED ring.
44+
45+
46+
### Clock
47+
[clock/clock.cpp](clock/clock.cpp)
48+
49+
Displays a 12 hour clock on Encoder Wheel's LED ring, getting time from the system.
50+
51+
52+
## Interactive Examples
53+
54+
### Colour Picker
55+
[colour_picker/colour_picker.cpp](colour_picker/colour_picker.cpp)
56+
57+
Create a colour wheel on the Encoder Wheel's LED ring, and use all functions of the wheel to interact with it.
58+
59+
60+
### Stop Watch
61+
[stop_watch/stop_watch.cpp](stop_watch/stop_watch.cpp)
62+
63+
Display a circular stop-watch on the Encoder Wheel's LED ring.
64+
65+
66+
### Chase Game
67+
[chase_game/chase_game.cpp](chase_game/chase_game.cpp)
68+
69+
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.
70+
71+
72+
## GPIO Examples
73+
74+
### GPIO PWM
75+
[gpio_pwm/gpio_pwm.cpp](gpio_pwm/gpio_pwm.cpp)
76+
77+
Output a sine wave PWM sequence on the Encoder Wheel's side GPIO pins.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
set(OUTPUT_NAME encoderwheel_buttons)
2+
add_executable(${OUTPUT_NAME} buttons.cpp)
3+
4+
# Pull in pico libraries that we need
5+
target_link_libraries(${OUTPUT_NAME}
6+
pico_stdlib
7+
breakout_encoder_wheel
8+
)
9+
10+
# enable usb output
11+
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
12+
13+
pico_add_extra_outputs(${OUTPUT_NAME})

0 commit comments

Comments
 (0)