Enable buttons and the display.
Kind-of-sort-of usable-ish.
This commit is contained in:
@@ -49,6 +49,10 @@ ReturnCode ButtonManager::init()
|
|||||||
SET(EXTI->FTSR, 1u << btn.m_gpio_idx);
|
SET(EXTI->FTSR, 1u << btn.m_gpio_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLR(SYSCFG->EXTICR[0],
|
||||||
|
SYSCFG_EXTICR1_EXTI3 | SYSCFG_EXTICR1_EXTI2 |
|
||||||
|
SYSCFG_EXTICR1_EXTI1 | SYSCFG_EXTICR1_EXTI0);
|
||||||
|
|
||||||
NVIC_EnableIRQ(EXTI0_1_IRQn);
|
NVIC_EnableIRQ(EXTI0_1_IRQn);
|
||||||
NVIC_EnableIRQ(EXTI2_3_IRQn);
|
NVIC_EnableIRQ(EXTI2_3_IRQn);
|
||||||
NVIC_EnableIRQ(EXTI4_15_IRQn);
|
NVIC_EnableIRQ(EXTI4_15_IRQn);
|
||||||
@@ -82,7 +86,7 @@ NextTime ButtonManager::execute()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Call less frequently, and let the buttonmanager re-add itself to the task list on interrupts
|
// TODO: Call less frequently, and let the buttonmanager re-add itself to the task list on interrupts
|
||||||
return NextTime::in(Common::Time::millis(1000));
|
return NextTime::asap();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonManager::set_callback(Button btn, ChangeCallback callback)
|
void ButtonManager::set_callback(Button btn, ChangeCallback callback)
|
||||||
@@ -119,7 +123,7 @@ void ButtonManager::irq()
|
|||||||
bool is_pressed = !(idr & (1 << btn.m_gpio_idx));
|
bool is_pressed = !(idr & (1 << btn.m_gpio_idx));
|
||||||
|
|
||||||
// Check if the button state has changed, and timestamp it
|
// Check if the button state has changed, and timestamp it
|
||||||
if (is_pressed && (btn.m_state != ButtonState::PRESSED)) {
|
if (is_pressed && (btn.m_state == ButtonState::NOT_PRESSED)) {
|
||||||
btn.m_state = ButtonState::PRESSED;
|
btn.m_state = ButtonState::PRESSED;
|
||||||
btn.m_state_change_ts = systime;
|
btn.m_state_change_ts = systime;
|
||||||
} else if (!is_pressed && (btn.m_state == ButtonState::PRESSED)) {
|
} else if (!is_pressed && (btn.m_state == ButtonState::PRESSED)) {
|
||||||
@@ -132,16 +136,23 @@ void ButtonManager::irq()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" void EXTI_1_0_IRQHandler() {
|
extern "C" void EXTI_1_0_IRQHandler() {
|
||||||
|
static uint32_t irq_count = 0;
|
||||||
ButtonManager::irq();
|
ButtonManager::irq();
|
||||||
|
irq_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void EXTI_3_2_IRQHandler() {
|
extern "C" void EXTI_3_2_IRQHandler() {
|
||||||
|
static uint32_t irq_count = 0;
|
||||||
ButtonManager::irq();
|
ButtonManager::irq();
|
||||||
|
irq_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void EXTI_15_4_IRQHandler() {
|
extern "C" void EXTI_15_4_IRQHandler() {
|
||||||
|
static uint32_t irq_count = 0;
|
||||||
ButtonManager::irq();
|
ButtonManager::irq();
|
||||||
|
irq_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include "Task.h"
|
#include "Task.h"
|
||||||
#include "ReturnCode.h"
|
#include "ReturnCode.h"
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ public:
|
|||||||
NOT_PRESSED
|
NOT_PRESSED
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*ChangeCallback)(ButtonState);
|
using ChangeCallback = std::function<void(ButtonState)>;
|
||||||
|
|
||||||
Common::Schedule::NextTime execute() override;
|
Common::Schedule::NextTime execute() override;
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ void DisplayDriver::set_dirty(unsigned int y)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayDriver::set_bit(unsigned int x, unsigned int y, uint8_t val)
|
void DisplayDriver::set_bit(uint32_t x, uint32_t y, uint8_t val)
|
||||||
{
|
{
|
||||||
if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) {
|
if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) {
|
||||||
return;
|
return;
|
||||||
@@ -93,7 +93,7 @@ void DisplayDriver::set_bit(unsigned int x, unsigned int y, uint8_t val)
|
|||||||
set_dirty(y);
|
set_dirty(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayDriver::set_byte(unsigned int x, unsigned int y, uint8_t val)
|
void DisplayDriver::set_byte(uint32_t x, uint32_t y, uint8_t val)
|
||||||
{
|
{
|
||||||
// if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) {
|
// if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) {
|
||||||
// return;
|
// return;
|
||||||
@@ -129,9 +129,9 @@ unsigned char ReverseBitsLookupTable(unsigned char v)
|
|||||||
* (obviously) requires that everything is aligned correctly.
|
* (obviously) requires that everything is aligned correctly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void DisplayDriver::clear_glyph_aligned(int x_off, int y_off, const struct font *, const struct glyph *g)
|
void DisplayDriver::clear_glyph_aligned(uint32_t x_off, uint32_t y_off, const struct font *, const struct glyph *g)
|
||||||
{
|
{
|
||||||
for (int16_t y = y_off; y < y_off + g->rows && y < DISPLAY_HEIGHT; y++) {
|
for (uint32_t y = y_off; y < y_off + g->rows && y < DISPLAY_HEIGHT; y++) {
|
||||||
uint8_t *start = (uint8_t *) &m_buffer.lines[y].data[(x_off) >> 3];
|
uint8_t *start = (uint8_t *) &m_buffer.lines[y].data[(x_off) >> 3];
|
||||||
uint8_t *end = (uint8_t *) &m_buffer.lines[y].data[(x_off + g->advance) >> 3];
|
uint8_t *end = (uint8_t *) &m_buffer.lines[y].data[(x_off + g->advance) >> 3];
|
||||||
memset(start, 0xFF, end - start);
|
memset(start, 0xFF, end - start);
|
||||||
@@ -176,13 +176,13 @@ void DisplayDriver::clear_glyph_aligned(int x_off, int y_off, const struct font
|
|||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
void DisplayDriver::clear_glyph_unaligned(int x_off, int y_off, const struct font *font, const struct glyph *g)
|
void DisplayDriver::clear_glyph_unaligned(uint32_t x_off, uint32_t y_off, const struct font *font, const struct glyph *g)
|
||||||
{
|
{
|
||||||
int16_t x = 0;
|
int16_t x = 0;
|
||||||
if (x & 7) {
|
if (x & 7) {
|
||||||
while ((x & 7) && x < g->advance) {
|
while ((x & 7) && x < g->advance) {
|
||||||
// TODO: use a switch on (x & 7) instead?
|
// TODO: use a switch on (x & 7) instead?
|
||||||
for (int16_t y = 0; y < g->rows && y < DISPLAY_HEIGHT; y++) {
|
for (int16_t y = 0; y < g->rows && y < (int16_t) DISPLAY_HEIGHT; y++) {
|
||||||
set_bit(x_off + x, y_off + y + font->size - g->top, 0);
|
set_bit(x_off + x, y_off + y + font->size - g->top, 0);
|
||||||
}
|
}
|
||||||
x++;
|
x++;
|
||||||
@@ -190,7 +190,7 @@ void DisplayDriver::clear_glyph_unaligned(int x_off, int y_off, const struct fon
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (g->advance - x > 0) {
|
while (g->advance - x > 0) {
|
||||||
for (int16_t y = 0; y < g->rows && y < DISPLAY_HEIGHT; y++) {
|
for (int16_t y = 0; y < g->rows && y < (int16_t) DISPLAY_HEIGHT; y++) {
|
||||||
set_bit(x_off + x, y_off + y + font->size - g->top, 0);
|
set_bit(x_off + x, y_off + y + font->size - g->top, 0);
|
||||||
}
|
}
|
||||||
x++;
|
x++;
|
||||||
@@ -199,7 +199,7 @@ void DisplayDriver::clear_glyph_unaligned(int x_off, int y_off, const struct fon
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DisplayDriver::write_glyph_unaligned(int x_off, int y_off, const struct font *font, const struct glyph *g)
|
void DisplayDriver::write_glyph_unaligned(uint32_t x_off, uint32_t y_off, const struct font *font, const struct glyph *g)
|
||||||
{
|
{
|
||||||
int byte_cols = g->cols / 8;
|
int byte_cols = g->cols / 8;
|
||||||
if (g->cols & 7) {
|
if (g->cols & 7) {
|
||||||
@@ -241,7 +241,7 @@ void DisplayDriver::write_glyph_unaligned(int x_off, int y_off, const struct fon
|
|||||||
* This variant is ~4x faster than the unaligned version, but
|
* This variant is ~4x faster than the unaligned version, but
|
||||||
* requires that everything is aligned correctly.
|
* requires that everything is aligned correctly.
|
||||||
*/
|
*/
|
||||||
void DisplayDriver::write_glyph_aligned(int x_off, int y_off,
|
void DisplayDriver::write_glyph_aligned(uint32_t x_off, uint32_t y_off,
|
||||||
const struct font *font, const struct glyph *g)
|
const struct font *font, const struct glyph *g)
|
||||||
{
|
{
|
||||||
int byte_cols = g->cols / 8;
|
int byte_cols = g->cols / 8;
|
||||||
@@ -261,20 +261,25 @@ void DisplayDriver::write_glyph_aligned(int x_off, int y_off,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayDriver::char_at(int *x_off, int y_off, char c, const struct font *font)
|
void DisplayDriver::char_at(uint32_t *x_off, uint32_t y_off, char c, const struct font *font)
|
||||||
{
|
{
|
||||||
const struct glyph *g = glyph_for_char(font, c);
|
const struct glyph *g = glyph_for_char(font, c);
|
||||||
if (g == NULL) {
|
if (g == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (*x_off + g->left + g->cols > DISPLAY_WIDTH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(*x_off & 7) && !((*x_off + g->advance) & 7)) {
|
if (!(*x_off & 7) && !((*x_off + g->advance) & 7)) {
|
||||||
clear_glyph_aligned(*x_off, y_off, font, g);
|
clear_glyph_aligned(*x_off, y_off, font, g);
|
||||||
} else {
|
} else {
|
||||||
clear_glyph_unaligned(*x_off, y_off, font, g);
|
clear_glyph_unaligned(*x_off, y_off, font, g);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check the right glyph boundary (g->left + g->cols)
|
// FIXME: REALLY DO THIS!
|
||||||
|
// Check the right glyph boundary (g->left + g->cols)
|
||||||
if (!((*x_off + g->left) & 7)) {
|
if (!((*x_off + g->left) & 7)) {
|
||||||
write_glyph_aligned(*x_off, y_off, font, g);
|
write_glyph_aligned(*x_off, y_off, font, g);
|
||||||
} else {
|
} else {
|
||||||
@@ -287,15 +292,24 @@ void DisplayDriver::char_at(int *x_off, int y_off, char c, const struct font *fo
|
|||||||
*x_off += g->advance;
|
*x_off += g->advance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayDriver::string_at(int x_off, int y_off, const char *string, const struct font *font)
|
void DisplayDriver::string_at(uint32_t *x_off, uint32_t y_off, const char *string, const struct font *font)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (string[i]) {
|
while (string[i]) {
|
||||||
char_at(&x_off, y_off, string[i], font);
|
char_at(x_off, y_off, string[i], font);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
// void DisplayDriver::rect_at(int x_off, int y_off,
|
||||||
|
// int width, int height,
|
||||||
|
// bool is_black,
|
||||||
|
// int line_width)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
void DisplayDriver::refresh()
|
void DisplayDriver::refresh()
|
||||||
{
|
{
|
||||||
if (!m_is_dirty) {
|
if (!m_is_dirty) {
|
||||||
@@ -325,20 +339,25 @@ void DisplayDriver::clear()
|
|||||||
//TODO: put me somewhere fonty
|
//TODO: put me somewhere fonty
|
||||||
const struct glyph *DisplayDriver::glyph_for_char(const struct font *font, char c)
|
const struct glyph *DisplayDriver::glyph_for_char(const struct font *font, char c)
|
||||||
{
|
{
|
||||||
// TODO: This is almost the least efficient way imaginable to implement this
|
std::size_t low = 0;
|
||||||
for (int i = 0; i < font->max; i++) {
|
std::size_t high = font->count - 1;
|
||||||
const struct glyph *g = font->glyphs[i];
|
|
||||||
if (g == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
std::size_t mid = (high - low) / 2;
|
||||||
|
mid += low;
|
||||||
|
|
||||||
|
const struct glyph *g = font->glyphs[mid];
|
||||||
if (g->glyph == c) {
|
if (g->glyph == c) {
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (g->glyph > c) {
|
||||||
|
high = mid - 1;
|
||||||
|
} else {
|
||||||
|
low = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,11 @@ public:
|
|||||||
/**
|
/**
|
||||||
* DisplayDriver
|
* DisplayDriver
|
||||||
*/
|
*/
|
||||||
void set_bit(unsigned int x, unsigned int y, uint8_t val);
|
void set_bit(uint32_t x, uint32_t y, uint8_t val);
|
||||||
void set_byte(unsigned int x, unsigned int y, uint8_t val);
|
void set_byte(uint32_t x, uint32_t y, uint8_t val);
|
||||||
void char_at(int *x_off, int y_off, char c, const struct font *font);
|
void char_at(uint32_t *x_off, uint32_t y_off, char c, const struct font *font);
|
||||||
void string_at(int x_off, int y_off, const char *string, const struct font *font);
|
void string_at(uint32_t *x_off, uint32_t y_off,
|
||||||
|
const char *string, const struct font *font);
|
||||||
void refresh();
|
void refresh();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
@@ -61,10 +62,14 @@ private:
|
|||||||
void buffer_init();
|
void buffer_init();
|
||||||
void set_dirty(unsigned int y);
|
void set_dirty(unsigned int y);
|
||||||
const struct glyph *glyph_for_char(const struct font *font, char c);
|
const struct glyph *glyph_for_char(const struct font *font, char c);
|
||||||
void clear_glyph_aligned(int x_off, int y_off, const struct font * font, const struct glyph *g);
|
void clear_glyph_aligned(uint32_t x_off, uint32_t y_off,
|
||||||
void clear_glyph_unaligned(int x_off, int y_off, const struct font * font, const struct glyph *g);
|
const struct font * font, const struct glyph *g);
|
||||||
void write_glyph_aligned(int x_off, int y_off, const struct font * font, const struct glyph *g);
|
void clear_glyph_unaligned(uint32_t x_off, uint32_t y_off,
|
||||||
void write_glyph_unaligned(int x_off, int y_off, const struct font * font, const struct glyph *g);
|
const struct font * font, const struct glyph *g);
|
||||||
|
void write_glyph_aligned(uint32_t x_off, uint32_t y_off,
|
||||||
|
const struct font * font, const struct glyph *g);
|
||||||
|
void write_glyph_unaligned(uint32_t x_off, uint32_t y_off,
|
||||||
|
const struct font * font, const struct glyph *g);
|
||||||
|
|
||||||
static constexpr uint32_t DISPLAY_WIDTH = 144;
|
static constexpr uint32_t DISPLAY_WIDTH = 144;
|
||||||
static constexpr uint32_t DISPLAY_HEIGHT = 168;
|
static constexpr uint32_t DISPLAY_HEIGHT = 168;
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "DisplayTimeTask.h"
|
#include "DisplayTimeScreen.h"
|
||||||
|
#include "RtcDriver.h"
|
||||||
#include "SystemTime.h"
|
#include "SystemTime.h"
|
||||||
|
|
||||||
#include "font-notomono-29.h"
|
#include "font-notomono-29.h"
|
||||||
@@ -29,14 +30,16 @@ using Common::ReturnCode;
|
|||||||
using Common::Time;
|
using Common::Time;
|
||||||
using Common::Schedule::NextTime;
|
using Common::Schedule::NextTime;
|
||||||
|
|
||||||
static const font &font_large = font_notomono_68;
|
static const struct font &font = font_notomono_68;
|
||||||
static const font &font_default = font_notomono_29;
|
|
||||||
|
|
||||||
DisplayTimeTask::DisplayTimeTask(BSP::DisplayDriver &driver)
|
DisplayTimeScreen::DisplayTimeScreen(BSP::DisplayDriver &driver,
|
||||||
|
ScreenManager &manager,
|
||||||
|
Screen &menu_screen)
|
||||||
: m_driver(driver)
|
: m_driver(driver)
|
||||||
, m_has_cleared(false)
|
|
||||||
, m_last_time()
|
, m_last_time()
|
||||||
, m_display_seconds(true)
|
, m_manager(manager)
|
||||||
|
, m_menu_screen(menu_screen)
|
||||||
|
, m_display_seconds(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
static char get_char_for_digit(uint8_t bcd_digit)
|
static char get_char_for_digit(uint8_t bcd_digit)
|
||||||
@@ -47,10 +50,8 @@ static char get_char_for_digit(uint8_t bcd_digit)
|
|||||||
return bcd_digit + '0';
|
return bcd_digit + '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReturnCode DisplayTimeScreen::init()
|
||||||
ReturnCode DisplayTimeTask::init()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
SET_TO(GPIOA->MODER, GPIO_MODER_MODE0, 1u << GPIO_MODER_MODE1_Pos);
|
SET_TO(GPIOA->MODER, GPIO_MODER_MODE0, 1u << GPIO_MODER_MODE1_Pos);
|
||||||
|
|
||||||
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_1;
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_1;
|
||||||
@@ -60,7 +61,7 @@ ReturnCode DisplayTimeTask::init()
|
|||||||
return ReturnCode::OK;
|
return ReturnCode::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayTimeTask::display_number(uint32_t x, uint32_t y, uint32_t tens, uint32_t ones, const font &f)
|
void DisplayTimeScreen::display_number(uint32_t *x, uint32_t y, uint32_t tens, uint32_t ones, const struct font &f)
|
||||||
{
|
{
|
||||||
char time_str[3] = { 0 };
|
char time_str[3] = { 0 };
|
||||||
|
|
||||||
@@ -71,68 +72,69 @@ void DisplayTimeTask::display_number(uint32_t x, uint32_t y, uint32_t tens, uint
|
|||||||
m_driver.string_at(x, y, time_str, &f);
|
m_driver.string_at(x, y, time_str, &f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayTimeTask::display_time()
|
void DisplayTimeScreen::display_time()
|
||||||
{
|
{
|
||||||
BSP::time_bcd time;
|
Common::WallClockTime time;
|
||||||
BSP::RtcDriver::get_time(time);
|
BSP::RtcDriver::get_time(time);
|
||||||
|
|
||||||
if (!m_has_cleared) {
|
// FIXME: Don't clear every redraw. Something is broken on screen
|
||||||
m_driver.clear();
|
// switching. enable/disable not called?
|
||||||
|
m_driver.clear();
|
||||||
|
|
||||||
|
uint32_t x = 0;
|
||||||
|
const uint32_t y_space = (m_driver.get_height() - (2 * font.size)) / 3;
|
||||||
|
const uint32_t x_space = (m_driver.get_width() - (2 * 54)) / 2;
|
||||||
|
|
||||||
|
if (m_last_time.get_hours_24() != time.get_hours_24()) {
|
||||||
|
x = x_space;
|
||||||
|
display_number(&x, y_space,
|
||||||
|
time.get_hours_12_tens(), time.get_hours_12_ones(), font);
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
if (m_last_time.get_minutes() != time.get_minutes()) {
|
||||||
|
x = x_space;
|
||||||
SET(GPIOA->ODR, GPIO_ODR_OD1);
|
display_number(&x, y_space * 2 + font.size,
|
||||||
CLR(GPIOA->ODR, GPIO_ODR_OD1);
|
time.get_minutes_tens(), time.get_minutes_ones(), font);
|
||||||
|
|
||||||
if (m_last_time.hour_tens != time.hour_tens ||
|
|
||||||
m_last_time.hour_ones != time.hour_ones ||
|
|
||||||
!m_has_cleared) {
|
|
||||||
display_number(8, (i * font_default.size) + (i + 1) * 4, time.hour_tens, time.hour_ones, font_default);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SET(GPIOA->ODR, GPIO_ODR_OD1);
|
|
||||||
CLR(GPIOA->ODR, GPIO_ODR_OD1);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
if (m_last_time.minute_tens != time.minute_tens ||
|
|
||||||
m_last_time.minute_ones != time.minute_ones ||
|
|
||||||
!m_has_cleared) {
|
|
||||||
display_number(8, (i * font_default.size) + (i + 1) * 4, time.minute_tens, time.minute_ones, font_default);
|
|
||||||
}
|
|
||||||
|
|
||||||
SET(GPIOA->ODR, GPIO_ODR_OD1);
|
|
||||||
CLR(GPIOA->ODR, GPIO_ODR_OD1);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
if (m_display_seconds) {
|
if (m_display_seconds) {
|
||||||
if (m_last_time.second_tens != time.second_tens ||
|
// display_number(&x, y,
|
||||||
m_last_time.second_ones != time.second_ones ||
|
// time.get_seconds_tens(), time.get_seconds_ones(), font_default);
|
||||||
!m_has_cleared) {
|
|
||||||
display_number(8, (i * font_default.size) + (i + 1) * 4, time.second_tens, time.second_ones, font_default);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// m_has_cleared = true;
|
|
||||||
m_last_time = time;
|
m_last_time = time;
|
||||||
|
|
||||||
SET(GPIOA->ODR, GPIO_ODR_OD1);
|
|
||||||
CLR(GPIOA->ODR, GPIO_ODR_OD1);
|
|
||||||
|
|
||||||
m_driver.refresh();
|
m_driver.refresh();
|
||||||
|
|
||||||
SET(GPIOA->ODR, GPIO_ODR_OD1);
|
|
||||||
CLR(GPIOA->ODR, GPIO_ODR_OD1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NextTime DisplayTimeTask::execute()
|
NextTime DisplayTimeScreen::execute()
|
||||||
{
|
{
|
||||||
|
display_time();
|
||||||
|
|
||||||
Common::time_t now;
|
Common::time_t now;
|
||||||
BSP::SystemTimer::get_time(now);
|
BSP::SystemTimer::get_time(now);
|
||||||
uint32_t next_secs = Time::to_seconds(now) + 1;
|
uint32_t next_secs = Time::to_seconds(now) + (m_display_seconds ? 1 : 60);
|
||||||
|
|
||||||
display_time();
|
|
||||||
|
|
||||||
return NextTime::at(Time::seconds(next_secs));
|
return NextTime::at(Time::seconds(next_secs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DisplayTimeScreen::enable() {
|
||||||
|
m_driver.clear();
|
||||||
|
m_last_time = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayTimeScreen::disable() {
|
||||||
|
m_driver.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayTimeScreen::notify_up_button() {
|
||||||
|
/* TODO: This should open a menu first */
|
||||||
|
m_manager.push_screen(m_menu_screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayTimeScreen::notify_middle_button() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayTimeScreen::notify_down_button() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,27 +23,36 @@
|
|||||||
|
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
|
||||||
#include "DisplayDriver.h"
|
#include "ScreenManager.h"
|
||||||
#include "ReturnCode.h"
|
#include "Screen.h"
|
||||||
#include "Task.h"
|
|
||||||
#include "RtcDriver.h"
|
|
||||||
|
|
||||||
class DisplayTimeTask : public Common::Schedule::Task {
|
class DisplayTimeScreen : public Screen {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
DisplayTimeTask(BSP::DisplayDriver &display);
|
DisplayTimeScreen(BSP::DisplayDriver &display,
|
||||||
|
ScreenManager &m_manager,
|
||||||
|
Screen &m_menu_screen);
|
||||||
|
|
||||||
Common::ReturnCode init();
|
Common::ReturnCode init();
|
||||||
Common::Schedule::NextTime execute();
|
Common::Schedule::NextTime execute() override;
|
||||||
|
|
||||||
|
void enable() override;
|
||||||
|
void disable() override;
|
||||||
|
|
||||||
|
void notify_up_button() override;
|
||||||
|
void notify_middle_button() override;
|
||||||
|
void notify_down_button() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void display_time();
|
void display_time();
|
||||||
void display_number(uint32_t x, uint32_t y, uint32_t tens, uint32_t ones, const font &f);
|
void display_number(uint32_t *x, uint32_t y, uint32_t tens, uint32_t ones, const font &f);
|
||||||
|
|
||||||
BSP::DisplayDriver &m_driver;
|
BSP::DisplayDriver &m_driver;
|
||||||
bool m_has_cleared;
|
Common::WallClockTime m_last_time;
|
||||||
BSP::time_bcd m_last_time;
|
|
||||||
|
ScreenManager &m_manager;
|
||||||
|
Screen &m_menu_screen;
|
||||||
|
|
||||||
const bool m_display_seconds;
|
const bool m_display_seconds;
|
||||||
};
|
};
|
||||||
@@ -48,12 +48,25 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_task(Task &task, NextTime &time) override
|
void add_task(Task &task, const NextTime &time) override
|
||||||
{
|
{
|
||||||
if (m_task_count == MAX_TASKS || time.get_type() == ScheduleType::NEVER) {
|
if (m_task_count == MAX_TASKS || time.get_type() == ScheduleType::NEVER) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the task is already in the task list, don't add, but update
|
||||||
|
for (size_t i = 0; i < m_task_count; i++) {
|
||||||
|
TaskEvent &event = m_tasks[i];
|
||||||
|
if (event.m_task == &task) {
|
||||||
|
// Task is already in the list
|
||||||
|
if (time < event.m_time) {
|
||||||
|
// Provided time is sooner than the existing time. Update.
|
||||||
|
event.m_time = time;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_tasks[m_task_count++] = TaskEvent(task, time);
|
m_tasks[m_task_count++] = TaskEvent(task, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,10 +129,10 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!execed && (next_time - time > Time::millis(2))) {
|
if (!execed && (next_time - time > Time::millis(2))) {
|
||||||
Common::ReturnCode rc = BSP::RtcDriver::set_wakeup_in(next_time - time);
|
// Common::ReturnCode rc = BSP::RtcDriver::set_wakeup_in(next_time - time);
|
||||||
if (rc == Common::ReturnCode::OK) {
|
// if (rc == Common::ReturnCode::OK) {
|
||||||
BSP::LowPower::stop();
|
// BSP::LowPower::stop();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cycle_count++;
|
m_cycle_count++;
|
||||||
|
|||||||
11
Makefile
11
Makefile
@@ -73,10 +73,11 @@ OUTPUT_ELF ?= $(OUTPUT_NAME).elf
|
|||||||
|
|
||||||
DEVICE_DEFINE = $(subst XX,xx,$(shell echo $(DEVICE_FAMILY) | tr '[:lower:]' '[:upper:]'))
|
DEVICE_DEFINE = $(subst XX,xx,$(shell echo $(DEVICE_FAMILY) | tr '[:lower:]' '[:upper:]'))
|
||||||
|
|
||||||
CPU_FLAGS = -mthumb -mcpu=cortex-m0 -mfloat-abi=soft
|
CPU_FLAGS = -mthumb -mcpu=cortex-m0plus -mfloat-abi=soft
|
||||||
|
|
||||||
# C pedantism
|
# C pedantism
|
||||||
CFLAGS = -Wall -Wextra -Wpedantic
|
CFLAGS = -Wall -Wextra -Wpedantic -Werror
|
||||||
|
CXX_FLAGS = -Wsuggest-override -Wsuggest-final-methods -Wsuggest-final-types
|
||||||
# Debug/optimization
|
# Debug/optimization
|
||||||
CFLAGS += -Os -ggdb -g3
|
CFLAGS += -Os -ggdb -g3
|
||||||
CFLAGS += -fdata-sections -ffunction-sections
|
CFLAGS += -fdata-sections -ffunction-sections
|
||||||
@@ -90,7 +91,7 @@ CFLAGS += -I./lib/stm32/$(DEVICE_LINE)/Include
|
|||||||
CFLAGS += -I./lib/CMSIS/Core/Include
|
CFLAGS += -I./lib/CMSIS/Core/Include
|
||||||
CFLAGS += -I./lib/fonts/
|
CFLAGS += -I./lib/fonts/
|
||||||
|
|
||||||
CXX_FLAGS = -std=c++14 -fno-exceptions -fno-rtti
|
CXX_FLAGS += -std=c++14 -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
# Startup Definitions
|
# Startup Definitions
|
||||||
ASFLAGS += $(CPU_FLAGS)
|
ASFLAGS += $(CPU_FLAGS)
|
||||||
@@ -130,7 +131,7 @@ $(OUTPUT_BIN): $(OUTPUT_ELF)
|
|||||||
|
|
||||||
$(OUTPUT_ELF): $(LINKER_SCRIPT) $(OBJS)
|
$(OUTPUT_ELF): $(LINKER_SCRIPT) $(OBJS)
|
||||||
@echo "LD $@"
|
@echo "LD $@"
|
||||||
$(LD) -T $(LINKER_SCRIPT) $(LDFLAGS) -o $(OUTPUT_ELF) $(OBJS)
|
@$(LD) -T $(LINKER_SCRIPT) $(LDFLAGS) -o $(OUTPUT_ELF) $(OBJS)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Utilities
|
# Utilities
|
||||||
@@ -141,7 +142,7 @@ STM32FLASH_DEVICE = /dev/ttyUSB0
|
|||||||
.PHONY: flash
|
.PHONY: flash
|
||||||
flash: $(OUTPUT_BIN)
|
flash: $(OUTPUT_BIN)
|
||||||
@echo "FLASH $(OUTPUT_BIN)"
|
@echo "FLASH $(OUTPUT_BIN)"
|
||||||
$(STM32_PROG) -vb 3 --connect port=SWD reset=Hwrst -w $(OUTPUT_BIN) 0x8000000 -v --go
|
$(STM32_PROG) --connect port=SWD reset=Hwrst -w $(OUTPUT_BIN) 0x8000000 -v --go
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "RtcDriver.h"
|
#include "RtcDriver.h"
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
#include <new>
|
||||||
|
|
||||||
namespace BSP {
|
namespace BSP {
|
||||||
|
|
||||||
@@ -29,6 +30,13 @@ using Common::time_t;
|
|||||||
|
|
||||||
RtcDriver::RtcSystemTimer RtcDriver::m_sys_timer;
|
RtcDriver::RtcSystemTimer RtcDriver::m_sys_timer;
|
||||||
|
|
||||||
|
ReturnCode RtcDriver::init()
|
||||||
|
{
|
||||||
|
init_hw();
|
||||||
|
|
||||||
|
return ReturnCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
void RtcDriver::enable_rtc_write()
|
void RtcDriver::enable_rtc_write()
|
||||||
{
|
{
|
||||||
/*<! Disable write protection */
|
/*<! Disable write protection */
|
||||||
@@ -48,7 +56,7 @@ void RtcDriver::enable_periodic_alarm()
|
|||||||
SET(RTC->ALRMAR, RTC_ALRMAR_MSK4 | RTC_ALRMAR_MSK3 | RTC_ALRMAR_MSK2 | RTC_ALRMAR_MSK1);
|
SET(RTC->ALRMAR, RTC_ALRMAR_MSK4 | RTC_ALRMAR_MSK3 | RTC_ALRMAR_MSK2 | RTC_ALRMAR_MSK1);
|
||||||
|
|
||||||
// Only calculate alarms when second rolls over
|
// Only calculate alarms when second rolls over
|
||||||
SET_TO(RTC->ALRMASSR, RTC_ALRMASSR_MASKSS, 0);
|
SET_TO(RTC->ALRMASSR, RTC_ALRMASSR_MASKSS, RTC_ALRMASSR_MASKSS);
|
||||||
|
|
||||||
SET(RTC->CR, RTC_CR_ALRAE | RTC_CR_ALRAIE);
|
SET(RTC->CR, RTC_CR_ALRAE | RTC_CR_ALRAIE);
|
||||||
SET(EXTI->IMR, EXTI_IMR_IM17);
|
SET(EXTI->IMR, EXTI_IMR_IM17);
|
||||||
@@ -84,7 +92,8 @@ ReturnCode RtcDriver::init_hw()
|
|||||||
|
|
||||||
/*<! Set the Clock Prescalers (32.768kHz / 1 / 32768 = 1Hz */
|
/*<! Set the Clock Prescalers (32.768kHz / 1 / 32768 = 1Hz */
|
||||||
/*<! Set the Async prescaler to the Maximum (divide the clock by 128) */
|
/*<! Set the Async prescaler to the Maximum (divide the clock by 128) */
|
||||||
SET_TO(RTC->PRER, RTC_PRER_PREDIV_A, 0);
|
// XXX reset to 0, this is to enable easier debugging
|
||||||
|
SET_TO(RTC->PRER, RTC_PRER_PREDIV_A, 0);
|
||||||
/*<! Set the Syncronous scaler so the RTC updates at 1Hz */
|
/*<! Set the Syncronous scaler so the RTC updates at 1Hz */
|
||||||
SET_TO(RTC->PRER, RTC_PRER_PREDIV_S, (LSE_CLOCK_FREQ - 1));
|
SET_TO(RTC->PRER, RTC_PRER_PREDIV_S, (LSE_CLOCK_FREQ - 1));
|
||||||
|
|
||||||
@@ -121,32 +130,60 @@ ReturnCode RtcDriver::init_hw()
|
|||||||
return ReturnCode::OK;
|
return ReturnCode::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnCode RtcDriver::get_time(time_bcd &tm_bcd)
|
ReturnCode RtcDriver::get_time(Common::WallClockTime &wall_time)
|
||||||
{
|
{
|
||||||
/*<! The value of TR in the shadow register is locked when SSR is
|
/*<! The value of TR in the shadow register is locked when SSR is
|
||||||
read (by the system timer), until the date register is read. We're
|
read (by the system timer), until the date register is read. We're
|
||||||
not using the date here, but we do need to clear the stale value. */
|
not using the date here, but we do need to clear the stale value. */
|
||||||
(void) RTC->DR;
|
(void) RTC->DR;
|
||||||
|
|
||||||
|
// TODO: reread TR + PM for consistency
|
||||||
uint32_t time = RTC->TR;
|
uint32_t time = RTC->TR;
|
||||||
|
unsigned int hours = 0, minutes = 0, seconds = 0;
|
||||||
|
|
||||||
tm_bcd.hour_tens = STM32_GET_FIELD(time, RTC_TR_HT);
|
hours += 10 * STM32_GET_FIELD(time, RTC_TR_HT);
|
||||||
tm_bcd.hour_ones = STM32_GET_FIELD(time, RTC_TR_HU);
|
hours += STM32_GET_FIELD(time, RTC_TR_HU);
|
||||||
|
|
||||||
tm_bcd.minute_tens = STM32_GET_FIELD(time, RTC_TR_MNT);
|
minutes += 10 * STM32_GET_FIELD(time, RTC_TR_MNT);
|
||||||
tm_bcd.minute_ones = STM32_GET_FIELD(time, RTC_TR_MNU);
|
minutes += STM32_GET_FIELD(time, RTC_TR_MNU);
|
||||||
|
|
||||||
tm_bcd.second_tens = STM32_GET_FIELD(time, RTC_TR_ST);
|
seconds += 10 * STM32_GET_FIELD(time, RTC_TR_ST);
|
||||||
tm_bcd.second_ones = STM32_GET_FIELD(time, RTC_TR_SU);
|
seconds += STM32_GET_FIELD(time, RTC_TR_SU);
|
||||||
|
|
||||||
tm_bcd.pm = STM32_GET_FIELD(time, RTC_TR_PM);
|
if (STM32_GET_FIELD(time, RTC_TR_PM)) {
|
||||||
|
hours += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
new (&wall_time) Common::WallClockTime(hours, minutes, seconds);
|
||||||
|
|
||||||
return ReturnCode::OK;
|
return ReturnCode::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnCode RtcDriver::init()
|
ReturnCode RtcDriver::set_time(const Common::WallClockTime &wall_time)
|
||||||
{
|
{
|
||||||
init_hw();
|
enable_rtc_write();
|
||||||
|
|
||||||
|
RTC->ISR = RTC_ISR_INIT;
|
||||||
|
while (!(RTC->ISR & RTC_ISR_INITF)) {}
|
||||||
|
|
||||||
|
/*<! Load initial date and time */
|
||||||
|
|
||||||
|
// 12-Hour format
|
||||||
|
SET(RTC->CR, RTC_CR_FMT);
|
||||||
|
|
||||||
|
uint32_t time = 0;
|
||||||
|
SET_TO(time, RTC_TR_PM, wall_time.get_is_pm() << RTC_TR_PM_Pos);
|
||||||
|
SET_TO(time, RTC_TR_HT, wall_time.get_hours_12_tens() << RTC_TR_HT_Pos);
|
||||||
|
SET_TO(time, RTC_TR_HU, wall_time.get_hours_12_ones() << RTC_TR_HU_Pos);
|
||||||
|
SET_TO(time, RTC_TR_MNT, wall_time.get_minutes_tens() << RTC_TR_MNT_Pos);
|
||||||
|
SET_TO(time, RTC_TR_MNU, wall_time.get_minutes_ones() << RTC_TR_MNU_Pos);
|
||||||
|
SET_TO(time, RTC_TR_ST, wall_time.get_seconds_tens() << RTC_TR_ST_Pos);
|
||||||
|
SET_TO(time, RTC_TR_SU, wall_time.get_seconds_tens() << RTC_TR_SU_Pos);
|
||||||
|
RTC->TR = time;
|
||||||
|
|
||||||
|
CLR(RTC->ISR, RTC_ISR_INIT);
|
||||||
|
|
||||||
|
disable_rtc_write();
|
||||||
|
|
||||||
return ReturnCode::OK;
|
return ReturnCode::OK;
|
||||||
}
|
}
|
||||||
@@ -156,7 +193,8 @@ ReturnCode RtcDriver::set_wakeup_in(Common::time_t wakeup_delay)
|
|||||||
/*<! 2^64 / (1000000 * 32768) / 60 / 60 / 24 / 365 = ~17.85 This
|
/*<! 2^64 / (1000000 * 32768) / 60 / 60 / 24 / 365 = ~17.85 This
|
||||||
value will only overflow for wakeup_delays > 17.85 years, so
|
value will only overflow for wakeup_delays > 17.85 years, so
|
||||||
this is fine. */
|
this is fine. */
|
||||||
uint64_t delay_cycles = Common::Time::to_micros(wakeup_delay) * LSE_CLOCK_FREQ / Common::Time::MICROS_PER_SEC;
|
uint64_t delay_cycles = Common::Time::to_micros(wakeup_delay) * LSE_CLOCK_FREQ /
|
||||||
|
Common::Time::MICROS_PER_SEC;
|
||||||
|
|
||||||
enable_rtc_write();
|
enable_rtc_write();
|
||||||
|
|
||||||
@@ -203,23 +241,24 @@ ReturnCode RtcDriver::set_wakeup_in(Common::time_t wakeup_delay)
|
|||||||
return ReturnCode::OK;
|
return ReturnCode::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::time_t RtcDriver::RtcSystemTimer::get_time()
|
Common::time_t RtcDriver::RtcSystemTimer::get_time()
|
||||||
{
|
{
|
||||||
uint32_t new_secs, old_secs, ssr, subsecond;
|
uint32_t new_secs, old_secs, ssr, subsecond;
|
||||||
do {
|
do {
|
||||||
old_secs = m_seconds;
|
old_secs = m_seconds;
|
||||||
ssr = RTC->SSR & 0xFFFF;
|
ssr = RTC->SSR & 0xFFFF;
|
||||||
new_secs = m_seconds;
|
new_secs = m_seconds;
|
||||||
} while (new_secs != old_secs);
|
} while (new_secs != old_secs);
|
||||||
|
|
||||||
new_secs = new_secs * LSE_CLOCK_FREQ;
|
new_secs = new_secs * LSE_CLOCK_FREQ;
|
||||||
/** SSR is a countdown register */
|
/** SSR is a countdown register */
|
||||||
subsecond = (new_secs + LSE_CLOCK_FREQ - 1 - ssr) * Common::Time::MILLIS_PER_SEC / LSE_CLOCK_FREQ;
|
subsecond = (new_secs + LSE_CLOCK_FREQ - 1 - ssr) * Common::Time::MILLIS_PER_SEC / LSE_CLOCK_FREQ;
|
||||||
return Common::Time::millis(subsecond);
|
return Common::Time::millis(subsecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtcDriver::RtcSystemTimer::increment_seconds()
|
void RtcDriver::RtcSystemTimer::increment_seconds()
|
||||||
{
|
{
|
||||||
|
/** TODO: Atomic increment */
|
||||||
m_seconds++;
|
m_seconds++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
RtcDriver.h
16
RtcDriver.h
@@ -31,16 +31,6 @@
|
|||||||
|
|
||||||
namespace BSP {
|
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 {
|
class RtcDriver {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -52,7 +42,8 @@ public:
|
|||||||
};
|
};
|
||||||
static Common::ReturnCode init();
|
static Common::ReturnCode init();
|
||||||
static void increment_seconds();
|
static void increment_seconds();
|
||||||
static Common::ReturnCode get_time(time_bcd &tm_bcd);
|
static Common::ReturnCode get_time(Common::WallClockTime &tm_bcd);
|
||||||
|
static Common::ReturnCode set_time(const Common::WallClockTime &tm_bcd);
|
||||||
static Common::ReturnCode set_wakeup_in(Common::time_t wakeup_delay);
|
static Common::ReturnCode set_wakeup_in(Common::time_t wakeup_delay);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -80,8 +71,7 @@ private:
|
|||||||
void increment_seconds();
|
void increment_seconds();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** I'll be dead before this rolls over */
|
/** ~136 years. Good enough for me. */
|
||||||
/** FIXME FIXME FIXME: XXX This should be an atomic */
|
|
||||||
uint32_t m_seconds;
|
uint32_t m_seconds;
|
||||||
|
|
||||||
static constexpr uint32_t LSE_CLOCK_FREQ = 32768;
|
static constexpr uint32_t LSE_CLOCK_FREQ = 32768;
|
||||||
|
|||||||
@@ -21,23 +21,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "macros.h"
|
|
||||||
|
|
||||||
#include "DisplayDriver.h"
|
|
||||||
#include "ReturnCode.h"
|
#include "ReturnCode.h"
|
||||||
#include "Task.h"
|
#include "Task.h"
|
||||||
|
|
||||||
class ScreenManager : public Common::Schedule::Task {
|
class Screen : public Common::Schedule::Task {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ScreenManager(BSP::DisplayDriver &display);
|
virtual void enable() = 0;
|
||||||
|
virtual void disable() = 0;
|
||||||
|
|
||||||
Common::Schedule::NextTime execute();
|
virtual void notify_up_button() = 0;
|
||||||
|
virtual void notify_middle_button() = 0;
|
||||||
private:
|
virtual void notify_down_button() = 0;
|
||||||
|
|
||||||
void display_menu();
|
|
||||||
|
|
||||||
BSP::DisplayDriver &m_driver;
|
|
||||||
bool m_displayed;
|
|
||||||
};
|
};
|
||||||
129
ScreenManager.cpp
Normal file
129
ScreenManager.cpp
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ScreenManager.h"
|
||||||
|
#include "ButtonManager.h"
|
||||||
|
|
||||||
|
// TODO: Basically all of the error checking
|
||||||
|
|
||||||
|
using Common::Schedule::Task;
|
||||||
|
using Common::Schedule::NextTime;
|
||||||
|
using Common::ReturnCode;
|
||||||
|
|
||||||
|
ScreenManager::ScreenManager(Common::Schedule::TaskScheduler &scheduler,
|
||||||
|
BSP::DisplayDriver &display,
|
||||||
|
BSP::ButtonManager &buttons)
|
||||||
|
: m_scheduler(scheduler)
|
||||||
|
, m_screen_stack{nullptr}
|
||||||
|
, m_screen_stack_depth(0)
|
||||||
|
, m_display(display)
|
||||||
|
, m_buttons(buttons)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnCode ScreenManager::init()
|
||||||
|
{
|
||||||
|
m_buttons.set_callback(
|
||||||
|
BSP::ButtonManager::Button::UP,
|
||||||
|
[this](BSP::ButtonManager::ButtonState state) {
|
||||||
|
if (state == BSP::ButtonManager::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) {
|
||||||
|
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) {
|
||||||
|
current_screen()->notify_down_button();
|
||||||
|
}});
|
||||||
|
|
||||||
|
m_display.clear();
|
||||||
|
|
||||||
|
return ReturnCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NextTime ScreenManager::execute()
|
||||||
|
{
|
||||||
|
// TODO: Handle updating execute times when the screen is swapped out.
|
||||||
|
return current_screen()->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnCode ScreenManager::set_root_screen(Screen &screen)
|
||||||
|
{
|
||||||
|
m_screen_stack[m_screen_stack_depth] = &screen;
|
||||||
|
m_screen_stack_depth = 1;
|
||||||
|
current_screen()->enable();
|
||||||
|
m_scheduler.add_task(*this, NextTime::asap());
|
||||||
|
|
||||||
|
return ReturnCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnCode ScreenManager::push_screen(Screen &screen)
|
||||||
|
{
|
||||||
|
if (m_screen_stack_depth == MAX_SCREEN_STACK) {
|
||||||
|
return ReturnCode::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: "Lock" changes during this operation. Don't allow
|
||||||
|
// disable/enable to push/pop.
|
||||||
|
|
||||||
|
current_screen()->disable();
|
||||||
|
m_screen_stack[m_screen_stack_depth] = &screen;
|
||||||
|
m_screen_stack_depth++;
|
||||||
|
current_screen()->enable();
|
||||||
|
m_scheduler.add_task(*this, NextTime::asap());
|
||||||
|
|
||||||
|
return ReturnCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnCode ScreenManager::pop_screen() {
|
||||||
|
if (m_screen_stack_depth == 1) {
|
||||||
|
return ReturnCode::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_screen()->disable();
|
||||||
|
m_screen_stack_depth--;
|
||||||
|
m_screen_stack[m_screen_stack_depth] = nullptr;
|
||||||
|
current_screen()->enable();
|
||||||
|
m_scheduler.add_task(*this, NextTime::asap());
|
||||||
|
|
||||||
|
return ReturnCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnCode ScreenManager::set_screen(Screen &screen) {
|
||||||
|
if (m_screen_stack_depth == 1) {
|
||||||
|
return ReturnCode::FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_screen()->disable();
|
||||||
|
m_screen_stack[m_screen_stack_depth - 1] = &screen;
|
||||||
|
current_screen()->enable();
|
||||||
|
m_scheduler.add_task(*this, NextTime::asap());
|
||||||
|
|
||||||
|
return ReturnCode::OK;
|
||||||
|
}
|
||||||
68
ScreenManager.h
Normal file
68
ScreenManager.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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 <array>
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include "DisplayDriver.h"
|
||||||
|
#include "ButtonManager.h"
|
||||||
|
#include "ReturnCode.h"
|
||||||
|
#include "Screen.h"
|
||||||
|
#include "Task.h"
|
||||||
|
|
||||||
|
class ScreenManager : public Common::Schedule::Task {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ScreenManager(Common::Schedule::TaskScheduler &scheduler,
|
||||||
|
BSP::DisplayDriver &display,
|
||||||
|
BSP::ButtonManager &buttons);
|
||||||
|
|
||||||
|
Common::ReturnCode init();
|
||||||
|
Common::ReturnCode set_root_screen(Screen &screen);
|
||||||
|
|
||||||
|
Common::Schedule::NextTime execute() override;
|
||||||
|
|
||||||
|
Common::ReturnCode pop_screen();
|
||||||
|
Common::ReturnCode push_screen(Screen &screen);
|
||||||
|
Common::ReturnCode set_screen(Screen &screen);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
inline Screen *current_screen() {
|
||||||
|
return m_screen_stack[m_screen_stack_depth - 1];
|
||||||
|
}
|
||||||
|
void notify_up_screen();
|
||||||
|
void notify_middle_screen();
|
||||||
|
void notify_down_screen();
|
||||||
|
|
||||||
|
static constexpr std::size_t MAX_SCREEN_STACK = 5;
|
||||||
|
|
||||||
|
Common::Schedule::TaskScheduler &m_scheduler;
|
||||||
|
|
||||||
|
std::array<Screen *, MAX_SCREEN_STACK> m_screen_stack;
|
||||||
|
std::size_t m_screen_stack_depth;
|
||||||
|
|
||||||
|
BSP::DisplayDriver &m_display;
|
||||||
|
BSP::ButtonManager &m_buttons;
|
||||||
|
};
|
||||||
219
SetTimeScreen.cpp
Normal file
219
SetTimeScreen.cpp
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* 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 "SetTimeScreen.h"
|
||||||
|
#include "SystemTime.h"
|
||||||
|
#include "RtcDriver.h"
|
||||||
|
|
||||||
|
#include "font-notomono-29.h"
|
||||||
|
|
||||||
|
using Common::ReturnCode;
|
||||||
|
using Common::Time;
|
||||||
|
using Common::Schedule::NextTime;
|
||||||
|
|
||||||
|
static const font &font = font_notomono_29;
|
||||||
|
|
||||||
|
SetTimeScreen::SetTimeScreen(BSP::DisplayDriver &display,
|
||||||
|
ScreenManager &manager)
|
||||||
|
: m_display(display)
|
||||||
|
, m_manager(manager)
|
||||||
|
, m_state(SetState::HOURS)
|
||||||
|
, m_is_acked(false)
|
||||||
|
, m_time()
|
||||||
|
{}
|
||||||
|
|
||||||
|
ReturnCode SetTimeScreen::init()
|
||||||
|
{
|
||||||
|
return ReturnCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NextTime SetTimeScreen::execute()
|
||||||
|
{
|
||||||
|
//TODO: Fix this so it doesn't constantly refresh
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
return NextTime::never();
|
||||||
|
}
|
||||||
|
|
||||||
|
static char get_char_for_digit(uint8_t bcd_digit)
|
||||||
|
{
|
||||||
|
if (bcd_digit > 9) {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
return bcd_digit + '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::display_number(uint32_t *x, uint32_t y,
|
||||||
|
uint32_t tens, uint32_t ones, const struct font &f)
|
||||||
|
{
|
||||||
|
char time_str[3] = { 0 };
|
||||||
|
time_str[0] = get_char_for_digit(tens);
|
||||||
|
time_str[1] = get_char_for_digit(ones);
|
||||||
|
time_str[2] = '\0';
|
||||||
|
|
||||||
|
m_display.string_at(x, y, time_str, &f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::render_time()
|
||||||
|
{
|
||||||
|
uint32_t x = 0;
|
||||||
|
uint32_t y = 32;
|
||||||
|
|
||||||
|
display_number(&x, y,
|
||||||
|
m_time.get_hours_12_tens(), m_time.get_hours_12_ones(),
|
||||||
|
font);
|
||||||
|
|
||||||
|
display_number(&x, y,
|
||||||
|
m_time.get_minutes_tens(), m_time.get_minutes_ones(),
|
||||||
|
font);
|
||||||
|
|
||||||
|
display_number(&x, y,
|
||||||
|
m_time.get_seconds_tens(), m_time.get_seconds_ones(),
|
||||||
|
font);
|
||||||
|
|
||||||
|
m_display.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::draw_line(uint32_t x, uint32_t y, uint32_t width)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < width; i += 8) {
|
||||||
|
m_display.set_byte(x + i, y, 0);
|
||||||
|
m_display.set_byte(x + i, y + 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::render_selection()
|
||||||
|
{
|
||||||
|
switch (m_state) {
|
||||||
|
case SetState::HOURS:
|
||||||
|
draw_line(0, 64, 24 * 2);
|
||||||
|
break;
|
||||||
|
case SetState::MINUTES:
|
||||||
|
draw_line(48, 64, 24 * 2);
|
||||||
|
break;
|
||||||
|
case SetState::SECONDS:
|
||||||
|
draw_line(96, 64, 24 * 2);
|
||||||
|
break;
|
||||||
|
case SetState::AM_PM:
|
||||||
|
break;
|
||||||
|
case SetState::ACK_NACK:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::refresh()
|
||||||
|
{
|
||||||
|
m_display.clear();
|
||||||
|
|
||||||
|
render_time();
|
||||||
|
render_selection();
|
||||||
|
|
||||||
|
m_display.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::enable()
|
||||||
|
{
|
||||||
|
|
||||||
|
BSP::RtcDriver::get_time(m_time);
|
||||||
|
m_state = SetState::HOURS;
|
||||||
|
m_is_acked = true;
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::disable()
|
||||||
|
{
|
||||||
|
m_display.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::notify_up_button()
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
case SetState::HOURS:
|
||||||
|
m_time.increment_hours();
|
||||||
|
break;
|
||||||
|
case SetState::MINUTES:
|
||||||
|
m_time.increment_minutes();
|
||||||
|
break;
|
||||||
|
case SetState::SECONDS:
|
||||||
|
m_time.increment_seconds();
|
||||||
|
break;
|
||||||
|
case SetState::AM_PM:
|
||||||
|
m_time.toggle_am_pm();
|
||||||
|
break;
|
||||||
|
case SetState::ACK_NACK:
|
||||||
|
m_is_acked = !m_is_acked;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::notify_middle_button()
|
||||||
|
{
|
||||||
|
switch (m_state) {
|
||||||
|
case SetState::HOURS:
|
||||||
|
m_state = SetState::MINUTES;
|
||||||
|
break;
|
||||||
|
case SetState::MINUTES:
|
||||||
|
m_state = SetState::SECONDS;
|
||||||
|
break;
|
||||||
|
case SetState::SECONDS:
|
||||||
|
m_state = SetState::AM_PM;
|
||||||
|
break;
|
||||||
|
case SetState::AM_PM:
|
||||||
|
m_state = SetState::ACK_NACK;
|
||||||
|
break;
|
||||||
|
case SetState::ACK_NACK:
|
||||||
|
if (m_is_acked) {
|
||||||
|
BSP::RtcDriver::set_time(m_time);
|
||||||
|
}
|
||||||
|
m_manager.pop_screen();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeScreen::notify_down_button()
|
||||||
|
{
|
||||||
|
switch(m_state) {
|
||||||
|
case SetState::HOURS:
|
||||||
|
m_time.decrement_hours();
|
||||||
|
break;
|
||||||
|
case SetState::MINUTES:
|
||||||
|
m_time.decrement_minutes();
|
||||||
|
break;
|
||||||
|
case SetState::SECONDS:
|
||||||
|
m_time.decrement_seconds();
|
||||||
|
break;
|
||||||
|
case SetState::AM_PM:
|
||||||
|
m_time.toggle_am_pm();
|
||||||
|
break;
|
||||||
|
case SetState::ACK_NACK:
|
||||||
|
m_is_acked = !m_is_acked;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
69
SetTimeScreen.h
Normal file
69
SetTimeScreen.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ScreenManager.h"
|
||||||
|
#include "DisplayDriver.h"
|
||||||
|
#include "ReturnCode.h"
|
||||||
|
#include "Task.h"
|
||||||
|
#include "Time.h"
|
||||||
|
#include "fontem.h"
|
||||||
|
|
||||||
|
class SetTimeScreen : public Screen {
|
||||||
|
public:
|
||||||
|
|
||||||
|
SetTimeScreen(BSP::DisplayDriver &display,
|
||||||
|
ScreenManager &m_manager);
|
||||||
|
|
||||||
|
Common::ReturnCode init();
|
||||||
|
Common::Schedule::NextTime execute() override;
|
||||||
|
|
||||||
|
void enable() override;
|
||||||
|
void disable() override;
|
||||||
|
void notify_up_button() override;
|
||||||
|
void notify_middle_button() override;
|
||||||
|
void notify_down_button() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void refresh();
|
||||||
|
void render_time();
|
||||||
|
void render_selection();
|
||||||
|
void display_number(uint32_t *x, uint32_t y, uint32_t tens, uint32_t ones, const font &f);
|
||||||
|
void draw_line(uint32_t x, uint32_t y, uint32_t width);
|
||||||
|
enum class SetState {
|
||||||
|
HOURS,
|
||||||
|
MINUTES,
|
||||||
|
SECONDS,
|
||||||
|
AM_PM,
|
||||||
|
ACK_NACK
|
||||||
|
};
|
||||||
|
|
||||||
|
BSP::DisplayDriver &m_display;
|
||||||
|
ScreenManager &m_manager;
|
||||||
|
|
||||||
|
SetState m_state;
|
||||||
|
bool m_is_acked = false;
|
||||||
|
Common::WallClockTime m_time;
|
||||||
|
};
|
||||||
38
Task.h
38
Task.h
@@ -29,8 +29,8 @@ namespace Common {
|
|||||||
namespace Schedule {
|
namespace Schedule {
|
||||||
|
|
||||||
enum class ScheduleType {
|
enum class ScheduleType {
|
||||||
AT_TIME,
|
AT_TIME = 0,
|
||||||
NEVER,
|
NEVER = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
class NextTime {
|
class NextTime {
|
||||||
@@ -63,8 +63,38 @@ public:
|
|||||||
return { ScheduleType::AT_TIME, time };
|
return { ScheduleType::AT_TIME, time };
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ScheduleType get_type() { return m_type; }
|
inline ScheduleType get_type() const { return m_type; }
|
||||||
inline time_t get_time() { return m_time; }
|
inline time_t get_time() const { return m_time; }
|
||||||
|
|
||||||
|
inline bool operator<(const NextTime other) const {
|
||||||
|
if (m_type < other.m_type) {
|
||||||
|
return true;
|
||||||
|
} else if (m_type == other.m_type) {
|
||||||
|
switch(m_type){
|
||||||
|
case ScheduleType::AT_TIME:
|
||||||
|
return m_time < other.m_time;
|
||||||
|
case ScheduleType::NEVER:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const NextTime other) const {
|
||||||
|
if (m_type != other.m_type) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
switch(m_type){
|
||||||
|
case ScheduleType::AT_TIME:
|
||||||
|
return m_time == other.m_time;
|
||||||
|
case ScheduleType::NEVER:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NextTime(ScheduleType type, time_t time) :
|
NextTime(ScheduleType type, time_t time) :
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace Schedule {
|
|||||||
|
|
||||||
class TaskScheduler {
|
class TaskScheduler {
|
||||||
public:
|
public:
|
||||||
virtual void add_task(Task &task, NextTime &time) = 0;
|
virtual void add_task(Task &task, const NextTime &time) = 0;
|
||||||
[[noreturn]] virtual void run() = 0;
|
[[noreturn]] virtual void run() = 0;
|
||||||
protected:
|
protected:
|
||||||
//virtual ~TaskScheduler() {}
|
//virtual ~TaskScheduler() {}
|
||||||
|
|||||||
141
Time.h
141
Time.h
@@ -79,4 +79,145 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class WallClockTime {
|
||||||
|
|
||||||
|
// TODO: Implement a saturating counter?
|
||||||
|
|
||||||
|
public:
|
||||||
|
WallClockTime()
|
||||||
|
: m_hours(0)
|
||||||
|
, m_minutes(0)
|
||||||
|
, m_seconds(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
WallClockTime(uint8_t hours, uint8_t minutes, uint8_t seconds)
|
||||||
|
: m_hours(hours)
|
||||||
|
, m_minutes(minutes)
|
||||||
|
, m_seconds(seconds)
|
||||||
|
{}
|
||||||
|
|
||||||
|
static inline uint8_t hour24_to_hour12(uint8_t hour24) {
|
||||||
|
if (hour24 == 0) {
|
||||||
|
return 12;
|
||||||
|
} else if (hour24 > 12) {
|
||||||
|
return hour24 - 12;
|
||||||
|
} else {
|
||||||
|
return hour24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t hour24_is_am(uint8_t hour24) {
|
||||||
|
return hour24 >= 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_hours_12() const {
|
||||||
|
return hour24_to_hour12(m_hours);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_hours_12_tens() const {
|
||||||
|
return get_hours_12() / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_hours_12_ones() const {
|
||||||
|
return get_hours_12() % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool get_is_pm() const {
|
||||||
|
return m_hours >= 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_hours_24() const {
|
||||||
|
return m_hours;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_hours_24_ones() const {
|
||||||
|
return m_hours % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_hours_24_tens() const {
|
||||||
|
return m_hours / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_minutes() const {
|
||||||
|
return m_minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_minutes_ones() const {
|
||||||
|
return m_minutes % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_minutes_tens() const {
|
||||||
|
return m_minutes / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_seconds() const {
|
||||||
|
return m_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_seconds_ones() const {
|
||||||
|
return m_seconds % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t get_seconds_tens() const {
|
||||||
|
return m_seconds / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void increment_hours() {
|
||||||
|
m_hours++;
|
||||||
|
if (m_hours >= 24) {
|
||||||
|
m_hours = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void increment_minutes() {
|
||||||
|
m_minutes++;
|
||||||
|
if (m_minutes >= 60) {
|
||||||
|
m_minutes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void increment_seconds() {
|
||||||
|
m_seconds++;
|
||||||
|
if (m_seconds >= 60) {
|
||||||
|
m_seconds = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void decrement_hours() {
|
||||||
|
if (m_hours == 0) {
|
||||||
|
m_hours = 23;
|
||||||
|
} else {
|
||||||
|
m_hours--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void decrement_minutes() {
|
||||||
|
if (m_minutes == 0) {
|
||||||
|
m_minutes = 59;
|
||||||
|
} else {
|
||||||
|
m_minutes--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void decrement_seconds() {
|
||||||
|
if (m_seconds == 0) {
|
||||||
|
m_seconds = 59;
|
||||||
|
} else {
|
||||||
|
m_seconds--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void toggle_am_pm() {
|
||||||
|
if (m_hours < 12) {
|
||||||
|
m_hours += 12;
|
||||||
|
} else {
|
||||||
|
m_hours -= 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t m_hours;
|
||||||
|
uint8_t m_minutes;
|
||||||
|
uint8_t m_seconds;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
68
main.cpp
68
main.cpp
@@ -23,10 +23,13 @@
|
|||||||
#include "RtcDriver.h"
|
#include "RtcDriver.h"
|
||||||
#include "DisplayDriver.h"
|
#include "DisplayDriver.h"
|
||||||
#include "SpiDriver.h"
|
#include "SpiDriver.h"
|
||||||
#include "DisplayTimeTask.h"
|
|
||||||
#include "LptimPwm.h"
|
#include "LptimPwm.h"
|
||||||
#include "ButtonManager.h"
|
#include "ButtonManager.h"
|
||||||
|
|
||||||
|
#include "ScreenManager.h"
|
||||||
|
#include "DisplayTimeScreen.h"
|
||||||
|
#include "SetTimeScreen.h"
|
||||||
|
|
||||||
#include "stm32l0xx.h"
|
#include "stm32l0xx.h"
|
||||||
|
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
@@ -37,8 +40,11 @@ static Common::Schedule::LowPowerTaskScheduler<10> g_sched;
|
|||||||
static BSP::SpiDriver g_spi(g_sched);
|
static BSP::SpiDriver g_spi(g_sched);
|
||||||
static BSP::DisplayDriver g_display(g_sched, g_spi);
|
static BSP::DisplayDriver g_display(g_sched, g_spi);
|
||||||
static BSP::LptimPwm g_lptim_pwm(LPTIM1);
|
static BSP::LptimPwm g_lptim_pwm(LPTIM1);
|
||||||
static BSP::ButtonManager g_btn_manager(0, 1, 3, Time::millis(1));
|
static BSP::ButtonManager g_btn_manager(2, 1, 0, Time::millis(1));
|
||||||
static DisplayTimeTask g_display_time(g_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 DisplayTimeScreen g_display_time_screen(g_display, g_screen_manager, g_set_time_screen);
|
||||||
|
|
||||||
extern "C" void __cxa_pure_virtual() { while(1) {} }
|
extern "C" void __cxa_pure_virtual() { while(1) {} }
|
||||||
|
|
||||||
@@ -97,7 +103,6 @@ void SystemInit()
|
|||||||
CLR(RCC->CFGR,
|
CLR(RCC->CFGR,
|
||||||
RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL | RCC_CFGR_PLLDIV);
|
RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL | RCC_CFGR_PLLDIV);
|
||||||
|
|
||||||
|
|
||||||
/*!< Disable all interrupts */
|
/*!< Disable all interrupts */
|
||||||
RCC->CIER = 0x00000000;
|
RCC->CIER = 0x00000000;
|
||||||
|
|
||||||
@@ -133,45 +138,42 @@ static void _init(void)
|
|||||||
g_spi.init();
|
g_spi.init();
|
||||||
g_btn_manager.init();
|
g_btn_manager.init();
|
||||||
g_display.init();
|
g_display.init();
|
||||||
g_display_time.init();
|
g_screen_manager.init();
|
||||||
|
g_screen_manager.set_root_screen(g_display_time_screen);
|
||||||
|
|
||||||
// Enqueue each of the tasks
|
// Enqueue each of the tasks
|
||||||
Common::Schedule::NextTime asap = Common::Schedule::NextTime::asap();
|
Common::Schedule::NextTime asap = Common::Schedule::NextTime::asap();
|
||||||
g_sched.add_task(g_spi, asap);
|
g_sched.add_task(g_spi, asap);
|
||||||
g_sched.add_task(g_btn_manager, asap);
|
g_sched.add_task(g_btn_manager, asap);
|
||||||
g_sched.add_task(g_display, asap);
|
g_sched.add_task(g_display, asap);
|
||||||
g_sched.add_task(g_display_time, asap);
|
g_sched.add_task(g_screen_manager, asap);
|
||||||
|
|
||||||
// And we're off! This will never return
|
// And we're off! This will never return
|
||||||
g_sched.run();
|
g_sched.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" void NMI_Handler() {while (1);}
|
extern "C" void NMI_Handler() {while (1);}
|
||||||
extern "C" void HardFault_Handler() {while (1);}
|
extern "C" void HardFault_Handler() {while (1);}
|
||||||
extern "C" void SVC_Handler() {while (1);}
|
extern "C" void SVC_Handler() {while (1);}
|
||||||
extern "C" void PendSV_Handler() {while (1);}
|
extern "C" void PendSV_Handler() {while (1);}
|
||||||
extern "C" void SysTick_Handler() {while (1);}
|
extern "C" void SysTick_Handler() {while (1);}
|
||||||
|
|
||||||
extern "C" void WWDG_IRQHandler() {while (1);}
|
extern "C" void WWDG_IRQHandler() {while (1);}
|
||||||
extern "C" void PVD_IRQHandler() {while (1);}
|
extern "C" void PVD_IRQHandler() {while (1);}
|
||||||
extern "C" void WDT_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 FLASH_IRQHandler() {while (1);}
|
extern "C" void RCC_CRS_IRQHandler() {while (1);}
|
||||||
extern "C" void RCC_CRS_IRQHandler() {while (1);}
|
extern "C" void DMA1_CHANNEL1_IRQHandler() {while (1);}
|
||||||
// extern "C" void EXTI_1_0_IRQHandler() {while (1);}
|
extern "C" void DMA1_CHANNEL3_2_IRQHandler() {while (1);}
|
||||||
// extern "C" void EXTI_3_2_IRQHandler() {while (1);}
|
extern "C" void DMA_CHANNEL_7_4_IRQHandler() {while (1);}
|
||||||
// extern "C" void EXTI_15_4_IRQHandler() {while (1);}
|
extern "C" void ADC_COMP_IRQHandler() {while (1);}
|
||||||
extern "C" void DMA1_CHANNEL1_IRQHandler() {while (1);}
|
extern "C" void LPTIM1_IRQHandler() {while (1);}
|
||||||
extern "C" void DMA1_CHANNEL3_2_IRQHandler() {while (1);}
|
extern "C" void USART4_USART5_IRQHandler() {while (1);}
|
||||||
extern "C" void DMA_CHANNEL_7_4_IRQHandler() {while (1);}
|
extern "C" void TIM2_IRQHandler() {while (1);}
|
||||||
extern "C" void ADC_COMP_IRQHandler() {while (1);}
|
extern "C" void TIM3_IRQHandler() {while (1);}
|
||||||
extern "C" void LPTIM1_IRQHandler() {while (1);}
|
extern "C" void TIM6_IRQHandler() {while (1);}
|
||||||
extern "C" void USART4_USART5_IRQHandler() {while (1);}
|
extern "C" void TIM7_IRQHandler() {while (1);}
|
||||||
extern "C" void TIM2_IRQHandler() {while (1);}
|
extern "C" void TIM21_IRQHandler() {while (1);}
|
||||||
extern "C" void TIM3_IRQHandler() {while (1);}
|
extern "C" void I2C3_IRQHandler() {while (1);}
|
||||||
extern "C" void TIM6_IRQHandler() {while (1);}
|
extern "C" void TIM22_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);}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user