First init.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* Arduino system code
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "system.h"
|
||||
|
||||
// Connections
|
||||
|
||||
#define DISPLAY_RESX PB0
|
||||
#define DISPLAY_CSX PB1
|
||||
#define DISPLAY_DCX PB10
|
||||
#define DISPLAY_SCL PA5
|
||||
#define DISPLAY_SDA PA7
|
||||
|
||||
// Display
|
||||
|
||||
static uint8_t display_text_buffer[80 * 80];
|
||||
|
||||
static const uint8_t display_init_sequence[] = {
|
||||
MR_SEND_COMMAND(MR_ST7789_INVON), // Inverse for IPS displays
|
||||
MR_END(),
|
||||
};
|
||||
|
||||
void on_display_sleep(uint32_t value);
|
||||
void on_display_set_reset(bool value);
|
||||
void on_Display_set_command(bool value);
|
||||
void on_display_send(uint16_t value);
|
||||
void on_display_send16(uint16_t value);
|
||||
|
||||
void on_display_sleep(uint32_t value) {
|
||||
delay(value);
|
||||
}
|
||||
|
||||
void on_display_set_reset(bool value) {
|
||||
digitalWrite(DISPLAY_RESX, !value);
|
||||
}
|
||||
|
||||
void on_display_set_command(bool value) {
|
||||
if (value) {
|
||||
// Trigger CS before command
|
||||
digitalWrite(DISPLAY_CSX, HIGH);
|
||||
digitalWrite(DISPLAY_CSX, LOW);
|
||||
|
||||
digitalWrite(DISPLAY_DCX, LOW);
|
||||
} else
|
||||
digitalWrite(DISPLAY_DCX, HIGH);
|
||||
}
|
||||
|
||||
void on_display_send(uint16_t value) {
|
||||
SPI.transfer(value);
|
||||
}
|
||||
|
||||
void on_display_send16(uint16_t value) {
|
||||
SPI.transfer((value >> 8) & 0xff);
|
||||
SPI.transfer((value >> 0) & 0xff);
|
||||
}
|
||||
|
||||
void init_display(mr_t *mr) {
|
||||
// Setup GPIO
|
||||
pinMode(DISPLAY_RESX, OUTPUT);
|
||||
pinMode(DISPLAY_CSX, OUTPUT);
|
||||
pinMode(DISPLAY_DCX, OUTPUT);
|
||||
|
||||
digitalWrite(DISPLAY_RESX, HIGH);
|
||||
digitalWrite(DISPLAY_CSX, HIGH);
|
||||
|
||||
// Setup SPI
|
||||
SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE3));
|
||||
|
||||
// Setup display
|
||||
mr_st7789_init(mr,
|
||||
DISPLAY_HEIGHT,
|
||||
DISPLAY_WIDTH,
|
||||
MR_DISPLAY_ROTATION_270,
|
||||
display_text_buffer,
|
||||
sizeof(display_text_buffer),
|
||||
on_display_sleep,
|
||||
on_display_set_reset,
|
||||
on_display_set_command,
|
||||
on_display_send,
|
||||
on_display_send16);
|
||||
|
||||
mr_send_sequence(mr, display_init_sequence);
|
||||
}
|
||||
|
||||
void set_display(mr_t *mr, bool value) {
|
||||
mr_st7789_set_display(mr, value);
|
||||
mr_st7789_set_sleep(mr, !value);
|
||||
}
|
||||
|
||||
void update_display(mr_t *mr) {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* Arduino system code
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(DISPLAY_H)
|
||||
#define DISPLAY_H
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#include "mcu-renderer-st7789.h"
|
||||
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 240
|
||||
|
||||
void init_display(mr_t *mr);
|
||||
void set_display(mr_t *mr, bool value);
|
||||
void update_display(mr_t *mr);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* MCU renderer example
|
||||
* Hello world
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_material_symbolsR12_4.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_robotoM12_4.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_robotoM48_1.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_2.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_3.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_4.h"
|
||||
|
||||
#define STATUSBAR_X 0
|
||||
#define STATUSBAR_Y 0
|
||||
#define STATUSBAR_WIDTH DISPLAY_WIDTH
|
||||
#define STATUSBAR_HEIGHT 40
|
||||
#define STATUSBAR_ITEMS 4
|
||||
|
||||
#define CONTENT_X 0
|
||||
#define CONTENT_Y STATUSBAR_HEIGHT
|
||||
#define CONTENT_WIDTH DISPLAY_WIDTH
|
||||
#define CONTENT_HEIGHT (DISPLAY_HEIGHT - STATUSBAR_HEIGHT)
|
||||
|
||||
#define CELL_WIDTH (CONTENT_WIDTH / 2)
|
||||
#define CELL_HEIGHT (CONTENT_HEIGHT / 2)
|
||||
|
||||
mr_t mr;
|
||||
|
||||
void setup() {
|
||||
init_system();
|
||||
init_display(&mr);
|
||||
|
||||
// Draw content
|
||||
mr_rectangle_t rectangle;
|
||||
mr_point_t offset;
|
||||
|
||||
const char *test_strings[] = {
|
||||
"1bit",
|
||||
"2bit",
|
||||
"3bit",
|
||||
"4bit",
|
||||
};
|
||||
const uint8_t *test_fonts[] = {
|
||||
font_robotoM48_1,
|
||||
font_robotoM48_2,
|
||||
font_robotoM48_3,
|
||||
font_robotoM48_4,
|
||||
};
|
||||
for (uint32_t y = 0; y < 2; y++) {
|
||||
for (uint32_t x = 0; x < 2; x++) {
|
||||
uint32_t index = y * 2 + x;
|
||||
|
||||
mr_set_font(&mr, test_fonts[index]);
|
||||
mr_set_fill_color(&mr,
|
||||
mr_get_color((x != y) ? 0xf7f7f7 : 0xe8ecf2));
|
||||
mr_set_text_color(&mr, mr_get_color(0xDF1B1B));
|
||||
|
||||
rectangle = (mr_rectangle_t){ CONTENT_X + x * CELL_WIDTH,
|
||||
CONTENT_Y + y * CELL_HEIGHT,
|
||||
CELL_WIDTH,
|
||||
CELL_HEIGHT };
|
||||
offset = (mr_point_t){
|
||||
(CELL_WIDTH - mr_get_utf8_text_width(&mr, (uint8_t *)test_strings[index])) / 2,
|
||||
(CELL_HEIGHT - mr_get_line_height(&mr)) / 2
|
||||
};
|
||||
|
||||
mr_draw_utf8_text(&mr,
|
||||
(uint8_t *)test_strings[index],
|
||||
&rectangle,
|
||||
&offset);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw status bar
|
||||
mr_set_fill_color(&mr, mr_get_color(0xffffff));
|
||||
|
||||
const char *statusbar_items[] = {
|
||||
"Hello world!",
|
||||
"12:34",
|
||||
"\xee\x86\xa7",
|
||||
"\xee\x86\xa4"
|
||||
};
|
||||
const uint32_t statusbar_x[] = {
|
||||
0,
|
||||
STATUSBAR_WIDTH - 124,
|
||||
STATUSBAR_WIDTH - 68,
|
||||
STATUSBAR_WIDTH - 40,
|
||||
STATUSBAR_WIDTH
|
||||
};
|
||||
|
||||
rectangle = (mr_rectangle_t){ STATUSBAR_X,
|
||||
STATUSBAR_Y,
|
||||
STATUSBAR_WIDTH / 2,
|
||||
STATUSBAR_HEIGHT };
|
||||
|
||||
for (int i = 0; i < STATUSBAR_ITEMS; i++) {
|
||||
rectangle.x = statusbar_x[i];
|
||||
rectangle.width = statusbar_x[i + 1] - statusbar_x[i];
|
||||
|
||||
if (i < 2)
|
||||
mr_set_font(&mr, font_robotoM12_4);
|
||||
else
|
||||
mr_set_font(&mr, font_material_symbolsR12_4);
|
||||
|
||||
if (i < 1)
|
||||
mr_set_text_color(&mr, mr_get_color(0x000000));
|
||||
else
|
||||
mr_set_text_color(&mr, mr_get_color(0x707070));
|
||||
|
||||
offset = (mr_point_t){
|
||||
FONT_ROBOTOM12_4_CAP_HEIGHT,
|
||||
(STATUSBAR_HEIGHT - mr_get_line_height(&mr)) / 2
|
||||
};
|
||||
|
||||
mr_draw_utf8_text(&mr,
|
||||
(uint8_t *)statusbar_items[i],
|
||||
&rectangle,
|
||||
&offset);
|
||||
}
|
||||
|
||||
set_display(&mr, true);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
update_display(&mr);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* Arduino system code
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "system.h"
|
||||
|
||||
void init_system(void) {
|
||||
}
|
||||
|
||||
void sleep(uint32_t value) {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* Arduino system code
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(SYSTEM_H)
|
||||
#define SYSTEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void init_system(void);
|
||||
|
||||
void sleep(uint32_t value);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"build": {
|
||||
"cpu": "cortex-m3",
|
||||
"extra_flags": "-DSTM32 -DSTM32F1 -DSTM32F103x8",
|
||||
"f_cpu": "72000000L",
|
||||
"mcu": "stm32f103c8t6",
|
||||
"product_line": "STM32F103x8"
|
||||
},
|
||||
"debug": {
|
||||
"default_tools": [
|
||||
"stlink"
|
||||
],
|
||||
"openocd_target": "stm32f1x",
|
||||
"svd_path": "STM32F103xx.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"libopencm3"
|
||||
],
|
||||
"name": "STM32F103C8",
|
||||
"upload": {
|
||||
"maximum_ram_size": 20480,
|
||||
"maximum_size": 65536,
|
||||
"protocol": "stlink",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"stlink",
|
||||
"blackmagic"
|
||||
]
|
||||
},
|
||||
"url": "https://www.st.com/",
|
||||
"vendor": "ST"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[env:bluepill]
|
||||
platform = ststm32
|
||||
board = stm32f103c8
|
||||
framework = libopencm3
|
||||
lib_deps =
|
||||
FooLib=symlink://../../../mcu-renderer
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* ST7789 on STM32
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/spi.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "system.h"
|
||||
|
||||
// Connections
|
||||
|
||||
#define DISPLAY_RESX_PORT GPIOB
|
||||
#define DISPLAY_RESX_PIN GPIO0
|
||||
#define DISPLAY_CSX_PORT GPIOB
|
||||
#define DISPLAY_CSX_PIN GPIO1
|
||||
#define DISPLAY_DCX_PORT GPIOB
|
||||
#define DISPLAY_DCX_PIN GPIO10
|
||||
#define DISPLAY_SCL_PORT GPIOA
|
||||
#define DISPLAY_SCL_PIN GPIO5
|
||||
#define DISPLAY_SDA_PORT GPIOA
|
||||
#define DISPLAY_SDA_PIN GPIO7
|
||||
|
||||
// Display
|
||||
|
||||
static uint8_t display_text_buffer[80 * 80];
|
||||
|
||||
static const uint8_t display_init_sequence[] = {
|
||||
MR_SEND_COMMAND(MR_ST7789_INVON), // Inverse for IPS displays
|
||||
MR_END(),
|
||||
};
|
||||
|
||||
void on_display_sleep(uint32_t value);
|
||||
void on_display_set_reset(bool value);
|
||||
void on_display_set_command(bool value);
|
||||
void on_display_send(uint16_t value);
|
||||
void on_display_send16(uint16_t value);
|
||||
|
||||
void on_display_sleep(uint32_t value)
|
||||
{
|
||||
sleep(value);
|
||||
}
|
||||
|
||||
void on_display_set_reset(bool value)
|
||||
{
|
||||
if (value)
|
||||
gpio_clear(DISPLAY_RESX_PORT,
|
||||
DISPLAY_RESX_PIN);
|
||||
else
|
||||
gpio_set(DISPLAY_RESX_PORT,
|
||||
DISPLAY_RESX_PIN);
|
||||
}
|
||||
|
||||
void on_display_set_command(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
// Trigger CS before command
|
||||
gpio_set(DISPLAY_CSX_PORT,
|
||||
DISPLAY_CSX_PIN);
|
||||
gpio_clear(DISPLAY_CSX_PORT,
|
||||
DISPLAY_CSX_PIN);
|
||||
|
||||
gpio_clear(DISPLAY_DCX_PORT,
|
||||
DISPLAY_DCX_PIN);
|
||||
}
|
||||
else
|
||||
gpio_set(DISPLAY_DCX_PORT,
|
||||
DISPLAY_DCX_PIN);
|
||||
}
|
||||
|
||||
void on_display_send(uint16_t value)
|
||||
{
|
||||
spi_send(SPI1, value);
|
||||
}
|
||||
|
||||
void on_display_send16(uint16_t value)
|
||||
{
|
||||
spi_send(SPI1, (value >> 8) & 0xff);
|
||||
spi_send(SPI1, (value >> 0) & 0xff);
|
||||
}
|
||||
|
||||
void init_display(mr_t *mr)
|
||||
{
|
||||
// Setup GPIO
|
||||
gpio_set(DISPLAY_RESX_PORT,
|
||||
DISPLAY_RESX_PIN);
|
||||
gpio_set(DISPLAY_CSX_PORT,
|
||||
DISPLAY_CSX_PIN);
|
||||
|
||||
gpio_set_mode(DISPLAY_RESX_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_PUSHPULL,
|
||||
DISPLAY_RESX_PIN);
|
||||
gpio_set_mode(DISPLAY_CSX_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_PUSHPULL,
|
||||
DISPLAY_CSX_PIN);
|
||||
gpio_set_mode(DISPLAY_DCX_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_PUSHPULL,
|
||||
DISPLAY_DCX_PIN);
|
||||
gpio_set_mode(DISPLAY_SCL_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,
|
||||
DISPLAY_SCL_PIN);
|
||||
gpio_set_mode(DISPLAY_SDA_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,
|
||||
DISPLAY_SDA_PIN);
|
||||
|
||||
// Setup SPI
|
||||
rcc_periph_clock_enable(RCC_SPI1);
|
||||
spi_init_master(SPI1,
|
||||
SPI_CR1_BAUDRATE_FPCLK_DIV_2,
|
||||
SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLE,
|
||||
SPI_CR1_CPHA_CLK_TRANSITION_2,
|
||||
SPI_CR1_DFF_8BIT,
|
||||
SPI_CR1_MSBFIRST);
|
||||
spi_enable_software_slave_management(SPI1);
|
||||
spi_set_nss_high(SPI1);
|
||||
spi_enable(SPI1);
|
||||
|
||||
// Setup display
|
||||
mr_st7789_init(mr,
|
||||
DISPLAY_HEIGHT,
|
||||
DISPLAY_WIDTH,
|
||||
MR_DISPLAY_ROTATION_270,
|
||||
display_text_buffer,
|
||||
sizeof(display_text_buffer),
|
||||
on_display_sleep,
|
||||
on_display_set_reset,
|
||||
on_display_set_command,
|
||||
on_display_send,
|
||||
on_display_send16);
|
||||
|
||||
mr_send_sequence(mr, display_init_sequence);
|
||||
}
|
||||
|
||||
void set_display(mr_t *mr, bool value)
|
||||
{
|
||||
mr_st7789_set_display(mr, value);
|
||||
mr_st7789_set_sleep(mr, !value);
|
||||
}
|
||||
|
||||
void update_display(mr_t *mr)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* ST7789 on STM32
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(DISPLAY_H)
|
||||
#define DISPLAY_H
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#include "mcu-renderer-st7789.h"
|
||||
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 240
|
||||
|
||||
void init_display(mr_t *mr);
|
||||
void set_display(mr_t *mr, bool value);
|
||||
void update_display(mr_t *mr);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* STM32 keyboard
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
|
||||
#include "keyboard.h"
|
||||
|
||||
void init_keyboard(void)
|
||||
{
|
||||
gpio_set(GPIOA,
|
||||
GPIO8 | GPIO9 | GPIO10 | GPIO11);
|
||||
gpio_set_mode(GPIOA,
|
||||
GPIO_MODE_INPUT,
|
||||
GPIO_CNF_INPUT_PULL_UPDOWN,
|
||||
GPIO8 | GPIO9 | GPIO10 | GPIO11);
|
||||
}
|
||||
|
||||
bool get_key_down(key_t index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case KEY_LEFT:
|
||||
return !gpio_get(GPIOA, GPIO8);
|
||||
|
||||
case KEY_RIGHT:
|
||||
return !gpio_get(GPIOA, GPIO9);
|
||||
|
||||
case KEY_UP:
|
||||
return !gpio_get(GPIOA, GPIO10);
|
||||
|
||||
case KEY_DOWN:
|
||||
return !gpio_get(GPIOA, GPIO11);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* STM32 keyboard
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(KEYBOARD_H)
|
||||
#define KEYBOARD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
KEY_LEFT,
|
||||
KEY_RIGHT,
|
||||
KEY_UP,
|
||||
KEY_DOWN,
|
||||
} key_t;
|
||||
|
||||
void init_keyboard(void);
|
||||
|
||||
bool get_key_down(key_t index);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* MCU renderer example
|
||||
* Gamma calibration demo
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "keyboard.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_robotoM24_4.h"
|
||||
|
||||
#define GRADIENT_HEIGHT ((DISPLAY_HEIGHT - 40) / 4)
|
||||
|
||||
struct gamma_entry
|
||||
{
|
||||
const char *label;
|
||||
uint8_t profile_index;
|
||||
uint8_t bit_num;
|
||||
uint8_t bit_offset;
|
||||
};
|
||||
|
||||
struct gamma_entry gamma_entries[] = {
|
||||
{"V0P", 0, 4, 0},
|
||||
{"V0N", 0 + 14, 4, 0},
|
||||
{"V1P", 1, 6, 0},
|
||||
{"V1N", 1 + 14, 6, 0},
|
||||
{"V2P", 2, 6, 0},
|
||||
{"V2N", 2 + 14, 6, 0},
|
||||
{"V4P", 3, 5, 0},
|
||||
{"V4N", 3 + 14, 5, 0},
|
||||
{"V6P", 4, 5, 0},
|
||||
{"V6N", 4 + 14, 5, 0},
|
||||
{"V13P", 5, 4, 0},
|
||||
{"V13N", 5 + 14, 4, 0},
|
||||
{"V20P", 6, 7, 0},
|
||||
{"V20N", 6 + 14, 7, 0},
|
||||
{"V27P", 7, 3, 0},
|
||||
{"V27N", 7 + 14, 3, 0},
|
||||
{"V36P", 7, 3, 4},
|
||||
{"V36N", 7 + 14, 3, 4},
|
||||
{"V43P", 8, 7, 0},
|
||||
{"V43N", 8 + 14, 7, 0},
|
||||
{"V50P", 9, 4, 0},
|
||||
{"V50N", 9 + 14, 4, 0},
|
||||
{"V57P", 10, 5, 0},
|
||||
{"V57N", 10 + 14, 5, 0},
|
||||
{"V59P", 11, 5, 0},
|
||||
{"V59N", 11 + 14, 5, 0},
|
||||
{"V61P", 12, 6, 0},
|
||||
{"V61N", 12 + 14, 6, 0},
|
||||
{"V62P", 13, 6, 0},
|
||||
{"V62N", 13 + 14, 6, 0},
|
||||
{"V63P", 0, 4, 4},
|
||||
{"V63N", 0 + 14, 4, 4},
|
||||
{"JP0P", 5, 2, 4},
|
||||
{"JP0N", 5 + 14, 2, 4},
|
||||
{"JP1P", 9, 2, 4},
|
||||
{"JP1N", 9 + 14, 2, 4},
|
||||
};
|
||||
|
||||
#define GAMMA_ENTRY_NUM (sizeof(gamma_entries) / sizeof(struct gamma_entry))
|
||||
|
||||
uint8_t gamma_profile[14 * 2] = {
|
||||
0xd0, 0x00, 0x02, 0x07, 0x0b, 0x1a, 0x31, 0x54, 0x40, 0x29, 0x12, 0x12, 0x12, 0x17,
|
||||
0xd0, 0x00, 0x02, 0x07, 0x05, 0x25, 0x2d, 0x44, 0x44, 0x1c, 0x18, 0x16, 0x1c, 0x1d};
|
||||
|
||||
void strcat_uint8(char *text, uint8_t value);
|
||||
|
||||
void strcat_uint8(char *text, uint8_t value)
|
||||
{
|
||||
uint32_t index = strlen(text);
|
||||
|
||||
uint8_t digit;
|
||||
|
||||
digit = value / 100;
|
||||
if (digit)
|
||||
{
|
||||
text[index++] = '0' + digit;
|
||||
value -= 100 * digit;
|
||||
}
|
||||
|
||||
digit = value / 10;
|
||||
if (digit)
|
||||
{
|
||||
text[index++] = '0' + digit;
|
||||
value -= 10 * digit;
|
||||
}
|
||||
|
||||
digit = value;
|
||||
text[index++] = '0' + digit;
|
||||
|
||||
text[index] = '\0';
|
||||
}
|
||||
|
||||
// Main
|
||||
|
||||
int main(void)
|
||||
{
|
||||
mr_t mr;
|
||||
|
||||
init_system();
|
||||
init_display(&mr);
|
||||
init_keyboard();
|
||||
|
||||
// Draw color gradients
|
||||
mr_rectangle_t rectangle = {
|
||||
0, 0,
|
||||
1, GRADIENT_HEIGHT};
|
||||
|
||||
for (uint32_t x = 0; x < DISPLAY_WIDTH; x++)
|
||||
{
|
||||
rectangle.x = x;
|
||||
|
||||
uint32_t value = x * 64 / DISPLAY_WIDTH;
|
||||
mr_color_t red = (value >> 1) << 11;
|
||||
mr_color_t green = value << 5;
|
||||
mr_color_t blue = (value >> 1);
|
||||
|
||||
rectangle.y = 0 * GRADIENT_HEIGHT;
|
||||
mr_set_fill_color(&mr, red);
|
||||
mr_draw_rectangle(&mr, &rectangle);
|
||||
|
||||
rectangle.y = 1 * GRADIENT_HEIGHT;
|
||||
mr_set_fill_color(&mr, green);
|
||||
mr_draw_rectangle(&mr, &rectangle);
|
||||
|
||||
rectangle.y = 2 * GRADIENT_HEIGHT;
|
||||
mr_set_fill_color(&mr, blue);
|
||||
mr_draw_rectangle(&mr, &rectangle);
|
||||
|
||||
rectangle.y = 3 * GRADIENT_HEIGHT;
|
||||
mr_set_fill_color(&mr, red | green | blue);
|
||||
mr_draw_rectangle(&mr, &rectangle);
|
||||
}
|
||||
|
||||
const mr_rectangle_t text_rectangle = {
|
||||
0, 4 * GRADIENT_HEIGHT,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT - 4 * GRADIENT_HEIGHT};
|
||||
|
||||
int32_t gamma_entry_index = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Draw text bar
|
||||
mr_send_command(&mr, MR_ST7789_PVGAMCTRL);
|
||||
for (uint32_t i = 0; i < 14; i++)
|
||||
mr_send_data(&mr, gamma_profile[i]);
|
||||
mr_send_command(&mr, MR_ST7789_NVGAMCTRL);
|
||||
for (uint32_t i = 14; i < 28; i++)
|
||||
mr_send_data(&mr, gamma_profile[i]);
|
||||
|
||||
uint8_t value = gamma_profile[gamma_entries[gamma_entry_index].profile_index];
|
||||
uint8_t bit_offset = gamma_entries[gamma_entry_index].bit_offset;
|
||||
uint8_t mask = (1 << gamma_entries[gamma_entry_index].bit_num) - 1;
|
||||
value >>= bit_offset;
|
||||
value &= mask;
|
||||
|
||||
char text[16];
|
||||
strcpy(text, gamma_entries[gamma_entry_index].label);
|
||||
strcat(text, "=");
|
||||
strcat_uint8(text, value);
|
||||
|
||||
mr_set_font(&mr, font_robotoM24_4);
|
||||
mr_set_fill_color(&mr, mr_get_color(0x000000));
|
||||
mr_set_text_color(&mr, mr_get_color(0xffffff));
|
||||
|
||||
const mr_point_t textOffset = {
|
||||
(DISPLAY_WIDTH - mr_get_text_width(&mr, text)) / 2, 0};
|
||||
|
||||
mr_draw_text(&mr, text, &text_rectangle, &textOffset);
|
||||
|
||||
set_display(&mr, true);
|
||||
|
||||
// Wait for keyboard event
|
||||
while (get_key_down(KEY_LEFT) ||
|
||||
get_key_down(KEY_RIGHT) ||
|
||||
get_key_down(KEY_UP) ||
|
||||
get_key_down(KEY_DOWN))
|
||||
{
|
||||
sleep(50);
|
||||
}
|
||||
|
||||
// Handle keyboard event
|
||||
while (true)
|
||||
{
|
||||
if (get_key_down(KEY_LEFT))
|
||||
{
|
||||
gamma_entry_index--;
|
||||
if (gamma_entry_index < 0)
|
||||
gamma_entry_index = GAMMA_ENTRY_NUM - 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_key_down(KEY_RIGHT))
|
||||
{
|
||||
gamma_entry_index++;
|
||||
if (gamma_entry_index >= (int32_t)GAMMA_ENTRY_NUM)
|
||||
gamma_entry_index = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_key_down(KEY_UP))
|
||||
{
|
||||
value++;
|
||||
value &= mask;
|
||||
value <<= bit_offset;
|
||||
gamma_profile[gamma_entries[gamma_entry_index].profile_index] &= ~mask;
|
||||
gamma_profile[gamma_entries[gamma_entry_index].profile_index] |= value;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_key_down(KEY_DOWN))
|
||||
{
|
||||
value--;
|
||||
value &= mask;
|
||||
value <<= bit_offset;
|
||||
gamma_profile[gamma_entries[gamma_entry_index].profile_index] &= ~mask;
|
||||
gamma_profile[gamma_entries[gamma_entry_index].profile_index] |= value;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* STM32 system code
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <libopencm3/cm3/nvic.h>
|
||||
#include <libopencm3/cm3/systick.h>
|
||||
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
|
||||
#include "system.h"
|
||||
|
||||
static volatile uint32_t system_current_tick;
|
||||
|
||||
void sys_tick_handler(void)
|
||||
{
|
||||
system_current_tick++;
|
||||
}
|
||||
|
||||
void init_system(void)
|
||||
{
|
||||
// Setup MCU clock
|
||||
rcc_clock_setup_in_hse_8mhz_out_72mhz();
|
||||
|
||||
// Setup systick
|
||||
systick_set_frequency(SYSTICK_FREQUENCY, rcc_ahb_frequency);
|
||||
systick_clear();
|
||||
nvic_set_priority(NVIC_SYSTICK_IRQ, 0xc0);
|
||||
systick_interrupt_enable();
|
||||
systick_counter_enable();
|
||||
|
||||
// Setup GPIO
|
||||
rcc_periph_clock_enable(RCC_GPIOA);
|
||||
rcc_periph_clock_enable(RCC_GPIOB);
|
||||
rcc_periph_clock_enable(RCC_GPIOC);
|
||||
}
|
||||
|
||||
void sleep(uint32_t value)
|
||||
{
|
||||
uint32_t start_tick = system_current_tick;
|
||||
|
||||
while ((system_current_tick - start_tick) < value)
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* STM32 system code
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(SYSTEM_H)
|
||||
#define SYSTEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SYSTICK_FREQUENCY 1000
|
||||
|
||||
void init_system(void);
|
||||
|
||||
void sleep(uint32_t value);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(sdl_example)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
||||
FILE(GLOB sources src/*.c)
|
||||
FILE(GLOB mcu_renderer_sources ../../src/*.c)
|
||||
|
||||
find_package(SDL2 CONFIG REQUIRED)
|
||||
|
||||
add_definitions(-D MCURENDERER_SDL)
|
||||
|
||||
add_executable(sdl_example ${sources} ${mcu_renderer_sources})
|
||||
|
||||
target_include_directories(sdl_example PRIVATE ../../src ../../fonts)
|
||||
target_link_libraries(sdl_example
|
||||
PRIVATE
|
||||
$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>
|
||||
$<IF:$<TARGET_EXISTS:SDL2::SDL2>,SDL2::SDL2,SDL2::SDL2-static>
|
||||
)
|
||||
53
libraries/mcu-renderer/examples/helloworld-sdl/src/display.c
Normal file
53
libraries/mcu-renderer/examples/helloworld-sdl/src/display.c
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* MCU renderer example
|
||||
* SDL display
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#include "display.h"
|
||||
|
||||
// Display
|
||||
|
||||
void init_display(mr_t *mr)
|
||||
{
|
||||
// Setup display
|
||||
#if defined(DISPLAY_MONOCHROME)
|
||||
mr_sdl_init(&mr,
|
||||
DISPLAY_WIDTH,
|
||||
DISPLAY_HEIGHT,
|
||||
MR_SDL_DISPLAY_TYPE_MONOCHROME,
|
||||
2,
|
||||
"mcu-renderer");
|
||||
#else
|
||||
mr_sdl_init(mr,
|
||||
DISPLAY_WIDTH,
|
||||
DISPLAY_HEIGHT,
|
||||
MR_SDL_DISPLAY_TYPE_COLOR,
|
||||
2,
|
||||
"mcu-renderer");
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_display(mr_t *mr, bool value)
|
||||
{
|
||||
mr_sdl_set_display(mr, value);
|
||||
mr_sdl_set_backlight(mr, value ? 255 : 0);
|
||||
}
|
||||
|
||||
void update_display(mr_t *mr)
|
||||
{
|
||||
mr_sdl_refresh_display(mr);
|
||||
|
||||
SDL_Event event;
|
||||
|
||||
if (SDL_PollEvent(&event))
|
||||
{
|
||||
if (event.type == SDL_QUIT)
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
25
libraries/mcu-renderer/examples/helloworld-sdl/src/display.h
Normal file
25
libraries/mcu-renderer/examples/helloworld-sdl/src/display.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* MCU renderer example
|
||||
* SDL display
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(DISPLAY_H)
|
||||
#define DISPLAY_H
|
||||
|
||||
#include "mcu-renderer-sdl.h"
|
||||
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 240
|
||||
|
||||
// Enable for monochrome output:
|
||||
// #define DISPLAY_MONOCHROME
|
||||
|
||||
void init_display(mr_t *mr);
|
||||
void set_display(mr_t *mr, bool value);
|
||||
void update_display(mr_t *mr);
|
||||
|
||||
#endif
|
||||
140
libraries/mcu-renderer/examples/helloworld-sdl/src/main.c
Normal file
140
libraries/mcu-renderer/examples/helloworld-sdl/src/main.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* MCU renderer example
|
||||
* Hello world
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_material_symbolsR12_4.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_robotoM12_4.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_robotoM48_1.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_2.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_3.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_4.h"
|
||||
|
||||
#define STATUSBAR_X 0
|
||||
#define STATUSBAR_Y 0
|
||||
#define STATUSBAR_WIDTH DISPLAY_WIDTH
|
||||
#define STATUSBAR_HEIGHT 40
|
||||
#define STATUSBAR_ITEMS 4
|
||||
|
||||
#define CONTENT_X 0
|
||||
#define CONTENT_Y STATUSBAR_HEIGHT
|
||||
#define CONTENT_WIDTH DISPLAY_WIDTH
|
||||
#define CONTENT_HEIGHT (DISPLAY_HEIGHT - STATUSBAR_HEIGHT)
|
||||
|
||||
#define CELL_WIDTH (CONTENT_WIDTH / 2)
|
||||
#define CELL_HEIGHT (CONTENT_HEIGHT / 2)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
mr_t mr;
|
||||
|
||||
init_system();
|
||||
init_display(&mr);
|
||||
|
||||
// Draw content
|
||||
mr_rectangle_t rectangle;
|
||||
mr_point_t offset;
|
||||
|
||||
const char *test_strings[] = {
|
||||
"1bit",
|
||||
"2bit",
|
||||
"3bit",
|
||||
"4bit",
|
||||
};
|
||||
const uint8_t *test_fonts[] = {
|
||||
font_robotoM48_1,
|
||||
font_robotoM48_2,
|
||||
font_robotoM48_3,
|
||||
font_robotoM48_4,
|
||||
};
|
||||
for (uint32_t y = 0; y < 2; y++)
|
||||
{
|
||||
for (uint32_t x = 0; x < 2; x++)
|
||||
{
|
||||
uint32_t index = y * 2 + x;
|
||||
|
||||
mr_set_font(&mr, test_fonts[index]);
|
||||
mr_set_fill_color(&mr,
|
||||
mr_get_color((x != y) ? 0xf7f7f7 : 0xe8ecf2));
|
||||
mr_set_text_color(&mr, mr_get_color(0xDF1B1B));
|
||||
|
||||
rectangle = (mr_rectangle_t){CONTENT_X + x * CELL_WIDTH,
|
||||
CONTENT_Y + y * CELL_HEIGHT,
|
||||
CELL_WIDTH,
|
||||
CELL_HEIGHT};
|
||||
offset = (mr_point_t){
|
||||
(CELL_WIDTH - mr_get_utf8_text_width(&mr,
|
||||
(uint8_t *)test_strings[index])) /
|
||||
2,
|
||||
(CELL_HEIGHT - mr_get_line_height(&mr)) / 2};
|
||||
|
||||
mr_draw_utf8_text(&mr,
|
||||
(uint8_t *)test_strings[index],
|
||||
&rectangle,
|
||||
&offset);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw status bar
|
||||
mr_set_fill_color(&mr, mr_get_color(0xffffff));
|
||||
|
||||
const char *statusbar_items[] = {
|
||||
"Hello world!",
|
||||
"12:34",
|
||||
"\xee\x86\xa7",
|
||||
"\xee\x86\xa4"};
|
||||
const uint32_t statusbar_x[] = {
|
||||
0,
|
||||
STATUSBAR_WIDTH - 124,
|
||||
STATUSBAR_WIDTH - 68,
|
||||
STATUSBAR_WIDTH - 40,
|
||||
STATUSBAR_WIDTH};
|
||||
|
||||
rectangle = (mr_rectangle_t){STATUSBAR_X,
|
||||
STATUSBAR_Y,
|
||||
STATUSBAR_WIDTH / 2,
|
||||
STATUSBAR_HEIGHT};
|
||||
|
||||
for (int i = 0; i < STATUSBAR_ITEMS; i++)
|
||||
{
|
||||
rectangle.x = statusbar_x[i];
|
||||
rectangle.width = statusbar_x[i + 1] - statusbar_x[i];
|
||||
|
||||
if (i < 2)
|
||||
mr_set_font(&mr, font_robotoM12_4);
|
||||
else
|
||||
mr_set_font(&mr, font_material_symbolsR12_4);
|
||||
|
||||
if (i < 1)
|
||||
mr_set_text_color(&mr, mr_get_color(0x000000));
|
||||
else
|
||||
mr_set_text_color(&mr, mr_get_color(0x707070));
|
||||
|
||||
offset = (mr_point_t){
|
||||
FONT_ROBOTOM12_4_CAP_HEIGHT,
|
||||
(STATUSBAR_HEIGHT - mr_get_line_height(&mr)) / 2};
|
||||
|
||||
mr_draw_utf8_text(&mr,
|
||||
(uint8_t *)statusbar_items[i],
|
||||
&rectangle,
|
||||
&offset);
|
||||
}
|
||||
|
||||
set_display(&mr, true);
|
||||
|
||||
// Update screen
|
||||
while (true)
|
||||
update_display(&mr);
|
||||
}
|
||||
18
libraries/mcu-renderer/examples/helloworld-sdl/src/system.c
Normal file
18
libraries/mcu-renderer/examples/helloworld-sdl/src/system.c
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* MCU renderer example
|
||||
* SDL system
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include "system.h"
|
||||
|
||||
void init_system(void)
|
||||
{
|
||||
}
|
||||
|
||||
void sleep(uint32_t value)
|
||||
{
|
||||
}
|
||||
19
libraries/mcu-renderer/examples/helloworld-sdl/src/system.h
Normal file
19
libraries/mcu-renderer/examples/helloworld-sdl/src/system.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* MCU renderer example
|
||||
* SDL system
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(SYSTEM_H)
|
||||
#define SYSTEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void init_system(void);
|
||||
|
||||
void sleep(uint32_t value);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"build": {
|
||||
"cpu": "cortex-m3",
|
||||
"extra_flags": "-DSTM32 -DSTM32F1 -DSTM32F103x8",
|
||||
"f_cpu": "72000000L",
|
||||
"mcu": "stm32f103c8t6",
|
||||
"product_line": "STM32F103x8"
|
||||
},
|
||||
"debug": {
|
||||
"default_tools": [
|
||||
"stlink"
|
||||
],
|
||||
"openocd_target": "stm32f1x",
|
||||
"svd_path": "STM32F103xx.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"libopencm3"
|
||||
],
|
||||
"name": "STM32F103C8",
|
||||
"upload": {
|
||||
"maximum_ram_size": 20480,
|
||||
"maximum_size": 65536,
|
||||
"protocol": "stlink",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"stlink",
|
||||
"blackmagic"
|
||||
]
|
||||
},
|
||||
"url": "https://www.st.com/",
|
||||
"vendor": "ST"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[env:bluepill]
|
||||
platform = ststm32
|
||||
board = stm32f103c8
|
||||
framework = libopencm3
|
||||
lib_deps =
|
||||
FooLib=symlink://../../../mcu-renderer
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* ST7789 on STM32
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/spi.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "system.h"
|
||||
|
||||
// Connections
|
||||
|
||||
#define DISPLAY_RESX_PORT GPIOB
|
||||
#define DISPLAY_RESX_PIN GPIO0
|
||||
#define DISPLAY_CSX_PORT GPIOB
|
||||
#define DISPLAY_CSX_PIN GPIO1
|
||||
#define DISPLAY_DCX_PORT GPIOB
|
||||
#define DISPLAY_DCX_PIN GPIO10
|
||||
#define DISPLAY_SCL_PORT GPIOA
|
||||
#define DISPLAY_SCL_PIN GPIO5
|
||||
#define DISPLAY_SDA_PORT GPIOA
|
||||
#define DISPLAY_SDA_PIN GPIO7
|
||||
|
||||
// Display
|
||||
|
||||
static uint8_t display_text_buffer[80 * 80];
|
||||
|
||||
static const uint8_t display_init_sequence[] = {
|
||||
MR_SEND_COMMAND(MR_ST7789_INVON), // Inverse for IPS displays
|
||||
MR_END(),
|
||||
};
|
||||
|
||||
void on_display_sleep(uint32_t value);
|
||||
void on_display_set_reset(bool value);
|
||||
void on_display_set_command(bool value);
|
||||
void on_display_send(uint16_t value);
|
||||
void on_display_send16(uint16_t value);
|
||||
|
||||
void on_display_sleep(uint32_t value)
|
||||
{
|
||||
sleep(value);
|
||||
}
|
||||
|
||||
void on_display_set_reset(bool value)
|
||||
{
|
||||
if (value)
|
||||
gpio_clear(DISPLAY_RESX_PORT,
|
||||
DISPLAY_RESX_PIN);
|
||||
else
|
||||
gpio_set(DISPLAY_RESX_PORT,
|
||||
DISPLAY_RESX_PIN);
|
||||
}
|
||||
|
||||
void on_display_set_command(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
// Trigger CS before command
|
||||
gpio_set(DISPLAY_CSX_PORT,
|
||||
DISPLAY_CSX_PIN);
|
||||
gpio_clear(DISPLAY_CSX_PORT,
|
||||
DISPLAY_CSX_PIN);
|
||||
|
||||
gpio_clear(DISPLAY_DCX_PORT,
|
||||
DISPLAY_DCX_PIN);
|
||||
}
|
||||
else
|
||||
gpio_set(DISPLAY_DCX_PORT,
|
||||
DISPLAY_DCX_PIN);
|
||||
}
|
||||
|
||||
void on_display_send(uint16_t value)
|
||||
{
|
||||
spi_send(SPI1, value);
|
||||
}
|
||||
|
||||
void on_display_send16(uint16_t value)
|
||||
{
|
||||
spi_send(SPI1, (value >> 8) & 0xff);
|
||||
spi_send(SPI1, (value >> 0) & 0xff);
|
||||
}
|
||||
|
||||
void init_display(mr_t *mr)
|
||||
{
|
||||
// Setup GPIO
|
||||
gpio_set(DISPLAY_RESX_PORT,
|
||||
DISPLAY_RESX_PIN);
|
||||
gpio_set(DISPLAY_CSX_PORT,
|
||||
DISPLAY_CSX_PIN);
|
||||
|
||||
gpio_set_mode(DISPLAY_RESX_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_PUSHPULL,
|
||||
DISPLAY_RESX_PIN);
|
||||
gpio_set_mode(DISPLAY_CSX_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_PUSHPULL,
|
||||
DISPLAY_CSX_PIN);
|
||||
gpio_set_mode(DISPLAY_DCX_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_PUSHPULL,
|
||||
DISPLAY_DCX_PIN);
|
||||
gpio_set_mode(DISPLAY_SCL_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,
|
||||
DISPLAY_SCL_PIN);
|
||||
gpio_set_mode(DISPLAY_SDA_PORT,
|
||||
GPIO_MODE_OUTPUT_50_MHZ,
|
||||
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,
|
||||
DISPLAY_SDA_PIN);
|
||||
|
||||
// Setup SPI
|
||||
rcc_periph_clock_enable(RCC_SPI1);
|
||||
spi_init_master(SPI1,
|
||||
SPI_CR1_BAUDRATE_FPCLK_DIV_2,
|
||||
SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLE,
|
||||
SPI_CR1_CPHA_CLK_TRANSITION_2,
|
||||
SPI_CR1_DFF_8BIT,
|
||||
SPI_CR1_MSBFIRST);
|
||||
spi_enable_software_slave_management(SPI1);
|
||||
spi_set_nss_high(SPI1);
|
||||
spi_enable(SPI1);
|
||||
|
||||
// Setup display
|
||||
mr_st7789_init(mr,
|
||||
DISPLAY_HEIGHT,
|
||||
DISPLAY_WIDTH,
|
||||
MR_DISPLAY_ROTATION_270,
|
||||
display_text_buffer,
|
||||
sizeof(display_text_buffer),
|
||||
on_display_sleep,
|
||||
on_display_set_reset,
|
||||
on_display_set_command,
|
||||
on_display_send,
|
||||
on_display_send16);
|
||||
|
||||
mr_send_sequence(mr, display_init_sequence);
|
||||
}
|
||||
|
||||
void set_display(mr_t *mr, bool value)
|
||||
{
|
||||
mr_st7789_set_display(mr, value);
|
||||
mr_st7789_set_sleep(mr, !value);
|
||||
}
|
||||
|
||||
void update_display(mr_t *mr)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* ST7789 on STM32
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(DISPLAY_H)
|
||||
#define DISPLAY_H
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#include "mcu-renderer-st7789.h"
|
||||
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 240
|
||||
|
||||
void init_display(mr_t *mr);
|
||||
void set_display(mr_t *mr, bool value);
|
||||
void update_display(mr_t *mr);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* MCU renderer example
|
||||
* Hello world
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_material_symbolsR12_4.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_robotoM12_4.h"
|
||||
|
||||
#include "mcu-renderer-fonts/font_robotoM48_1.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_2.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_3.h"
|
||||
#include "mcu-renderer-fonts/font_robotoM48_4.h"
|
||||
|
||||
#define STATUSBAR_X 0
|
||||
#define STATUSBAR_Y 0
|
||||
#define STATUSBAR_WIDTH DISPLAY_WIDTH
|
||||
#define STATUSBAR_HEIGHT 40
|
||||
#define STATUSBAR_ITEMS 4
|
||||
|
||||
#define CONTENT_X 0
|
||||
#define CONTENT_Y STATUSBAR_HEIGHT
|
||||
#define CONTENT_WIDTH DISPLAY_WIDTH
|
||||
#define CONTENT_HEIGHT (DISPLAY_HEIGHT - STATUSBAR_HEIGHT)
|
||||
|
||||
#define CELL_WIDTH (CONTENT_WIDTH / 2)
|
||||
#define CELL_HEIGHT (CONTENT_HEIGHT / 2)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
mr_t mr;
|
||||
|
||||
init_system();
|
||||
init_display(&mr);
|
||||
|
||||
// Draw content
|
||||
mr_rectangle_t rectangle;
|
||||
mr_point_t offset;
|
||||
|
||||
const char *test_strings[] = {
|
||||
"1bit",
|
||||
"2bit",
|
||||
"3bit",
|
||||
"4bit",
|
||||
};
|
||||
const uint8_t *test_fonts[] = {
|
||||
font_robotoM48_1,
|
||||
font_robotoM48_2,
|
||||
font_robotoM48_3,
|
||||
font_robotoM48_4,
|
||||
};
|
||||
for (uint32_t y = 0; y < 2; y++)
|
||||
{
|
||||
for (uint32_t x = 0; x < 2; x++)
|
||||
{
|
||||
uint32_t index = y * 2 + x;
|
||||
|
||||
mr_set_font(&mr, test_fonts[index]);
|
||||
mr_set_fill_color(&mr,
|
||||
mr_get_color((x != y) ? 0xf7f7f7 : 0xe8ecf2));
|
||||
mr_set_text_color(&mr, mr_get_color(0xDF1B1B));
|
||||
|
||||
rectangle = (mr_rectangle_t){CONTENT_X + x * CELL_WIDTH,
|
||||
CONTENT_Y + y * CELL_HEIGHT,
|
||||
CELL_WIDTH,
|
||||
CELL_HEIGHT};
|
||||
offset = (mr_point_t){
|
||||
(CELL_WIDTH - mr_get_utf8_text_width(&mr,
|
||||
(uint8_t *)test_strings[index])) /
|
||||
2,
|
||||
(CELL_HEIGHT - mr_get_line_height(&mr)) / 2};
|
||||
|
||||
mr_draw_utf8_text(&mr,
|
||||
(uint8_t *)test_strings[index],
|
||||
&rectangle,
|
||||
&offset);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw status bar
|
||||
mr_set_fill_color(&mr, mr_get_color(0xffffff));
|
||||
|
||||
const char *statusbar_items[] = {
|
||||
"Hello world!",
|
||||
"12:34",
|
||||
"\xee\x86\xa7",
|
||||
"\xee\x86\xa4"};
|
||||
const uint32_t statusbar_x[] = {
|
||||
0,
|
||||
STATUSBAR_WIDTH - 124,
|
||||
STATUSBAR_WIDTH - 68,
|
||||
STATUSBAR_WIDTH - 40,
|
||||
STATUSBAR_WIDTH};
|
||||
|
||||
rectangle = (mr_rectangle_t){STATUSBAR_X,
|
||||
STATUSBAR_Y,
|
||||
STATUSBAR_WIDTH / 2,
|
||||
STATUSBAR_HEIGHT};
|
||||
|
||||
for (int i = 0; i < STATUSBAR_ITEMS; i++)
|
||||
{
|
||||
rectangle.x = statusbar_x[i];
|
||||
rectangle.width = statusbar_x[i + 1] - statusbar_x[i];
|
||||
|
||||
if (i < 2)
|
||||
mr_set_font(&mr, font_robotoM12_4);
|
||||
else
|
||||
mr_set_font(&mr, font_material_symbolsR12_4);
|
||||
|
||||
if (i < 1)
|
||||
mr_set_text_color(&mr, mr_get_color(0x000000));
|
||||
else
|
||||
mr_set_text_color(&mr, mr_get_color(0x707070));
|
||||
|
||||
offset = (mr_point_t){
|
||||
FONT_ROBOTOM12_4_CAP_HEIGHT,
|
||||
(STATUSBAR_HEIGHT - mr_get_line_height(&mr)) / 2};
|
||||
|
||||
mr_draw_utf8_text(&mr,
|
||||
(uint8_t *)statusbar_items[i],
|
||||
&rectangle,
|
||||
&offset);
|
||||
}
|
||||
|
||||
set_display(&mr, true);
|
||||
|
||||
// Update screen
|
||||
while (true)
|
||||
update_display(&mr);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* STM32 system code
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <libopencm3/cm3/nvic.h>
|
||||
#include <libopencm3/cm3/systick.h>
|
||||
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
|
||||
#include "system.h"
|
||||
|
||||
static volatile uint32_t system_current_tick;
|
||||
|
||||
void sys_tick_handler(void)
|
||||
{
|
||||
system_current_tick++;
|
||||
}
|
||||
|
||||
void init_system(void)
|
||||
{
|
||||
// Setup MCU clock
|
||||
rcc_clock_setup_in_hse_8mhz_out_72mhz();
|
||||
|
||||
// Setup systick
|
||||
systick_set_frequency(SYSTICK_FREQUENCY, rcc_ahb_frequency);
|
||||
systick_clear();
|
||||
nvic_set_priority(NVIC_SYSTICK_IRQ, 0xc0);
|
||||
systick_interrupt_enable();
|
||||
systick_counter_enable();
|
||||
|
||||
// Setup GPIO
|
||||
rcc_periph_clock_enable(RCC_GPIOA);
|
||||
rcc_periph_clock_enable(RCC_GPIOB);
|
||||
rcc_periph_clock_enable(RCC_GPIOC);
|
||||
}
|
||||
|
||||
void sleep(uint32_t value)
|
||||
{
|
||||
uint32_t start_tick = system_current_tick;
|
||||
|
||||
while ((system_current_tick - start_tick) < value)
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* MCU renderer
|
||||
* STM32 system code
|
||||
*
|
||||
* (C) 2023-2024 Gissio
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#if !defined(SYSTEM_H)
|
||||
#define SYSTEM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SYSTICK_FREQUENCY 1000
|
||||
|
||||
void init_system(void);
|
||||
|
||||
void sleep(uint32_t value);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user