Enable text display on Sharp display
1. Add basic code for Sharp Display 2. Add embedded font (generated by fontem) 3. Add the worst code possible to print strings to the display 4. Rejoice in the beautiful (small) text.
This commit is contained in:
2
Makefile
2
Makefile
@@ -80,6 +80,8 @@ CFLAGS += -D$(DEVICE_DEFINE)
|
|||||||
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/
|
||||||
|
|
||||||
# Startup Definitions
|
# Startup Definitions
|
||||||
ASFLAGS += -D__STARTUP_CLEAR_BSS
|
ASFLAGS += -D__STARTUP_CLEAR_BSS
|
||||||
ASFLAGS += -D__HEAP_SIZE=0 # No heap- let the linker decide it all
|
ASFLAGS += -D__HEAP_SIZE=0 # No heap- let the linker decide it all
|
||||||
|
|||||||
2468
lib/fonts/font-notomono-10.c
Normal file
2468
lib/fonts/font-notomono-10.c
Normal file
File diff suppressed because it is too large
Load Diff
15
lib/fonts/font-notomono-10.h
Normal file
15
lib/fonts/font-notomono-10.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/* AUTOMATICALLY GENERATED FILE! EDITING NOT RECOMMENDED!
|
||||||
|
*
|
||||||
|
* This file is distributed under the terms of the MIT License.
|
||||||
|
* See the LICENSE file at the top of this tree, or if it is missing a copy can
|
||||||
|
* be found at http://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FONTEM_notomono_10_H
|
||||||
|
#define _FONTEM_notomono_10_H
|
||||||
|
|
||||||
|
#include "fontem.h"
|
||||||
|
|
||||||
|
extern const struct font font_notomono_10;
|
||||||
|
|
||||||
|
#endif /* _FONTEM_notomono_10_H */
|
||||||
84
lib/fonts/fontem.h
Normal file
84
lib/fonts/fontem.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/** Font structure definitions.
|
||||||
|
*
|
||||||
|
* This file is distributed under the terms of the MIT License.
|
||||||
|
* See the LICENSE file at the top of this tree, or if it is missing a copy can
|
||||||
|
* be found at http://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FONTEM_H
|
||||||
|
#define _FONTEM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/** Alpha compositing "A over B" mechanism */
|
||||||
|
#define alpha_blend(in, in_alpha, out, out_alpha) \
|
||||||
|
((0x100 * in * in_alpha * (255 - out_alpha) + out * out_alpha * (255 - in_alpha)) >> 16)
|
||||||
|
#define blend(a, b, alpha) \
|
||||||
|
(((a) * (255 - (alpha)) + (b) * (alpha)) >> 8)
|
||||||
|
|
||||||
|
/** Extract the Alpha channel from a 32-bit RGBA value */
|
||||||
|
#define rgba32_get_a(rgba) ((rgba >> 24) & 0xff)
|
||||||
|
/** Extract the Red channel from a 32-bit RGBA value */
|
||||||
|
#define rgba32_get_r(rgba) ((rgba >> 16) & 0xff)
|
||||||
|
/** Extract the Green channel from a 32-bit RGBA value */
|
||||||
|
#define rgba32_get_g(rgba) ((rgba >> 8) & 0xff)
|
||||||
|
/** Extract the Blue channel from a 32-bit RGBA value */
|
||||||
|
#define rgba32_get_b(rgba) (rgba & 0xff)
|
||||||
|
|
||||||
|
/** Extract the Red channel from a 16-bit RGB value */
|
||||||
|
#define rgb16_get_r(rgb) ((rgb >> 8) & 0xf8)
|
||||||
|
/** Extract the Green channel from a 16-bit RGB value */
|
||||||
|
#define rgb16_get_g(rgb) ((rgb >> 3) & 0xfc)
|
||||||
|
/** Extract the Blue channel from a 16-bit RGB value */
|
||||||
|
#define rgb16_get_b(rgb) ((rgb << 3) & 0xf8)
|
||||||
|
/** Combine Red, Green and Blue channels into a 16-bit RGB value */
|
||||||
|
#define rgb16_combine(r, g, b) \
|
||||||
|
((((r) & 0xf8) << 8) | \
|
||||||
|
(((g) & 0xfc) << 3) | \
|
||||||
|
(((b) & 0xf8) >> 3))
|
||||||
|
|
||||||
|
|
||||||
|
/** Glyph character value rype */
|
||||||
|
typedef uint16_t glyph_t;
|
||||||
|
|
||||||
|
/** Description of a glyph; a single character in a font. */
|
||||||
|
struct glyph {
|
||||||
|
glyph_t glyph; /** The glyph this entry refers to */
|
||||||
|
|
||||||
|
int16_t left; /** Offset of the left edge of the glyph */
|
||||||
|
int16_t top; /** Offset of the top edge of the glyph */
|
||||||
|
int16_t advance; /** Horizonal offset when advancing to the next glyph */
|
||||||
|
|
||||||
|
uint16_t cols; /** Width of the bitmap */
|
||||||
|
uint16_t rows; /** Height of the bitmap */
|
||||||
|
const uint8_t *bitmap; /** Bitmap data */
|
||||||
|
|
||||||
|
const struct kerning *kerning; /** Font kerning data */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Kerning table; for a pair of glyphs, provides the horizontal adjustment. */
|
||||||
|
struct kerning {
|
||||||
|
glyph_t left; /** The left-glyph */
|
||||||
|
int16_t offset; /** The kerning offset for this glyph pair */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Description of a font. */
|
||||||
|
struct font {
|
||||||
|
char *name; /** Name of the font */
|
||||||
|
char *style; /** Style of the font */
|
||||||
|
|
||||||
|
uint16_t size; /** Point size of the font */
|
||||||
|
uint16_t dpi; /** Resolution of the font */
|
||||||
|
|
||||||
|
int16_t ascender; /** Ascender height */
|
||||||
|
int16_t descender; /** Descender height */
|
||||||
|
int16_t height; /** Baseline-to-baseline height */
|
||||||
|
|
||||||
|
uint16_t count; /** Number of glyphs */
|
||||||
|
uint16_t max; /** Maximum glyph index */
|
||||||
|
const struct glyph **glyphs; /** Font glyphs */
|
||||||
|
char compressed; /** TRUE if glyph bitmaps are RLE compressed */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _FONTEM_H */
|
||||||
74
macros.h
Normal file
74
macros.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
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>
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bitwise Operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SET(x, y) \
|
||||||
|
do { \
|
||||||
|
(x) |= (y); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SET_POS(x, y) \
|
||||||
|
do { \
|
||||||
|
(x) |= 1u << (y); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CLEAR(x, y) \
|
||||||
|
do { \
|
||||||
|
(x) &= ~(y); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CLEAR_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) \
|
||||||
|
static_assert((clear_mask & val) == val, \
|
||||||
|
"'value' in SET_TO() has bits set that are not in clear_mask"); \
|
||||||
|
do { \
|
||||||
|
CLEAR(x, clear_mask); \
|
||||||
|
SET(x, val); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
221
test.c
221
test.c
@@ -21,10 +21,15 @@
|
|||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "stm32l0xx.h"
|
|
||||||
|
|
||||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
#include "macros.h"
|
||||||
|
#include "stm32l0xx.h"
|
||||||
|
#include "font-notomono-10.h"
|
||||||
|
|
||||||
|
/* TODO: Start cleaning up code and adding bounds checking! */
|
||||||
|
|
||||||
void SystemInit()
|
void SystemInit()
|
||||||
{
|
{
|
||||||
@@ -38,10 +43,10 @@ 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. */
|
||||||
RCC->CR |= RCC_CR_MSION;
|
SET(RCC->CR, RCC_CR_MSION);
|
||||||
|
CLEAR(RCC->ICSCR, RCC_ICSCR_MSIRANGE);
|
||||||
|
|
||||||
RCC->ICSCR &= ~RCC_ICSCR_MSIRANGE;
|
SET_TO(RCC->ICSCR, RCC_ICSCR_MSIRANGE, RCC_ICSCR_MSIRANGE_6);
|
||||||
RCC->ICSCR |= RCC_ICSCR_MSIRANGE_6; // | RCC_ICSCR_MSIRANGE_0;;
|
|
||||||
|
|
||||||
/*!< Reset
|
/*!< Reset
|
||||||
* SW[1:0] (use MSI oscillator as system clock),
|
* SW[1:0] (use MSI oscillator as system clock),
|
||||||
@@ -50,8 +55,9 @@ 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) */
|
||||||
RCC->CFGR &= ~RCC_CFGR_SW & ~RCC_CFGR_HPRE & ~RCC_CFGR_PPRE1 &
|
CLEAR(RCC->CFGR,
|
||||||
~RCC_CFGR_PPRE2 & ~RCC_CFGR_MCOSEL & ~RCC_CFGR_MCOPRE;
|
RCC_CFGR_SW | ~RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2 |
|
||||||
|
RCC_CFGR_MCOSEL | RCC_CFGR_MCOPRE);
|
||||||
|
|
||||||
/*!< Reset
|
/*!< Reset
|
||||||
* HSION (disable HSI),
|
* HSION (disable HSI),
|
||||||
@@ -60,8 +66,9 @@ void SystemInit()
|
|||||||
* CSSHSEON (disable HSE clock monitoring)
|
* CSSHSEON (disable HSE clock monitoring)
|
||||||
* PLLON (disable PLL)
|
* PLLON (disable PLL)
|
||||||
*/
|
*/
|
||||||
RCC->CR &= ~RCC_CR_HSION & ~RCC_CR_HSIDIVEN & ~RCC_CR_HSEON &
|
CLEAR(RCC->CR,
|
||||||
~RCC_CR_CSSHSEON & ~RCC_CR_PLLON;
|
RCC_CR_HSION | RCC_CR_HSIDIVEN | RCC_CR_HSEON |
|
||||||
|
RCC_CR_CSSHSEON | RCC_CR_PLLON);
|
||||||
|
|
||||||
/*!< Reset HSEBYP bit (disable HSE bypass) */
|
/*!< Reset HSEBYP bit (disable HSE bypass) */
|
||||||
RCC->CR &= ~RCC_CR_HSEBYP;
|
RCC->CR &= ~RCC_CR_HSEBYP;
|
||||||
@@ -77,8 +84,8 @@ void SystemInit()
|
|||||||
/*!< Disable all interrupts */
|
/*!< Disable all interrupts */
|
||||||
RCC->CIER = 0x00000000;
|
RCC->CIER = 0x00000000;
|
||||||
|
|
||||||
/* Configure the Vector Table location add offset address ------------------*/
|
/* Vector Table Relocation in Internal FLASH */
|
||||||
SCB->VTOR = FLASH_BASE; /* Vector Table Relocation in Internal FLASH */
|
SCB->VTOR = FLASH_BASE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_lptim()
|
void init_lptim()
|
||||||
@@ -91,8 +98,7 @@ void init_lptim()
|
|||||||
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 */
|
||||||
RCC->CCIPR &= ~RCC_CCIPR_LPTIM1SEL;
|
SET_TO(RCC->CCIPR, RCC_CCIPR_LPTIM1SEL, RCC_CCIPR_LPTIM1SEL_0);
|
||||||
RCC->CCIPR |= 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) */
|
||||||
|
|
||||||
@@ -134,9 +140,65 @@ void init_lptim()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!< The buffer that will be used in the future to display on the watch display. */
|
/*!< The buffer that will be used in the future to display on the watch display. */
|
||||||
//static uint8_t display_buffer[144 * 168 / 8];
|
|
||||||
|
|
||||||
void init_lptim_toggler() {
|
#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()
|
||||||
|
{
|
||||||
init_lptim();
|
init_lptim();
|
||||||
|
|
||||||
/* Assign LPTIM1_OUT to PA7 */
|
/* Assign LPTIM1_OUT to PA7 */
|
||||||
@@ -159,70 +221,145 @@ void init_spi_display()
|
|||||||
/* Assign SPI_MOSI to PA12 (AFRH5), since PA7 is taken by LPTIM_OUT */
|
/* Assign SPI_MOSI to PA12 (AFRH5), since PA7 is taken by LPTIM_OUT */
|
||||||
GPIOA->AFR[1] &= ~GPIO_AFRH_AFRH4;
|
GPIOA->AFR[1] &= ~GPIO_AFRH_AFRH4;
|
||||||
|
|
||||||
GPIOA->MODER &= ~GPIO_MODER_MODE12;
|
SET_TO(GPIOA->MODER, GPIO_MODER_MODE12, 2u << GPIO_MODER_MODE12_Pos);
|
||||||
GPIOA->MODER |= 2u << GPIO_MODER_MODE12_Pos;
|
|
||||||
|
|
||||||
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_12;
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_12;
|
||||||
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD12;
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD12;
|
||||||
|
|
||||||
// SPI1 NSS
|
// SPI1 NSS (PA4)
|
||||||
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL4;
|
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL4;
|
||||||
|
|
||||||
GPIOA->MODER &= ~GPIO_MODER_MODE4;
|
SET_TO(GPIOA->MODER, GPIO_MODER_MODE4, 2u << GPIO_MODER_MODE4_Pos);
|
||||||
GPIOA->MODER |= 2u << GPIO_MODER_MODE4_Pos;
|
|
||||||
|
|
||||||
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4;
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4;
|
||||||
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD4;
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD4;
|
||||||
// enable pullup, since the pin doesn't seem to stay up
|
// enable pullup, since the pin doesn't seem to stay up
|
||||||
GPIOA->PUPDR |= 2u << GPIO_PUPDR_PUPD4_Pos;
|
GPIOA->PUPDR |= 2u << GPIO_PUPDR_PUPD4_Pos;
|
||||||
|
|
||||||
// SPI1 SCK
|
// SPI1 SCK (PA5)
|
||||||
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL5;
|
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL5;
|
||||||
|
|
||||||
GPIOA->MODER &= ~GPIO_MODER_MODE5;
|
SET_TO(GPIOA->MODER, GPIO_MODER_MODE5, 2u << GPIO_MODER_MODE5_Pos);
|
||||||
GPIOA->MODER |= 2u << GPIO_MODER_MODE5_Pos;
|
|
||||||
|
|
||||||
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5;
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5;
|
||||||
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5;
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5;
|
||||||
|
|
||||||
// SPI1 MISO
|
// SPI1 MISO (PA6)
|
||||||
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL6;
|
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL6;
|
||||||
|
|
||||||
GPIOA->MODER &= ~GPIO_MODER_MODE6;
|
SET_TO(GPIOA->MODER, GPIO_MODER_MODE6, 2u << GPIO_MODER_MODE6_Pos);
|
||||||
GPIOA->MODER |= 2u << GPIO_MODER_MODE6_Pos;
|
|
||||||
|
|
||||||
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_6;
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_6;
|
||||||
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD6;
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD6;
|
||||||
|
|
||||||
SPI1->CR1 |= SPI_CR1_SPE | SPI_CR1_MSTR | SPI_CR1_SSOE;// | SPI_CR1_SSM | SPI_CR1_SSI;
|
// Enable Master mode and half the baud rate, so it's set to ~1MHz
|
||||||
// SPI1->CR1 |= SPI_CR1_BR;
|
SPI1->CR1 |= SPI_CR1_MSTR | SPI_CR1_LSBFIRST;
|
||||||
|
SPI1->CR1 |= 1u << SPI_CR1_BR_Pos;
|
||||||
|
SPI1->CR2 |= SPI_CR2_SSOE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
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()
|
||||||
|
{
|
||||||
/** Enable Port A,B clock */
|
/** Enable Port A,B clock */
|
||||||
RCC->IOPENR |= RCC_IOPENR_IOPAEN;
|
RCC->IOPENR |= RCC_IOPENR_IOPAEN;
|
||||||
RCC->IOPENR |= RCC_IOPENR_IOPBEN;
|
RCC->IOPENR |= RCC_IOPENR_IOPBEN;
|
||||||
|
|
||||||
/** Enable pin P3 for output */
|
/** Enable pin P3 for output */
|
||||||
GPIOB->MODER &= ~GPIO_MODER_MODE3;
|
SET_TO(GPIOB->MODER, GPIO_MODER_MODE3, GPIO_MODER_MODE3_0);
|
||||||
GPIOB->MODER |= GPIO_MODER_MODE3_0;
|
|
||||||
|
|
||||||
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_3;
|
CLEAR(GPIOB->OTYPER, GPIO_OTYPER_OT_3);
|
||||||
GPIOB->PUPDR &= GPIO_PUPDR_PUPD3;
|
CLEAR(GPIOB->PUPDR, GPIO_PUPDR_PUPD3);
|
||||||
|
|
||||||
init_lptim_toggler();
|
init_lptim_toggler();
|
||||||
init_spi_display();
|
init_spi_display();
|
||||||
|
|
||||||
// GPIOB->ODR |= GPIO_ODR_OD3;
|
display_buffer_init(&buffer);
|
||||||
// for (volatile int i = 0; i < 100000; i++) {}
|
|
||||||
// GPIOB->ODR &= ~GPIO_ODR_OD3;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (SPI1->SR & SPI_SR_TXE) {
|
display_update();
|
||||||
SPI1->DR = 0xA5;
|
spi_send_blocking(SPI1, (uint8_t *) &buffer, sizeof(buffer));
|
||||||
GPIOB->ODR |= GPIO_ODR_OD3;
|
FLIP(GPIOB->ODR, GPIO_ODR_OD3);
|
||||||
} else {
|
// for (int i = 0; i < 100000; i++);
|
||||||
GPIOB->ODR &= ~GPIO_ODR_OD3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user