Files
timely-reference/test.c
Max Regan d5bfecedb2 Use the builtin RTC and display it
Its roughly %15 fast, and initializes to zero, but the display
currently shows HH:MM:SS AM/PM.
2019-03-11 22:56:08 -07:00

235 lines
7.1 KiB
C

/*
* 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 <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "stm32l0xx.h"
#include "macros.h"
#include "rtc.h"
#include "spi.h"
#include "display.h"
#include "font-notomono-10.h"
/* TODO: Start cleaning up code and adding bounds checking! */
extern uint32_t system_clk_freq;
void SystemInit()
{
/**
* Use the MSI for the system clock, and disable all other clocks.
*/
/*!< Set MSION bit. Set by hardware to force the MSI oscillator ON
* when exiting from Stop or Standby mode, or in case of a failure
* of the HSE oscillator used directly or indirectly as system
* clock. This bit cannot be cleared if the MSI is used as system
* clock. */
SET(RCC->CR,
RCC_CR_MSION);
SET_TO(RCC->ICSCR,
RCC_ICSCR_MSIRANGE,
RCC_ICSCR_MSIRANGE_6);
/*!< Set internal representation of clock frequency to 4MHz */
system_clk_freq = 4u << 22;
/*!< Reset
* SW[1:0] (use MSI oscillator as system clock),
* HPRE[3:0] (do not divide AHB clock in prescaler) ,
* PPRE1[2:0] (do not divide APB low-speed clock)
* PPRE2[2:0] (do not divide APB high-speed clock),
* MCOSEL[2:0] (disable MCO clock),
* MCOPRE[2:0] (disable MCO prescaler) */
CLR(RCC->CFGR,
RCC_CFGR_SW | ~RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2 |
RCC_CFGR_MCOSEL | RCC_CFGR_MCOPRE);
/*!< Reset
* HSION (disable HSI),
* HSIDIVEN (disable 18MHz HSI division)
* HSEON (disable HSE clock)
* CSSHSEON (disable HSE clock monitoring)
* PLLON (disable PLL)
*/
CLR(RCC->CR,
RCC_CR_HSION | RCC_CR_HSIDIVEN | RCC_CR_HSEON |
RCC_CR_CSSHSEON | RCC_CR_PLLON);
/*!< Reset HSEBYP bit (disable HSE bypass) */
CLR(RCC->CR,
RCC_CR_HSEBYP);
/*!< Reset
* PLLSRC (HSI16 is the PLL source),
* PLLMUL[3:0] (3x PLL multiplication)
* Don't touch PLLDIV[1:0], since 0 is undefined
*/
CLR(RCC->CFGR,
RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL | RCC_CFGR_PLLDIV);
/*!< Disable all interrupts */
RCC->CIER = 0x00000000;
/* Vector Table Relocation in Internal FLASH */
SCB->VTOR = FLASH_BASE;
}
void init_lptim()
{
/* Enable APB1 for LPTIM */
SET(RCC->APB1ENR,
RCC_APB1ENR_LPTIM1EN);
// Enable low-speed internal
RCC->CSR |= RCC_CSR_LSION;
while (!(RCC->CSR & RCC_CSR_LSIRDY)) {};
/*!< Set the LSI 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 using internal clock source)
* 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 LSI as input, this should
* correspond to 250Hz counting.
*/
SET_TO(LPTIM1->CFGR,
LPTIM_CFGR_PRESC,
7u << LPTIM_CFGR_PRESC_Pos);
SET(LPTIM1->CR, LPTIM_CR_ENABLE);
/*!< Do not modify ARR and CMP until after ENABLE bit is set */
/*!< Produce a 60Hz, 50% duty cycle square wave. These values were
* determined experimentally. */
LPTIM1->ARR = 9;
LPTIM1->CMP = 4;
while(!(LPTIM1->ISR & LPTIM_ISR_ARROK)) {}
while(!(LPTIM1->ISR & LPTIM_ISR_CMPOK)) {}
/*!< Enable and start the timer */
SET(LPTIM1->CR, LPTIM_CR_CNTSTRT);
}
static struct display display;
void init_lptim_toggler()
{
init_lptim();
/* 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);
}
static char get_char_for_digit(uint8_t bcd_digit)
{
return bcd_digit + '0';
}
struct time_bcd time;
char time_str[11] = { 0 };
int main()
{
/** Enable Port A,B clock */
SET(RCC->IOPENR, RCC_IOPENR_IOPAEN);
SET(RCC->IOPENR, RCC_IOPENR_IOPBEN);
/** Enable pin P3 for output */
SET_TO(GPIOB->MODER,
GPIO_MODER_MODE3,
GPIO_MODER_MODE3_0);
CLR(GPIOB->OTYPER, GPIO_OTYPER_OT_3);
CLR(GPIOB->PUPDR, GPIO_PUPDR_PUPD3);
init_lptim_toggler();
display_init(&display, SPI1);
rtc_init();
int x = 0;
while (1) {
rtc_get_time_bcd(&time);
time_str[0] = get_char_for_digit(time.hour_tens);
time_str[1] = get_char_for_digit(time.hour_ones);
time_str[2] = ':';
time_str[3] = get_char_for_digit(time.minute_tens);
time_str[4] = get_char_for_digit(time.minute_ones);
time_str[5] = ':';
time_str[6] = get_char_for_digit(time.second_tens);
time_str[7] = get_char_for_digit(time.second_ones);
time_str[8] = time.pm ? 'P' : 'A';
time_str[9] = 'M';
time_str[10] = '\0';
FLIP(GPIOB->ODR, GPIO_ODR_OD3);
// for (int i = 0; i < 10000; i++);
display_clear(&display);
display_string_at(&display, 32, font_notomono_10.height * 0, time_str, &font_notomono_10);
display_string_at(&display, 0, font_notomono_10.height * 2, "> Option 1", &font_notomono_10);
display_string_at(&display, 0, font_notomono_10.height * 3, " Option 2", &font_notomono_10);
display_string_at(&display, 0, font_notomono_10.height * 4, " Option 3", &font_notomono_10);
display_refresh(&display);
x++;
if (x == DISPLAY_WIDTH) {
x = 0;
}
}
}