diff --git a/.gdbinit b/.gdbinit index bbeee75..def27ca 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,6 +1,5 @@ -tar rem :4242 file watch.elf -load watch.elf +tar rem :4242 set $PERIPH_BASE = (uint32_t)0x40000000U set $APBPERIPH_BASE = $PERIPH_BASE @@ -11,6 +10,8 @@ set $RCC = (RCC_TypeDef *)(0x40000000 + 0x00020000U + 0x1000U) set $RTC = (RTC_TypeDef *)(0x40000000 + 0x00002800U) set $PWR = (PWR_TypeDef *)(0x40000000 + 0x00007000U) set $EXTI = (EXTI_TypeDef *) ($APBPERIPH_BASE + 0x00010400U) +set $GPIOA = (GPIO_TypeDef *)($IOPPERIPH_BASE + 0x00000000U) +set $LPTIM1 = (LPTIM_TypeDef *)($APBPERIPH_BASE + 0x00007C00U) set history filename .gdb_history set history save on diff --git a/ButtonManager.cpp b/ButtonManager.cpp new file mode 100644 index 0000000..78edf38 --- /dev/null +++ b/ButtonManager.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2019 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 "ButtonManager.h" +#include "SystemTime.h" +#include "macros.h" +#include "stm32l0xx.h" + +namespace BSP { + +using Common::ReturnCode; +using Common::Schedule::NextTime; + +ButtonManager *ButtonManager::m_instance = nullptr; + +ReturnCode ButtonManager::init() +{ + for (auto &btn: m_buttons) { + /** Enable pin for input (pulled up) */ + CLR(GPIOA->MODER, 3u << (2 * btn.m_gpio_idx)); + CLR(GPIOA->OTYPER, 1u << btn.m_gpio_idx); + SET_TO(GPIOA->PUPDR, 3u << (2 * btn.m_gpio_idx), 1u << (2 * btn.m_gpio_idx)); + + // Unmask this interrupt + SET(EXTI->IMR, 1u << btn.m_gpio_idx); + // Enable this interrupt + SET(EXTI->EMR, 1u << btn.m_gpio_idx); + // Enable interrupt for rising edge + SET(EXTI->RTSR, 1u << btn.m_gpio_idx); + // Enable interrupt for falling edge + SET(EXTI->FTSR, 1u << btn.m_gpio_idx); + } + + NVIC_EnableIRQ(EXTI0_1_IRQn); + NVIC_EnableIRQ(EXTI2_3_IRQn); + NVIC_EnableIRQ(EXTI4_15_IRQn); + + NVIC_SetPriority(EXTI0_1_IRQn, 1); + NVIC_SetPriority(EXTI2_3_IRQn, 1); + NVIC_SetPriority(EXTI4_15_IRQn, 1); + + return ReturnCode::OK; +} + +NextTime ButtonManager::execute() +{ + // TODO: is this call too expensive for an interrupt handler? + Common::time_t systime; + BSP::SystemTimer::get_time(systime); + + + for (auto &btn: m_buttons) { + // Has the state changed? + if (btn.m_prev_call_state != btn.m_state) { + // Have we 'debounced' this state? + if (btn.m_state_change_ts + btn.m_debounce_time < systime) { + // It's tiiiiime + if (btn.m_callback != nullptr) { + btn.m_callback(btn.m_state); + } + btn.m_prev_call_state = btn.m_state; + } + } + } + + // TODO: Call less frequently, and let the buttonmanager re-add itself to the task list on interrupts + return NextTime::in(Common::Time::millis(1000)); +} + +void ButtonManager::set_callback(Button btn, ChangeCallback callback) +{ + if (btn == Button::Count) { + return; + } + + m_buttons[btn].m_callback = callback; +} + +void ButtonManager::remove_callback(Button btn) +{ + if (btn == Button::Count) { + return; + } + + m_buttons[btn].m_callback = nop_callback; +} + +void ButtonManager::irq() +{ + uint32_t idr = GPIOA->IDR; + Common::time_t systime; + + // TODO: is this call too expensive for an interrupt handler? + BSP::SystemTimer::get_time(systime); + + if (!m_instance) { + return; + } + + for (auto &btn: m_instance->m_buttons) { + bool is_pressed = !(idr & (1 << btn.m_gpio_idx)); + + // Check if the button state has changed, and timestamp it + if (is_pressed && (btn.m_state != ButtonState::PRESSED)) { + btn.m_state = ButtonState::PRESSED; + btn.m_state_change_ts = systime; + } else if (!is_pressed && (btn.m_state == ButtonState::PRESSED)) { + btn.m_state = ButtonState::NOT_PRESSED; + btn.m_state_change_ts = systime; + } + + // Clear the event + SET(EXTI->PR, 1u << btn.m_gpio_idx); + } +} + +extern "C" void EXTI_1_0_IRQHandler() { + ButtonManager::irq(); +} + +extern "C" void EXTI_3_2_IRQHandler() { + ButtonManager::irq(); +} + +extern "C" void EXTI_15_4_IRQHandler() { + ButtonManager::irq(); +} + +} diff --git a/ButtonManager.h b/ButtonManager.h new file mode 100644 index 0000000..d017f65 --- /dev/null +++ b/ButtonManager.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "Task.h" +#include "ReturnCode.h" + +namespace BSP { + +class ButtonManager : public Common::Schedule::Task { +public: + ButtonManager(uint8_t up_gpio_idx, + uint8_t mid_gpio_idx, + uint8_t down_gpio_idx, + Common::time_t debounce_time) + : m_buttons + { + button_state(up_gpio_idx, debounce_time, ButtonManager::nop_callback), + button_state(mid_gpio_idx, debounce_time, ButtonManager::nop_callback), + button_state(down_gpio_idx, debounce_time, ButtonManager::nop_callback), + } + { + m_instance = this; + } + + ButtonManager() = delete; + + // TODO: Make a real singleton instead + static ButtonManager *m_instance; + + enum Button { + UP = 0, + MID, + DOWN, + Count + }; + + enum class ButtonState { + PRESSED, + NOT_PRESSED + }; + + typedef void (*ChangeCallback)(ButtonState); + + Common::Schedule::NextTime execute() override; + + Common::ReturnCode init(); + void set_callback(Button btn, ChangeCallback callback); + void remove_callback(Button btn); + + static void irq(); + +private: + + struct button_state { + + button_state(uint8_t gpio_index, + Common::time_t debounce_time, + ChangeCallback callback) + : m_gpio_idx(gpio_index) + , m_debounce_time(debounce_time) + , m_state(ButtonState::NOT_PRESSED) + , m_state_change_ts(0) + , m_callback(callback) + {} + + uint8_t const m_gpio_idx; + Common::time_t const m_debounce_time; + + ButtonState m_prev_call_state; /*IOPENR, RCC_IOPENR_IOPBEN); - /** Enable pin P3 for output */ - SET_TO(GPIOB->MODER, - GPIO_MODER_MODE3, - GPIO_MODER_MODE3_0); - - CLR(GPIOB->OTYPER, GPIO_OTYPER_OT_3); - CLR(GPIOB->PUPDR, GPIO_PUPDR_PUPD3); - - return Common::ReturnCode::OK; - - - return ReturnCode::OK; + return Common::ReturnCode::OK; } NextTime DisplayDriver::execute() @@ -66,7 +53,7 @@ void DisplayDriver::buffer_init() for (size_t i = 0; i < ARRAY_SIZE(m_buffer.lines); i++) { struct display_line *line = &m_buffer.lines[i]; line->mode = 1; // Update display - line->line = i; + line->line = i + 1; // Line numbers start at 1 for (size_t j = 0; j < ARRAY_SIZE(line->data); j++) { line->data[j] = 0xFF; } @@ -126,21 +113,32 @@ void DisplayDriver::char_at(int *x_off, int y_off, char c, const struct font *fo return; } - // TODO: Don't hardcode this - int byte_cols = (g->cols / 8); + int byte_cols = g->cols / 8; if (g->cols & 7) { byte_cols++; } if (byte_cols & 1) { byte_cols++; } + + for (size_t x = 0; x < g->left; x++) { + for (size_t y = 0; y < g->rows; y++) { + set_bit(*x_off + x, y_off + y + font->size - g->top, 0); + } + } + + for (size_t x = g->left + g->cols; x < g->advance; x++) { + for (size_t y = 0; y < g->rows; y++) { + set_bit(*x_off + x, y_off + y + font->size - g->top, 0); + } + } + for (size_t x = 0; x < g->cols; x++) { for (size_t y = 0; y < g->rows; y++) { - int byte_x = x >> 3; + int byte_x = x / 8; int byte_y = y; uint8_t bit = (g->bitmap[byte_y * byte_cols + byte_x] >> (7 - (x & 7))) & 1; - /* 16 is font max height */ - set_bit(g->left + *x_off + x, y_off + y + 16 - g->top, bit); + set_bit(g->left + *x_off + x, y_off + y + font->size - g->top, bit); } } *x_off += g->advance; diff --git a/DisplayDriver.h b/DisplayDriver.h index 29614be..fa5040a 100644 --- a/DisplayDriver.h +++ b/DisplayDriver.h @@ -31,8 +31,6 @@ class DisplayDriver : public Common::Schedule::Task { public: DisplayDriver(Common::Schedule::TaskScheduler &scheduler, SpiDriver &spi); - static constexpr uint8_t DISPLAY_WIDTH = 144; - static constexpr uint8_t DISPLAY_HEIGHT = 168; /** @@ -51,11 +49,22 @@ public: void refresh(); void clear(); + inline uint32_t get_width() { + return DISPLAY_WIDTH; + } + + inline uint32_t get_height() { + return DISPLAY_HEIGHT; + } + private: void buffer_init(); void set_dirty(unsigned int y); const struct glyph *glyph_for_char(const struct font *font, char c); + static constexpr uint32_t DISPLAY_WIDTH = 144; + static constexpr uint32_t DISPLAY_HEIGHT = 168; + struct display_line { uint8_t mode; diff --git a/DisplayManager.h b/DisplayManager.h new file mode 100644 index 0000000..3d86ebb --- /dev/null +++ b/DisplayManager.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "macros.h" + +#include "DisplayDriver.h" +#include "ReturnCode.h" +#include "Task.h" + +class ScreenManager : public Common::Schedule::Task { +public: + + ScreenManager(BSP::DisplayDriver &display); + + Common::Schedule::NextTime execute(); + +private: + + void display_menu(); + + BSP::DisplayDriver &m_driver; + bool m_displayed; +}; diff --git a/DisplayScreen.h b/DisplayScreen.h new file mode 100644 index 0000000..1b9253d --- /dev/null +++ b/DisplayScreen.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "Task.h" + +class Screen : Common::Task::Schedule { +public: + virtual void init() = 0; + virtual void cleanup() = 0; +}; diff --git a/DisplayTimeTask.cpp b/DisplayTimeTask.cpp index dcd557d..ead4e12 100644 --- a/DisplayTimeTask.cpp +++ b/DisplayTimeTask.cpp @@ -20,38 +20,98 @@ */ #include "DisplayTimeTask.h" -#include "LowPower.h" - -#include "font-notomono-10.h" +#include "SystemTime.h" +#include "font-notomono-24.h" +#include "font-notomono-64.h" using Common::ReturnCode; using Common::Time; using Common::Schedule::NextTime; +static const font &font_large = font_notomono_64; +static const font &font_default = font_notomono_24; + DisplayTimeTask::DisplayTimeTask(BSP::DisplayDriver &driver) : m_driver(driver) - , m_y_pos(0) + , m_has_cleared(false) + , m_last_time() + , m_display_seconds(true) {} -ReturnCode DisplayTimeTask::init() { +static char get_char_for_digit(uint8_t bcd_digit) +{ + if (bcd_digit > 9) { + return '0'; + } + return bcd_digit + '0'; +} + + +ReturnCode DisplayTimeTask::init() +{ return ReturnCode::OK; } -NextTime DisplayTimeTask::execute() { - //static const char msg_str[] = "Hello world!"; +void DisplayTimeTask::display_number(uint32_t x, uint32_t y, uint32_t tens, uint32_t ones, const font &f) +{ + char time_str[3] = { 0 }; - int x = 20; + time_str[0] = get_char_for_digit(tens); + time_str[1] = get_char_for_digit(ones); + time_str[2] = '\0'; - m_driver.clear(); - m_driver.string_at(x, m_y_pos++, "Hello world!", &font_notomono_10); - m_driver.refresh(); + m_driver.string_at(x, y, time_str, &f); +} - if (m_y_pos > 160) { - m_y_pos = 0; +void DisplayTimeTask::display_time() +{ + uint32_t width = m_driver.get_width(); + uint32_t height = m_driver.get_height(); + + BSP::time_bcd time; + BSP::RtcDriver::get_time(time); + + if (!m_has_cleared) { + m_driver.clear(); } - BSP::LowPower::stop(); + int i = 0; - return NextTime::asap(); + if (m_last_time.hour_tens != time.hour_tens || + m_last_time.hour_ones != time.hour_ones || + !m_has_cleared) { + display_number(0, (i * font_large.size) + (i + 1) * 4, time.hour_tens, time.hour_ones, font_large); + } + + i++; + if (m_last_time.minute_tens != time.minute_tens || + m_last_time.minute_ones != time.minute_ones || + !m_has_cleared) { + display_number(0, (i * font_large.size) + (i + 1) * 4, time.minute_tens, time.minute_ones, font_large); + } + + i++; + if (m_display_seconds) { + if (m_last_time.second_tens != time.second_tens || + m_last_time.second_ones != time.second_ones || + !m_has_cleared) { + display_number(0, (i * font_large.size) + (i + 1) * 4, time.second_tens, time.second_ones, font_default); + } + } + + m_has_cleared = true; + m_last_time = time; + + m_driver.refresh(); +} + +NextTime DisplayTimeTask::execute() +{ + display_time(); + + Common::time_t now; + BSP::SystemTimer::get_time(now); + uint32_t next_secs = Time::to_seconds(now) + 1; + return NextTime::at(Time::seconds(next_secs)); } diff --git a/DisplayTimeTask.h b/DisplayTimeTask.h index 022adb1..106c2fa 100644 --- a/DisplayTimeTask.h +++ b/DisplayTimeTask.h @@ -26,6 +26,7 @@ #include "DisplayDriver.h" #include "ReturnCode.h" #include "Task.h" +#include "RtcDriver.h" class DisplayTimeTask : public Common::Schedule::Task { public: @@ -36,6 +37,14 @@ public: Common::Schedule::NextTime execute(); private: + + void display_time(); + void display_number(uint32_t x, uint32_t y, uint32_t tens, uint32_t ones, const font &f); + BSP::DisplayDriver &m_driver; - unsigned int m_y_pos; + bool m_has_cleared; + BSP::time_bcd m_last_time; + + const bool m_display_seconds; + }; diff --git a/LowPower.cpp b/LowPower.cpp index 6a68ff1..c30bb8b 100644 --- a/LowPower.cpp +++ b/LowPower.cpp @@ -43,25 +43,20 @@ ReturnCode LowPower::init() ReturnCode LowPower::sleep() { + // TODO: unimplemented + return ReturnCode::FAIL; } ReturnCode LowPower::stop() { - - /* Prepare to enter stop mode */ - SET(PWR->CR, PWR_CR_CWUF); // clear WUF + SET(PWR->CR, PWR_CR_CWUF); // clear wakeup flag while(PWR->CSR & PWR_CSR_WUF) {}; + CLR(PWR->CR, PWR_CR_PDDS); // Enter stop mode when the CPU enters deepsleep CLR(RCC->CFGR, RCC_CFGR_STOPWUCK); // MSI oscillator is wake-up from stop clock SET(SCB->SCR, SCB_SCR_SLEEPDEEP_Msk); // low-power mode = stop mode - // Common::time_t time0 = ~0; - // SystemTimer::get_time(time0); - __WFI(); // enter low-power mode - // Common::time_t time1 = ~0; - // SystemTimer::get_time(time1); - // Common::time_t timediff = time1 - time0; - // volatile Common::time_t timediff2 = timediff; + __WFI(); // enter low-power mode (Wake from interrupt) return ReturnCode::OK; } diff --git a/ConcreteTaskScheduler.h b/LowPowerTaskScheduler.h similarity index 86% rename from ConcreteTaskScheduler.h rename to LowPowerTaskScheduler.h index 9e5f2ed..28bdb62 100644 --- a/ConcreteTaskScheduler.h +++ b/LowPowerTaskScheduler.h @@ -26,14 +26,15 @@ #include "TaskScheduler.h" #include "SystemTime.h" #include "LowPower.h" +#include "RtcDriver.h" namespace Common { namespace Schedule { template -class ConcreteTaskScheduler : public TaskScheduler { +class LowPowerTaskScheduler : public TaskScheduler { public: - ConcreteTaskScheduler() : + LowPowerTaskScheduler() : m_tasks(), m_task_count(0), m_cycle_count(0) @@ -56,7 +57,7 @@ public: m_tasks[m_task_count++] = TaskEvent(task, time); } - // ~ConcreteTaskScheduler() {} + // ~LowPowerTaskScheduler() {} private: struct TaskEvent { @@ -92,33 +93,35 @@ private: bool task_died = false; /* Keep state for when the next task will execute. */ - // bool execed = false; - // Common::time_t next_time = ~0; + bool execed = false; + Common::time_t next_time = ~0; for (size_t i = 0; i < m_task_count; i++) { TaskEvent &event = m_tasks[i]; if (event.m_time.get_type() == ScheduleType::AT_TIME) { if (time >= event.m_time.get_time()) { - // execed = true; + execed = true; call_task(event); } else { - // next_time = MIN(next_time, event.m_time.get_time()); + next_time = MIN(next_time, event.m_time.get_time()); } } else if (event.m_time.get_type() == ScheduleType::NEVER) { task_died = true; } } - /* If nothing happened this cycle, and nothing will happen for - awhile, go to sleep */ - //if (!execed && (next_time - time > Time::millis(10))) { - //BSP::LowPower::stop(); - //} if (task_died) { remove_dead_tasks(); } + if (!execed && (next_time - time > Time::millis(2))) { + Common::ReturnCode rc = BSP::RtcDriver::set_wakeup_in(next_time - time); + if (rc == Common::ReturnCode::OK) { + BSP::LowPower::stop(); + } + } + m_cycle_count++; } diff --git a/LptimPwm.cpp b/LptimPwm.cpp new file mode 100644 index 0000000..0ee4eee --- /dev/null +++ b/LptimPwm.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 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 "LptimPwm.h" +#include "macros.h" + +namespace BSP { + +using Common::ReturnCode; + +LptimPwm::LptimPwm(LPTIM_TypeDef *lptim) + : m_lptim(lptim) +{} + + +void LptimPwm::init_lptim() +{ + /* Enable LPTIM in APB1 */ + SET(RCC->APB1ENR, + RCC_APB1ENR_LPTIM1EN); + + // Enable low-speed internal + RCC->CSR |= RCC_CSR_LSION; + while (!(RCC->CSR & RCC_CSR_LSIRDY)) {}; + + /*!< Set the LSE clock to be the source of the LPTIM */ + SET_TO(RCC->CCIPR, + RCC_CCIPR_LPTIM1SEL, + RCC_CCIPR_LPTIM1SEL_0); + + /** Write CR CFGR and IER while LPTIM is disabled (LPTIM_CR_ENABLE not yet set) */ + /*!< Disable Interrupts (not needed, this is the default */ + LPTIM1->IER = 0; + + /*!< Reset + * ENC (Disable encoder mode) + * TIMOUT (disable timeout mode) + * TRIGEN (Trigger count start with software only) + * PRELOAD (Update ARR and CMP registers immediately after write) + * CKSEL (LPTIM is not using an input clock) + * COUNTMODE (LPTIM counter updated on every clock pulse) + * TRGFLT (Do not debounce triggers) + */ + CLR(LPTIM1->CFGR, + LPTIM_CFGR_ENC | LPTIM_CFGR_TIMOUT | LPTIM_CFGR_TRIGEN | + LPTIM_CFGR_TRIGSEL | LPTIM_CFGR_PRELOAD | LPTIM_CFGR_CKSEL | + LPTIM_CFGR_COUNTMODE); + + /*!< Set + * PRESC (Set prescaler to 128. Using 32kHz LSE as input, this should + * correspond to 250Hz counting. + */ + CLR(LPTIM1->CFGR, LPTIM_CFGR_PRESC); + + SET(LPTIM1->CR, LPTIM_CR_ENABLE); + + /*!< Do not modify ARR and CMP until after ENABLE bit is set */ + /*!< Produce a 60Hz, signal with minimal "high" time. The display + only needs 2us of "high" time on EXTCOMM, and it draws a fair + amount of power. */ + LPTIM1->ARR = 0x4FF; + LPTIM1->CMP = 0x4FE; + while(!(LPTIM1->ISR & LPTIM_ISR_ARROK)) {} + while(!(LPTIM1->ISR & LPTIM_ISR_CMPOK)) {} + + /*!< Enable and start the timer */ + SET(LPTIM1->CR, LPTIM_CR_CNTSTRT); +} + + +ReturnCode LptimPwm::init() +{ + init_lptim(); + + /* Enable GPIO port A */ + SET(RCC->IOPENR, RCC_IOPENR_IOPAEN); + + /* Assign LPTIM1_OUT to PA7 */ + SET_TO(GPIOA->AFR[0], + GPIO_AFRL_AFRL7, + 1u << GPIO_AFRL_AFRL7_Pos); + + SET_TO(GPIOA->MODER, + GPIO_MODER_MODE7, + 2u << GPIO_MODER_MODE7_Pos); + + CLR(GPIOA->OTYPER, GPIO_OTYPER_OT_7); + CLR(GPIOA->PUPDR, GPIO_PUPDR_PUPD7); + + return ReturnCode::OK; +} + +} diff --git a/LptimPwm.h b/LptimPwm.h new file mode 100644 index 0000000..3fea77c --- /dev/null +++ b/LptimPwm.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include "ReturnCode.h" +#include "stm32l0xx.h" + +namespace BSP { + +class LptimPwm { + +public: + LptimPwm() = delete; + LptimPwm(LPTIM_TypeDef *lptim); + + Common::ReturnCode init(); + +private: + void init_lptim(); + const LPTIM_TypeDef *m_lptim; +}; + +} diff --git a/Makefile b/Makefile index e58e2fd..c068f63 100644 --- a/Makefile +++ b/Makefile @@ -24,14 +24,14 @@ # Tools # -TOOL_PREFIX ?= arm-eabi- +TOOL_PREFIX ?= arm-none-eabi- CC = $(TOOL_PREFIX)gcc CXX = $(TOOL_PREFIX)g++ CPP = $(TOOL_PREFIX)cpp AS = $(TOOL_PREFIX)as -LD = $(TOOL_PREFIX)gcc +LD = $(TOOL_PREFIX)g++ OBJCOPY = $(TOOL_PREFIX)objcopy - +STM32_PROG = STM32_Programmer.sh # # Device Variables # @@ -73,13 +73,15 @@ OUTPUT_ELF ?= $(OUTPUT_NAME).elf DEVICE_DEFINE = $(subst XX,xx,$(shell echo $(DEVICE_FAMILY) | tr '[:lower:]' '[:upper:]')) +CPU_FLAGS = -mthumb -mcpu=cortex-m0 -mfloat-abi=soft + # C pedantism CFLAGS = -Wall -Wextra -Wpedantic # Debug/optimization -CFLAGS += -ggdb -g3 +CFLAGS += -ggdb -g3 -Os CFLAGS += -fdata-sections -ffunction-sections # Architecture -CFLAGS += -mthumb -mcpu=cortex-m0plus +CFLAGS += $(CPU_FLAGS) CFLAGS += -ffreestanding # Defines CFLAGS += -D$(DEVICE_DEFINE) @@ -91,12 +93,14 @@ CFLAGS += -I./lib/fonts/ CXX_FLAGS = -std=c++14 -fno-exceptions -fno-rtti # Startup Definitions +ASFLAGS += $(CPU_FLAGS) ASFLAGS += -D__STARTUP_CLEAR_BSS ASFLAGS += -D__HEAP_SIZE=0 # No heap- let the linker decide it all -LDFLAGS += -lc -lstdc++ -nostdinc -lnosys -Wl,--gc-sections -Wl,--build-id=None -LDFLAGS += -Wl,--wrap=malloc -Wl,--wrap=free # Fail to compile if dynamic allocation is sneaking through -LDFLAGS += -mthumb -mcpu=cortex-m0plus +LDFLAGS += $(CPU_FLAGS) +LDFLAGS += -Wl,--gc-sections -Wl,--build-id=none -static +LDFLAGS += -Wl,--wrap=malloc -Wl,--wrap=free # Fail to link if dynamic allocation is sneaking through +LDFLAGS += -Wl,-print-memory-usage # # Default Target @@ -126,7 +130,7 @@ $(OUTPUT_BIN): $(OUTPUT_ELF) $(OUTPUT_ELF): $(LINKER_SCRIPT) $(OBJS) @echo "LD $@" - @$(LD) $(LDFLAGS) -T $(LINKER_SCRIPT) -o $(OUTPUT_ELF) $(OBJS) + $(LD) -T $(LINKER_SCRIPT) $(LDFLAGS) -o $(OUTPUT_ELF) $(OBJS) # # Utilities @@ -137,8 +141,7 @@ STM32FLASH_DEVICE = /dev/ttyUSB0 .PHONY: flash flash: $(OUTPUT_BIN) @echo "FLASH $(OUTPUT_BIN)" - @st-flash write $(OUTPUT_BIN) 0x8000000 - + $(STM32_PROG) --connect port=SWD reset=Hwrst -w $(OUTPUT_BIN) 0x8000000 -v --go .PHONY: clean clean: diff --git a/RtcDriver.cpp b/RtcDriver.cpp new file mode 100644 index 0000000..2cb7371 --- /dev/null +++ b/RtcDriver.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2019 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 "RtcDriver.h" +#include "macros.h" + +namespace BSP { + +using Common::ReturnCode; +using Common::time_t; + +RtcDriver::RtcSystemTimer RtcDriver::m_sys_timer; + +void RtcDriver::enable_rtc_write() +{ + /*WPR = 0xCA; + RTC->WPR = 0x53; +} + +void RtcDriver::disable_rtc_write() +{ + /*WPR = 0x00; +} + +void RtcDriver::enable_periodic_alarm() +{ + + SET(RTC->ALRMAR, RTC_ALRMAR_MSK4 | RTC_ALRMAR_MSK3 | RTC_ALRMAR_MSK2 | RTC_ALRMAR_MSK1); + + SET(RTC->ALRMASSR, RTC_ALRMASSR_MASKSS); + CLR(RTC->ALRMASSR, RTC_ALRMASSR_SS); + + SET(RTC->CR, RTC_CR_ALRAE); +} + +ReturnCode RtcDriver::init_hw() +{ + uint32_t temp = RCC->CSR; + + SET(RCC->CSR, RCC_CSR_RTCRST); + SET(RCC->APB1ENR, RCC_APB1ENR_PWREN); + SET(PWR->CR, PWR_CR_DBP); + + /*CSR & RCC_CSR_LSERDY)) { + // TODO: Does this help? + SET(temp, RCC_CSR_LSEON); + } + + SET_TO(temp, RCC_CSR_RTCSEL, RCC_CSR_RTCSEL_0); + SET(temp, RCC_CSR_RTCEN); + RCC->CSR = temp; + + while (!(RCC->CSR & RCC_CSR_LSERDY)) {} + + enable_rtc_write(); + + RTC->ISR = RTC_ISR_INIT; + while (!(RTC->ISR & RTC_ISR_INITF)) {} + + /*PRER, RTC_PRER_PREDIV_A, 0); + /*PRER, RTC_PRER_PREDIV_S, (LSE_CLOCK_FREQ - 1)); + + /*CR, RTC_CR_FMT); + + uint32_t time = 0; + SET(time, RTC_TR_PM); + SET_TO(time, RTC_TR_HT, 1 << RTC_TR_HT_Pos); + SET_TO(time, RTC_TR_HU, 2 << RTC_TR_HU_Pos); + SET_TO(time, RTC_TR_MNT, 5 << RTC_TR_MNT_Pos); + SET_TO(time, RTC_TR_MNU, 9 << RTC_TR_MNU_Pos); + SET_TO(time, RTC_TR_ST, 0 << RTC_TR_ST_Pos); + SET_TO(time, RTC_TR_SU, 0 << RTC_TR_SU_Pos); + RTC->TR = time; + + CLR(RTC->ISR, RTC_ISR_INIT); + + SET(EXTI->IMR, EXTI_IMR_IM20); + SET(EXTI->EMR, EXTI_EMR_EM20); + SET(EXTI->RTSR, EXTI_RTSR_RT20); + + // Enable Wakeup interrupts, we may/will use them later + SET(RTC->CR, RTC_CR_WUTIE); + NVIC_EnableIRQ(RTC_IRQn); + NVIC_SetPriority(RTC_IRQn, 0); + + enable_periodic_alarm(); + + disable_rtc_write(); + + return ReturnCode::OK; +} + +ReturnCode RtcDriver::get_time(time_bcd &tm_bcd) +{ + /*DR; + + uint32_t time = RTC->TR; + + tm_bcd.hour_tens = STM32_GET_FIELD(time, RTC_TR_HT); + tm_bcd.hour_ones = STM32_GET_FIELD(time, RTC_TR_HU); + + tm_bcd.minute_tens = STM32_GET_FIELD(time, RTC_TR_MNT); + tm_bcd.minute_ones = STM32_GET_FIELD(time, RTC_TR_MNU); + + tm_bcd.second_tens = STM32_GET_FIELD(time, RTC_TR_ST); + tm_bcd.second_ones = STM32_GET_FIELD(time, RTC_TR_SU); + + tm_bcd.pm = STM32_GET_FIELD(time, RTC_TR_PM); + + return ReturnCode::OK; +} + +ReturnCode RtcDriver::init() +{ + init_hw(); + + return ReturnCode::OK; +} + +ReturnCode RtcDriver::set_wakeup_in(Common::time_t wakeup_delay) +{ + /* 17.85 years, so + this is fine. */ + uint64_t delay_cycles = Common::Time::to_micros(wakeup_delay) * LSE_CLOCK_FREQ / Common::Time::MICROS_PER_SEC; + + enable_rtc_write(); + + /*CR & RTC_CR_WUTE) { + CLR(RTC->CR, RTC_CR_WUTE); + while (!(RTC->ISR & RTC_ISR_WUTWF)) {} + } + + uint32_t wucksel = 0; + + if (delay_cycles == 0) { + return ReturnCode::FAIL; + } else if (delay_cycles < 0x10000) { + delay_cycles /= 2; + wucksel = 3; + } else if (delay_cycles <= 0x20000) { + delay_cycles /= 4; + wucksel = 2; + } else if (delay_cycles <= 0x40000) { + delay_cycles /= 8; + wucksel = 1; + } else if (delay_cycles <= 0x80000) { + delay_cycles /= 16; + wucksel = 0; + } else { +#if 0 + // TODO: implement longer delays using ck_spre as clock source + // TODO: the datasheet text and block diagram disagree- is it using clock_spre or clock_apre? + wucksel = 4; + delay_cycles >>= async_prediv; // +#else + return ReturnCode::FAIL; +#endif + } + + SET_TO(RTC->WUTR, RTC_WUTR_WUT, delay_cycles - 1); + SET_TO(RTC->CR, RTC_CR_WUCKSEL, wucksel << RTC_CR_WUCKSEL_Pos); + + SET(RTC->CR, RTC_CR_WUTE); + + disable_rtc_write(); + + return ReturnCode::OK; +} + +Common::time_t RtcDriver::RtcSystemTimer::get_time() +{ + uint32_t new_secs, old_secs, ssr, subsecond; + do { + old_secs = m_seconds; + ssr = RTC->SSR & 0xFFFF; + new_secs = m_seconds; + } while (new_secs != old_secs); + + new_secs = new_secs * LSE_CLOCK_FREQ; + /** SSR is a countdown register */ + subsecond = (new_secs + LSE_CLOCK_FREQ - 1 - ssr) * Common::Time::MILLIS_PER_SEC / LSE_CLOCK_FREQ; + return Common::Time::millis(subsecond); +} + +void RtcDriver::RtcSystemTimer::increment_seconds() +{ + m_seconds++; +} + +void RtcDriver::increment_seconds() +{ + m_sys_timer.increment_seconds(); +} + +extern "C" void RTC_IRQHandler() +{ + // Clear the interrupt in the EXTI + SET(EXTI->PR, EXTI_PR_PIF20); + + if (RTC->ISR & RTC_ISR_ALRAF) { + RtcDriver::increment_seconds(); + + CLR(RTC->ISR, RTC_ISR_ALRAF); + + } + + if (RTC->ISR & RTC_ISR_WUTF) { + // Clear the interrupt in the RTC + CLR(RTC->ISR, RTC_ISR_WUTF); + // Disable the Wakeup timer (its periodic, but we use it as a + // one-shot timer + CLR(RTC->CR, RTC_CR_WUTE); + } + +} + +} diff --git a/RtcDriver.h b/RtcDriver.h new file mode 100644 index 0000000..0a8b742 --- /dev/null +++ b/RtcDriver.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include + +#include "SystemTime.h" +#include "ReturnCode.h" +#include "Time.h" + +#include "stm32l0xx.h" + +namespace BSP { + +struct time_bcd { + uint8_t hour_tens; + uint8_t hour_ones; + uint8_t minute_tens; + uint8_t minute_ones; + uint8_t second_tens; + uint8_t second_ones; + bool pm; +}; + +class RtcDriver { +public: + + RtcDriver() = delete; + ~RtcDriver() = delete; + + static SystemTimerImpl& get_system_timer() { + return m_sys_timer; + }; + static Common::ReturnCode init(); + static void increment_seconds(); + static Common::ReturnCode get_time(time_bcd &tm_bcd); + static Common::ReturnCode set_wakeup_in(Common::time_t wakeup_delay); + +private: + + static Common::ReturnCode init_hw(); + static void enable_rtc_write(); + static void disable_rtc_write(); + static void enable_rtc_wakeup_interrupt(); + static void enable_periodic_alarm(); + + static constexpr uint32_t LSE_CLOCK_FREQ = 32768; + + static RTC_TypeDef *m_rtc; + + class RtcSystemTimer : public BSP::SystemTimerImpl { + public: + + RtcSystemTimer() + : m_seconds(0) + {} + + ~RtcSystemTimer() {} + + Common::time_t get_time() override; + void increment_seconds(); + + private: + /** I'll be dead before this rolls over */ + /** FIXME FIXME FIXME: XXX This should be an atomic */ + uint32_t m_seconds; + + static constexpr uint32_t LSE_CLOCK_FREQ = 32768; + }; + + static RtcSystemTimer m_sys_timer; +}; + +} diff --git a/SystemTime.cpp b/SystemTime.cpp index b2cee6f..b1cee50 100644 --- a/SystemTime.cpp +++ b/SystemTime.cpp @@ -27,145 +27,21 @@ namespace BSP { using Common::ReturnCode; using Common::time_t; -uint32_t SystemTimer::m_seconds(0); -RTC_TypeDef *SystemTimer::m_rtc = nullptr; +SystemTimerImpl *SystemTimer::m_impl = nullptr; -void SystemTimer::enable_rtc_write() -{ - /*WPR = 0xCA; - RTC->WPR = 0x53; -} - -void SystemTimer::disable_rtc_write() -{ - /*WPR = 0x00; -} - -void SystemTimer::enable_rtc_wakeup_interrupt() -{ - CLR(RTC->CR, RTC_CR_WUTE); - while (!(RTC->ISR & RTC_ISR_WUTWF)) {} - SET_TO(RTC->WUTR, RTC_WUTR_WUT, 0); - SET_TO(RTC->CR, RTC_CR_WUCKSEL, RTC_CR_WUCKSEL_2); - - SET(EXTI->IMR, EXTI_IMR_IM20); - SET(EXTI->EMR, EXTI_EMR_EM20); - SET(EXTI->RTSR, EXTI_RTSR_RT20); - - NVIC_EnableIRQ(RTC_IRQn); - NVIC_SetPriority(RTC_IRQn, 0); - - SET(RTC->CR, RTC_CR_WUTE | RTC_CR_WUTIE); -} - -ReturnCode SystemTimer::init_hw() -{ - uint32_t temp = RCC->CSR; - - SET(RCC->CSR, RCC_CSR_RTCRST); - SET(RCC->APB1ENR, RCC_APB1ENR_PWREN); - SET(PWR->CR, PWR_CR_DBP); - - /*CSR = temp; - - while (!(RCC->CSR & RCC_CSR_LSERDY)) {} - - enable_rtc_write(); - - RTC->ISR = RTC_ISR_INIT; - while (!(RTC->ISR & RTC_ISR_INITF)) {} - - // FIXME: Make this use the minimum prescaler value - /*PRER, RTC_PRER_PREDIV_A, 0); - /*PRER, RTC_PRER_PREDIV_S, (LSE_CLOCK_FREQ - 1)); - - /*CR, RTC_CR_FMT); - - uint32_t time = 0; - SET(time, RTC_TR_PM); - SET_TO(time, RTC_TR_HT, 1 << RTC_TR_HT_Pos); - SET_TO(time, RTC_TR_HU, 2 << RTC_TR_HU_Pos); - SET_TO(time, RTC_TR_MNT, 5 << RTC_TR_MNT_Pos); - SET_TO(time, RTC_TR_MNU, 9 << RTC_TR_MNU_Pos); - SET_TO(time, RTC_TR_ST, 0 << RTC_TR_ST_Pos); - SET_TO(time, RTC_TR_SU, 0 << RTC_TR_SU_Pos); - RTC->TR = time; - - - CLR(RTC->ISR, RTC_ISR_INIT); - - enable_rtc_wakeup_interrupt(); - - disable_rtc_write(); - - return ReturnCode::OK; -} - -ReturnCode SystemTimer::init(RTC_TypeDef *rtc) -{ - if (rtc == nullptr) { - return ReturnCode::FAIL; - } - - m_rtc = rtc; - m_seconds = 0; - - init_hw(); - - return ReturnCode::OK; +void SystemTimer::set_timer(SystemTimerImpl &timer) { + m_impl = &timer; } ReturnCode SystemTimer::get_time(time_t &time) { - if (m_rtc == nullptr) { + if (m_impl == nullptr) { return ReturnCode::FAIL; } - uint32_t new_secs, old_secs, ssr, subsecond; - do { - old_secs = m_seconds; - ssr = m_rtc->SSR & 0xFFFF; - new_secs = m_seconds; - } while (new_secs != old_secs); - - new_secs = new_secs * LSE_CLOCK_FREQ; - /** SSR is a countdown register */ - subsecond = (new_secs + LSE_CLOCK_FREQ - 1 - ssr) * Common::Time::MILLIS_PER_SEC / LSE_CLOCK_FREQ; - time += Common::Time::millis(subsecond); + time = m_impl->get_time(); return ReturnCode::OK; } -void SystemTimer::increment_seconds() -{ - m_seconds++; -} - -extern "C" void RTC_IRQHandler(void); - -void RTC_IRQHandler() { - SystemTimer::increment_seconds(); - // Clear the interrupt in the EXTI - SET(EXTI->PR, EXTI_PR_PIF20); - // Clear the interrupt in the RTC - CLR(RTC->ISR, RTC_ISR_WUTF); -} - } diff --git a/SystemTime.h b/SystemTime.h index 22d5f25..29e96d7 100644 --- a/SystemTime.h +++ b/SystemTime.h @@ -30,24 +30,20 @@ namespace BSP { -class SystemTimer { +class SystemTimerImpl { public: - static Common::ReturnCode init(RTC_TypeDef *rtc); + virtual Common::time_t get_time() = 0; +}; + +class SystemTimer final { +public: + SystemTimer() = delete; + ~SystemTimer() = delete; + static Common::ReturnCode get_time(Common::time_t &time); - static void increment_seconds(); + static void set_timer(SystemTimerImpl& timer); private: - - static Common::ReturnCode init_hw(); - static void enable_rtc_write(); - static void disable_rtc_write(); - static void enable_rtc_wakeup_interrupt(); - - static constexpr uint32_t LSE_CLOCK_FREQ = 32768; - - /** I'll be dead before this rolls over */ - /** FIXME FIXME FIXME: XXX This should be an atomic */ - static uint32_t m_seconds; - static RTC_TypeDef *m_rtc; + static SystemTimerImpl *m_impl; }; } diff --git a/Time.h b/Time.h index 3857f88..7ba97e9 100644 --- a/Time.h +++ b/Time.h @@ -57,6 +57,26 @@ public: { return value * MICROS_PER_SEC; } + + static inline uint64_t to_nanos(time_t value) + { + return value * NANOS_PER_MICRO; + } + + static inline uint64_t to_micros(time_t value) + { + return value; + } + + static inline uint64_t to_millis(time_t value) + { + return value / MICROS_PER_MILLI; + } + + static inline uint64_t to_seconds(time_t value) + { + return value / MICROS_PER_SEC; + } }; } diff --git a/macros.h b/macros.h index bf322a8..397a928 100644 --- a/macros.h +++ b/macros.h @@ -78,8 +78,6 @@ } while (0) #define SET_TO(x, clear_mask, val) \ - static_assert((clear_mask & val) == val, \ - "'value' in SET_TO() has bits set that are not in clear_mask"); \ do { \ CLR(x, clear_mask); \ SET(x, val); \ diff --git a/main.cpp b/main.cpp index b142193..e97a62e 100644 --- a/main.cpp +++ b/main.cpp @@ -19,12 +19,13 @@ * THE SOFTWARE. */ -#include "ConcreteTaskScheduler.h" +#include "LowPowerTaskScheduler.h" +#include "RtcDriver.h" #include "DisplayDriver.h" #include "SpiDriver.h" -#include "BlinkTask.h" -#include "LowPowerDelay.h" #include "DisplayTimeTask.h" +#include "LptimPwm.h" +#include "ButtonManager.h" #include "stm32l0xx.h" @@ -32,12 +33,12 @@ using Common::Time; -static Common::Schedule::ConcreteTaskScheduler<10> g_sched; +static Common::Schedule::LowPowerTaskScheduler<10> g_sched; static BSP::SpiDriver g_spi(g_sched); static BSP::DisplayDriver g_display(g_sched, g_spi); -//static BlinkTask g_blink(Common::Time::seconds(2)); +static BSP::LptimPwm g_lptim_pwm(LPTIM1); +static BSP::ButtonManager g_btn_manager(0, 1, 3, Time::millis(1)); static DisplayTimeTask g_display_time(g_display); -//static LowPowerDelay g_lp_delay; extern "C" void __cxa_pure_virtual() { while(1) {} } @@ -116,25 +117,61 @@ static void _init(void) (*func)(); } } - } [[noreturn]] void main() { _init(); - BSP::SystemTimer::init(RTC); + // Set up the system clock + BSP::RtcDriver::init(); + BSP::SystemTimer::set_timer(BSP::RtcDriver::get_system_timer()); + BSP::LowPower::init(); + + // Initialize the tasks + g_lptim_pwm.init(); g_spi.init(); + g_btn_manager.init(); g_display.init(); - //g_blink.init(); g_display_time.init(); + // Enqueue each of the tasks Common::Schedule::NextTime asap = Common::Schedule::NextTime::asap(); - //g_sched.add_task(g_blink, asap); - //g_sched.add_task(g_lp_delay, asap); g_sched.add_task(g_spi, asap); + g_sched.add_task(g_btn_manager, asap); g_sched.add_task(g_display, asap); g_sched.add_task(g_display_time, asap); + // And we're off! This will never return g_sched.run(); } + + +extern "C" void NMI_Handler() {while (1);} +extern "C" void HardFault_Handler() {while (1);} +extern "C" void SVC_Handler() {while (1);} +extern "C" void PendSV_Handler() {while (1);} +extern "C" void SysTick_Handler() {while (1);} + +extern "C" void WWDG_IRQHandler() {while (1);} +extern "C" void PVD_IRQHandler() {while (1);} +extern "C" void WDT_IRQHandler() {while (1);} +//extern "C" void RTC_IRQHandler() {while (1);} +extern "C" void FLASH_IRQHandler() {while (1);} +extern "C" void RCC_CRS_IRQHandler() {while (1);} +// extern "C" void EXTI_1_0_IRQHandler() {while (1);} +// extern "C" void EXTI_3_2_IRQHandler() {while (1);} +// extern "C" void EXTI_15_4_IRQHandler() {while (1);} +extern "C" void DMA1_CHANNEL1_IRQHandler() {while (1);} +extern "C" void DMA1_CHANNEL3_2_IRQHandler() {while (1);} +extern "C" void DMA_CHANNEL_7_4_IRQHandler() {while (1);} +extern "C" void ADC_COMP_IRQHandler() {while (1);} +extern "C" void LPTIM1_IRQHandler() {while (1);} +extern "C" void USART4_USART5_IRQHandler() {while (1);} +extern "C" void TIM2_IRQHandler() {while (1);} +extern "C" void TIM3_IRQHandler() {while (1);} +extern "C" void TIM6_IRQHandler() {while (1);} +extern "C" void TIM7_IRQHandler() {while (1);} +extern "C" void TIM21_IRQHandler() {while (1);} +extern "C" void I2C3_IRQHandler() {while (1);} +extern "C" void TIM22_IRQHandler() {while (1);} diff --git a/stm32l031k6.ld b/stm32l031k6.ld index fad22d1..d344c29 100644 --- a/stm32l031k6.ld +++ b/stm32l031k6.ld @@ -34,7 +34,7 @@ MEMORY } /* Library configurations */ -GROUP(libgcc.a libc.a libm.a libnosys.a) +/* GROUP(libgcc.a libc.a libm.a libnosys.a) */ /* Linker script to place sections and symbol values. Should be used together * with other linker script that defines memory regions FLASH and RAM.