Start beautifying the code
* Split out SPI and display (a little) * Use more of the common Macros * Remove wasted bytes from notomono font * Create a mock "menu"
This commit is contained in:
3
Makefile
3
Makefile
@@ -79,7 +79,6 @@ CFLAGS += -D$(DEVICE_DEFINE)
|
|||||||
# Includes
|
# Includes
|
||||||
CFLAGS += -I./lib/stm32/$(DEVICE_LINE)/Include
|
CFLAGS += -I./lib/stm32/$(DEVICE_LINE)/Include
|
||||||
CFLAGS += -I./lib/CMSIS/Core/Include
|
CFLAGS += -I./lib/CMSIS/Core/Include
|
||||||
|
|
||||||
CFLAGS += -I./lib/fonts/
|
CFLAGS += -I./lib/fonts/
|
||||||
|
|
||||||
# Startup Definitions
|
# Startup Definitions
|
||||||
@@ -100,7 +99,7 @@ build: $(OUTPUT_BIN)
|
|||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
@echo "CC $@"
|
@echo "CC $@"
|
||||||
@$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
@echo "AS $@"
|
@echo "AS $@"
|
||||||
|
|||||||
162
display.c
Normal file
162
display.c
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* 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_buffer_init(&display->buffer);
|
||||||
|
display_spi_init(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int byte_cols = 1; // TODO: Don't hardcode this
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
spi_send_blocking(display->spi, (uint8_t *) &display->buffer, sizeof(display->buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_clear(struct display *display)
|
||||||
|
{
|
||||||
|
display_buffer_init(&display->buffer);
|
||||||
|
}
|
||||||
73
display.h
Normal file
73
display.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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 _DISPLAY_H_
|
||||||
|
#define _DISPLAY_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "spi.h"
|
||||||
|
#include "font.h"
|
||||||
|
|
||||||
|
#define DISPLAY_WIDTH 144
|
||||||
|
#define DISPLAY_HEIGHT 168
|
||||||
|
|
||||||
|
struct display_line
|
||||||
|
{
|
||||||
|
uint8_t mode;
|
||||||
|
uint8_t line;
|
||||||
|
uint8_t data[DISPLAY_WIDTH / 8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct display_buffer
|
||||||
|
{
|
||||||
|
struct display_line lines[DISPLAY_HEIGHT];
|
||||||
|
uint16_t dummy;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(struct display_buffer) == (DISPLAY_WIDTH / 8 + 2) * DISPLAY_HEIGHT + 2,
|
||||||
|
"The display buffer structure must be packed");
|
||||||
|
|
||||||
|
struct display
|
||||||
|
{
|
||||||
|
SPI_TypeDef *spi;
|
||||||
|
struct display_buffer buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void display_init(struct display *display, SPI_TypeDef *spi);
|
||||||
|
|
||||||
|
void display_set_bit(struct display *display, unsigned int x, unsigned int y, uint8_t val);
|
||||||
|
|
||||||
|
void display_set_byte(struct display *display, unsigned int x, unsigned int y, uint8_t val);
|
||||||
|
|
||||||
|
void display_char_at(struct display *display, int *x_off, int y_off, char c, const struct font *font);
|
||||||
|
|
||||||
|
void display_string_at(struct display *display, int x_off, int y_off, const char *string, const struct font *font);
|
||||||
|
|
||||||
|
void display_refresh(struct display *display);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
void display_clear(struct display *display);
|
||||||
|
|
||||||
|
#endif
|
||||||
39
font.c
Normal file
39
font.c
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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 "font.h"
|
||||||
|
|
||||||
|
const struct glyph *glyph_for_char(const struct font *font, char c)
|
||||||
|
{
|
||||||
|
// TODO: This is almost the least efficient way imaginable to implement this
|
||||||
|
for (int i = 0; i < font->max; i++) {
|
||||||
|
const struct glyph *g = font->glyphs[i];
|
||||||
|
if (g == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g->glyph == c) {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
29
font.h
Normal file
29
font.h
Normal file
@@ -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 _FONT_H_
|
||||||
|
#define _FONT_H_
|
||||||
|
|
||||||
|
#include "fontem.h"
|
||||||
|
|
||||||
|
const struct glyph *glyph_for_char(const struct font *font, char c);
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
6
macros.h
6
macros.h
@@ -43,12 +43,12 @@
|
|||||||
(x) |= 1u << (y); \
|
(x) |= 1u << (y); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CLEAR(x, y) \
|
#define CLR(x, y) \
|
||||||
do { \
|
do { \
|
||||||
(x) &= ~(y); \
|
(x) &= ~(y); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define CLEAR_POS(x, y) \
|
#define CLR_POS(x, y) \
|
||||||
do { \
|
do { \
|
||||||
(x) &= ~(1u << (y)); \
|
(x) &= ~(1u << (y)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
static_assert((clear_mask & val) == val, \
|
static_assert((clear_mask & val) == val, \
|
||||||
"'value' in SET_TO() has bits set that are not in clear_mask"); \
|
"'value' in SET_TO() has bits set that are not in clear_mask"); \
|
||||||
do { \
|
do { \
|
||||||
CLEAR(x, clear_mask); \
|
CLR(x, clear_mask); \
|
||||||
SET(x, val); \
|
SET(x, val); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|||||||
46
spi.c
Normal file
46
spi.c
Normal 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 "spi.h"
|
||||||
|
|
||||||
|
#include "stm32l0xx.h"
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
void spi_send_blocking(SPI_TypeDef *spi, const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
if (len <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi->CR1 |= SPI_CR1_SPE;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
while (!(spi->SR & SPI_SR_TXE)) {}
|
||||||
|
spi->DR = data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!(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++);
|
||||||
|
|
||||||
|
spi->CR1 &= ~SPI_CR1_SPE;
|
||||||
|
}
|
||||||
35
spi.h
Normal file
35
spi.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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 _SPI_H_
|
||||||
|
#define _SPI_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// TODO: Include something more general (only needed for SPI typedef)
|
||||||
|
#include "stm32l0xx.h"
|
||||||
|
|
||||||
|
void spi_send_blocking(SPI_TypeDef *spi, const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
void spi_send_dma(SPI_TypeDef *spi);
|
||||||
|
|
||||||
|
#endif
|
||||||
316
test.c
316
test.c
@@ -1,32 +1,33 @@
|
|||||||
/**
|
/*
|
||||||
Copyright 2019, Max Regan
|
* Copyright (C) 2019 Max Regan
|
||||||
|
*
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
a copy of this software and associated documentation files (the
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
"Software"), to deal in the Software without restriction, including
|
* in the Software without restriction, including without limitation the rights
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
* furnished to do so, subject to the following conditions:
|
||||||
the following conditions:
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
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,
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
* THE SOFTWARE.
|
||||||
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 <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "macros.h"
|
|
||||||
#include "stm32l0xx.h"
|
#include "stm32l0xx.h"
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "spi.h"
|
||||||
|
#include "display.h"
|
||||||
#include "font-notomono-10.h"
|
#include "font-notomono-10.h"
|
||||||
|
|
||||||
/* TODO: Start cleaning up code and adding bounds checking! */
|
/* TODO: Start cleaning up code and adding bounds checking! */
|
||||||
@@ -43,10 +44,12 @@ void SystemInit()
|
|||||||
* of the HSE oscillator used directly or indirectly as system
|
* of the HSE oscillator used directly or indirectly as system
|
||||||
* clock. This bit cannot be cleared if the MSI is used as system
|
* clock. This bit cannot be cleared if the MSI is used as system
|
||||||
* clock. */
|
* clock. */
|
||||||
SET(RCC->CR, RCC_CR_MSION);
|
SET(RCC->CR,
|
||||||
CLEAR(RCC->ICSCR, RCC_ICSCR_MSIRANGE);
|
RCC_CR_MSION);
|
||||||
|
|
||||||
SET_TO(RCC->ICSCR, RCC_ICSCR_MSIRANGE, RCC_ICSCR_MSIRANGE_6);
|
SET_TO(RCC->ICSCR,
|
||||||
|
RCC_ICSCR_MSIRANGE,
|
||||||
|
RCC_ICSCR_MSIRANGE_6);
|
||||||
|
|
||||||
/*!< Reset
|
/*!< Reset
|
||||||
* SW[1:0] (use MSI oscillator as system clock),
|
* SW[1:0] (use MSI oscillator as system clock),
|
||||||
@@ -55,7 +58,7 @@ void SystemInit()
|
|||||||
* PPRE2[2:0] (do not divide APB high-speed clock),
|
* PPRE2[2:0] (do not divide APB high-speed clock),
|
||||||
* MCOSEL[2:0] (disable MCO clock),
|
* MCOSEL[2:0] (disable MCO clock),
|
||||||
* MCOPRE[2:0] (disable MCO prescaler) */
|
* MCOPRE[2:0] (disable MCO prescaler) */
|
||||||
CLEAR(RCC->CFGR,
|
CLR(RCC->CFGR,
|
||||||
RCC_CFGR_SW | ~RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2 |
|
RCC_CFGR_SW | ~RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2 |
|
||||||
RCC_CFGR_MCOSEL | RCC_CFGR_MCOPRE);
|
RCC_CFGR_MCOSEL | RCC_CFGR_MCOPRE);
|
||||||
|
|
||||||
@@ -66,19 +69,21 @@ void SystemInit()
|
|||||||
* CSSHSEON (disable HSE clock monitoring)
|
* CSSHSEON (disable HSE clock monitoring)
|
||||||
* PLLON (disable PLL)
|
* PLLON (disable PLL)
|
||||||
*/
|
*/
|
||||||
CLEAR(RCC->CR,
|
CLR(RCC->CR,
|
||||||
RCC_CR_HSION | RCC_CR_HSIDIVEN | RCC_CR_HSEON |
|
RCC_CR_HSION | RCC_CR_HSIDIVEN | RCC_CR_HSEON |
|
||||||
RCC_CR_CSSHSEON | RCC_CR_PLLON);
|
RCC_CR_CSSHSEON | RCC_CR_PLLON);
|
||||||
|
|
||||||
/*!< Reset HSEBYP bit (disable HSE bypass) */
|
/*!< Reset HSEBYP bit (disable HSE bypass) */
|
||||||
RCC->CR &= ~RCC_CR_HSEBYP;
|
CLR(RCC->CR,
|
||||||
|
RCC_CR_HSEBYP);
|
||||||
|
|
||||||
/*!< Reset
|
/*!< Reset
|
||||||
* PLLSRC (HSI16 is the PLL source),
|
* PLLSRC (HSI16 is the PLL source),
|
||||||
* PLLMUL[3:0] (3x PLL multiplication)
|
* PLLMUL[3:0] (3x PLL multiplication)
|
||||||
* Don't touch PLLDIV[1:0], since 0 is undefined
|
* Don't touch PLLDIV[1:0], since 0 is undefined
|
||||||
*/
|
*/
|
||||||
RCC->CFGR &= ~RCC_CFGR_PLLSRC & ~RCC_CFGR_PLLMUL & ~RCC_CFGR_PLLDIV;
|
CLR(RCC->CFGR,
|
||||||
|
RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL | RCC_CFGR_PLLDIV);
|
||||||
|
|
||||||
|
|
||||||
/*!< Disable all interrupts */
|
/*!< Disable all interrupts */
|
||||||
@@ -91,14 +96,17 @@ void SystemInit()
|
|||||||
void init_lptim()
|
void init_lptim()
|
||||||
{
|
{
|
||||||
/* Enable APB1 for LPTIM */
|
/* Enable APB1 for LPTIM */
|
||||||
RCC->APB1ENR |= RCC_APB1ENR_LPTIM1EN;
|
SET(RCC->APB1ENR,
|
||||||
|
RCC_APB1ENR_LPTIM1EN);
|
||||||
|
|
||||||
// Enable low-speed internal
|
// Enable low-speed internal
|
||||||
RCC->CSR |= RCC_CSR_LSION;
|
RCC->CSR |= RCC_CSR_LSION;
|
||||||
while (!(RCC->CSR & RCC_CSR_LSIRDY)) {};
|
while (!(RCC->CSR & RCC_CSR_LSIRDY)) {};
|
||||||
|
|
||||||
/*!< Set the LSI clock to be the source of the LPTIM */
|
/*!< Set the LSI clock to be the source of the LPTIM */
|
||||||
SET_TO(RCC->CCIPR, RCC_CCIPR_LPTIM1SEL, RCC_CCIPR_LPTIM1SEL_0);
|
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) */
|
/** Write CR CFGR and IER while LPTIM is disabled (LPTIM_CR_ENABLE not yet set) */
|
||||||
|
|
||||||
@@ -114,18 +122,20 @@ void init_lptim()
|
|||||||
* COUNTMODE (LPTIM counter updated on every clock pulse)
|
* COUNTMODE (LPTIM counter updated on every clock pulse)
|
||||||
* TRGFLT (Do not debounce triggers)
|
* TRGFLT (Do not debounce triggers)
|
||||||
*/
|
*/
|
||||||
LPTIM1->CFGR &= ~LPTIM_CFGR_ENC & ~LPTIM_CFGR_TIMOUT & ~LPTIM_CFGR_TRIGEN &
|
CLR(LPTIM1->CFGR,
|
||||||
~LPTIM_CFGR_TRIGSEL & ~LPTIM_CFGR_PRELOAD & ~LPTIM_CFGR_CKSEL &
|
LPTIM_CFGR_ENC | LPTIM_CFGR_TIMOUT | LPTIM_CFGR_TRIGEN |
|
||||||
~LPTIM_CFGR_COUNTMODE;
|
LPTIM_CFGR_TRIGSEL | LPTIM_CFGR_PRELOAD | LPTIM_CFGR_CKSEL |
|
||||||
|
LPTIM_CFGR_COUNTMODE);
|
||||||
|
|
||||||
/*!< Set
|
/*!< Set
|
||||||
* PRESC (Set prescaler to 128. Using 32kHz LSI as input, this should
|
* PRESC (Set prescaler to 128. Using 32kHz LSI as input, this should
|
||||||
* correspond to 250Hz counting.
|
* correspond to 250Hz counting.
|
||||||
*/
|
*/
|
||||||
LPTIM1->CFGR &= LPTIM_CFGR_PRESC;
|
SET_TO(LPTIM1->CFGR,
|
||||||
LPTIM1->CFGR |= 7u << LPTIM_CFGR_PRESC_Pos;
|
LPTIM_CFGR_PRESC,
|
||||||
|
7u << LPTIM_CFGR_PRESC_Pos);
|
||||||
|
|
||||||
LPTIM1->CR |= LPTIM_CR_ENABLE;
|
SET(LPTIM1->CR, LPTIM_CR_ENABLE);
|
||||||
|
|
||||||
/*!< Do not modify ARR and CMP until after ENABLE bit is set */
|
/*!< Do not modify ARR and CMP until after ENABLE bit is set */
|
||||||
/*!< Produce a 60Hz, 50% duty cycle square wave. These values were
|
/*!< Produce a 60Hz, 50% duty cycle square wave. These values were
|
||||||
@@ -136,230 +146,58 @@ void init_lptim()
|
|||||||
while(!(LPTIM1->ISR & LPTIM_ISR_CMPOK)) {}
|
while(!(LPTIM1->ISR & LPTIM_ISR_CMPOK)) {}
|
||||||
|
|
||||||
/*!< Enable and start the timer */
|
/*!< Enable and start the timer */
|
||||||
LPTIM1->CR |= LPTIM_CR_CNTSTRT;
|
SET(LPTIM1->CR, LPTIM_CR_CNTSTRT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!< The buffer that will be used in the future to display on the watch display. */
|
static struct display display;
|
||||||
|
|
||||||
#define DISPLAY_WIDTH 144
|
|
||||||
#define DISPLAY_HEIGHT 168
|
|
||||||
|
|
||||||
struct display_line
|
|
||||||
{
|
|
||||||
uint8_t mode;
|
|
||||||
uint8_t line;
|
|
||||||
uint8_t data[DISPLAY_WIDTH / 8];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct display_buffer
|
|
||||||
{
|
|
||||||
struct display_line lines[DISPLAY_HEIGHT];
|
|
||||||
uint16_t dummy;
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(sizeof(struct display_buffer) == (DISPLAY_WIDTH / 8 + 2) * DISPLAY_HEIGHT + 2,
|
|
||||||
"The display buffer structure must be packed");
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_set_bit(struct display_buffer *buffer, unsigned int x, unsigned int y, uint8_t val)
|
|
||||||
{
|
|
||||||
struct display_line *line = &buffer->lines[y];
|
|
||||||
uint8_t *byte = &line->data[x >> 3];
|
|
||||||
if (val) {
|
|
||||||
CLEAR_POS(*byte, x & 7);
|
|
||||||
} else {
|
|
||||||
SET_POS(*byte, x & 7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_set_byte(struct display_buffer *buffer, unsigned int x, unsigned int y, uint8_t val)
|
|
||||||
{
|
|
||||||
if (x & 7) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct display_line *line = &buffer->lines[y];
|
|
||||||
line->data[x >> 3] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct display_buffer buffer;
|
|
||||||
|
|
||||||
void init_lptim_toggler()
|
void init_lptim_toggler()
|
||||||
{
|
{
|
||||||
init_lptim();
|
init_lptim();
|
||||||
|
|
||||||
/* Assign LPTIM1_OUT to PA7 */
|
/* Assign LPTIM1_OUT to PA7 */
|
||||||
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL7;
|
SET_TO(GPIOA->AFR[0],
|
||||||
GPIOA->AFR[0] |= 1 << GPIO_AFRL_AFRL7_Pos;
|
GPIO_AFRL_AFRL7,
|
||||||
|
1u << GPIO_AFRL_AFRL7_Pos);
|
||||||
|
|
||||||
GPIOA->MODER &= ~GPIO_MODER_MODE7;
|
SET_TO(GPIOA->MODER,
|
||||||
GPIOA->MODER |= 2u << GPIO_MODER_MODE7_Pos;
|
GPIO_MODER_MODE7,
|
||||||
|
2u << GPIO_MODER_MODE7_Pos);
|
||||||
|
|
||||||
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_7;
|
CLR(GPIOA->OTYPER, GPIO_OTYPER_OT_7);
|
||||||
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD7;
|
CLR(GPIOA->PUPDR, GPIO_PUPDR_PUPD7);
|
||||||
}
|
|
||||||
|
|
||||||
void init_spi_display()
|
|
||||||
{
|
|
||||||
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
|
|
||||||
SPI1->CR1 |= SPI_CR1_MSTR | SPI_CR1_LSBFIRST;
|
|
||||||
SPI1->CR1 |= 1u << SPI_CR1_BR_Pos;
|
|
||||||
SPI1->CR2 |= SPI_CR2_SSOE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void spi_send_blocking(SPI_TypeDef *spi, const uint8_t *data, size_t len)
|
|
||||||
{
|
|
||||||
if (len <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spi->CR1 |= SPI_CR1_SPE;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
while (!(spi->SR & SPI_SR_TXE)) {}
|
|
||||||
spi->DR = data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!(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++);
|
|
||||||
|
|
||||||
spi->CR1 &= ~SPI_CR1_SPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct glyph *glyph_for_char(const struct font *font, char c)
|
|
||||||
{
|
|
||||||
// TODO: This is almost the least efficient way imaginable to implement this
|
|
||||||
for (int i = 0; i < font->max; i++) {
|
|
||||||
const struct glyph *g = font->glyphs[i];
|
|
||||||
if (g == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g->glyph == c) {
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_char_at(int *x_off, int y_off, char c)
|
|
||||||
{
|
|
||||||
const struct glyph *g = glyph_for_char(&font_notomono_10, c);
|
|
||||||
if (g == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int byte_cols = 2;
|
|
||||||
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(&buffer, g->left + *x_off + x, y_off + y + 16 - g->top, bit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*x_off += g->advance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_string_at(int x_off, int y_off, const char *string)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
while (string[i]) {
|
|
||||||
display_char_at(&x_off, y_off, string[i]);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_update()
|
|
||||||
{
|
|
||||||
static uint16_t count = 0;
|
|
||||||
|
|
||||||
// for (size_t x = 0; x < DISPLAY_WIDTH / 8; x++) {
|
|
||||||
// for (size_t y = 0; y < DISPLAY_HEIGHT; y++) {
|
|
||||||
// display_set_byte(&buffer, (x << 3), y, (((y + count) >> 4) & 1) ? 0xFF : 0);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
display_string_at(0, 0, "Hello world!");
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
/** Enable Port A,B clock */
|
/** Enable Port A,B clock */
|
||||||
RCC->IOPENR |= RCC_IOPENR_IOPAEN;
|
SET(RCC->IOPENR, RCC_IOPENR_IOPAEN);
|
||||||
RCC->IOPENR |= RCC_IOPENR_IOPBEN;
|
SET(RCC->IOPENR, RCC_IOPENR_IOPBEN);
|
||||||
|
|
||||||
/** Enable pin P3 for output */
|
/** Enable pin P3 for output */
|
||||||
SET_TO(GPIOB->MODER, GPIO_MODER_MODE3, GPIO_MODER_MODE3_0);
|
SET_TO(GPIOB->MODER,
|
||||||
|
GPIO_MODER_MODE3,
|
||||||
|
GPIO_MODER_MODE3_0);
|
||||||
|
|
||||||
CLEAR(GPIOB->OTYPER, GPIO_OTYPER_OT_3);
|
CLR(GPIOB->OTYPER, GPIO_OTYPER_OT_3);
|
||||||
CLEAR(GPIOB->PUPDR, GPIO_PUPDR_PUPD3);
|
CLR(GPIOB->PUPDR, GPIO_PUPDR_PUPD3);
|
||||||
|
|
||||||
init_lptim_toggler();
|
init_lptim_toggler();
|
||||||
init_spi_display();
|
|
||||||
|
|
||||||
display_buffer_init(&buffer);
|
display_init(&display, SPI1);
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
display_update();
|
|
||||||
spi_send_blocking(SPI1, (uint8_t *) &buffer, sizeof(buffer));
|
|
||||||
FLIP(GPIOB->ODR, GPIO_ODR_OD3);
|
FLIP(GPIOB->ODR, GPIO_ODR_OD3);
|
||||||
// for (int i = 0; i < 100000; i++);
|
// 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_refresh(&display);
|
||||||
|
x++;
|
||||||
|
if (x == DISPLAY_WIDTH) {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user