diff --git a/examples/pico_explorer/demo.cpp b/examples/pico_explorer/demo.cpp index 865d322b..29806f36 100644 --- a/examples/pico_explorer/demo.cpp +++ b/examples/pico_explorer/demo.cpp @@ -106,6 +106,7 @@ int main() { shapes.push_back(shape); } + pico_explorer.set_audio_pin(pico_explorer.GP0); uint32_t i = 0; while(true) { pico_explorer.set_pen(120, 40, 60); @@ -123,13 +124,30 @@ int main() { pico_explorer.circle(shape.x, shape.y, shape.r); } - float led_step = fmod(i / 20.0f, M_PI * 2.0f); - int r = (sin(led_step) * 25.0f) + 25.0f; - pico_explorer.set_led(r, r / 1.2f, r); - pico_explorer.set_pen(255, 255, 255); pico_explorer.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", 10, 10, 180); + float rv = pico_explorer.get_adc(pico_explorer.ADC0); + pico_explorer.set_pen(255, 255, 255); + pico_explorer.circle(rv * 140 + 50, 110, 20); + pico_explorer.set_pen(rv * 255, 0, 0); + pico_explorer.circle(rv * 140 + 50, 110, 15); + + float gv = pico_explorer.get_adc(pico_explorer.ADC1); + pico_explorer.set_pen(255, 255, 255); + pico_explorer.circle(gv * 140 + 50, 160, 20); + pico_explorer.set_pen(0, gv * 255, 0); + pico_explorer.circle(gv * 140 + 50, 160, 15); + + float bv = pico_explorer.get_adc(pico_explorer.ADC2); + pico_explorer.set_pen(255, 255, 255); + pico_explorer.circle(bv * 140 + 50, 210, 20); + pico_explorer.set_pen(0, 0, bv * 255); + pico_explorer.circle(bv * 140 + 50, 210, 15); + + pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, bv); + + pico_explorer.set_tone(100 + (bv * 1000), rv); /* if(pico_display.is_pressed(pico_display.A)) { pico_display.rectangle(0, 0, 18, 18); diff --git a/libraries/pico_explorer/pico_explorer.cpp b/libraries/pico_explorer/pico_explorer.cpp index 66276e9a..b2df3df0 100644 --- a/libraries/pico_explorer/pico_explorer.cpp +++ b/libraries/pico_explorer/pico_explorer.cpp @@ -1,69 +1,110 @@ #include #include "hardware/pwm.h" +#include "hardware/adc.h" #include "pico_explorer.hpp" -const uint8_t LED_R = 6; -const uint8_t LED_G = 7; -const uint8_t LED_B = 8; +const uint8_t MOTOR1N = 8; +const uint8_t MOTOR1P = 9; +const uint8_t MOTOR2N = 10; +const uint8_t MOTOR2P = 11; namespace pimoroni { PicoExplorer::PicoExplorer() : screen(240, 240, __fb), PicoGraphics(240, 240, __fb) { - // setup the rgb led for pwm control - pwm_config cfg = pwm_get_default_config(); - pwm_config_set_output_polarity(&cfg, true, true); - - // red - pwm_set_wrap(pwm_gpio_to_slice_num(LED_R), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_R), &cfg, true); - gpio_set_function(LED_R, GPIO_FUNC_PWM); - - // green - pwm_set_wrap(pwm_gpio_to_slice_num(LED_G), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_G), &cfg, true); - gpio_set_function(LED_G, GPIO_FUNC_PWM); - - // blue - pwm_set_wrap(pwm_gpio_to_slice_num(LED_B), 65535); - pwm_init(pwm_gpio_to_slice_num(LED_B), &cfg, true); - gpio_set_function(LED_B, GPIO_FUNC_PWM); - // setup button inputs gpio_set_function(A, GPIO_FUNC_SIO); gpio_set_dir(A, GPIO_IN); gpio_pull_up(A); gpio_set_function(B, GPIO_FUNC_SIO); gpio_set_dir(B, GPIO_IN); gpio_pull_up(B); gpio_set_function(X, GPIO_FUNC_SIO); gpio_set_dir(X, GPIO_IN); gpio_pull_up(X); gpio_set_function(Y, GPIO_FUNC_SIO); gpio_set_dir(Y, GPIO_IN); gpio_pull_up(Y); + // setup ADC channels + adc_init(); + const uint8_t ADC_BASE_PIN = 26; + adc_gpio_init(ADC0 + ADC_BASE_PIN); + adc_gpio_init(ADC1 + ADC_BASE_PIN); + adc_gpio_init(ADC2 + ADC_BASE_PIN); + + // setup motor pins + pwm_config motor_pwm_cfg = pwm_get_default_config(); + pwm_config_set_wrap(&motor_pwm_cfg, 255); + + pwm_init(pwm_gpio_to_slice_num(MOTOR1N), &motor_pwm_cfg, true); + gpio_set_function(MOTOR1N, GPIO_FUNC_PWM); + + pwm_init(pwm_gpio_to_slice_num(MOTOR1P), &motor_pwm_cfg, true); + gpio_set_function(MOTOR1P, GPIO_FUNC_PWM); + + pwm_init(pwm_gpio_to_slice_num(MOTOR2N), &motor_pwm_cfg, true); + gpio_set_function(MOTOR2N, GPIO_FUNC_PWM); + + pwm_init(pwm_gpio_to_slice_num(MOTOR2P), &motor_pwm_cfg, true); + gpio_set_function(MOTOR2P, GPIO_FUNC_PWM); + // initialise the screen screen.init(); } - void PicoExplorer::set_led(uint8_t r, uint8_t g, uint8_t b) { - // gamma correct the provided 0-255 brightness value onto a - // 0-65535 range for the pwm counter - static const float gamma = 2.8; - - uint16_t value; - - // red - value = (uint16_t)(pow((float)(r) / 255.0f, gamma) * 65536.0f + 0.5f); - pwm_set_gpio_level(LED_R, value); - - // green - value = (uint16_t)(pow((float)(g) / 255.0f, gamma) * 65536.0f + 0.5f); - pwm_set_gpio_level(LED_G, value); - - // blue - value = (uint16_t)(pow((float)(b) / 255.0f, gamma) * 65536.0f + 0.5f); - pwm_set_gpio_level(LED_B, value); - } - bool PicoExplorer::is_pressed(uint8_t button) { return !gpio_get(button); } + float PicoExplorer::get_adc(uint8_t channel) { + adc_select_input(channel); + // scale raw 12-bit adc value to 0 .. 1 float + float result = float(adc_read()) / (1 << 12); + // clamp result to 0 .. 1 + result = std::min(1.0f, std::max(0.0f, result)); + return result; + } + + void PicoExplorer::set_motor(uint8_t channel, uint8_t action, float speed) { + uint8_t p = channel == MOTOR1 ? MOTOR1P : MOTOR2P; + uint8_t n = channel == MOTOR1 ? MOTOR1N : MOTOR2N; + + switch(action) { + case FORWARD: { + pwm_set_gpio_level(p, speed * 255); + pwm_set_gpio_level(n, 0); + break; + } + + case REVERSE: { + pwm_set_gpio_level(p, 0); + pwm_set_gpio_level(n, speed * 255); + break; + } + + case STOP: { + pwm_set_gpio_level(p, 0); + pwm_set_gpio_level(n, 0); + break; + } + } + } + + void PicoExplorer::set_audio_pin(uint8_t pin) { + pwm_config tone_pwm_cfg = pwm_get_default_config(); + + // calculate the pwm wrap value for this frequency + // first we set the clock divider to give us exactly + // ten thousand cycles per second + pwm_config_set_clkdiv(&tone_pwm_cfg, 255); + pwm_init(pwm_gpio_to_slice_num(pin), &tone_pwm_cfg, true); + gpio_set_function(pin, GPIO_FUNC_PWM); + audio_pin = pin; + } + + void PicoExplorer::set_tone(uint16_t frequency, float duty) { + // output a square wave, so 50% duty cycle + if(audio_pin != -1) { + uint16_t pwm_wrap = 490196 / frequency; + pwm_set_wrap(audio_pin, pwm_wrap); + pwm_set_gpio_level(audio_pin, pwm_wrap * duty); + } + } + } \ No newline at end of file diff --git a/libraries/pico_explorer/pico_explorer.hpp b/libraries/pico_explorer/pico_explorer.hpp index d9784f5d..c44438e0 100644 --- a/libraries/pico_explorer/pico_explorer.hpp +++ b/libraries/pico_explorer/pico_explorer.hpp @@ -8,6 +8,7 @@ namespace pimoroni { class PicoExplorer : public PicoGraphics { uint16_t __fb[240 * 240]; ST7789 screen; + int8_t audio_pin = -1; public: PicoExplorer(); @@ -15,13 +16,40 @@ namespace pimoroni { void set_backlight(uint8_t brightness) {screen.set_backlight(brightness);} void update() {screen.update();} - void set_led(uint8_t r, uint8_t g, uint8_t b); bool is_pressed(uint8_t button); + float get_adc(uint8_t channel); + + void set_motor(uint8_t channel, uint8_t action, float speed = 0.0f); + + void set_audio_pin(uint8_t pin); + void set_tone(uint16_t frequency, float duty = 0.2f); + static const uint8_t A = 12; static const uint8_t B = 13; static const uint8_t X = 14; static const uint8_t Y = 15; + + static const uint8_t ADC0 = 0; + static const uint8_t ADC1 = 1; + static const uint8_t ADC2 = 2; + + static const uint8_t MOTOR1 = 0; + static const uint8_t MOTOR2 = 1; + + static const uint8_t FORWARD = 0; + static const uint8_t REVERSE = 1; + static const uint8_t STOP = 2; + + static const uint8_t GP0 = 0; + static const uint8_t GP1 = 1; + static const uint8_t GP2 = 2; + static const uint8_t GP3 = 3; + static const uint8_t GP4 = 4; + static const uint8_t GP5 = 5; + static const uint8_t GP6 = 6; + static const uint8_t GP7 = 7; + }; } \ No newline at end of file