Skip to content

Commit 653090c

Browse files
committed
Exposed support for GPIO pins on encoder wheel
1 parent 8966cbf commit 653090c

File tree

7 files changed

+154
-36
lines changed

7 files changed

+154
-36
lines changed

drivers/ioexpander/ioexpander.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,13 +523,35 @@ namespace pimoroni {
523523
return divider_good;
524524
}
525525

526-
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) {
527527
value &= 0xffff;
528528
i2c->reg_write_uint8(address, reg::PWMPL, (uint8_t)(value & 0xff));
529529
i2c->reg_write_uint8(address, reg::PWMPH, (uint8_t)(value >> 8));
530530

531531
if(load)
532-
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;
533555
}
534556

535557
uint8_t IOExpander::get_mode(uint8_t pin) {
@@ -701,7 +723,7 @@ namespace pimoroni {
701723
}
702724
}
703725

704-
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) {
705727
if(pin < 1 || pin > NUM_PINS) {
706728
printf("Pin should be in range 1-14.");
707729
return;
@@ -717,7 +739,7 @@ namespace pimoroni {
717739
i2c->reg_write_uint8(address, io_pin.reg_pwml, (uint8_t)(value & 0xff));
718740
i2c->reg_write_uint8(address, io_pin.reg_pwmh, (uint8_t)(value >> 8));
719741
if(load)
720-
pwm_load();
742+
pwm_load(wait_for_load);
721743
}
722744
else {
723745
if(value == LOW) {

drivers/ioexpander/ioexpander.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ namespace pimoroni {
2727
static const uint8_t PIN_MODE_ADC = 0b01010; // ADC, Input-only (high-impedance)
2828

2929
static const uint32_t RESET_TIMEOUT_MS = 1000;
30+
static const uint32_t CLOCK_FREQ = 24000000;
31+
static const uint32_t MAX_PERIOD = (1 << 16) - 1;
32+
static const uint32_t MAX_DIVIDER = (1 << 7);
3033

3134
public:
3235
static const uint8_t DEFAULT_I2C_ADDRESS = 0x18;
@@ -205,15 +208,16 @@ namespace pimoroni {
205208
void pwm_clear(bool wait_for_clear = true);
206209
bool pwm_clearing();
207210
bool set_pwm_control(uint8_t divider);
208-
void set_pwm_period(uint16_t value, bool load = true);
211+
void set_pwm_period(uint16_t value, bool load = true, bool wait_for_load = true);
212+
uint16_t set_pwm_frequency(float frequency, bool load = true, bool wait_for_load = true);
209213

210214
uint8_t get_mode(uint8_t pin);
211215
void set_mode(uint8_t pin, uint8_t mode, bool schmitt_trigger = false, bool invert = false);
212216

213217
int16_t input(uint8_t pin, uint32_t adc_timeout = 1);
214218
float input_as_voltage(uint8_t pin, uint32_t adc_timeout = 1);
215219

216-
void output(uint8_t pin, uint16_t value, bool load = true);
220+
void output(uint8_t pin, uint16_t value, bool load = true, bool wait_for_load = true);
217221

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

examples/breakout_encoder_wheel/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ add_subdirectory(chase_game)
33
add_subdirectory(clock)
44
add_subdirectory(colour_picker)
55
add_subdirectory(encoder)
6-
#add_subdirectory(gpio_pwm)
6+
add_subdirectory(gpio_pwm)
77
add_subdirectory(led_rainbow)
88
add_subdirectory(stop_watch)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
set(OUTPUT_NAME encoderwheel_gpio_pwm)
2+
add_executable(${OUTPUT_NAME} gpio_pwm.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})
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <math.h>
2+
#include <string>
3+
#include "pimoroni_i2c.hpp"
4+
#include "breakout_encoder_wheel.hpp"
5+
#include "time.h"
6+
7+
using namespace pimoroni;
8+
using namespace encoderwheel;
9+
10+
/*
11+
Output a sine wave PWM sequence on the Encoder Wheel's side GPIO pins.
12+
13+
Press the centre button to stop the program.
14+
*/
15+
16+
// Constants
17+
constexpr float SPEED = 5.0f; // The speed that the LEDs will cycle at
18+
const uint UPDATES = 50; // How many times the LEDs will be updated per second
19+
const uint UPDATE_RATE_US = 1000000 / UPDATES;
20+
constexpr float FREQUENCY = 1000.0f; // The frequency to run the PWM at
21+
22+
// Create a new BreakoutEncoderWheel
23+
I2C i2c(BOARD::BREAKOUT_GARDEN);
24+
BreakoutEncoderWheel wheel(&i2c);
25+
26+
// Variables
27+
float offset = 0.0f;
28+
29+
30+
int main() {
31+
stdio_init_all();
32+
33+
// Attempt to initialise the encoder wheel
34+
if(wheel.init()) {
35+
36+
// Set the PWM frequency for the GPIOs
37+
uint16_t period = wheel.gpio_pwm_frequency(FREQUENCY);
38+
39+
// Set the GPIO pins to PWM outputs
40+
for(int i = 0; i < NUM_GPIOS; i++) {
41+
wheel.gpio_pin_mode(GPIOS[i], IOExpander::PIN_PWM);
42+
}
43+
44+
// Loop forever
45+
while(!wheel.pressed(CENTRE)) {
46+
// Record the start time of this loop
47+
absolute_time_t start_time = get_absolute_time();
48+
49+
offset += SPEED / 1000.0f;
50+
51+
// Update all the PWMs
52+
for(int i = 0; i < NUM_GPIOS; i++) {
53+
float angle = (((float)i / NUM_GPIOS) + offset) * M_PI;
54+
uint16_t duty = (uint16_t)(((sinf(angle) / 2.0f) + 0.5f) * period);
55+
56+
// Set the GPIO pin to the new duty cycle, but do not load it yet
57+
wheel.gpio_pin_value(GPIOS[i], duty, false);
58+
}
59+
60+
// Have all the PWMs load at once
61+
wheel.gpio_pwm_load();
62+
63+
// Sleep until the next update, accounting for how long the above operations took to perform
64+
sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
65+
}
66+
67+
// Turn off the PWM outputs
68+
for(int i = 0; i < NUM_GPIOS; i++) {
69+
wheel.gpio_pin_value(GPIOS[i], 0);
70+
}
71+
}
72+
73+
return 0;
74+
}

libraries/breakout_encoder_wheel/breakout_encoder_wheel.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -190,32 +190,37 @@ namespace encoderwheel {
190190
led_ring.update();
191191
}
192192

193-
int BreakoutEncoderWheel::gpio_pin_mode(int gpio) {
194-
return 0; // TODO
193+
uint8_t BreakoutEncoderWheel::gpio_pin_mode(uint8_t gpio) {
194+
assert(gpio < GP7 || gpio > GP9);
195+
return ioe.get_mode(gpio);
195196
}
196197

197-
void BreakoutEncoderWheel::gpio_pin_mode(int gpio, int mode) {
198-
198+
void BreakoutEncoderWheel::gpio_pin_mode(uint8_t gpio, uint8_t mode) {
199+
assert(gpio < GP7 || gpio > GP9);
200+
ioe.set_mode(gpio, mode);
199201
}
200202

201-
int BreakoutEncoderWheel::gpio_pin_value(int gpio) {
202-
return 0; // TODO
203+
int16_t BreakoutEncoderWheel::gpio_pin_value(uint8_t gpio) {
204+
assert(gpio < GP7 || gpio > GP9);
205+
return ioe.input(gpio);
203206
}
204207

205-
float BreakoutEncoderWheel::gpio_pin_value_as_voltage(int gpio) {
206-
return 0; // TODO
208+
float BreakoutEncoderWheel::gpio_pin_value_as_voltage(uint8_t gpio) {
209+
assert(gpio < GP7 || gpio > GP9);
210+
return ioe.input_as_voltage(gpio);
207211
}
208212

209-
void BreakoutEncoderWheel::gpio_pin_value(int gpio, int value, bool load, bool wait_for_load) {
210-
213+
void BreakoutEncoderWheel::gpio_pin_value(uint8_t gpio, uint16_t value, bool load, bool wait_for_load) {
214+
assert(gpio < GP7 || gpio > GP9);
215+
ioe.output(gpio, value, load, wait_for_load);
211216
}
212217

213218
void BreakoutEncoderWheel::gpio_pwm_load(bool wait_for_load) {
214-
219+
ioe.pwm_load(wait_for_load);
215220
}
216221

217222
int BreakoutEncoderWheel::gpio_pwm_frequency(float frequency, bool load, bool wait_for_load) {
218-
return 0; // TODO
223+
return ioe.set_pwm_frequency(frequency, load, wait_for_load);
219224
}
220225

221226
void BreakoutEncoderWheel::take_encoder_reading() {

libraries/breakout_encoder_wheel/breakout_encoder_wheel.hpp

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,17 @@ namespace encoderwheel {
4141
static const uint32_t DEFAULT_TIMEOUT = 1;
4242

4343
private:
44-
static const uint8_t ENC_CHANNEL = 1;
45-
static const uint8_t ENC_TERM_A = 3;
46-
static const uint8_t ENC_TERM_B = 12;
47-
static const uint8_t ENC_COUNTS_PER_REV = 24;
48-
static const uint8_t ENC_COUNT_DIVIDER = 2;
49-
50-
static const uint8_t SW_UP = 13;
51-
static const uint8_t SW_DOWN = 4;
52-
static const uint8_t SW_LEFT = 11;
53-
static const uint8_t SW_RIGHT = 2;
54-
static const uint8_t SW_CENTRE = 1;
44+
static const uint8_t ENC_CHANNEL = 1;
45+
static const uint8_t ENC_TERM_A = 3;
46+
static const uint8_t ENC_TERM_B = 12;
47+
static const uint8_t ENC_COUNTS_PER_REV = 24;
48+
static const uint8_t ENC_COUNT_DIVIDER = 2;
49+
50+
static const uint8_t SW_UP = 13;
51+
static const uint8_t SW_DOWN = 4;
52+
static const uint8_t SW_LEFT = 11;
53+
static const uint8_t SW_RIGHT = 2;
54+
static const uint8_t SW_CENTRE = 1;
5555

5656
// This wonderful lookup table maps the LEDs on the encoder wheel
5757
// from their 3x24 (remember, they're RGB) configuration to
@@ -146,12 +146,12 @@ namespace encoderwheel {
146146
void clear();
147147
void show();
148148

149-
int gpio_pin_mode(int gpio);
150-
void gpio_pin_mode(int gpio, int mode);
151-
int gpio_pin_value(int gpio);
152-
float gpio_pin_value_as_voltage(int gpio);
153-
void gpio_pin_value(int gpio, int value, bool load = true, bool wait_for_load = false);
154-
void gpio_pwm_load(bool wait_for_load = false);
149+
uint8_t gpio_pin_mode(uint8_t gpio);
150+
void gpio_pin_mode(uint8_t gpio, uint8_t mode);
151+
int16_t gpio_pin_value(uint8_t gpio);
152+
float gpio_pin_value_as_voltage(uint8_t gpio);
153+
void gpio_pin_value(uint8_t gpio, uint16_t value, bool load = true, bool wait_for_load = false);
154+
void gpio_pwm_load(bool wait_for_load = true);
155155
int gpio_pwm_frequency(float frequency, bool load = true, bool wait_for_load = false);
156156

157157
private:

0 commit comments

Comments
 (0)