From d5bfecedb2efbe363d95c438abb935ac52052a6a Mon Sep 17 00:00:00 2001 From: Max Regan Date: Mon, 11 Mar 2019 22:56:08 -0700 Subject: [PATCH] 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. --- .gdbinit | 3 ++ Makefile | 4 +-- display.c | 38 ++++++++++++++++++++++-- display.h | 5 ++++ macros.h | 14 +++++++++ rtc.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ rtc.h | 43 +++++++++++++++++++++++++++ system.c | 29 ++++++++++++++++++ system.h | 29 ++++++++++++++++++ test.c | 39 +++++++++++++++++++++--- 10 files changed, 285 insertions(+), 8 deletions(-) create mode 100644 .gdbinit create mode 100644 rtc.c create mode 100644 rtc.h create mode 100644 system.c create mode 100644 system.h diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..b85525f --- /dev/null +++ b/.gdbinit @@ -0,0 +1,3 @@ +set $PWR = (PWR_TypeDef *)(0x40000000 + 0x00007000U) +set $RCC = (RCC_TypeDef *)(0x40000000 + 0x00020000U + 0x1000U) +set $RTC = (RTC_TypeDef *)(0x40000000 + 0x00002800U) diff --git a/Makefile b/Makefile index f6f3dce..f3972e2 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ DEVICE_DEFINE = $(subst XX,xx,$(shell echo $(DEVICE_FAMILY) | tr '[:lower:]' '[: # C pedantism CFLAGS = -Wall -Wextra -Wpedantic # Debug/optimization -CFLAGS += -Og -g3 +CFLAGS += -Og -ggdb # Architecture CFLAGS += -mthumb -mcpu=cortex-m0plus CFLAGS += -ffreestanding @@ -99,7 +99,7 @@ build: $(OUTPUT_BIN) %.o: %.c @echo "CC $@" - $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) $(CFLAGS) -c $< -o $@ %.o: %.S @echo "AS $@" diff --git a/display.c b/display.c index a786b73..af8667f 100644 --- a/display.c +++ b/display.c @@ -89,10 +89,25 @@ static void display_spi_init(struct display *display) void display_init(struct display *display, SPI_TypeDef* spi) { display->spi = spi; + display->is_dirty = true; + display->dirty_line_min = 0; + display->dirty_line_max = DISPLAY_HEIGHT - 1; display_buffer_init(&display->buffer); display_spi_init(display); } +static void display_set_dirty(struct display *display, unsigned int y) +{ + if (!display->is_dirty) { + display->is_dirty = true; + display->dirty_line_min = y; + display->dirty_line_max = y; + } else { + display->dirty_line_min = MIN(y, display->dirty_line_min); + display->dirty_line_max = MAX(y, display->dirty_line_max); + } +} + void display_set_bit(struct display *display, unsigned int x, unsigned int y, uint8_t val) { if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) { @@ -106,6 +121,8 @@ void display_set_bit(struct display *display, unsigned int x, unsigned int y, ui } else { SET_POS(*byte, x & 7); } + + display_set_dirty(display, y); } void display_set_byte(struct display *display, unsigned int x, unsigned int y, uint8_t val) @@ -120,6 +137,7 @@ void display_set_byte(struct display *display, unsigned int x, unsigned int y, u struct display_line *line = &display->buffer.lines[y]; line->data[x >> 3] = val; + display_set_dirty(display, y); } void display_char_at(struct display *display, int *x_off, int y_off, char c, const struct font *font) @@ -129,7 +147,8 @@ void display_char_at(struct display *display, int *x_off, int y_off, char c, con return; } - int byte_cols = 1; // TODO: Don't hardcode this + // TODO: Don't hardcode this + int byte_cols = 1; for (size_t x = 0; x < g->cols; x++) { for (size_t y = 0; y < g->rows; y++) { int byte_x = x >> 3; @@ -153,10 +172,25 @@ void display_string_at(struct display *display, int x_off, int y_off, const char void display_refresh(struct display *display) { - spi_send_blocking(display->spi, (uint8_t *) &display->buffer, sizeof(display->buffer)); + if (!display->is_dirty) { + return; + } + + uint8_t *start = (uint8_t *) &display->buffer.lines[display->dirty_line_min]; + // Data size + size_t size = sizeof(display->buffer.lines[0]) * + (display->dirty_line_max - display->dirty_line_min + 1); + // Trailer dummy data + size += 2; + + spi_send_blocking(display->spi, start , size); + display->is_dirty = false; } void display_clear(struct display *display) { display_buffer_init(&display->buffer); + display->is_dirty = true; + display->dirty_line_min = 0; + display->dirty_line_max = DISPLAY_HEIGHT - 1; } diff --git a/display.h b/display.h index 2958c46..f74c14c 100644 --- a/display.h +++ b/display.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "spi.h" #include "font.h" @@ -52,6 +53,10 @@ struct display { SPI_TypeDef *spi; struct display_buffer buffer; + + bool is_dirty; + uint8_t dirty_line_min; + uint8_t dirty_line_max; }; diff --git a/macros.h b/macros.h index 67ac6e0..bf2165f 100644 --- a/macros.h +++ b/macros.h @@ -27,8 +27,22 @@ #include +/** + * 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_FIELD(x, name) \ + ((x) & name##_Msk) >> (name##_Pos) + /* * Bitwise Operations */ diff --git a/rtc.c b/rtc.c new file mode 100644 index 0000000..4b75197 --- /dev/null +++ b/rtc.c @@ -0,0 +1,89 @@ +/* + * 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 "rtc.h" +#include "stm32l0xx.h" +#include "macros.h" + +static void enable_rtc_write() +{ + /*WPR = 0xCA; + RTC->WPR = 0x53; +} + +static void disable_rtc_write() +{ + /*WPR = 0x00; +} + +void rtc_init() +{ + uint32_t temp = RCC->CSR; + SET(RCC->CSR, RCC_CSR_RTCRST); + SET(RCC->APB1ENR, RCC_APB1ENR_PWREN); + SET(PWR->CR, PWR_CR_DBP); + + /*CSR = temp; + + enable_rtc_write(); + + RTC->ISR = RTC_ISR_INIT; + while (!(RTC->ISR & RTC_ISR_INITF)) {} + + /*PRER, RTC_PRER_PREDIV_A, RTC_PRER_PREDIV_A); + /*PRER, RTC_PRER_PREDIV_S, 255); + + /*ISR, RTC_ISR_INIT); + + disable_rtc_write(); +} + + +void rtc_get_time_bcd(struct time_bcd *tm_bcd) +{ + uint32_t time = RTC->TR; + + tm_bcd->hour_tens = STM32_FIELD(time, RTC_TR_HT); + tm_bcd->hour_ones = STM32_FIELD(time, RTC_TR_HU); + + tm_bcd->minute_tens = STM32_FIELD(time, RTC_TR_MNT); + tm_bcd->minute_ones = STM32_FIELD(time, RTC_TR_MNU); + + tm_bcd->second_tens = STM32_FIELD(time, RTC_TR_ST); + tm_bcd->second_ones = STM32_FIELD(time, RTC_TR_SU); + + tm_bcd->pm = STM32_FIELD(time, RTC_TR_PM); +} diff --git a/rtc.h b/rtc.h new file mode 100644 index 0000000..d45da4b --- /dev/null +++ b/rtc.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Max Regan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef _RTC_H_ +#define _RTC_H_ + +#include +#include + +struct time_bcd { + uint8_t hour_tens; + uint8_t hour_ones; + uint8_t minute_tens; + uint8_t minute_ones; + uint8_t second_tens; + uint8_t second_ones; + bool pm; +}; + +void rtc_init(); + +void rtc_get_time_bcd(struct time_bcd *tm_bcd); + +#endif diff --git a/system.c b/system.c new file mode 100644 index 0000000..d0a6a59 --- /dev/null +++ b/system.c @@ -0,0 +1,29 @@ +/* + * 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 "system.h" + +uint32_t system_clk_freq = 0; + +uint32_t system_get_clk_freq() +{ + return system_clk_freq; +} diff --git a/system.h b/system.h new file mode 100644 index 0000000..c3f77e0 --- /dev/null +++ b/system.h @@ -0,0 +1,29 @@ +/* + * 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 + +uint32_t system_get_clk_freq(void); + +#endif diff --git a/test.c b/test.c index b13ae20..906e3f3 100644 --- a/test.c +++ b/test.c @@ -26,15 +26,16 @@ #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. */ @@ -51,6 +52,9 @@ void SystemInit() 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) , @@ -168,6 +172,14 @@ void init_lptim_toggler() 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 */ @@ -185,15 +197,34 @@ int main() 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, 0, font_notomono_10.height * 0, "> Option 1", &font_notomono_10); - display_string_at(&display, 0, font_notomono_10.height * 1, " Option 2", &font_notomono_10); - display_string_at(&display, 0, font_notomono_10.height * 2, " Option 3", &font_notomono_10); + 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) {