| // Copyright 2026 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include <chrono> |
| |
| #include "hardware/gpio.h" |
| #include "hardware/uart.h" |
| #include "pw_system/config.h" |
| #include "pw_system/system.h" |
| #include "pw_thread/attrs.h" |
| #include "pw_thread/detached_thread.h" |
| #include "pw_thread/sleep.h" |
| |
| namespace pw::hil { |
| namespace { |
| |
| inline constexpr ThreadPriority kThreadPriorityUartBlaster = ThreadPriority(); |
| |
| inline constexpr pw::ThreadAttrs kUartBlasterThread = |
| pw::ThreadAttrs() |
| .set_stack_size_bytes(4096) |
| .set_name("UartBlasterThread") |
| .set_priority(kThreadPriorityUartBlaster); |
| |
| static constexpr char ABC_LUT[26] = { |
| 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', |
| 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; |
| |
| } // namespace |
| } // namespace pw::hil |
| |
| namespace pw { |
| template <typename T> |
| class Debouncer { |
| public: |
| Debouncer(const T& initial_state, size_t threshold) |
| : state_(initial_state), |
| last_state_(initial_state), |
| threshold_(threshold), |
| count_(0) {} |
| |
| const T& state() const { return state_; } |
| |
| const T& Update(const T& new_state) { |
| if (new_state == state_) { |
| count_ = 0; |
| } else if (new_state == last_state_) { |
| if (count_ >= threshold_) { |
| state_ = last_state_; |
| count_ = 0; |
| } else { |
| count_++; |
| } |
| } else { |
| last_state_ = new_state; |
| count_ = 1; |
| } |
| return state_; |
| } |
| |
| private: |
| T state_; |
| T last_state_; |
| size_t threshold_; |
| size_t count_; |
| }; |
| } // namespace pw |
| |
| inline void pause(uint32_t delay_loops = 10) { |
| volatile uint32_t loop = 0; |
| for (loop = 0; loop < delay_loops;) { |
| loop = loop + 1; |
| } |
| } |
| |
| namespace pw::system { |
| |
| void UserAppInit() { |
| PW_CONSTINIT static ThreadContextFor<::pw::hil::kUartBlasterThread> |
| uart_blaster_thread; |
| |
| pw::Thread(uart_blaster_thread, []() { |
| static constexpr uint32_t TRIGGER_GPIO = 17; |
| static constexpr uint32_t LED_GPIO = 24; |
| |
| static constexpr uint32_t UART1_RX_PIN = 5; |
| static constexpr uint32_t UART1_TX_PIN = 4; |
| |
| static constexpr uint32_t LOOP_TIME_MS = 10; |
| static constexpr uint32_t LED_PERIOD_MS = 500; |
| |
| gpio_init(TRIGGER_GPIO); |
| gpio_set_dir(TRIGGER_GPIO, GPIO_IN); |
| gpio_init(LED_GPIO); |
| gpio_set_dir(LED_GPIO, GPIO_OUT); |
| |
| uart_init(uart1, 115200); |
| uart_set_format(uart1, 8, 1, UART_PARITY_NONE); |
| gpio_set_function(UART1_RX_PIN, |
| UART_FUNCSEL_NUM(uart1, UART1_RX_PIN)); // TEST_UART_RX |
| gpio_set_function(UART1_TX_PIN, |
| UART_FUNCSEL_NUM(uart1, UART1_TX_PIN)); // TEST_UART_TX |
| |
| pw::Debouncer<bool> debouncer(false, 10); |
| size_t loop_counts = 0; |
| while (true) { |
| loop_counts++; |
| debouncer.Update(gpio_get(TRIGGER_GPIO)); |
| |
| // While GPIO is high, blast UART |
| if (debouncer.state() == true) { |
| uart_putc(uart1, pw::hil::ABC_LUT[loop_counts % 26]); |
| } |
| |
| if (loop_counts % (LED_PERIOD_MS / LOOP_TIME_MS) == 0) { |
| gpio_put(LED_GPIO, !gpio_get(LED_GPIO)); |
| } |
| |
| pw::this_thread::sleep_for(std::chrono::milliseconds(LOOP_TIME_MS)); |
| } |
| }).detach(); |
| } |
| |
| } // namespace pw::system |