diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b2e8d4..a6d8302 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,9 +23,15 @@ target_include_directories(rp2040_hid PRIVATE src) pico_add_extra_outputs(rp2040_hid) add_executable(ledtest + src/buttons.c src/ledtest.c + src/sevenseg.c + src/statusleds.c + src/rotary.c ) -pico_generate_pio_header(ledtest ${CMAKE_CURRENT_LIST_DIR}/src/ledtest.pio) +pico_generate_pio_header(ledtest ${CMAKE_CURRENT_LIST_DIR}/src/sevenseg.pio) +pico_generate_pio_header(ledtest ${CMAKE_CURRENT_LIST_DIR}/src/shift_in.pio) +pico_generate_pio_header(ledtest ${CMAKE_CURRENT_LIST_DIR}/src/statusleds.pio) target_link_libraries(ledtest pico_stdlib hardware_pio) pico_add_extra_outputs(ledtest) diff --git a/src/buttons.c b/src/buttons.c new file mode 100644 index 0000000..083d08d --- /dev/null +++ b/src/buttons.c @@ -0,0 +1,52 @@ +#include "buttons.h" + +#include "pinmap.h" +#include +#include + +#include "shift_in.pio.h" + +extern PIO pio; +static uint sm_but; + +static volatile unsigned int button_val; + +static void rxnempty_isr(void) { + if (pio_sm_is_rx_fifo_empty(pio, sm_but)) return; + + static unsigned button_prev = 0; + unsigned button_new = button_prev; + while (!pio_sm_is_rx_fifo_empty(pio, sm_but)) { + button_prev = button_new; + button_new = (~pio_sm_get(pio, sm_but)) >> 4; + } + // debounce, only buttons where button_new and button_prev agree are stored to button_val + unsigned button_match = ~(button_new ^ button_prev); + button_val = (button_val & ~button_match) | (button_new & button_match); + button_prev = button_new; +} + +unsigned buttons_read(void) +{ + return button_val; +} + +void buttons_init(void) +{ + uint offset_but = pio_add_program(pio, &shiftin_program); + sm_but = pio_claim_unused_sm(pio, true); + pio_sm_config c_but = shiftin_program_get_default_config(offset_but); + sm_config_set_in_pins(&c_but, BUTTONS_SDI_PIN); + sm_config_set_sideset_pins(&c_but, BUTTONS_CLK_PIN); + sm_config_set_in_shift(&c_but, false, false, 24); + sm_config_set_clkdiv(&c_but, 2600); // 1 kHz sample rate + pio_sm_set_pindirs_with_mask(pio, sm_but, (1u << BUTTONS_CLK_PIN) | (1u << BUTTONS_PL_PIN), + (1u << BUTTONS_SDI_PIN) | (1u << BUTTONS_CLK_PIN) | (1u << BUTTONS_PL_PIN)); + pio_gpio_init(pio, BUTTONS_SDI_PIN); + pio_gpio_init(pio, BUTTONS_CLK_PIN); + pio_gpio_init(pio, BUTTONS_PL_PIN); + pio_set_irq1_source_enabled(pio, PIO_INTR_SM0_RXNEMPTY_LSB+sm_but, true); + irq_add_shared_handler(PIO0_IRQ_1, &rxnempty_isr, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); + pio_sm_init(pio, sm_but, offset_but, &c_but); + pio_sm_set_enabled(pio, sm_but, true); +} diff --git a/src/buttons.h b/src/buttons.h new file mode 100644 index 0000000..b750ba2 --- /dev/null +++ b/src/buttons.h @@ -0,0 +1,5 @@ +#pragma once + +unsigned buttons_read(void); + +void buttons_init(void); diff --git a/src/ledtest.c b/src/ledtest.c index feb1ca1..c73e327 100644 --- a/src/ledtest.c +++ b/src/ledtest.c @@ -1,101 +1,55 @@ -#include -#include +#include "buttons.h" +#include "pinmap.h" +#include "rotary.h" +#include "sevenseg.h" +#include "statusleds.h" #include -#include "ledtest.pio.h" +#include +#include -#define SDO_PIN 21 -#define OE_PIN 20 -#define FCLK_PIN 19 -#define SCLK_PIN 18 +PIO pio = pio0; -#define LED1_GPIO 22 - -static void output_byte_manual(uint8_t val) -{ - gpio_put(FCLK_PIN, false); - gpio_put(SCLK_PIN, false); - for (int i = 0; i < 8; ++i) { - sleep_us(1); - gpio_put(SCLK_PIN, false); - gpio_put(SDO_PIN, val & 0x1); - sleep_us(1); - gpio_put(SCLK_PIN, true); - val >>= 1; +void rotary_event(int rot_ch, bool ccw) { + static int count[4] = { 0 }; + if (ccw) { + ++count[rot_ch]; + } else { + count[rot_ch] = count[rot_ch] ? count[rot_ch] - 1 : 9; } - sleep_us(1); - gpio_put(SCLK_PIN, false); - gpio_put(FCLK_PIN, true); - sleep_us(1); - gpio_put(FCLK_PIN, false); + sevenseg_set_digit(rot_ch+12, count[rot_ch] % 10); } -static PIO pio = pio0; -static uint sm; - -static void output_pio(uint32_t val) +int main(void) { - *(volatile uint32_t*)&pio->txf[sm] = val; -} + setup_default_uart(); -void isr_pio0_0(void) -{ - gpio_put(OE_PIN, false); - // only need to enable outputs after first output - pio_set_irq0_source_enabled(pio, PIO_INTR_SM0_LSB, false); -} + rotary_init(); + sevenseg_init(); + buttons_init(); + statusleds_init(); -static const uint16_t pattern[] = { - 0x8001, - 0x4002, - 0x2004, - 0x1008, - 0x0810, - 0x0420, - 0x0240, - 0x0180, -}; - -int main() -{ - gpio_init(LED1_GPIO); - gpio_set_dir(LED1_GPIO, GPIO_OUT); - gpio_put(LED1_GPIO, false); + irq_set_enabled(PIO0_IRQ_1, true); - gpio_init(SDO_PIN); - gpio_set_dir(SDO_PIN, GPIO_OUT); - gpio_put(SDO_PIN, false); - gpio_init(OE_PIN); - gpio_set_dir(OE_PIN, GPIO_OUT); - gpio_put(OE_PIN, true); - gpio_init(FCLK_PIN); - gpio_set_dir(FCLK_PIN, GPIO_OUT); - gpio_put(FCLK_PIN, false); - gpio_init(SCLK_PIN); - gpio_set_dir(SCLK_PIN, GPIO_OUT); - gpio_put(SCLK_PIN, false); + printf("ledtest.c\n"); - uint offset = pio_add_program(pio, &ledtest_program); - sm = pio_claim_unused_sm(pio, true); - pio_sm_config c = ledtest_program_get_default_config(offset); - sm_config_set_out_pins(&c, SDO_PIN, 1); - sm_config_set_sideset_pins(&c, SCLK_PIN); - sm_config_set_out_shift(&c, false, false, 16); - sm_config_set_clkdiv(&c, 13); // 130 / 13 / 2 = 5 MHz - pio_sm_set_pindirs_with_mask(pio, sm, (1u << SDO_PIN) | (1u << SCLK_PIN) | (1u << FCLK_PIN), - (1u << SDO_PIN) | (1u << SCLK_PIN) | (1u << FCLK_PIN)); - pio_gpio_init(pio, SDO_PIN); - pio_gpio_init(pio, SCLK_PIN); - pio_gpio_init(pio, FCLK_PIN); - pio_set_irq0_source_enabled(pio, PIO_INTR_SM0_LSB, true); - irq_set_enabled(PIO0_IRQ_0, true); - pio_sm_init(pio, sm, offset, &c); - pio_sm_set_enabled(pio, sm, true); + for (int i = 0; i < 4; ++i) + sevenseg_set_digit(i + 12, 0); - int pos = 0; + for (int i = 0; i < 5; ++i) + sevenseg_set_digit(i + 3, 0xf); + + int led_time = time_us_32(); + int led_ctr = 0; while (true) { - output_pio(pattern[pos++]<<16); - if (pos >= (sizeof(pattern) / sizeof(pattern[0]))) - pos = 0; - sleep_ms(100); + unsigned buttons = buttons_read(); + for (int i = 0; i < 5; ++i) { + sevenseg_set_digit(i + 3, buttons >> (i * 4) & 0xf); + } + if (time_us_32() - led_time >= 1000000) { + statusleds_set(1<= 12 ? 0 : led_ctr+1; + led_time = time_us_32(); + } + sleep_ms(1); } } diff --git a/src/pinmap.h b/src/pinmap.h new file mode 100644 index 0000000..e84d78d --- /dev/null +++ b/src/pinmap.h @@ -0,0 +1,19 @@ +#pragma once + +#define SEVENSEG_SDO_PIN 21 +#define SEVENSEG_OE_PIN 20 +#define SEVENSEG_FCLK_PIN 19 +#define SEVENSEG_SCLK_PIN 18 + +#define LED1_GPIO 22 + +#define ROTARY_FIRST_GPIO 2 + +#define BUTTONS_SDI_PIN 11 +#define BUTTONS_CLK_PIN 12 +#define BUTTONS_PL_PIN 13 + +#define LEDS_SDO_PIN 17 +#define LEDS_OE_PIN 16 +#define LEDS_FCLK_PIN 15 +#define LEDS_SCLK_PIN 14 diff --git a/src/rotary.c b/src/rotary.c new file mode 100644 index 0000000..62fbb06 --- /dev/null +++ b/src/rotary.c @@ -0,0 +1,71 @@ +#include "rotary.h" + +#include "pinmap.h" +#include + +static int rotary_alarm; +static uint64_t rotary_timer_next; + +#define ROTARY_INTERVAL 500 + +enum rotary_states { + rotary_state_idle = 0, + rotary_state_B, + rotary_state_A, +}; + +static void rotary_timer_event(uint alarm_num) +{ + rotary_timer_next += ROTARY_INTERVAL; + hardware_alarm_set_target(alarm_num, rotary_timer_next); + + static enum rotary_states state[4] = { rotary_state_idle }; + static int count[4] = { 0 }; + bool rot_gpio[8]; + for (int i = 0; i < 8; ++i) { + rot_gpio[i] = gpio_get(ROTARY_FIRST_GPIO + i); + } + + for (int i = 0; i < 4; ++i) { + switch (state[i]) { + case rotary_state_idle: + if (!rot_gpio[i * 2] && rot_gpio[i * 2 + 1]) + state[i] = rotary_state_B; + if (rot_gpio[i * 2] && !rot_gpio[i * 2 + 1]) + state[i] = rotary_state_A; + break; + case rotary_state_B: + if (rot_gpio[i * 2] && rot_gpio[i * 2 + 1]) + state[i] = rotary_state_idle; + if (rot_gpio[i * 2] && !rot_gpio[i * 2 + 1]) { + state[i] = rotary_state_A; + rotary_event(i, true); + } + break; + case rotary_state_A: + if (rot_gpio[i * 2] && rot_gpio[i * 2 + 1]) + state[i] = rotary_state_idle; + if (!rot_gpio[i * 2] && rot_gpio[i * 2 + 1]) { + state[i] = rotary_state_B; + rotary_event(i, false); + } + break; + } + } +} + +void rotary_init(void) +{ + for (int i = 0; i < 8; ++i) { + gpio_init(ROTARY_FIRST_GPIO + i); + gpio_set_dir(ROTARY_FIRST_GPIO + i, GPIO_IN); + gpio_pull_up(ROTARY_FIRST_GPIO + i); + } + + rotary_alarm = hardware_alarm_claim_unused(true); + hardware_alarm_set_callback(rotary_alarm, &rotary_timer_event); + rotary_timer_next = time_us_64() + ROTARY_INTERVAL; + hardware_alarm_set_target(rotary_alarm, rotary_timer_next); +} + + diff --git a/src/rotary.h b/src/rotary.h new file mode 100644 index 0000000..0a8ce59 --- /dev/null +++ b/src/rotary.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +extern void rotary_event(int rot_ch, bool ccw); + +void rotary_init(void); diff --git a/src/sevenseg.c b/src/sevenseg.c new file mode 100644 index 0000000..c915cba --- /dev/null +++ b/src/sevenseg.c @@ -0,0 +1,73 @@ +#include "sevenseg.h" + +#include +#include "pinmap.h" + +#include "sevenseg.pio.h" + +extern PIO pio; +static uint sm; +static uint16_t row_vals[8] = { 0 }; + +static const int dim = (1 << 3); + +static void txnfull_isr(void) +{ + static int line = 0; + static int dim_ctr = 0; + while (!pio_sm_is_tx_fifo_full(pio, sm)) { + if (dim_ctr == dim) { + pio_sm_put(pio, sm, (0x1000000lu << line) | (((uint32_t)row_vals[line]) << 8)); + } else { + pio_sm_put(pio, sm, (0x1000000lu << line)); + } + if (++line >= 8) { + if (dim_ctr++ >= dim) + dim_ctr = 0; + line = 0; + } + } +} + +static const uint8_t digit_row_map[NUM_DIGITS] = { + 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71, 0x00, 0x40, +}; + +void sevenseg_set_digit(int index, enum sevenseg_digits digit) +{ + int dig_idx = (int)digit & 0xff; + for (int i = 0; i < 7; ++i) { + if (digit_row_map[dig_idx] & (1 << i)) { + row_vals[i] |= (1 << index); + } else { + row_vals[i] &= ~(1 << index); + } + } + if (digit & LED_DECIMAL) { + row_vals[7] |= (1 << index); + } else { + row_vals[7] &= ~(1 << index); + } +} + +void sevenseg_init(void) +{ + uint offset = pio_add_program(pio, &sevenseg_program); + sm = pio_claim_unused_sm(pio, true); + pio_sm_config c = sevenseg_program_get_default_config(offset); + sm_config_set_out_pins(&c, SEVENSEG_SDO_PIN, 1); + sm_config_set_sideset_pins(&c, SEVENSEG_SCLK_PIN); + sm_config_set_out_shift(&c, false, false, 24); + sm_config_set_clkdiv(&c, 245.28f); // 20 kHz refresh cycle, 53 pio instrs/cycle + pio_sm_set_pindirs_with_mask(pio, sm, (1u << SEVENSEG_SDO_PIN) | (1u << SEVENSEG_SCLK_PIN) | (1u << SEVENSEG_FCLK_PIN) | (1u << SEVENSEG_OE_PIN), + (1u << SEVENSEG_SDO_PIN) | (1u << SEVENSEG_SCLK_PIN) | (1u << SEVENSEG_FCLK_PIN) | (1u << SEVENSEG_OE_PIN)); + pio_gpio_init(pio, SEVENSEG_SDO_PIN); + pio_gpio_init(pio, SEVENSEG_SCLK_PIN); + pio_gpio_init(pio, SEVENSEG_FCLK_PIN); + pio_gpio_init(pio, SEVENSEG_OE_PIN); + pio_set_irq0_source_enabled(pio, PIO_INTR_SM0_TXNFULL_LSB+sm, true); + irq_set_exclusive_handler(PIO0_IRQ_0, &txnfull_isr); + irq_set_enabled(PIO0_IRQ_0, true); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} diff --git a/src/sevenseg.h b/src/sevenseg.h new file mode 100644 index 0000000..33846ca --- /dev/null +++ b/src/sevenseg.h @@ -0,0 +1,28 @@ +#pragma once + +enum sevenseg_digits { + LED_ZERO = 0, + LED_ONE, + LED_TWO, + LED_THREE, + LED_FOUR, + LED_FIVE, + LED_SIX, + LED_SEVEN, + LED_EIGHT, + LED_NINE, + LED_A, + LED_B, + LED_C, + LED_D, + LED_E, + LED_F, + LED_BLANK, + LED_MINUS, + NUM_DIGITS, + LED_DECIMAL = 0x100, +}; + +void sevenseg_set_digit(int index, enum sevenseg_digits digit); + +void sevenseg_init(void); diff --git a/src/sevenseg.pio b/src/sevenseg.pio new file mode 100644 index 0000000..5337d82 --- /dev/null +++ b/src/sevenseg.pio @@ -0,0 +1,12 @@ +.program sevenseg +.side_set 3 + +set x,0 side 0 +.wrap_target +pull noblock side 0 +loop: +out pins, 1 side 0 +jmp !OSRE loop side 1 +nop side 6 [2] +nop side 4 +.wrap diff --git a/src/shift_in.pio b/src/shift_in.pio new file mode 100644 index 0000000..3c549ff --- /dev/null +++ b/src/shift_in.pio @@ -0,0 +1,10 @@ +.program shiftin +.side_set 2 + +.wrap_target +set x, 23 side 1 ;; load, clk high +loop: +in pins, 1 side 2 ;; shift, clk low +jmp x-- loop side 3 ;; shift, clk high +push side 3 ;; shift, clk high +.wrap diff --git a/src/statusleds.c b/src/statusleds.c new file mode 100644 index 0000000..42f401f --- /dev/null +++ b/src/statusleds.c @@ -0,0 +1,55 @@ +#include "statusleds.h" + +#include "pinmap.h" +#include +#include + +#include "statusleds.pio.h" + +extern PIO pio; +static uint sm_leds; + +static const int led_dim = (1 << 2); +static uint16_t led_val = 0xffff; + +static void txnfull_isr(void) +{ + static int dim_ctr = 0; + while (!pio_sm_is_tx_fifo_full(pio, sm_leds)) { + if (dim_ctr == led_dim) + pio_sm_put(pio, sm_leds, ((uint32_t)led_val)<<16); + else + pio_sm_put(pio, sm_leds, 0); + dim_ctr = dim_ctr >= led_dim ? 0 : dim_ctr + 1; + } +} + +void statusleds_set(uint16_t val) +{ + led_val = val; +} + +void statusleds_init(void) +{ + uint offset_leds = pio_add_program(pio, &statusleds_program); + sm_leds = pio_claim_unused_sm(pio, true); + pio_sm_config c_leds = statusleds_program_get_default_config(offset_leds); + sm_config_set_out_pins(&c_leds, LEDS_SDO_PIN, 1); + sm_config_set_sideset_pins(&c_leds, LEDS_SCLK_PIN); + sm_config_set_out_shift(&c_leds, false, false, 16); + sm_config_set_clkdiv(&c_leds, 394); // 10 kHz refresh cycle, 33 pio instrs/cycle + pio_sm_set_pindirs_with_mask(pio, sm_leds, (1u << LEDS_SDO_PIN) | (1u << LEDS_SCLK_PIN) | (1u << LEDS_FCLK_PIN), + (1u << LEDS_SDO_PIN) | (1u << LEDS_SCLK_PIN) | (1u << LEDS_FCLK_PIN)); + pio_gpio_init(pio, LEDS_SDO_PIN); + pio_gpio_init(pio, LEDS_SCLK_PIN); + pio_gpio_init(pio, LEDS_FCLK_PIN); + pio_set_irq1_source_enabled(pio, PIO_INTR_SM0_TXNFULL_LSB+sm_leds, true); + irq_add_shared_handler(PIO0_IRQ_1, &txnfull_isr, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); + pio_sm_init(pio, sm_leds, offset_leds, &c_leds); + pio_sm_set_enabled(pio, sm_leds, true); + pio_sm_put(pio, sm_leds, 0); + + gpio_init(LEDS_OE_PIN); + gpio_set_dir(LEDS_OE_PIN, GPIO_OUT); + gpio_put(LEDS_OE_PIN, false); +} diff --git a/src/statusleds.h b/src/statusleds.h new file mode 100644 index 0000000..85eb122 --- /dev/null +++ b/src/statusleds.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void statusleds_set(uint16_t val); + +void statusleds_init(void); diff --git a/src/ledtest.pio b/src/statusleds.pio similarity index 71% rename from src/ledtest.pio rename to src/statusleds.pio index c5d96b7..f25b4e5 100644 --- a/src/ledtest.pio +++ b/src/statusleds.pio @@ -1,4 +1,4 @@ -.program ledtest +.program statusleds .side_set 2 .wrap_target @@ -6,5 +6,5 @@ pull side 0 loop: out pins, 1 side 0 jmp !OSRE loop side 1 -irq 0 side 2 +nop side 2 .wrap