Full color support, rework the directory structure

This commit is contained in:
2019-08-05 22:15:40 -07:00
parent 77f09bca16
commit d5ddd76bef
121 changed files with 93737 additions and 702 deletions

View File

@@ -0,0 +1,189 @@
/*
* 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 <cstring>
#include "Bsp/Drivers/DisplayDriver.h"
#include "Bsp/macros.h"
#include "Bsp/font.h"
namespace BSP {
using Common::Schedule::NextTime;
using Common::ReturnCode;
DisplayDriver::DisplayDriver(Common::Schedule::TaskScheduler &scheduler, SpiDriver &spi)
: m_scheduler(scheduler)
, m_spi(spi)
, m_is_dirty(true)
, m_dirty_line_min(0)
, m_dirty_line_max(0)
{
buffer_init(DEFAULT_COLOR);
}
ReturnCode DisplayDriver::init()
{
return Common::ReturnCode::OK;
}
NextTime DisplayDriver::execute()
{
return NextTime::never();
}
// TODO: write my own implementation
#define R2(n) n, n + 2*64, n + 1*64, n + 3*64
#define R4(n) R2(n), R2(n + 2*16), R2(n + 1*16), R2(n + 3*16)
#define R6(n) R4(n), R4(n + 2*4 ), R4(n + 1*4 ), R4(n + 3*4 )
static constexpr unsigned char BitReverseTable256[256] =
{
R6(0), R6(2), R6(1), R6(3)
};
unsigned char ReverseBitsLookupTable(unsigned char v)
{
return BitReverseTable256[v];
}
void DisplayDriver::buffer_init(Color color)
{
uint8_t dataval = 0xFF;
if (color == Color::BLACK) {
dataval = 0x00;
}
// TODO: Support initializing to other colors
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 + 1; // Line numbers start at 1
for (size_t j = 0; j < ARRAY_SIZE(line.data); j++) {
line.data[j] = dataval;
}
}
m_buffer.dummy = 0;
}
void DisplayDriver::set_dirty(unsigned int y)
{
if (!m_is_dirty) {
m_is_dirty = true;
m_dirty_line_min = y;
m_dirty_line_max = y;
} else {
m_dirty_line_min = MIN(y, m_dirty_line_min);
m_dirty_line_max = MAX(y, m_dirty_line_max);
}
}
// TODO: write my own implementation
#define R2(n) n, n + 2*64, n + 1*64, n + 3*64
#define R4(n) R2(n), R2(n + 2*16), R2(n + 1*16), R2(n + 3*16)
#define R6(n) R4(n), R4(n + 2*4 ), R4(n + 1*4 ), R4(n + 3*4 )
void DisplayDriver::draw_hline(uint32_t x, uint32_t y, uint32_t width, Color color)
{
for (uint32_t i = 0; i < width; i++) {
set_pixel(x + i, y, color);
}
}
void DisplayDriver::write_glyph(uint32_t x_off, uint32_t y_off,
const struct font *f, const struct glyph *g,
Color color)
{
for (size_t x = 0; x < g->width; x++) {
for (size_t y = 0; y < g->height; y++) {
uint32_t byte_x = x / 8;
uint32_t bit_x = 7 - (x % 8);
uint8_t bit = (g->bitmap[byte_x + g->width_bytes * y] >> bit_x) & 1;
if (bit) {
set_pixel(g->left + x_off + x, y_off + y + f->height - g->top, color);
}
}
}
}
void DisplayDriver::char_at(uint32_t *x_off, uint32_t y_off, char c, const struct font *font, Color color)
{
const struct glyph *g = glyph_for_char(font, c);
if (g == NULL) {
return;
}
if (*x_off + font->width >= DISPLAY_WIDTH) {
return;
}
write_glyph(*x_off, y_off, font, g, color);
m_dirty_line_min = MIN(m_dirty_line_min, y_off);
m_dirty_line_max = MAX(m_dirty_line_max, y_off + g->height);
m_is_dirty = true;
*x_off += font->width;
}
void DisplayDriver::string_at(uint32_t *x_off, uint32_t y_off, const char *string, const struct font *font, Color color)
{
int i = 0;
while (string[i]) {
char_at(x_off, y_off, string[i], font, color);
i++;
}
}
void DisplayDriver::refresh()
{
if (!m_is_dirty) {
return;
}
uint8_t *start = (uint8_t *) &m_buffer.lines[m_dirty_line_min];
// Data size
size_t size = sizeof(m_buffer.lines[0]) * (m_dirty_line_max - m_dirty_line_min + 1);
// Trailer dummy data
size += 2;
m_spi.tx_blocking(start, size);
m_is_dirty = false;
m_dirty_line_min = DISPLAY_HEIGHT - 1;
m_dirty_line_max = 0;
}
void DisplayDriver::clear(Color color)
{
buffer_init(color);
m_is_dirty = true;
m_dirty_line_min = 0;
m_dirty_line_max = DISPLAY_HEIGHT - 1;
}
//TODO: put me somewhere fonty
const struct glyph *DisplayDriver::glyph_for_char(const struct font *font, char c)
{
return font->glyphs[(size_t) c];
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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 "Bsp/Task.h"
#include "Bsp/Drivers/SpiDriver.h"
#include "Bsp/font.h"
namespace BSP {
class DisplayDriver final : public Common::Schedule::Task {
public:
DisplayDriver(Common::Schedule::TaskScheduler &scheduler, SpiDriver &spi);
/**
* Common::Schedule::Task
*/
Common::ReturnCode init();
Common::Schedule::NextTime execute() override;
static constexpr uint32_t BITS_PER_PIXEL = 3;
enum class Color : uint8_t {
BLACK = 0,
BLUE = 4,
GREEN = 2,
CYAN = 6,
RED = 1,
MAGENTA = 5,
YELLOW = 3,
WHITE = 7,
};
static constexpr Color DEFAULT_COLOR = Color::BLACK;
/**
* DisplayDriver
*/
void inline set_pixel(uint32_t x, uint32_t y, Color color=DEFAULT_COLOR);
void char_at(uint32_t *x_off, uint32_t y_off, char c, const struct font *font, Color color=DEFAULT_COLOR);
void string_at(uint32_t *x_off, uint32_t y_off,
const char *string, const struct font *font,
Color color=DEFAULT_COLOR);
void draw_hline(uint32_t x, uint32_t y, uint32_t width, Color color=DEFAULT_COLOR);
void refresh();
void clear(Color color=Color::WHITE);
inline uint32_t get_width() {
return DISPLAY_WIDTH;
}
inline uint32_t get_height() {
return DISPLAY_HEIGHT;
}
private:
void buffer_init(Color color);
void set_dirty(unsigned int y);
const struct glyph *glyph_for_char(const struct font *font, char c);
void write_glyph(uint32_t x_off, uint32_t y_off,
const struct font *font, const struct glyph *g,
Color color);
static constexpr uint32_t DISPLAY_WIDTH = 128;
static constexpr uint32_t DISPLAY_HEIGHT = 128;
struct display_line
{
uint8_t mode;
uint8_t line;
uint8_t data[DISPLAY_WIDTH * BITS_PER_PIXEL / 8];
};
struct display_buffer
{
struct display_line lines[DISPLAY_HEIGHT];
uint16_t dummy;
};
Common::Schedule::TaskScheduler &m_scheduler;
SpiDriver &m_spi;
struct display_buffer m_buffer;
bool m_is_dirty;
uint8_t m_dirty_line_min;
uint8_t m_dirty_line_max;
};
void inline DisplayDriver::set_pixel(uint32_t x, uint32_t y, Color color)
{
struct display_line &line = m_buffer.lines[y];
uint32_t x_pos = x * BITS_PER_PIXEL;
uint32_t x_byte = x_pos / 8;
uint32_t x_bit = x_pos % 8;
uint8_t c = (uint8_t) color;
constexpr uint8_t mask = (1 << BITS_PER_PIXEL) - 1;
if (x_bit + BITS_PER_PIXEL > 8) {
line.data[x_byte] &= ~(mask << x_bit);
line.data[x_byte] |= c << x_bit;
x_byte++;
line.data[x_byte] &= ~(mask >> (8 - x_bit));
line.data[x_byte] |= c >> (8 - x_bit);
} else {
line.data[x_byte] &= ~(mask << x_bit);
line.data[x_byte] |= c << x_bit;
}
set_dirty(y);
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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 "Bsp/SystemTime.h"
#include "Bsp/Drivers/LowPower.h"
#include "Bsp/macros.h"
#include "stm32l0xx.h"
uint32_t wakeups = 0;
namespace BSP {
using Common::ReturnCode;
ReturnCode LowPower::init()
{
enable_debug();
return ReturnCode::OK;
}
ReturnCode LowPower::enable_debug()
{
/* Enable Clocks */
SET(RCC->APB2ENR, RCC_APB2ENR_DBGEN);
SET(RCC->APB2SMENR, RCC_APB2SMENR_DBGSMEN);
SET(DBGMCU->CR, DBGMCU_CR_DBG_STOP);
SET(DBGMCU->CR, DBGMCU_CR_DBG_SLEEP);
SET(DBGMCU->CR, DBGMCU_CR_DBG_STANDBY);
return ReturnCode::OK;
}
ReturnCode LowPower::disable_debug()
{
CLR(DBGMCU->CR, DBGMCU_CR_DBG_STOP);
CLR(DBGMCU->CR, DBGMCU_CR_DBG_SLEEP);
CLR(DBGMCU->CR, DBGMCU_CR_DBG_STANDBY);
CLR(RCC->APB2SMENR, RCC_APB2SMENR_DBGSMEN);
return ReturnCode::OK;
}
ReturnCode LowPower::sleep()
{
// TODO: unimplemented
return ReturnCode::FAIL;
}
ReturnCode LowPower::stop()
{
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
__WFI(); // enter low-power mode (Wake from interrupt)
wakeups++;
return ReturnCode::OK;
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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 "Bsp/ReturnCode.h"
extern uint32_t wakeups;
namespace BSP {
class LowPower {
public:
LowPower() = delete;
static Common::ReturnCode init();
static Common::ReturnCode sleep();
static Common::ReturnCode stop();
static Common::ReturnCode enable_debug();
static Common::ReturnCode disable_debug();
};
}

View File

@@ -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 "Bsp/Drivers/LptimPwm.h"
#include "Bsp/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;
}
}

View File

@@ -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 "Bsp/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;
};
}

View File

@@ -0,0 +1,296 @@
/*
* 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 <new>
#include "Bsp/Drivers/RtcDriver.h"
#include "Bsp/macros.h"
namespace BSP {
using Common::ReturnCode;
using Common::time_t;
RtcDriver::RtcSystemTimer RtcDriver::m_sys_timer;
ReturnCode RtcDriver::init()
{
init_hw();
return ReturnCode::OK;
}
void RtcDriver::enable_rtc_write()
{
/*<! Disable write protection */
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
}
void RtcDriver::disable_rtc_write()
{
/*<! Enable write protection */
RTC->WPR = 0x00;
}
void RtcDriver::enable_periodic_alarm()
{
SET(RTC->ALRMAR, RTC_ALRMAR_MSK4 | RTC_ALRMAR_MSK3 | RTC_ALRMAR_MSK2 | RTC_ALRMAR_MSK1);
// Only calculate alarms when second rolls over
SET_TO(RTC->ALRMASSR, RTC_ALRMASSR_MASKSS, RTC_ALRMASSR_MASKSS);
SET(RTC->CR, RTC_CR_ALRAE | RTC_CR_ALRAIE);
SET(EXTI->IMR, EXTI_IMR_IM17);
SET(EXTI->EMR, EXTI_EMR_EM17);
SET(EXTI->RTSR, EXTI_RTSR_RT17);
}
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);
/*<! Set RTC input clock to the LSE (low-speed external 32.768kHz) clock */
if (!(RCC->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)) {}
/*<! Set the Clock Prescalers (32.768kHz / 1 / 32768 = 1Hz */
/*<! Set the Async prescaler to the Maximum (divide the clock by 128) */
// 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_TO(RTC->PRER, RTC_PRER_PREDIV_S, (LSE_CLOCK_FREQ - 1));
/*<! Load initial date and time */
// 12-Hour format
SET(RTC->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(Common::WallClockTime &wall_time)
{
/*<! 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
not using the date here, but we do need to clear the stale value. */
(void) RTC->DR;
// TODO: reread TR + PM for consistency
uint32_t time = RTC->TR;
unsigned int hours = 0, minutes = 0, seconds = 0;
hours += 10 * STM32_GET_FIELD(time, RTC_TR_HT);
hours += STM32_GET_FIELD(time, RTC_TR_HU);
minutes += 10 * STM32_GET_FIELD(time, RTC_TR_MNT);
minutes += STM32_GET_FIELD(time, RTC_TR_MNU);
seconds += 10 * STM32_GET_FIELD(time, RTC_TR_ST);
seconds += STM32_GET_FIELD(time, RTC_TR_SU);
if (STM32_GET_FIELD(time, RTC_TR_PM)) {
hours += 12;
}
new (&wall_time) Common::WallClockTime(hours, minutes, seconds);
return ReturnCode::OK;
}
ReturnCode RtcDriver::set_time(const Common::WallClockTime &wall_time)
{
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_ones() << RTC_TR_SU_Pos);
RTC->TR = time;
CLR(RTC->ISR, RTC_ISR_INIT);
while ((RTC->ISR & RTC_ISR_INITF)) {}
while (!(RTC->ISR & RTC_ISR_RSF)) {}
disable_rtc_write();
return ReturnCode::OK;
}
ReturnCode RtcDriver::set_wakeup_in(Common::time_t wakeup_delay)
{
/*<! 2^64 / (1000000 * 32768) / 60 / 60 / 24 / 365 = ~17.85 This
value will only overflow for wakeup_delays > 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();
/*<! If there is an ongoing wakeup, disable it */
if (RTC->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()
{
/** TODO: Atomic increment */
m_seconds++;
}
void RtcDriver::increment_seconds()
{
m_sys_timer.increment_seconds();
}
static uint32_t wakeup_alarms = 0;
extern "C" void RTC_IRQHandler()
{
// Clear the wakeup and alarm interrupts in the EXTI
SET(EXTI->PR, EXTI_PR_PIF20 | EXTI_PR_PIF17);
if (RTC->ISR & RTC_ISR_ALRAF) {
RtcDriver::increment_seconds();
CLR(RTC->ISR, RTC_ISR_ALRAF);
}
if (RTC->ISR & RTC_ISR_WUTF) {
wakeup_alarms++;
// 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);
}
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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 <stdint.h>
#include "Bsp/SystemTime.h"
#include "Bsp/ReturnCode.h"
#include "Bsp/Time.h"
#include "stm32l0xx.h"
namespace BSP {
class RtcDriver final {
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(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);
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:
/** ~136 years. Good enough for me. */
uint32_t m_seconds;
static constexpr uint32_t LSE_CLOCK_FREQ = 32768;
};
static RtcSystemTimer m_sys_timer;
};
}

View File

@@ -0,0 +1,117 @@
/*
* 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 "Bsp/Drivers/SpiDriver.h"
#include "Bsp/macros.h"
namespace BSP {
using RC = Common::ReturnCode;
using Common::Schedule::TaskScheduler;
using Common::Schedule::NextTime;
using Common::Time;
SpiDriver::SpiDriver(TaskScheduler &scheduler)
: m_scheduler(scheduler)
, m_spi(SPI1)
{}
void SpiDriver::init()
{
SET(RCC->IOPENR, RCC_IOPENR_IOPAEN);
SET(RCC->IOPENR, RCC_IOPENR_IOPBEN);
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
/* Assign SPI_MOSI to PB1, since PA7 is taken by LPTIM_OUT */
GPIOB->AFR[0] &= ~GPIO_AFRH_AFRH1;
GPIOB->AFR[0] |= 1u << GPIO_AFRH_AFRH1_Pos;
SET_TO(GPIOB->MODER, GPIO_MODER_MODE1, 2u << GPIO_MODER_MODE1_Pos);
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_1;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPD12;
// SPI1 NSS (PA4)
SET_TO(GPIOA->MODER, GPIO_MODER_MODE4, 1u << GPIO_MODER_MODE4_Pos);
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD4;
// SPI1 SCK (PA5)
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL5;
SET_TO(GPIOA->MODER, GPIO_MODER_MODE5, 2u << GPIO_MODER_MODE5_Pos);
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5;
// SPI1 MISO (PA6)
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL6;
SET_TO(GPIOA->MODER, GPIO_MODER_MODE6, 2u << GPIO_MODER_MODE6_Pos);
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_6;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD6;
// Enable Master mode and half the baud rate, so it's set to ~1MHz
m_spi->CR1 |= SPI_CR1_MSTR | SPI_CR1_LSBFIRST | SPI_CR1_SSM;
//m_spi->CR1 |= 1u << SPI_CR1_BR_Pos;
m_spi->CR2 |= SPI_CR2_SSOE;
}
NextTime SpiDriver::execute()
{
return NextTime::never();
}
RC SpiDriver::tx_blocking(const uint8_t *data, size_t len)
{
if (len <= 0) {
return RC::FAIL;
}
m_spi->CR1 |= SPI_CR1_SPE;
//FLIP(GPIOB->ODR, GPIO_ODR_OD3);
CLR(m_spi->CR1, SPI_CR1_SSI);
SET(GPIOA->ODR, GPIO_ODR_OD4);
for (size_t i = 0; i < len; i++) {
while (!(m_spi->SR & SPI_SR_TXE)) {}
m_spi->DR = data[i];
}
//FLIP(GPIOB->ODR, GPIO_ODR_OD3);
while (!(m_spi->SR & SPI_SR_TXE)) {}
// Ensure that NSS is held for long enough to meet the display's thSCS
for (int i = 0; i < 4; i++);
m_spi->CR1 &= ~SPI_CR1_SPE;
SET(m_spi->CR1, SPI_CR1_SSI);
CLR(GPIOA->ODR, GPIO_ODR_OD4);
return RC::OK;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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 "Bsp/ReturnCode.h"
#include "Bsp/TaskScheduler.h"
// TODO: Find a better include for this
#include "stm32l0xx.h"
namespace BSP {
class SpiDriver : public Common::Schedule::Task {
public:
// TODO: Add configurability / provide a real abstraction
SpiDriver(Common::Schedule::TaskScheduler &scheduler);
void init();
Common::Schedule::NextTime execute() override;
Common::ReturnCode tx_blocking(const uint8_t *data, size_t len);
private:
Common::Schedule::TaskScheduler &m_scheduler;
SPI_TypeDef *m_spi;
};
}

View File

@@ -0,0 +1,171 @@
/*
* 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 "TaskScheduler.h"
#include "SystemTime.h"
#include "Drivers/LowPower.h"
#include "Drivers/RtcDriver.h"
namespace Common {
namespace Schedule {
template <uint32_t MAX_TASKS>
class LowPowerTaskScheduler final : public TaskScheduler {
public:
LowPowerTaskScheduler() :
m_tasks(),
m_task_count(0),
m_cycle_count(0)
{}
[[noreturn]] void run() override
{
while (1) {
cycle();
}
}
void add_task(Task &task, const NextTime &time) override
{
if (m_task_count == MAX_TASKS || time.get_type() == ScheduleType::NEVER) {
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);
}
// ~LowPowerTaskScheduler() {}
private:
struct TaskEvent {
TaskEvent() :
m_task(nullptr),
m_time()
{}
TaskEvent(Task &task, NextTime time) :
m_task(&task),
m_time(time)
{}
Task *m_task;
NextTime m_time;
};
/* FIXME: implement some sort of fixed-size priority queue */
TaskEvent m_tasks[MAX_TASKS];
std::size_t m_task_count;
uint64_t m_cycle_count;
void inline call_task(TaskEvent &task)
{
task.m_time = task.m_task->execute();
}
void inline cycle()
{
Common::time_t time = 0;
BSP::SystemTimer::get_time(time);
bool task_died = false;
/* Keep state for when the next task will execute. */
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;
call_task(event);
} else {
next_time = MIN(next_time, event.m_time.get_time());
}
} else if (event.m_time.get_type() == ScheduleType::NEVER) {
task_died = true;
}
}
if (task_died) {
remove_dead_tasks();
}
if (m_task_count == 0) {
Common::ReturnCode rc = BSP::RtcDriver::set_wakeup_in(Time::seconds(5));
if (rc == Common::ReturnCode::OK) {
BSP::LowPower::stop();
}
} else 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++;
}
void inline remove_dead_tasks()
{
std::size_t i_new = 0;
std::size_t i_old = 0;
while (i_old < m_task_count) {
//FIXME: this is broken
bool is_dead = true;
if (m_tasks[i_old].m_time.get_type() != ScheduleType::NEVER) {
is_dead = false;
}
if (i_old != i_new) {
m_tasks[i_new] = m_tasks[i_old];
}
if (!is_dead) {
i_new++;
}
i_old++;
}
m_task_count = i_new;
}
};
}
}

View File

@@ -0,0 +1,88 @@
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 4K
}
/* Enforce emmition of the vector table. */
EXTERN (vector_table)
/* Define the entry point of the output file. */
ENTRY(reset_handler)
/* Define sections. */
SECTIONS
{
.text : {
*(.vectors) /* Vector table */
*(.text*) /* Program code */
. = ALIGN(4);
*(.rodata*) /* Read-only data */
. = ALIGN(4);
} >rom
/* C++ Static constructors/destructors, also used for __attribute__
* ((constructor)) and the likes */
.preinit_array : {
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
} >rom
.init_array : {
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
} >rom
.fini_array : {
. = ALIGN(4);
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
} >rom
/*
* Another section used by C++ stuff, appears when using newlib with
* 64bit (long long) printf support
*/
.ARM.extab : {
*(.ARM.extab*)
} >rom
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >rom
. = ALIGN(4);
_etext = .;
.data : {
_data = .;
*(.data*) /* Read-write initialized data */
. = ALIGN(4);
_edata = .;
} >ram AT >rom
_data_loadaddr = LOADADDR(.data);
.bss : {
*(.bss*) /* Read-write zero initialized data */
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >ram
/*
* The .eh_frame section appears to be used for C++ exception handling.
* You may need to fix this if you're using C++.
*/
/** DISCARD/ : { *(.eh_frame) } */
. = ALIGN(4);
end = .;
}
PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram));

View File

@@ -0,0 +1,320 @@
/* File: startup_ARMCM0.S
* Purpose: startup file for Cortex-M0 devices. Should use with
* GCC for ARM Embedded Processors
* Version: V2.01
* Date: 12 June 2014
*
*/
/* Copyright (c) 2011 - 2014 ARM LIMITED
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*/
.syntax unified
.arch armv6-m
.section .stack
.align 3
#ifdef __STACK_SIZE
.equ Stack_Size, __STACK_SIZE
#else
.equ Stack_Size, 0x00000400
#endif
.globl __StackTop
.globl __StackLimit
__StackLimit:
.space Stack_Size
.size __StackLimit, . - __StackLimit
__StackTop:
.size __StackTop, . - __StackTop
.section .heap
.align 3
#ifdef __HEAP_SIZE
.equ Heap_Size, __HEAP_SIZE
#else
.equ Heap_Size, 0x00000C00
#endif
.globl __HeapBase
.globl __HeapLimit
__HeapBase:
.if Heap_Size
.space Heap_Size
.endif
.size __HeapBase, . - __HeapBase
__HeapLimit:
.size __HeapLimit, . - __HeapLimit
.section .vectors
.align 2
.globl __Vectors
__Vectors:
.long __StackTop /* Top of Stack */
.long Reset_Handler /* Reset Handler */
.long NMI_Handler /* NMI Handler */
.long HardFault_Handler /* Hard Fault Handler */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long SVC_Handler /* SVCall Handler */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long PendSV_Handler /* PendSV Handler */
.long SysTick_Handler /* SysTick Handler */
/* External interrupts */
.long WDT_IRQHandler /* 0: Watchdog Timer */
.long RTC_IRQHandler /* 1: Real Time Clock */
.long TIM0_IRQHandler /* 2: Timer0 / Timer1 */
.long TIM2_IRQHandler /* 3: Timer2 / Timer3 */
.long MCIA_IRQHandler /* 4: MCIa */
.long MCIB_IRQHandler /* 5: MCIb */
.long UART0_IRQHandler /* 6: UART0 - DUT FPGA */
.long UART1_IRQHandler /* 7: UART1 - DUT FPGA */
.long UART2_IRQHandler /* 8: UART2 - DUT FPGA */
.long UART4_IRQHandler /* 9: UART4 - not connected */
.long AACI_IRQHandler /* 10: AACI / AC97 */
.long CLCD_IRQHandler /* 11: CLCD Combined Interrupt */
.long ENET_IRQHandler /* 12: Ethernet */
.long USBDC_IRQHandler /* 13: USB Device */
.long USBHC_IRQHandler /* 14: USB Host Controller */
.long CHLCD_IRQHandler /* 15: Character LCD */
.long FLEXRAY_IRQHandler /* 16: Flexray */
.long CAN_IRQHandler /* 17: CAN */
.long LIN_IRQHandler /* 18: LIN */
.long I2C_IRQHandler /* 19: I2C ADC/DAC */
.long 0 /* 20: Reserved */
.long 0 /* 21: Reserved */
.long 0 /* 22: Reserved */
.long 0 /* 23: Reserved */
.long 0 /* 24: Reserved */
.long 0 /* 25: Reserved */
.long 0 /* 26: Reserved */
.long 0 /* 27: Reserved */
.long CPU_CLCD_IRQHandler /* 28: Reserved - CPU FPGA CLCD */
.long 0 /* 29: Reserved - CPU FPGA */
.long UART3_IRQHandler /* 30: UART3 - CPU FPGA */
.long SPI_IRQHandler /* 31: SPI Touchscreen - CPU FPGA */
.size __Vectors, . - __Vectors
.text
.thumb
.thumb_func
.align 1
.globl Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Firstly it copies data from read only memory to RAM. There are two schemes
* to copy. One can copy more than one sections. Another can only copy
* one section. The former scheme needs more instructions and read-only
* data to implement than the latter.
* Macro __STARTUP_COPY_MULTIPLE is used to choose between two schemes. */
#ifdef __STARTUP_COPY_MULTIPLE
/* Multiple sections scheme.
*
* Between symbol address __copy_table_start__ and __copy_table_end__,
* there are array of triplets, each of which specify:
* offset 0: LMA of start of a section to copy from
* offset 4: VMA of start of a section to copy to
* offset 8: size of the section to copy. Must be multiply of 4
*
* All addresses must be aligned to 4 bytes boundary.
*/
ldr r4, =__copy_table_start__
ldr r5, =__copy_table_end__
.L_loop0:
cmp r4, r5
bge .L_loop0_done
ldr r1, [r4]
ldr r2, [r4, #4]
ldr r3, [r4, #8]
.L_loop0_0:
subs r3, #4
blt .L_loop0_0_done
ldr r0, [r1, r3]
str r0, [r2, r3]
b .L_loop0_0
.L_loop0_0_done:
adds r4, #12
b .L_loop0
.L_loop0_done:
#else
/* Single section scheme.
*
* The ranges of copy from/to are specified by following symbols
* __etext: LMA of start of the section to copy from. Usually end of text
* __data_start__: VMA of start of the section to copy to
* __data_end__: VMA of end of the section to copy to
*
* All addresses must be aligned to 4 bytes boundary.
*/
ldr r1, =__etext
ldr r2, =__data_start__
ldr r3, =__data_end__
subs r3, r2
ble .L_loop1_done
.L_loop1:
subs r3, #4
ldr r0, [r1,r3]
str r0, [r2,r3]
bgt .L_loop1
.L_loop1_done:
#endif /*__STARTUP_COPY_MULTIPLE */
/* This part of work usually is done in C library startup code. Otherwise,
* define this macro to enable it in this startup.
*
* There are two schemes too. One can clear multiple BSS sections. Another
* can only clear one section. The former is more size expensive than the
* latter.
*
* Define macro __STARTUP_CLEAR_BSS_MULTIPLE to choose the former.
* Otherwise efine macro __STARTUP_CLEAR_BSS to choose the later.
*/
#ifdef __STARTUP_CLEAR_BSS_MULTIPLE
/* Multiple sections scheme.
*
* Between symbol address __copy_table_start__ and __copy_table_end__,
* there are array of tuples specifying:
* offset 0: Start of a BSS section
* offset 4: Size of this BSS section. Must be multiply of 4
*/
ldr r3, =__zero_table_start__
ldr r4, =__zero_table_end__
.L_loop2:
cmp r3, r4
bge .L_loop2_done
ldr r1, [r3]
ldr r2, [r3, #4]
movs r0, 0
.L_loop2_0:
subs r2, #4
blt .L_loop2_0_done
str r0, [r1, r2]
b .L_loop2_0
.L_loop2_0_done:
adds r3, #8
b .L_loop2
.L_loop2_done:
#elif defined (__STARTUP_CLEAR_BSS)
/* Single BSS section scheme.
*
* The BSS section is specified by following symbols
* __bss_start__: start of the BSS section.
* __bss_end__: end of the BSS section.
*
* Both addresses must be aligned to 4 bytes boundary.
*/
ldr r1, =__bss_start__
ldr r2, =__bss_end__
movs r0, 0
subs r2, r1
ble .L_loop3_done
.L_loop3:
subs r2, #4
str r0, [r1, r2]
bgt .L_loop3
.L_loop3_done:
#endif /* __STARTUP_CLEAR_BSS_MULTIPLE || __STARTUP_CLEAR_BSS */
#ifndef __NO_SYSTEM_INIT
bl SystemInit
#endif
#ifndef __START
#define __START _start
#endif
bl __START
.pool
.size Reset_Handler, . - Reset_Handler
.align 1
.thumb_func
.weak Default_Handler
.type Default_Handler, %function
Default_Handler:
b .
.size Default_Handler, . - Default_Handler
/* Macro to define default handlers. Default handler
* will be weak symbol and just dead loops. They can be
* overwritten by other handlers */
.macro def_irq_handler handler_name
.weak \handler_name
.set \handler_name, Default_Handler
.endm
def_irq_handler NMI_Handler
def_irq_handler HardFault_Handler
def_irq_handler SVC_Handler
def_irq_handler PendSV_Handler
def_irq_handler SysTick_Handler
def_irq_handler WDT_IRQHandler
def_irq_handler RTC_IRQHandler
def_irq_handler TIM0_IRQHandler
def_irq_handler TIM2_IRQHandler
def_irq_handler MCIA_IRQHandler
def_irq_handler MCIB_IRQHandler
def_irq_handler UART0_IRQHandler
def_irq_handler UART1_IRQHandler
def_irq_handler UART2_IRQHandler
def_irq_handler UART3_IRQHandler
def_irq_handler UART4_IRQHandler
def_irq_handler AACI_IRQHandler
def_irq_handler CLCD_IRQHandler
def_irq_handler ENET_IRQHandler
def_irq_handler USBDC_IRQHandler
def_irq_handler USBHC_IRQHandler
def_irq_handler CHLCD_IRQHandler
def_irq_handler FLEXRAY_IRQHandler
def_irq_handler CAN_IRQHandler
def_irq_handler LIN_IRQHandler
def_irq_handler I2C_IRQHandler
def_irq_handler CPU_CLCD_IRQHandler
def_irq_handler SPI_IRQHandler
.end

View File

@@ -0,0 +1,224 @@
/**
Copyright (C) 2009-2015 ARM Limited. All rights reserved.
Copyright 2019, Max Regan
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/* Linker script to configure memory regions. */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 4K
}
/* Library configurations */
/* 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.
* It references following symbols, which must be defined in code:
* Reset_Handler : Entry of reset handler
*
* It defines following symbols, which code can use without definition:
* __exidx_start
* __exidx_end
* __copy_table_start__
* __copy_table_end__
* __zero_table_start__
* __zero_table_end__
* __etext
* __data_start__
* __preinit_array_start
* __preinit_array_end
* __init_array_start
* __init_array_end
* __fini_array_start
* __fini_array_end
* __data_end__
* __bss_start__
* __bss_end__
* __end__
* end
* __HeapLimit
* __StackLimit
* __StackTop
* __stack
* __Vectors_End
* __Vectors_Size
*/
ENTRY(Reset_Handler)
SECTIONS
{
.text :
{
KEEP(*(.vectors))
__Vectors_End = .;
__Vectors_Size = __Vectors_End - __Vectors;
__end__ = .;
*(.text*)
KEEP(*(.init))
KEEP(*(.fini))
/* .ctors */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* .dtors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.rodata*)
KEEP(*(.eh_frame*))
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* To copy multiple ROM to RAM sections,
* uncomment .copy.table section and,
* define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */
/*
.copy.table :
{
. = ALIGN(4);
__copy_table_start__ = .;
LONG (__etext)
LONG (__data_start__)
LONG (__data_end__ - __data_start__)
LONG (__etext2)
LONG (__data2_start__)
LONG (__data2_end__ - __data2_start__)
__copy_table_end__ = .;
} > FLASH
*/
/* To clear multiple BSS sections,
* uncomment .zero.table section and,
* define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */
/*
.zero.table :
{
. = ALIGN(4);
__zero_table_start__ = .;
LONG (__bss_start__)
LONG (__bss_end__ - __bss_start__)
LONG (__bss2_start__)
LONG (__bss2_end__ - __bss2_start__)
__zero_table_end__ = .;
} > FLASH
*/
__etext = .;
.data : AT (__etext)
{
__data_start__ = .;
*(vtable)
*(.data*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.jcr*))
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM
.bss :
{
. = ALIGN(4);
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (COPY):
{
__HeapBase = .;
__end__ = .;
end = __end__;
KEEP(*(.heap*))
__HeapLimit = .;
} > RAM
/* .stack_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later */
.stack_dummy (COPY):
{
KEEP(*(.stack*)) /* changed MG 30.05.14 */
} > RAM
/* Set stack top to end of RAM, and stack limit move down by
* size of stack_dummy section */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
}

View File

@@ -0,0 +1,315 @@
/* File: startup_ARMCM0.S
* Purpose: startup file for Cortex-M0 devices. Should use with
* GCC for ARM Embedded Processors
* Version: V2.01
* Date: 12 June 2014
*
*/
/* Copyright (c) 2011 - 2014 ARM LIMITED
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*/
.syntax unified
.arch armv6-m
.section .stack
.align 3
.equ Stack_Size, __STACK_SIZE
.globl __StackTop
.globl __StackLimit
__StackLimit:
.space Stack_Size
.size __StackLimit, . - __StackLimit
__StackTop:
.size __StackTop, . - __StackTop
.section .heap
.align 3
#ifdef __HEAP_SIZE
.equ Heap_Size, __HEAP_SIZE
#else
.equ Heap_Size, 0x00000C00
#endif
.globl __HeapBase
.globl __HeapLimit
__HeapBase:
.if Heap_Size
.space Heap_Size
.endif
.size __HeapBase, . - __HeapBase
__HeapLimit:
.size __HeapLimit, . - __HeapLimit
.section .vectors
.align 2
.globl __Vectors
__Vectors:
.long __StackTop /* Top of Stack */
.long Reset_Handler /* Reset Handler */
.long NMI_Handler /* NMI Handler */
.long HardFault_Handler /* Hard Fault Handler */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long SVC_Handler /* SVCall Handler */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long PendSV_Handler /* PendSV Handler */
.long SysTick_Handler /* SysTick Handler */
/* External interrupts */
.long WWDG_IRQHandler /* 0: Watchdog Timer */
.long PVD_IRQHandler /* 1: Real Time Clock */
.long RTC_IRQHandler /* 2: Timer0 / Timer1 */
.long FLASH_IRQHandler /* 3: Timer2 / Timer3 */
.long RCC_CRS_IRQHandler /* 4: MCIa */
.long EXTI_1_0_IRQHandler /* 5: MCIb */
.long EXTI_3_2_IRQHandler /* 6: UART0 - DUT FPGA */
.long EXTI_15_4_IRQHandler /* 7: UART1 - DUT FPGA */
.long 0 /* 8: UART2 - reserved */
.long DMA1_CHANNEL1_IRQHandler /* 8: UART2 - DUT FPGA */
.long DMA1_CHANNEL3_2_IRQHandler /* 9: UART4 - not connected */
.long DMA_CHANNEL_7_4_IRQHandler /* 10: AACI / AC97 */
.long ADC_COMP_IRQHandler /* 11: CLCD Combined Interrupt */
.long LPTIM1_IRQHandler /* 12: Ethernet */
.long USART4_USART5_IRQHandler /* 13: USB Device */
.long TIM2_IRQHandler /* 14: USB Host Controller */
.long TIM3_IRQHandler /* 15: Character LCD */
.long TIM6_IRQHandler /* 16: Flexray */
.long TIM7_IRQHandler /* 17: CAN */
.long 0 /* 18: LIN */
.long TIM21_IRQHandler /* 19: I2C ADC/DAC */
.long I2C3_IRQHandler /* 20: Reserved */
.long TIM22_IRQHandler /* 21: Reserved */
/* TODO: There are more but I'm lazy */
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.size __Vectors, . - __Vectors
.text
.thumb
.thumb_func
.align 1
.globl Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Firstly it copies data from read only memory to RAM. There are two schemes
* to copy. One can copy more than one sections. Another can only copy
* one section. The former scheme needs more instructions and read-only
* data to implement than the latter.
* Macro __STARTUP_COPY_MULTIPLE is used to choose between two schemes. */
#ifdef __STARTUP_COPY_MULTIPLE
/* Multiple sections scheme.
*
* Between symbol address __copy_table_start__ and __copy_table_end__,
* there are array of triplets, each of which specify:
* offset 0: LMA of start of a section to copy from
* offset 4: VMA of start of a section to copy to
* offset 8: size of the section to copy. Must be multiply of 4
*
* All addresses must be aligned to 4 bytes boundary.
*/
ldr r4, =__copy_table_start__
ldr r5, =__copy_table_end__
.L_loop0:
cmp r4, r5
bge .L_loop0_done
ldr r1, [r4]
ldr r2, [r4, #4]
ldr r3, [r4, #8]
.L_loop0_0:
subs r3, #4
blt .L_loop0_0_done
ldr r0, [r1, r3]
str r0, [r2, r3]
b .L_loop0_0
.L_loop0_0_done:
adds r4, #12
b .L_loop0
.L_loop0_done:
#else
/* Single section scheme.
*
* The ranges of copy from/to are specified by following symbols
* __etext: LMA of start of the section to copy from. Usually end of text
* __data_start__: VMA of start of the section to copy to
* __data_end__: VMA of end of the section to copy to
*
* All addresses must be aligned to 4 bytes boundary.
*/
ldr r1, =__etext
ldr r2, =__data_start__
ldr r3, =__data_end__
subs r3, r2
ble .L_loop1_done
.L_loop1:
subs r3, #4
ldr r0, [r1,r3]
str r0, [r2,r3]
bgt .L_loop1
.L_loop1_done:
#endif /*__STARTUP_COPY_MULTIPLE */
/* This part of work usually is done in C library startup code. Otherwise,
* define this macro to enable it in this startup.
*
* There are two schemes too. One can clear multiple BSS sections. Another
* can only clear one section. The former is more size expensive than the
* latter.
*
* Define macro __STARTUP_CLEAR_BSS_MULTIPLE to choose the former.
* Otherwise efine macro __STARTUP_CLEAR_BSS to choose the later.
*/
#ifdef __STARTUP_CLEAR_BSS_MULTIPLE
/* Multiple sections scheme.
*
* Between symbol address __copy_table_start__ and __copy_table_end__,
* there are array of tuples specifying:
* offset 0: Start of a BSS section
* offset 4: Size of this BSS section. Must be multiply of 4
*/
ldr r3, =__zero_table_start__
ldr r4, =__zero_table_end__
.L_loop2:
cmp r3, r4
bge .L_loop2_done
ldr r1, [r3]
ldr r2, [r3, #4]
movs r0, 0
.L_loop2_0:
subs r2, #4
blt .L_loop2_0_done
str r0, [r1, r2]
b .L_loop2_0
.L_loop2_0_done:
adds r3, #8
b .L_loop2
.L_loop2_done:
#elif defined (__STARTUP_CLEAR_BSS)
/* Single BSS section scheme.
*
* The BSS section is specified by following symbols
* __bss_start__: start of the BSS section.
* __bss_end__: end of the BSS section.
*
* Both addresses must be aligned to 4 bytes boundary.
*/
ldr r1, =__bss_start__
ldr r2, =__bss_end__
movs r0, 0
subs r2, r1
ble .L_loop3_done
.L_loop3:
subs r2, #4
str r0, [r1, r2]
bgt .L_loop3
.L_loop3_done:
#endif /* __STARTUP_CLEAR_BSS_MULTIPLE || __STARTUP_CLEAR_BSS */
#ifndef __NO_SYSTEM_INIT
bl SystemInit
#endif
#ifndef __START
#define __START _start
#endif
// bl __START
bl main
.pool
.size Reset_Handler, . - Reset_Handler
.align 1
.thumb_func
.weak Default_Handler
.type Default_Handler, %function
Default_Handler:
b .
.size Default_Handler, . - Default_Handler
/* Macro to define default handlers. Default handler
* will be weak symbol and just dead loops. They can be
* overwritten by other handlers */
.macro def_irq_handler handler_name
.weak \handler_name
.set \handler_name, Default_Handler
.endm
def_irq_handler NMI_Handler
def_irq_handler HardFault_Handler
def_irq_handler SVC_Handler
def_irq_handler PendSV_Handler
def_irq_handler SysTick_Handler
def_irq_handler WWDG_IRQHandler
def_irq_handler PVD_IRQHandler
def_irq_handler WDT_IRQHandler
def_irq_handler RTC_IRQHandler
def_irq_handler FLASH_IRQHandler
def_irq_handler RCC_CRS_IRQHandler
def_irq_handler EXTI_1_0_IRQHandler
def_irq_handler EXTI_3_2_IRQHandler
def_irq_handler EXTI_15_4_IRQHandler
def_irq_handler DMA1_CHANNEL1_IRQHandler
def_irq_handler DMA1_CHANNEL3_2_IRQHandler
def_irq_handler DMA_CHANNEL_7_4_IRQHandler
def_irq_handler ADC_COMP_IRQHandler
def_irq_handler LPTIM1_IRQHandler
def_irq_handler USART4_USART5_IRQHandler
def_irq_handler TIM2_IRQHandler
def_irq_handler TIM3_IRQHandler
def_irq_handler TIM6_IRQHandler
def_irq_handler TIM7_IRQHandler
def_irq_handler TIM21_IRQHandler
def_irq_handler I2C3_IRQHandler
def_irq_handler TIM22_IRQHandler
.end

View File

@@ -0,0 +1,223 @@
/**
Copyright (C) 2009-2015 ARM Limited. All rights reserved.
Copyright 2019, Max Regan
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/* Linker script to configure memory regions. */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 8K
}
/* Library configurations */
/* 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.
* It references following symbols, which must be defined in code:
* Reset_Handler : Entry of reset handler
*
* It defines following symbols, which code can use without definition:
* __exidx_start
* __exidx_end
* __copy_table_start__
* __copy_table_end__
* __zero_table_start__
* __zero_table_end__
* __etext
* __data_start__
* __preinit_array_start
* __preinit_array_end
* __init_array_start
* __init_array_end
* __fini_array_start
* __fini_array_end
* __data_end__
* __bss_start__
* __bss_end__
* __end__
* end
* __HeapLimit
* __StackLimit
* __StackTop
* __stack
* __Vectors_End
* __Vectors_Size
*/
ENTRY(Reset_Handler)
SECTIONS
{
.text :
{
KEEP(*(.vectors))
__Vectors_End = .;
__Vectors_Size = __Vectors_End - __Vectors;
__end__ = .;
*(.text*)
KEEP(*(.init))
KEEP(*(.fini))
/* .ctors */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* .dtors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.rodata*)
KEEP(*(.eh_frame*))
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* To copy multiple ROM to RAM sections,
* uncomment .copy.table section and,
* define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */
/*
.copy.table :
{
. = ALIGN(4);
__copy_table_start__ = .;
LONG (__etext)
LONG (__data_start__)
LONG (__data_end__ - __data_start__)
LONG (__etext2)
LONG (__data2_start__)
LONG (__data2_end__ - __data2_start__)
__copy_table_end__ = .;
} > FLASH
*/
/* To clear multiple BSS sections,
* uncomment .zero.table section and,
* define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */
/*
.zero.table :
{
. = ALIGN(4);
__zero_table_start__ = .;
LONG (__bss_start__)
LONG (__bss_end__ - __bss_start__)
LONG (__bss2_start__)
LONG (__bss2_end__ - __bss2_start__)
__zero_table_end__ = .;
} > FLASH
*/
__etext = .;
.data : AT (__etext)
{
__data_start__ = .;
*(vtable)
*(.data*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.jcr*))
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM
.bss :
{
. = ALIGN(4);
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (COPY):
{
__HeapBase = .;
__end__ = .;
end = __end__;
KEEP(*(.heap*))
__HeapLimit = .;
} > RAM
/* .stack_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later */
.stack_dummy (COPY):
{
KEEP(*(.stack*)) /* changed MG 30.05.14 */
} > RAM
/* Set stack top to end of RAM, and stack limit move down by
* size of stack_dummy section */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
}

32
firmware/Bsp/ReturnCode.h Normal file
View File

@@ -0,0 +1,32 @@
/*
* 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
namespace Common {
enum class ReturnCode : int {
OK = 0,
BUSY = 1,
FAIL = 2,
};
}

View File

@@ -0,0 +1,47 @@
/*
* 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 "SystemTime.h"
#include "macros.h"
namespace BSP {
using Common::ReturnCode;
using Common::time_t;
SystemTimerImpl *SystemTimer::m_impl = nullptr;
void SystemTimer::set_timer(SystemTimerImpl &timer) {
m_impl = &timer;
}
ReturnCode SystemTimer::get_time(time_t &time)
{
if (m_impl == nullptr) {
return ReturnCode::FAIL;
}
time = m_impl->get_time();
return ReturnCode::OK;
}
}

49
firmware/Bsp/SystemTime.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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 <stdint.h>
#include "ReturnCode.h"
#include "Time.h"
#include "stm32l0xx.h"
namespace BSP {
class SystemTimerImpl {
public:
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 set_timer(SystemTimerImpl& timer);
private:
static SystemTimerImpl *m_impl;
};
}

24
firmware/Bsp/Task.cpp Normal file
View File

@@ -0,0 +1,24 @@
/*
* 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.
*/
namespace Common {
}

115
firmware/Bsp/Task.h Normal file
View File

@@ -0,0 +1,115 @@
/*
* 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 "Time.h"
#include "SystemTime.h"
namespace Common {
namespace Schedule {
enum class ScheduleType {
AT_TIME = 0,
NEVER = 1,
};
class NextTime final {
public:
NextTime() :
m_type(ScheduleType::NEVER),
m_time(0)
{}
static inline NextTime in(time_t offset) {
time_t time = 0;
/* If this call fails, we likely haven't initialized the timer
* yet, so just treat offset as the time- hopefully the system
* timer will be initialzed soon
*/
BSP::SystemTimer::get_time(time);
time += offset;
return {ScheduleType::AT_TIME, time };
}
static inline NextTime never() {
return { ScheduleType::NEVER, 0 };
}
static inline NextTime asap() {
return { ScheduleType::AT_TIME, 0 };
}
static inline NextTime at(time_t time) {
return { ScheduleType::AT_TIME, time };
}
inline ScheduleType get_type() const { return m_type; }
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:
NextTime(ScheduleType type, time_t time) :
m_type(type),
m_time(time)
{}
ScheduleType m_type;
time_t m_time;
};
class Task {
public:
virtual NextTime execute() = 0;
};
} // namespace Schedule
} // namespace Common

View File

@@ -0,0 +1,41 @@
/*
* 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 <cstddef>
#include "Task.h"
#include "system.h"
namespace Common {
namespace Schedule {
class TaskScheduler {
public:
virtual void add_task(Task &task, const NextTime &time) = 0;
[[noreturn]] virtual void run() = 0;
protected:
//virtual ~TaskScheduler() {}
};
}
}

223
firmware/Bsp/Time.h Normal file
View File

@@ -0,0 +1,223 @@
/*
* 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 <stdint.h>
namespace Common {
using time_t = uint64_t;
class Time final {
public:
static constexpr uint64_t MILLIS_PER_SEC = 1e3;
static constexpr uint64_t MICROS_PER_SEC = 1e6;
static constexpr uint64_t NANOS_PER_SEC = 1e9;
static constexpr uint64_t MICROS_PER_MILLI = 1e3;
static constexpr uint64_t NANOS_PER_MILLI = 1e6;
static constexpr uint64_t NANOS_PER_MICRO = 1e3;
static inline time_t nanos(uint64_t value)
{
return value / NANOS_PER_MICRO;
}
static inline time_t micros(uint64_t value)
{
return value;
}
static inline time_t millis(uint64_t value)
{
return value * MICROS_PER_MILLI;
}
static inline time_t seconds(uint64_t value)
{
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;
}
};
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;
};
}

46
firmware/Bsp/font.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* 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 <stdint.h>
#include <stddef.h>
#ifndef _FONT_H_
#define _FONT_H_
struct glyph {
const uint8_t *bitmap;
uint8_t width;
uint8_t width_bytes;
uint8_t height;
uint8_t top;
uint8_t left;
};
struct font {
const char *name;
const struct glyph *glyphs[128];
uint8_t width;
uint8_t height;
};
const struct glyph *glyph_for_char(const struct font *font, char c);
#endif

86
firmware/Bsp/macros.h Normal file
View File

@@ -0,0 +1,86 @@
/**
Copyright 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.
**/
#ifndef _MACROS_H_
#define _MACROS_H_
#include <assert.h>
/**
* I'll probably regret writing all of these at some point.
*/
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define MIN(a,b) ((a > b) ? (b) : (a))
#define MAX(a,b) ((a < b) ? (b) : (a))
/*
* For usage with STM32 libraries
*/
#define STM32_GET_FIELD(x, name) \
((x) & name##_Msk) >> (name##_Pos)
/*
* Bitwise Operations
*/
#define SET(x, y) \
do { \
(x) |= (y); \
} while (0)
#define SET_POS(x, y) \
do { \
(x) |= 1u << (y); \
} while (0)
#define CLR(x, y) \
do { \
(x) &= ~(y); \
} while (0)
#define CLR_POS(x, y) \
do { \
(x) &= ~(1u << (y)); \
} while (0)
#define FLIP(x, y) \
do { \
(x) ^= (y); \
} while (0)
#define FLIP_POS(x, y) \
do { \
(x) ^= 1u << y; \
} while (0)
#define SET_TO(x, clear_mask, val) \
do { \
CLR(x, clear_mask); \
SET(x, val); \
} while (0)
#endif

31
firmware/Bsp/system.h Normal file
View File

@@ -0,0 +1,31 @@
/*
* 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.
*/
#ifndef _SYSTEM_H_
#define _SYSTEM_H_
#include <stdint.h>
uint32_t system_get_clk_freq(void);
uint64_t system_get_time_micros(void);
#endif