diff --git a/CMakeLists.txt b/CMakeLists.txt index 261f7b8..038ab61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ target_link_libraries(raspikeyer hardware_flash hardware_exception hardware_pwm + hardware_pio pico_flash pico_multicore hardware_adc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 294f223..b5630ba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,10 @@ target_include_directories(raspikeyer PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ) +pico_generate_pio_header(raspikeyer +${CMAKE_CURRENT_LIST_DIR}/tm1637.pio +) + target_sources(raspikeyer PRIVATE raspikeyer.cpp settings.cpp @@ -19,5 +23,6 @@ target_sources(raspikeyer PRIVATE morse.cpp winkeyer.cpp utils.cpp + tm1637.cpp usb_descriptors.c ) diff --git a/src/raspikeyer.cpp b/src/raspikeyer.cpp index b6b3ac7..cb5614f 100644 --- a/src/raspikeyer.cpp +++ b/src/raspikeyer.cpp @@ -3,6 +3,7 @@ #include "bsp/board.h" #include "hardware/adc.h" +#include "pico/binary_info/code.h" #include "pico/multicore.h" #include "pico/stdlib.h" #include "pico/util/queue.h" @@ -10,10 +11,11 @@ #include "keyer.h" #include "settings.h" -#include +#include "tm1637.h" #include "tusb_config.h" #include "utils.h" #include "winkeyer.h" +#include extern const uint LED_PIN = PICO_DEFAULT_LED_PIN; extern const uint LEFT_PADDLE_PIN = 14; @@ -22,6 +24,8 @@ extern const uint BUZZER_PIN = 18; extern const uint CW_OUT_PIN = 17; // extern const uint AUDIO_OUT_PIN = 16; extern const uint ADC_PIN = 26; +const uint DISPLAY_DIO_PIN = 20; +const uint DISPLAY_CLK_PIN = 21; queue_t keyerQueue; @@ -120,6 +124,13 @@ int main() WinKeyer winKeyer; + pio_hw_t *pio = pio0; + TM1637 display {DISPLAY_DIO_PIN, DISPLAY_CLK_PIN, pio}; + bi_decl(bi_1pin_with_name(DISPLAY_DIO_PIN, "[DIO] LED segments data pin")); + bi_decl(bi_1pin_with_name(DISPLAY_CLK_PIN, "[CLK] LED segments clock pin")); + display.displayIambicMode(settings.mode); + //display.displaySpeed(11); + while (true) { currentWpm = calcWPM(potiRead(), settings.wpmPotiMin, settings.wpmPotiMax); @@ -129,6 +140,8 @@ int main() queue_add_blocking(&keyerQueue, &keyerQueueData); printf("WPM has changed to: %d\n", currentWpm); lastWpm = currentWpm; + display.displaySpeed(currentWpm); + } tud_task(); // Internal PICO purposes diff --git a/src/tm1637.cpp b/src/tm1637.cpp new file mode 100644 index 0000000..ea1198a --- /dev/null +++ b/src/tm1637.cpp @@ -0,0 +1,150 @@ +#include "tm1637.h" + +#include + +TM1637::TM1637(uint8_t dio, uint8_t clk, PIO pio) : m_pio(pio) +{ + gpio_pull_up(dio); + gpio_pull_up(clk); + pio_gpio_init(pio, dio); + pio_gpio_init(pio, clk); + init(dio, clk); +} + +void TM1637::init(uint8_t dio, uint8_t clk) +{ + static constexpr size_t PULL_THRESHOLD = 32; + + state_machine = static_cast(pio_claim_unused_sm(m_pio, true)); + auto offset = pio_add_program(m_pio, &tm1637_program); + state_machine_config = tm1637_program_get_default_config(offset); + + sm_config_set_sideset_pins(&state_machine_config, clk); + uint32_t both_pins = (1 << clk) | (1 << dio); + pio_sm_set_pins_with_mask(m_pio, state_machine, both_pins, both_pins); + pio_sm_set_pindirs_with_mask(m_pio, state_machine, both_pins, both_pins); + + sm_config_set_out_pins(&state_machine_config, dio, 1); + sm_config_set_set_pins(&state_machine_config, dio, 1); + + sm_config_set_out_shift(&state_machine_config, true, false, PULL_THRESHOLD); + + setClockDivider(); + + pio_sm_init(m_pio, state_machine, offset, &state_machine_config); + pio_sm_set_enabled(m_pio, state_machine, true); +} + +void TM1637::setClockDivider() +{ + static constexpr size_t FREQUENCY = 45'000; + static constexpr size_t MAX_DIVIDER_VALUE = 65'536; + + uint32_t system_frequency = clock_get_hz(clk_sys); + float divider = static_cast(system_frequency) / FREQUENCY; + + if (divider > MAX_DIVIDER_VALUE) { + divider = MAX_DIVIDER_VALUE; + } else if (divider < 1) { + divider = 1; + } + + sm_config_set_clkdiv(&state_machine_config, divider); +} + +void TM1637::setColon([[maybe_unused]] bool on) +{ + static constexpr size_t CONTROL_DISPLAY = 0x80'00; + + // is_colon = true; + + if (on) + m_currentSegments |= CONTROL_DISPLAY; + else + m_currentSegments &= ~CONTROL_DISPLAY; + + send4Bytes(m_currentSegments); +} + +void TM1637::send4Bytes(uint32_t data) +{ + static constexpr size_t BIT_MASK = 0xFF'FF; + static constexpr size_t SHIFT_POSITIONS = 16; + + static uint32_t data_1; + static uint32_t data_2; + + data_1 = data & BIT_MASK; + data_2 = data >> SHIFT_POSITIONS; + + pio_sm_put_blocking(m_pio, state_machine, (data_1 << (2 * BYTE_SIZE)) + (WRITE_ADDRESS << BYTE_SIZE) + WRITE_MODE); + pio_sm_put_blocking(m_pio, state_machine, data_2 << (2 * BYTE_SIZE)); + + pio_sm_put_blocking(m_pio, state_machine, BRIGHTNESS_BASE + m_brightness); +} + +void TM1637::displayIambicMode(Mode iambic) +{ + static constexpr size_t LEFT_BYTE_MASK = 0xFF'FF'00'00; + + uint16_t segments {0}; + + switch (iambic) { + case Mode::IambicA: + segments = 0b0000'0000'0111'0111; + break; + case Mode::IambicB: + segments = 0b0000'0000'0111'1100; + break; + case Mode::Straight: + segments = 0b0000'0000'0110'1101; + break; + default: + segments = 0b0000'0000'0000'0000; + break; + } + + m_currentSegments = (m_currentSegments & LEFT_BYTE_MASK) + segments; + if (m_isColon) { + setColon(m_isColon); + } + send4Bytes(m_currentSegments); +} + +void TM1637::displaySpeed(uint8_t speed) +{ + static constexpr std::array DIGIT_TO_SEGMENTS { + 0x3F /* 0 */, 0x06 /* 1 */, 0x5B /* 2 */, 0x4F /* 3 */, 0x66 /* 4 */, 0x6D /* 5 */, 0x7D /* 6 */, 0x07 /* 7 */, + 0x7F /* 8 */, 0x6F /* 9 */, 0x77 /* A */, 0x7C /* B */, 0x39 /* C */, 0x5E /* D */, 0x79 /* E */, 0x71 /* F */}; + + static constexpr size_t RIGHT_BYTE_MASK = 0x00'00'FF'FF; + + uint8_t speed_orig = speed; + auto base = 10; + auto max_one_digit = base - 1; + uint32_t segments {0}; + uint32_t temp_segments {0}; + + if (speed <= max_one_digit) { + segments = DIGIT_TO_SEGMENTS.at(speed); + } + + else { + while (speed != 0) { + temp_segments = DIGIT_TO_SEGMENTS.at(speed % base); + speed /= base; + segments = temp_segments + (segments << BYTE_SIZE); + } + } + + auto num_div = speed_orig / base; + if (num_div == 0) { + segments = segments << BYTE_SIZE; + } + + m_currentSegments = (m_currentSegments & RIGHT_BYTE_MASK) + (segments << (2 * BYTE_SIZE)); + if (m_isColon) { + setColon(m_isColon); + } + send4Bytes(m_currentSegments); +} \ No newline at end of file diff --git a/src/tm1637.h b/src/tm1637.h new file mode 100644 index 0000000..d824f68 --- /dev/null +++ b/src/tm1637.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +#include "settings.h" + +class TM1637 +{ + public: + TM1637(uint8_t dio, uint8_t clk, PIO pio); + void setColon(bool on); + void displayIambicMode(Mode iambic); + void displaySpeed(uint8_t speed); + + private: + void init(uint8_t dio, uint8_t clk); + void setClockDivider(); + void send4Bytes(uint32_t data); + + PIO m_pio {}; + uint8_t state_machine {}; + pio_sm_config state_machine_config {}; + uint8_t m_brightness {0}; + uint32_t m_currentSegments {0}; + bool m_isColon {false}; + static constexpr uint8_t BYTE_SIZE = 8; + static constexpr uint8_t WRITE_MODE = 0x40; + static constexpr uint8_t WRITE_ADDRESS = 0xC0; + static constexpr uint8_t BRIGHTNESS_BASE = 0x88; +}; \ No newline at end of file diff --git a/src/tm1637.pio b/src/tm1637.pio new file mode 100644 index 0000000..9aec3aa --- /dev/null +++ b/src/tm1637.pio @@ -0,0 +1,56 @@ +.program tm1637 +.side_set 1 opt + + set pins, 1 side 1 + +START: + pull block + +START_COND: + set x, 7 + set pins, 1 side 1 [7] ; set DIO (pins) and CLK (side) to 1. delay 8 PIO cycles + set pins, 0 + set pins, 0 side 0 + +BIT_SHIFT_LOOP: + out pins, 1 side 0 + nop side 1 + jmp x-- BIT_SHIFT_LOOP side 0 + +ACK_COND: + set pins, 0 side 0 + nop side 1 + nop side 0 [1] + +STOP_COND: + set pins, 0 side 1 + set pins, 1 side 1 + + jmp !OSRE START_COND_SEND_DIGIT + jmp START + +START_COND_SEND_DIGIT: + set pins, 1 side 1 [7] + set pins, 0 + set pins, 0 side 0 + +SEND_CMD_AND_DATA: + set x, 7 + +BIT_SHIFT_LOOP_SEND_DIGIT: + out pins, 1 side 0 + nop side 1 + jmp x-- BIT_SHIFT_LOOP_SEND_DIGIT side 0 + +ACK_COND_SEND_DIGIT: + set pins, 0 side 0 + set y, 1 side 1 + nop side 0 [1] + jmp !OSRE SEND_CMD_AND_DATA + +STOP_COND_SEND_DIGIT: + set pins, 0 side 1 + set pins, 1 side 1 + + IN y, 32; + PUSH noblock; \ No newline at end of file