Skip to content

Commit

Permalink
WIP on clock modes and add a lot of things.
Browse files Browse the repository at this point in the history
  • Loading branch information
LambdAurora committed Jun 24, 2020
1 parent 813b4d1 commit 22cac8b
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 16 deletions.
2 changes: 1 addition & 1 deletion firmware/CMakeLists.txt
Expand Up @@ -34,5 +34,5 @@ add_otterpill_executable(lambda_nixie_clock ${STM32CubeF0_SOURCES}
src/console.cpp
src/nixie.cpp
src/config.cpp
#src/clock_mode.cpp WIP
src/clock_mode.cpp
src/main.cpp)
72 changes: 72 additions & 0 deletions firmware/include/clock_mode.hpp
@@ -0,0 +1,72 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of lambda_nixie_clock.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/

#ifndef LAMBDA_NIXIE_CLOCK_CLOCK_MODE_HPP
#define LAMBDA_NIXIE_CLOCK_CLOCK_MODE_HPP

#include <rtc.hpp>
#include <nixie.hpp>
#include <config.hpp>
#include <map>

class ClockManager;

/**
* Represents a clock mode.
*/
class ClockMode {
public:
virtual void update(ClockManager& manager) = 0;

/**
* Returns the timeout before the clock manager switches back to the default mode.
* @return 0 to disable the timeout, else a timeout value in seconds.
*/
virtual uint32_t timeout() {
return 0;
}
};

class TimeClockMode : public ClockMode {
public:
void update(ClockManager& manager) override;
};

class ClockManager {
private:
std::map<std::string, ClockMode*> modes;
std::string _default;
std::string _current_mode;
DS3231 _rtc;
rtc_t _current_clock;
rtc_t _last_clock;
Config _config;
Configuration _config_manager;

public:
NixieArray nixies;

explicit ClockManager(const DS3231& rtc, const Configuration& config, std::string default_name, ClockMode* default_mode);

void register_mode(const std::string& name, ClockMode* mode);

ClockMode* get_mode(const std::string& name);

void update();

DS3231& rtc();

Config& config();

Configuration& config_manager();

rtc_t& current_clock();
};

#endif //LAMBDA_NIXIE_CLOCK_CLOCK_MODE_HPP
4 changes: 4 additions & 0 deletions firmware/include/common.h
Expand Up @@ -24,6 +24,10 @@ extern "C" {
// The size of the nixie tube array of the clock.
// Even numbers are recommended.
#define NIXIE_COUNT 8
// Allow the blinking of the status LED of the OtterPill.
#define STATUS_LED 1
#define STATUS_LED_GPIO_PORT GPIOB
#define STATUS_LED_GPIO_PIN GPIO_PIN_13
/* Definition for shift registers */
// PA1 -> SDI
#define SDI_PIN GPIO_PIN_1
Expand Down
2 changes: 2 additions & 0 deletions firmware/include/console.hpp
Expand Up @@ -42,6 +42,8 @@ namespace console

outstream& operator<<(int i);

outstream& operator<<(size_t i);

outstream& operator<<(char ch);
};

Expand Down
5 changes: 5 additions & 0 deletions firmware/include/nixie.hpp
Expand Up @@ -139,6 +139,11 @@ class NixieArray
return *this;
}

/**
* Resets every nixie tube state.
*/
void reset();

[[nodiscard]] constexpr inline size_t size() const {
return NIXIE_COUNT;
}
Expand Down
29 changes: 28 additions & 1 deletion firmware/include/rtc.hpp
Expand Up @@ -22,6 +22,25 @@
#include <stm32f0xx_hal_i2c.h>
#include <cinttypes>

/**
* Returns whether the specified year is a leap year.
* @param year The year to check.
* @return True if the year is a leap year, else false.
*/
constexpr bool is_leap_year(uint32_t year);

/**
* Returns the days in the specified month of the specified year.
* @param month The month.
* @param year The year.
* @return The number of days in month of the specified year.
*/
constexpr uint8_t days_in_month(uint8_t month, uint32_t year);

constexpr uint16_t days_before_month(uint8_t month, uint32_t year);

constexpr uint64_t days_before_year(uint32_t year);

/**
* Represents the real time clock.
*/
Expand All @@ -33,9 +52,17 @@ typedef struct
uint8_t day_of_week;
uint8_t day_of_month;
uint8_t month;
uint32_t year;
uint32_t year = 0;
} rtc_t;

constexpr uint64_t date_to_ordinal(uint8_t day_of_month, uint8_t month, uint32_t year);

constexpr uint64_t date_to_ordinal(const rtc_t& date);

constexpr uint8_t get_day_of_week(uint8_t day_of_month, uint8_t month, uint32_t year);

constexpr uint8_t get_day_of_week(const rtc_t& date);

enum Alarm : uint8_t {
ALARM_1 = 0,
ALARM_2 = 1
Expand Down
115 changes: 115 additions & 0 deletions firmware/src/clock_mode.cpp
@@ -0,0 +1,115 @@
/*
* Copyright © 2020 LambdAurora <aurora42lambda@gmail.com>
*
* This file is part of lambda_nixie_clock.
*
* Licensed under the MIT license. For more information,
* see the LICENSE file.
*/

#include <clock_mode.hpp>
#include <console.hpp>

void TimeClockMode::update(ClockManager& manager) {
bool h24 = manager.config().h24;

auto hour = manager.current_clock().hour;
// If 12H mode, then fix hour value.
if (!h24 && hour > 12)
hour -= 12;

auto hour_unit = hour % 10;
hour /= 10;
auto minute = manager.current_clock().minute;
auto minute_unit = minute % 10;
minute /= 10;
auto second = manager.current_clock().second;
auto second_unit = second % 10;
second /= 10;

auto separator_on = !(second_unit % 2);

// Reset to avoid display bugs.
manager.nixies.reset();

// Straightforward but not very good.
#if NIXIE_COUNT <= 6
manager.nixies.at(0).number(hour);
manager.nixies.at(1).number(hour_unit);
manager.nixies.at(2).number(minute);
manager.nixies.at(3).number(minute_unit);
if (manager.nixies.size() == 6) {
manager.nixies.at(4).number(second);
manager.nixies.at(5).number(second_unit);
}
#else
// Align to center if there's more than 8 nixie tubes.
size_t start = NIXIE_COUNT / 2 - 4;
manager.nixies.at(start).number(hour);
manager.nixies.at(start + 1).number(hour_unit);
manager.nixies.at(start + 3).number(minute);
manager.nixies.at(start + 4).number(minute_unit);
manager.nixies.at(start + 6).number(second);
manager.nixies.at(start + 7).number(second_unit);
for (size_t i = start + 2; i <= start + 5; i += 3) {
manager.nixies.at(i).left_comma(separator_on);
manager.nixies.at(i).right_comma(separator_on);
}
#endif

manager.nixies.display(false);
}

ClockManager::ClockManager(const DS3231& rtc, const Configuration& config, std::string default_name, ClockMode* default_mode) : _default(std::move(default_name)),
_rtc(rtc), _config_manager(config) {
register_mode(this->_default, default_mode);
this->_current_mode = this->_default;
this->_config_manager.read(&this->_config);
}

void ClockManager::register_mode(const std::string& name, ClockMode* mode) {
this->modes[name] = mode;
}

ClockMode* ClockManager::get_mode(const std::string& name) {
if (!this->modes.count(name))
return this->modes[this->_default];
return this->modes[name];
}

void ClockManager::update() {
this->_rtc.get_time(&this->_current_clock);
if (this->_last_clock.year == 0)
this->_last_clock = this->_current_clock;

// Blink the status LED every seconds if enabled.
#if STATUS_LED == 1
if (this->_current_clock.second % 2) {
HAL_GPIO_WritePin(STATUS_LED_GPIO_PORT, STATUS_LED_GPIO_PIN, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(STATUS_LED_GPIO_PORT, STATUS_LED_GPIO_PIN, GPIO_PIN_RESET);
}
#endif

ClockMode* mode = this->get_mode(this->_current_mode);

mode->update(*this);

this->_last_clock = this->_current_clock;
}

DS3231& ClockManager::rtc() {
return this->_rtc;
}

Config& ClockManager::config() {
return this->_config;
}

Configuration& ClockManager::config_manager() {
return this->_config_manager;
}

rtc_t& ClockManager::current_clock() {
return this->_current_clock;
}
4 changes: 4 additions & 0 deletions firmware/src/console.cpp
Expand Up @@ -46,6 +46,10 @@ namespace console
return this->operator<<(std::to_string(i));
}

outstream& outstream::operator<<(size_t i) {
return this->operator<<(std::to_string(i));
}

outstream& outstream::operator<<(char ch) {
this->_buffer += ch;
return *this;
Expand Down
58 changes: 46 additions & 12 deletions firmware/src/main.cpp
Expand Up @@ -8,12 +8,7 @@
*/

#include <main.h>
#include <rtc.hpp>
#include <nixie.hpp>
#include <config.hpp>

#define LED_PIN GPIO_PIN_13
#define INVERT_STATE(state) (state == GPIO_PIN_RESET ? GPIO_PIN_SET : GPIO_PIN_RESET)
#include <clock_mode.hpp>

#define INIT_OUTPUT_GPIO(PIN, PORT) {\
HAL_GPIO_WritePin(PORT, PIN, GPIO_PIN_RESET);\
Expand All @@ -36,14 +31,14 @@ static void setup_gpio() {
__HAL_RCC_GPIOA_CLK_ENABLE();

// Let's initialize the blinking LED pin.
HAL_GPIO_WritePin(GPIOB, LED_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(STATUS_LED_GPIO_PORT, STATUS_LED_GPIO_PIN, GPIO_PIN_RESET);

// HAL GPIO initialization is a bit long honestly.
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Pin = STATUS_LED_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_Init(STATUS_LED_GPIO_PORT, &GPIO_InitStruct);

// Shift register GPIO.
INIT_OUTPUT_GPIO(SDI_PIN, SDI_GPIO_PORT)
Expand Down Expand Up @@ -77,18 +72,57 @@ int main() {
console::setup();

auto ds3231 = DS3231(i2c1h);
if (ds3231.has_lost_power()) {
// If the RTC module lost power, restore the last time and date the firmware knew.
// Mmm dd yyyy
std::string date_str = __DATE__;
// HH:mm:ss
std::string time_str = __TIME__;

const auto ASCII_ZERO = static_cast<uint8_t>('0');

rtc_t clock;
clock.hour = (time_str[0] - ASCII_ZERO) * 10 + (time_str[1] - ASCII_ZERO);
clock.minute = (time_str[3] - ASCII_ZERO) * 10 + (time_str[4] - ASCII_ZERO);
clock.second = (time_str[6] - ASCII_ZERO) * 10 + (time_str[7] - ASCII_ZERO);

std::array<std::string, 12> months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
for (uint8_t i = 0; i < months.size(); i++) {
if (date_str.find(months[i]) != std::string::npos)
clock.month = i + 1;
}

auto day_of_month_ten = (date_str[4] - ASCII_ZERO) * 10;
if (date_str[4] == ' ') day_of_month_ten = 0;
clock.day_of_month = day_of_month_ten + (date_str[5] - ASCII_ZERO);
clock.year = (date_str[7] - ASCII_ZERO) * 1000
+ (date_str[8] - ASCII_ZERO) * 100
+ (date_str[9] - ASCII_ZERO) * 10
+ (date_str[10] - ASCII_ZERO);
clock.day_of_week = get_day_of_week(clock);

ds3231.set_time(clock);
}

auto configuration = Configuration({EE24_ADDRESS, i2c1h});
if (!configuration.init())
return 1;
// @TODO remove reset
configuration.reset();

auto nixies = NixieArray();
nixies.from_string("4;2;0;0;0;0;0;0");
nixies.reset();

ds3231.enable_oscillator();

rtc_t clock;
TimeClockMode time_clock_mode;
auto clock = ClockManager(ds3231, configuration, "time", &time_clock_mode);

while (true) {
clock.update();
}

/*rtc_t clock;
Config config;
ds3231.reset_alarm(ALARM_2);
Expand All @@ -109,7 +143,7 @@ int main() {
nixies.display(false);
HAL_Delay(500);
}
}*/

return 0;
}

0 comments on commit 22cac8b

Please sign in to comment.