Files
timely-reference/display.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

197 lines
5.8 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 "display.h"
#include "macros.h"
static void display_buffer_init(struct display_buffer *buffer)
{
for (size_t i = 0; i < ARRAY_SIZE(buffer->lines); i++) {
struct display_line *line = &buffer->lines[i];
line->mode = 1; // Update display
line->line = i;
for (size_t j = 0; j < ARRAY_SIZE(line->data); j++) {
line->data[j] = 0xFF;
}
}
buffer->dummy = 0;
}
static void display_spi_init(struct display *display)
{
// TODO: Not all of this should be in here, probably
RCC->APB2ENR = RCC_APB2ENR_SPI1EN;
GPIOB->OSPEEDR = ~0;
/* Assign SPI_MOSI to PA12 (AFRH5), since PA7 is taken by LPTIM_OUT */
GPIOA->AFR[1] &= ~GPIO_AFRH_AFRH4;
SET_TO(GPIOA->MODER, GPIO_MODER_MODE12, 2u << GPIO_MODER_MODE12_Pos);
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_12;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD12;
// SPI1 NSS (PA4)
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL4;
SET_TO(GPIOA->MODER, GPIO_MODER_MODE4, 2u << GPIO_MODER_MODE4_Pos);
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD4;
// enable pullup, since the pin doesn't seem to stay up
GPIOA->PUPDR |= 2u << GPIO_PUPDR_PUPD4_Pos;
// 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
display->spi->CR1 |= SPI_CR1_MSTR | SPI_CR1_LSBFIRST;
display->spi->CR1 |= 1u << SPI_CR1_BR_Pos;
display->spi->CR2 |= SPI_CR2_SSOE;
}
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) {
return;
}
struct display_line *line = &display->buffer.lines[y];
uint8_t *byte = &line->data[x >> 3];
if (val) {
CLR_POS(*byte, x & 7);
} 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)
{
if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) {
return;
}
if (x & 7) {
return;
}
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)
{
const struct glyph *g = glyph_for_char(font, c);
if (g == NULL) {
return;
}
// 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;
int byte_y = y;
uint8_t bit = (g->bitmap[byte_y * byte_cols + byte_x] >> (7 - (x & 7))) & 1;
/* 16 is font max height */
display_set_bit(display, g->left + *x_off + x, y_off + y + 16 - g->top, bit);
}
}
*x_off += g->advance;
}
void display_string_at(struct display *display, int x_off, int y_off, const char *string, const struct font *font)
{
int i = 0;
while (string[i]) {
display_char_at(display, &x_off, y_off, string[i], font);
i++;
}
}
void display_refresh(struct display *display)
{
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;
}