diff --git a/firmware/Application/ButtonManager.h b/firmware/Application/ButtonManager.h index 80ab409..86ad638 100644 --- a/firmware/Application/ButtonManager.h +++ b/firmware/Application/ButtonManager.h @@ -54,9 +54,9 @@ public: static ButtonManager *m_instance; enum Button { - UP = 0, + DOWN = 0, MID, - DOWN, + UP, Count }; diff --git a/firmware/Application/ScreenManager.cpp b/firmware/Application/ScreenManager.cpp index f60feff..ff4db28 100644 --- a/firmware/Application/ScreenManager.cpp +++ b/firmware/Application/ScreenManager.cpp @@ -27,10 +27,13 @@ using BSP::Schedule::Task; using BSP::Schedule::NextTime; using BSP::ReturnCode; +using BSP::ButtonManager; +using Button = ButtonManager::Button; +using ButtonState = ButtonManager::ButtonState; ScreenManager::ScreenManager(BSP::Schedule::TaskScheduler &scheduler, BSP::DisplayDriver &display, - BSP::ButtonManager &buttons) + ButtonManager &buttons) : m_scheduler(scheduler) , m_screen_stack{nullptr} , m_screen_stack_depth(0) @@ -43,23 +46,23 @@ ScreenManager::ScreenManager(BSP::Schedule::TaskScheduler &scheduler, ReturnCode ScreenManager::init() { m_buttons.set_callback( - BSP::ButtonManager::Button::UP, - [this](BSP::ButtonManager::ButtonState state) { - if (state == BSP::ButtonManager::ButtonState::PRESSED) { + Button::UP, + [this](ButtonState state) { + if (state == ButtonState::PRESSED) { current_screen()->notify_up_button(); }}); m_buttons.set_callback( - BSP::ButtonManager::Button::MID, - [this](BSP::ButtonManager::ButtonState state) { - if (state == BSP::ButtonManager::ButtonState::PRESSED) { + Button::MID, + [this](ButtonState state) { + if (state == ButtonState::PRESSED) { return current_screen()->notify_middle_button(); }}); m_buttons.set_callback( - BSP::ButtonManager::Button::DOWN, - [this](BSP::ButtonManager::ButtonState state) { - if (state == BSP::ButtonManager::ButtonState::PRESSED) { + Button::DOWN, + [this](ButtonState state) { + if (state == ButtonState::PRESSED) { current_screen()->notify_down_button(); }}); diff --git a/firmware/Application/main.cpp b/firmware/Application/main.cpp index cc57fc4..38cba53 100644 --- a/firmware/Application/main.cpp +++ b/firmware/Application/main.cpp @@ -40,47 +40,47 @@ #include "Mcu.h" -using BSP::Time; +using namespace BSP; // GPIOs -static BSP::GpioDriver g_gpioa(GPIOA); +static GpioDriver g_gpioa(GPIOA); -static BSP::GpioPin g_dbg0(g_gpioa, 3); -static BSP::GpioPin g_dbg1(g_gpioa, 6); +static GpioPin g_dbg0(g_gpioa, 3); +static GpioPin g_dbg1(g_gpioa, 6); -static BSP::GpioPin g_tx(g_gpioa, 9); -static BSP::GpioPin g_rx(g_gpioa, 10); +static GpioPin g_tx(g_gpioa, 9); +static GpioPin g_rx(g_gpioa, 10); -static BSP::GpioPin g_btn_down(g_gpioa, 0); -static BSP::GpioPin g_btn_mid(g_gpioa, 1); -static BSP::GpioPin g_btn_up(g_gpioa, 2); +static GpioPin g_btn_down(g_gpioa, 0); +static GpioPin g_btn_mid(g_gpioa, 1); +static GpioPin g_btn_up(g_gpioa, 2); -static BSP::GpioPin g_nss(g_gpioa, 4); -static BSP::GpioPin g_sck(g_gpioa, 5); -static BSP::GpioPin g_mosi(g_gpioa, 12); -static BSP::GpioPin g_extcomm(g_gpioa, 7); +static GpioPin g_nss(g_gpioa, 4); +static GpioPin g_sck(g_gpioa, 5); +static GpioPin g_mosi(g_gpioa, 12); +static GpioPin g_extcomm(g_gpioa, 7); // Scheduler and Tasks -static BSP::Schedule::LowPowerTaskScheduler<5> g_sched; -static BSP::SpiDriver g_spi(g_sched, g_nss); -static BSP::DisplayDriver g_display(g_sched, g_spi); -static BSP::LptimPwm g_lptim_pwm(LPTIM1); -static BSP::ButtonManager g_btn_manager( +static Schedule::LowPowerTaskScheduler<5> g_sched; +static SpiDriver g_spi(g_sched, g_nss); +static DisplayDriver g_display(g_sched, g_spi); +static LptimPwm g_lptim_pwm(LPTIM1); +static ButtonManager g_btn_mgr( g_sched, g_btn_up, g_btn_mid, g_btn_down, Time::millis(200)); // Screens- contexts for the display -static ScreenManager g_screen_manager(g_sched, g_display, g_btn_manager); -static SetTimeScreen g_set_time_screen(g_display, g_screen_manager); -static SetTimeScreen g_set_date_screen(g_display, g_screen_manager); -static StopwatchScreen g_stopwatch_screen(g_display, g_screen_manager); +static ScreenManager g_screen_mgr(g_sched, g_display, g_btn_mgr); +static SetTimeScreen g_set_time_screen(g_display, g_screen_mgr); +static SetTimeScreen g_set_date_screen(g_display, g_screen_mgr); +static StopwatchScreen g_stopwatch_screen(g_display, g_screen_mgr); static MenuScreen g_set_face_screen(g_display, - g_screen_manager, + g_screen_mgr, "Face", std::initializer_list()); static MenuScreen g_settings_menu_screen(g_display, - g_screen_manager, + g_screen_mgr, "Settings", std::initializer_list( { @@ -89,45 +89,45 @@ static MenuScreen g_settings_menu_screen(g_display, MenuScreenItem("Set Face", g_set_face_screen) })); static MenuScreen g_apps_menu_screen(g_display, - g_screen_manager, + g_screen_mgr, "Apps", std::initializer_list({MenuScreenItem("Stopwatch", g_stopwatch_screen)})); static MenuScreen g_main_menu_screen(g_display, - g_screen_manager, + g_screen_mgr, "Main Menu", std::initializer_list( { MenuScreenItem("Apps", g_apps_menu_screen), MenuScreenItem("Settings", g_settings_menu_screen) })); -static AnalogTimeScreen g_analog_time_screen(g_display, g_screen_manager, g_main_menu_screen); -static BigDigitalTimeScreen g_digital_time_screen(g_display, g_screen_manager, g_main_menu_screen); +static AnalogTimeScreen g_analog_time_screen(g_display, g_screen_mgr, g_main_menu_screen); +static BigDigitalTimeScreen g_digital_time_screen(g_display, g_screen_mgr, g_main_menu_screen); [[noreturn]] void main() { // Set up the system clock - BSP::RtcDriver::init(); - BSP::SystemTimer::set_timer(BSP::RtcDriver::get_system_timer()); - BSP::LowPower::init(); + RtcDriver::init(); + SystemTimer::set_timer(RtcDriver::get_system_timer()); + LowPower::init(); // Initialize the tasks g_lptim_pwm.init(); g_spi.init(); - g_btn_manager.init(); + g_btn_mgr.init(); g_display.init(); - g_screen_manager.init(); - g_screen_manager.set_root_screen(g_analog_time_screen); + g_screen_mgr.init(); + g_screen_mgr.set_root_screen(g_analog_time_screen); g_set_face_screen.add_item(MenuScreenItem("Analog", - []() { g_screen_manager.set_root_screen(g_analog_time_screen); })); + []() { g_screen_mgr.set_root_screen(g_analog_time_screen); })); g_set_face_screen.add_item(MenuScreenItem("Digital", - []() { g_screen_manager.set_root_screen(g_digital_time_screen); })); + []() { g_screen_mgr.set_root_screen(g_digital_time_screen); })); // Enqueue each of the tasks - BSP::Schedule::NextTime asap = BSP::Schedule::NextTime::asap(); + Schedule::NextTime asap = Schedule::NextTime::asap(); g_sched.add_task(g_spi, asap); - g_sched.add_task(g_btn_manager, asap); + g_sched.add_task(g_btn_mgr, asap); g_sched.add_task(g_display, asap); - g_sched.add_task(g_screen_manager, asap); + g_sched.add_task(g_screen_mgr, asap); // And we're off! This will never return g_sched.run(); diff --git a/firmware/Bsp/Drivers/GpioDriver.cpp b/firmware/Bsp/Drivers/GpioDriver.cpp index 44a3671..08683db 100644 --- a/firmware/Bsp/Drivers/GpioDriver.cpp +++ b/firmware/Bsp/Drivers/GpioDriver.cpp @@ -83,7 +83,7 @@ namespace BSP { moder_value = 2; break; } - SET_STRIDE_TO(m_gpio->MODER, 2, index, moder_value); + SET_STRIDE_TO(m_gpio->MODER, 2, index, moder_value); } void GpioDriver::set_pin_pupdr(uint32_t index, input_pull_t pull_mode) { diff --git a/firmware/Makefile b/firmware/Makefile index d04eb12..1886520 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -83,7 +83,7 @@ S_SOURCES := $(call find_important, $(SOURCEDIR), '*.s') SPP_SOURCES := Bsp/Mcu/$(DEVICE_TYPE).S SOURCES = $(C_SOURCES) $(S_SOURCES) $(SPP_SOURCES) $(CPP_SOURCES) -APPS := ./Application/main ./Test/pass ./Test/fail ./Test/timeout ./Test/clock ./Test/stop ./Test/no_start ./Test/lptim ./Test/set_time ./Test/periodic_alarms ./Test/wakeup_irq +APPS := ./Application/main ./Test/pass ./Test/fail ./Test/timeout ./Test/clock ./Test/stop ./Test/no_start ./Test/lptim ./Test/set_time ./Test/periodic_alarms ./Test/wakeup_irq ./Test/button APP_ELFS = $(addsuffix .elf, $(APPS)) APP_MAPS = $(addsuffix .map, $(APPS)) APP_BINS = $(addsuffix .bin, $(APPS)) @@ -223,22 +223,22 @@ $(FONT_GEN_DIR)/large_digits.h $(FONT_GEN_DIR)/large_digits.c: Gen/fixedfont-to- # STM32FLASH_DEVICE = /dev/ttyUSB0 +FLASH_BIN ?= $(OUTPUT_BIN) .PHONY: flash flash: $(OUTPUT_BIN) @echo "FLASH $(OUTPUT_BIN)" - $(STM32_PROG) --connect port=SWD reset=Hwrst -w $(OUTPUT_BIN) 0x8000000 -v --go + $(STM32_PROG) --connect port=SWD reset=Hwrst -w $(FLASH_BIN) 0x8000000 -v --go .PHONY: jlink jlink: $(OUTPUT_BIN) @echo "FLASH $(OUTPUT_BIN)" - JLinkExe -device $$(echo $(DEVICE_TYPE) | tr '[:lower:]' '[:upper:]') -if SWD \ - -speed auto -autoconnect 1 -CommanderScript cmd.jlink - + ./jlink.sh $(DEVICE_TYPE) $(FLASH_BIN) .PHONY: clean clean: - rm -f $(OBJS) $(OUTPUT_BIN) $(OUTPUT_ELF) $(FONT_C_FILES) $(FONT_H_FILES) $(OUTPUT_MAP) $(addsuffix .su,$(basename $(OBJS))) + rm -f $(OBJS) $(OUTPUT_BIN) $(OUTPUT_ELF) $(FONT_C_FILES) $(FONT_H_FILES) $(OUTPUT_MAP) $(APPS) $(APP_ELFS) $(APP_MAPS) $(APP_BINS) $(APP_OBJS) $(addsuffix .su,$(basename $(ALL_OBJS))) + # Please do not delete my files. .SECONDARY: $(ALL_OBJS) $(APP_ELFS) diff --git a/firmware/Test/button.cpp b/firmware/Test/button.cpp new file mode 100644 index 0000000..61dd36a --- /dev/null +++ b/firmware/Test/button.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 Max Regan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Bsp/Drivers/GpioDriver.h" +#include "Bsp/Drivers/RtcDriver.h" +#include "Bsp/Drivers/UsartDriver.h" +#include "Bsp/LowPowerTaskScheduler.h" +#include "Bsp/macros.h" +#include "Application/ButtonManager.h" + +#include "printf.h" +#include "test.h" + +#include "Mcu.h" + +using namespace BSP; + +using Button = ButtonManager::Button; +using ButtonState = ButtonManager::ButtonState; + +static Schedule::LowPowerTaskScheduler<5> g_sched; +static GpioDriver g_gpioa(GPIOA); +#if defined(BOARD_WATCH) +static UsartDriver g_test_uart(USART2, g_sched); +static GpioPin g_tx_pin(g_gpioa, 9); +#elif defined(BOARD_DEVBOARD) +static UsartDriver g_test_uart(USART1, g_sched); +#endif + +static GpioPin g_btn_down(g_gpioa, 0); +static GpioPin g_btn_mid(g_gpioa, 1); +static GpioPin g_btn_up(g_gpioa, 2); + +static ButtonManager g_btn_mgr(g_sched, + g_btn_down, + g_btn_mid, + g_btn_up, + Time::millis(50)); + +class IdleTask : public BSP::Schedule::Task { +public: + IdleTask() + {} + + BSP::Schedule::NextTime execute() override { + return BSP::Schedule::NextTime::asap(); + } +}; + +static IdleTask g_idle; + +[[noreturn]] void main() { + + g_gpioa.enable(); + +#if defined(BOARD_WATCH) + g_tx_pin.configure_alternate_function(4); +#endif + + g_test_uart.init(); + g_test_uart.tx_blocking(test_start_text); + + RtcDriver::init(); + SystemTimer::set_timer(RtcDriver::get_system_timer()); + LowPower::init(); + + g_btn_down.configure_input(GpioDriver::input_pull_t::PULL_UP); + g_btn_mid.configure_input(GpioDriver::input_pull_t::PULL_UP); + g_btn_up.configure_input(GpioDriver::input_pull_t::PULL_UP); + + g_btn_mgr.init(); + + ButtonManager::ChangeCallback callback = + [&](ButtonState state) { + g_test_uart.tx_blocking("up:"); + if (state == ButtonState::PRESSED) { + g_test_uart.tx_blocking("pressed\r\n"); + } else { + g_test_uart.tx_blocking("released\r\n"); + } + }; + + g_btn_mgr.set_callback(Button::UP, callback); + g_test_uart.tx_blocking("Waiting for button press...\r\n"); + + Schedule::NextTime asap = Schedule::NextTime::asap(); + g_sched.add_task(g_test_uart, asap); + g_sched.add_task(g_btn_mgr, asap); + g_sched.add_task(g_idle, asap); + g_sched.run(); + + TEST_SPIN(); +} diff --git a/firmware/cmd.jlink b/firmware/cmd.jlink deleted file mode 100644 index 215e5e4..0000000 --- a/firmware/cmd.jlink +++ /dev/null @@ -1,6 +0,0 @@ -exitonerror 1 -h -r -loadbin Application/main.bin 0x8000000 -g -q diff --git a/firmware/jlink.sh b/firmware/jlink.sh new file mode 100755 index 0000000..f6caf46 --- /dev/null +++ b/firmware/jlink.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright (C) 2020 Max Regan + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +set -e + +DEVICE_TYPE=$1 +APP_BIN="$2" +ADDR=0x8000000 + +FLASH_SCRIPT="\ +exitonerror 1 +h +r +loadbin ${APP_BIN} ${ADDR} +g +q" + +flash_script="$(mktemp)" +echo "$FLASH_SCRIPT" > "$flash_script" + +JLinkExe -device "$DEVICE_TYPE" -if SWD \ + -speed auto -autoconnect 1 -CommanderScript "$flash_script" + +rm "$flash_script" diff --git a/test/src/tr_test/test.py b/test/src/tr_test/test.py index 774f1e9..f1d4635 100755 --- a/test/src/tr_test/test.py +++ b/test/src/tr_test/test.py @@ -150,7 +150,7 @@ def test_periodic_alarms(context_factory, logger): def test_clock(context_factory, logger): serial_dev, jlink = context_factory("Test/clock.bin") EXPECTED_RUNTIME = 10 - TOLERANCE = 0.1 + TOLERANCE = 0.2 serial_dev.timeout = EXPECTED_RUNTIME * 1.2 @@ -297,6 +297,52 @@ def test_lptim(context_factory, logger): assert max_f < 51 +def test_button_slow(context_factory, logger): + serial_dev, jlink = context_factory("Test/button.bin") + serial_dev.timeout = 0.3 + ASSERTED = True + + serial_dev.dtr = not ASSERTED + while (line := serial_dev.readline()) is not None and len(line) > 0: + pass + + for _ in range(5): + serial_dev.dtr = ASSERTED + press_line = serial_dev.readline() + serial_dev.dtr = not ASSERTED + release_line = serial_dev.readline() + assert press_line == b"up:pressed\r\n" + assert release_line == b"up:released\r\n" + + +def test_button_fast(context_factory, logger): + serial_dev, jlink = context_factory("Test/button.bin") + serial_dev.timeout = 0.3 + ASSERTED = True + + serial_dev.dtr = not ASSERTED + time.sleep(0.3) + serial_dev.timeout = 0 + while (line := serial_dev.readline()) is not None and len(line) > 0: + pass + + for _ in range(25): + serial_dev.dtr = ASSERTED + time.sleep(0.075) + serial_dev.dtr = not ASSERTED + time.sleep(0.075) + + serial_dev.timeout = 0.3 + for _ in range(25): + press_line = serial_dev.readline() + release_line = serial_dev.readline() + assert press_line == b"up:pressed\r\n" + assert release_line == b"up:released\r\n" + + serial_dev.timeout = 0 + assert serial_dev.readline() == b"" + + def main(): pytest.main(sys.argv)