diff --git a/CMakeLists.txt b/CMakeLists.txt index c712c83..983fd1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,14 +16,17 @@ set(PICO_CXX_ENABLE_RTTI 1) pico_sdk_init() add_executable(stofzuiger src/main.c) +pico_generate_pio_header(stofzuiger ${CMAKE_CURRENT_LIST_DIR}/src/ws2812.pio OUTPUT_DIR ${CMAKE_CURRENT_LIST_DIR}/src/generated) target_link_libraries(stofzuiger pico_stdlib hardware_sleep + hardware_pio + hardware_pwm ) pico_enable_stdio_uart(stofzuiger 0) -pico_enable_stdio_usb(stofzuiger 1) +pico_enable_stdio_usb(stofzuiger 0) # create map/bin/hex/uf2 file in addition to ELF. pico_add_extra_outputs(stofzuiger) diff --git a/src/generated/ws2812.pio.h b/src/generated/ws2812.pio.h new file mode 100644 index 0000000..0808662 --- /dev/null +++ b/src/generated/ws2812.pio.h @@ -0,0 +1,66 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------ // +// ws2812 // +// ------ // + +#define ws2812_wrap_target 0 +#define ws2812_wrap 3 +#define ws2812_pio_version 0 + +#define ws2812_T1 3 +#define ws2812_T2 3 +#define ws2812_T3 4 + +static const uint16_t ws2812_program_instructions[] = { + // .wrap_target + 0x6321, // 0: out x, 1 side 0 [3] + 0x1223, // 1: jmp !x, 3 side 1 [2] + 0x1200, // 2: jmp 0 side 1 [2] + 0xa242, // 3: nop side 0 [2] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program ws2812_program = { + .instructions = ws2812_program_instructions, + .length = 4, + .origin = -1, + .pio_version = ws2812_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config ws2812_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + ws2812_wrap_target, offset + ws2812_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} + +#include "hardware/clocks.h" +static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) { + pio_gpio_init(pio, pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); + pio_sm_config c = ws2812_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, pin); + sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; + float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +#endif + diff --git a/src/main.c b/src/main.c index 0f8ec5d..884edb8 100644 --- a/src/main.c +++ b/src/main.c @@ -1,25 +1,28 @@ -#include #include #include #include #include +#include #include +#include #include +#include "generated/ws2812.pio.h" + #define IS_RGBW false #define NUM_PIXELS 1 #define WS2812_PIN 16 -#define PIN_LED_MODE_RED 0 -#define PIN_LED_MODE_GREEN 1 -#define PIN_LED_BATT1 2 -#define PIN_LED_BATT2 3 -#define PIN_LED_BATT3 4 -#define PIN_BTN 5 -#define PIN_MOTOR_IO1 6 -#define PIN_MOTOR_IO2 7 +#define PIN_LED_MODE_GREEN 3 +#define PIN_LED_MODE_RED 4 +#define PIN_LED_BATT1 5 +#define PIN_LED_BATT2 6 +#define PIN_LED_BATT3 7 +#define PIN_BTN 2 #define PIN_MOTOR_PWM 8 +#define PIN_MOTOR_IO1 9 +#define PIN_MOTOR_IO2 10 typedef enum { STATE_OFF, @@ -27,33 +30,22 @@ typedef enum { STATE_LOW } mainState_t; mainState_t mainState; +bool btn_pressed = false; -void update_state(mainState_t state) -{ - switch (state) - { - case STATE_OFF: - gpio_put(PIN_MOTOR_IO1, 0); - gpio_put(PIN_MOTOR_IO2, 0); - break; - case STATE_HIGE: - gpio_put(PIN_MOTOR_IO1, 1); - gpio_put(PIN_MOTOR_IO2, 0); - break; - case STATE_LOW: - gpio_put(PIN_MOTOR_IO1, 0); - gpio_put(PIN_MOTOR_IO2, 1); - break; +static inline void put_pixel(PIO pio, uint sm, uint32_t pixel_grb) { + pio_sm_put_blocking(pio, sm, pixel_grb << 8u); +} - - } +static inline uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t) (r) << 16) | ((uint32_t) (g) << 8) | (uint32_t) (b); } int main() { //set_sys_clock_48(); - stdio_init_all(); + // stdio_init_all(); mainState = STATE_OFF; + uint pwm_out; // init gpio outputs gpio_init(PIN_LED_MODE_RED); @@ -71,31 +63,89 @@ int main() { gpio_init(PIN_LED_BATT3); gpio_set_dir(PIN_LED_BATT3, GPIO_OUT); gpio_put(PIN_LED_BATT3, 0); + gpio_init(PIN_MOTOR_IO1); gpio_set_dir(PIN_MOTOR_IO1, GPIO_OUT); gpio_put(PIN_MOTOR_IO1, 0); + gpio_init(PIN_MOTOR_IO2); gpio_set_dir(PIN_MOTOR_IO2, GPIO_OUT); gpio_put(PIN_MOTOR_IO2, 0); + // gpio_init(PIN_MOTOR_PWM); + gpio_set_function(PIN_MOTOR_PWM, GPIO_FUNC_PWM); + pwm_out = pwm_gpio_to_slice_num(PIN_MOTOR_PWM); + pwm_config config = pwm_get_default_config(); + // set to about 25 kHz + pwm_config_set_clkdiv(&config, 255.f); + pwm_config_set_wrap(&config, 19); + pwm_init(pwm_out, &config, true); + pwm_set_gpio_level(PIN_MOTOR_PWM, 0); // init gpio inputs gpio_init(PIN_BTN); gpio_set_dir(PIN_BTN, GPIO_IN); + gpio_set_pulls(PIN_BTN, false, true); + // gpio_set_irq_enabled_with_callback(PIN_BTN, GPIO_IRQ_EDGE_FALL, true, &switch_state); + // todo get free sm + PIO pio; + uint sm; + uint offset; + pio_claim_free_sm_and_add_program_for_gpio_range(&ws2812_program, &pio, &sm, &offset, WS2812_PIN, 1, false); + ws2812_program_init(pio, sm, offset, WS2812_PIN, 800000, false); + put_pixel(pio, sm, urgb_u32(0, 0, 0)); + // pio_remove_program_and_unclaim_sm(&ws2812_program, pio, sm, offset); + + mainState = STATE_OFF; + + uint8_t counter = 0; + bool last_state = true; while (true) { - mainState = STATE_OFF; - update_state(STATE_OFF); - sleep_goto_dormant_until_edge_high(PIN_BTN); - mainState = STATE_HIGE; - while (mainState != STATE_OFF) - { + // sleep_run_from_xosc(); + // sleep_goto_dormant_until_pin(PIN_BTN, true, false); // falling edge + // sleep_goto_sleep_for(100, &switch_state); // debounce + // sleep_power_up(); + + bool new_state; + while ((new_state = gpio_get(PIN_BTN)) == last_state) { + tight_loop_contents(); + } + sleep_ms(100); + last_state = new_state; + if (new_state) { switch (mainState) { + case STATE_OFF: + mainState = STATE_HIGE; + gpio_put(PIN_MOTOR_IO1, 0); + gpio_put(PIN_MOTOR_IO2, 1); + + gpio_put(PIN_LED_MODE_RED, 1); + gpio_put(PIN_LED_MODE_GREEN, 0); + pwm_set_gpio_level(PIN_MOTOR_PWM, 19); + + put_pixel(pio, sm, urgb_u32(50, 0, 0)); + break; case STATE_HIGE: mainState = STATE_LOW; + gpio_put(PIN_MOTOR_IO1, 0); + gpio_put(PIN_MOTOR_IO2, 1); + + gpio_put(PIN_LED_MODE_RED, 0); + gpio_put(PIN_LED_MODE_GREEN, 1); + pwm_set_gpio_level(PIN_MOTOR_PWM, 12); + + put_pixel(pio, sm, urgb_u32(0, 50, 0)); break; case STATE_LOW: mainState = STATE_OFF; + gpio_put(PIN_MOTOR_IO1, 0); + gpio_put(PIN_MOTOR_IO2, 0); + + gpio_put(PIN_LED_MODE_RED, 0); + gpio_put(PIN_LED_MODE_GREEN, 0); + pwm_set_gpio_level(PIN_MOTOR_PWM, 0); + + put_pixel(pio, sm, urgb_u32(0, 0, 50)); break; } - update_state(mainState); } } return 0; diff --git a/src/ws2812.pio b/src/ws2812.pio new file mode 100644 index 0000000..a294f37 --- /dev/null +++ b/src/ws2812.pio @@ -0,0 +1,53 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; +.pio_version 0 // only requires PIO version 0 + +.program ws2812 +.side_set 1 + +; The following constants are selected for broad compatibility with WS2812, +; WS2812B, and SK6812 LEDs. Other constants may support higher bandwidths for +; specific LEDs, such as (7,10,8) for WS2812B LEDs. + +.define public T1 3 +.define public T2 3 +.define public T3 4 + +.lang_opt python sideset_init = pico.PIO.OUT_HIGH +.lang_opt python out_init = pico.PIO.OUT_HIGH +.lang_opt python out_shiftdir = 1 + +.wrap_target +bitloop: + out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls + jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse +do_one: + jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse +do_zero: + nop side 0 [T2 - 1] ; Or drive low, for a short pulse +.wrap + +% c-sdk { +#include "hardware/clocks.h" + +static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) { + + pio_gpio_init(pio, pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); + + pio_sm_config c = ws2812_program_get_default_config(offset); + sm_config_set_sideset_pins(&c, pin); + sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; + float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +%}