This commit is contained in:
Peter D. Gray 2023-07-31 08:46:59 -04:00 committed by scgbckbone
parent f3a3191730
commit ec0254739f
3 changed files with 138 additions and 65 deletions

View File

@ -11,6 +11,8 @@
#include <string.h>
#include "stm32c0xx_hal_gpio_ex.h"
lcd_state_t lcd_state;
const int LCD_WIDTH = 320;
const int LCD_HEIGHT = 240;
const int NUM_PIXELS = (LCD_WIDTH*LCD_HEIGHT);
@ -25,7 +27,6 @@ const uint16_t PROG_HEIGHT = 3;
const uint16_t PROG_Y = LCD_HEIGHT - PROG_HEIGHT;
static const int NUM_PHASES = 16;
static int phase = 0;
// forward refs
void lcd_write_rows(int y, int num_rows, uint16_t *pixels);
@ -160,7 +161,19 @@ lcd_setup(void)
LL_SPI_Init(SPI1, &init);
LL_SPI_Enable(SPI1);
phase = 0;
// debug values?
lcd_state.activity_bar = true;
#if 0
lcd_state.cursor_x = 9;
lcd_state.cursor_y = 2;
lcd_state.outline_cursor = true;
#else
lcd_state.dbl_wide = true;
lcd_state.cursor_x = 16;
lcd_state.cursor_y = 4;
lcd_state.solid_cursor = true;
#endif
//lcd_state.outline_cursor = true;
}
// take_control()
@ -209,6 +222,81 @@ release_control(void)
LL_GPIO_Init(GPIOA, &init);
}
// send_window()
//
static void
send_window(int x, int y, int w, int h, const void *data)
{
// write inclusive range
// note, MADCTL MV/MX/MY setting causes row vs. col swap here
lcd_write_cmd4(0x2a, x, x+w-1); // CASET - Column address set range (x)
lcd_write_cmd4(0x2b, y, y+h-1); // RASET - Row address set range (y)
lcd_write_cmd(0x2c); // RAMWR - memory write
if(data) {
// follow with data write of 2*w*h bytes
lcd_write_data(2*w*h, (uint8_t *)data);
}
}
// send_solid()
//
static void
send_solid(int x, int y, int w, int h, uint16_t pixel)
{
// NOTE: stack size limit here
uint16_t buf[w*h];
memset2(buf, pixel, sizeof(buf));
send_window(x, y, w, h, buf);
}
// cursor_draw()
//
void
cursor_draw(int char_x, int char_y, bool outline, bool phase, bool dbl_wide)
{
// see shared/lcd.py and shared/font_iosevka.py
const int LEFT_MARGIN = 7;
const int TOP_MARGIN = 15;
const int CHARS_W = 34;
const int CHARS_H = 10;
const int CELL_W = 9;
const int CELL_H = 22;
ASSERT(char_x < CHARS_W);
ASSERT(char_y < CHARS_H);
STATIC_ASSERT(CELL_H > 2*CELL_W); // for dbl_wide case
// top left corner, just on edge of character cell
int x = LEFT_MARGIN + (char_x * CELL_W);
int y = TOP_MARGIN + (char_y * CELL_H);
int cell_w = CELL_W + (dbl_wide?CELL_W:0);
// make some pixels big enough for either vert or horz lines
uint16_t colour = phase ? COL_FOREGROUND : COL_BLACK;
uint16_t row[CELL_H];
memset2(row, colour, sizeof(row));
if(outline) {
// horz
send_window(x,y, cell_w, 1, &row);
send_window(x,y+CELL_H-1, cell_w, 1, &row);
// vert
send_window(x, y+1, 1, CELL_H-2, &row);
send_window(x+cell_w-1, y+1, 1, CELL_H-2, &row);
} else {
if(!phase) {
// solid fill -- draw first time
send_solid(x,y, cell_w, CELL_H, COL_FOREGROUND);
} else {
// box shape, blank interior pixels
send_solid(x+1,y+1, cell_w-2, CELL_H-2, COL_BLACK);
}
}
}
// lcd_show_raw()
//
// No decompression. Just used for factory show. 1k bytes
@ -226,10 +314,7 @@ lcd_show_raw(uint32_t len, const uint8_t *pixels)
void
lcd_fill_solid(uint16_t pattern)
{
// note, MADCTL MV/MX/MY setting causes row vs. col swap here
lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x)
lcd_write_cmd4(0x2b, 0, LCD_HEIGHT-1); // RASET - Row address set range (y)
lcd_write_cmd(0x2c); // RAMWR - memory write
send_window(0, 0, LCD_WIDTH, LCD_HEIGHT, NULL);
uint16_t row[LCD_WIDTH];
memset2(row, pattern, sizeof(row));
@ -244,19 +329,26 @@ lcd_fill_solid(uint16_t pattern)
void
lcd_draw_progress(void)
{
static int phase = 0;
uint16_t row[LCD_WIDTH + NUM_PHASES + 1];
for(int i=0; i<numberof(row); i++) {
row[i] = ((i % 8) < 2) ? COL_BLACK : COL_FOREGROUND;
}
send_window(0, PROG_Y, LCD_WIDTH, PROG_Y-LCD_HEIGHT, NULL);
#if 0
lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x)
lcd_write_cmd4(0x2b, PROG_Y, LCD_HEIGHT-1); // RASET - Row address set range (y)
lcd_write_cmd(0x2c); // RAMWR - memory write
#endif
for(int y=0; y<PROG_HEIGHT; y++) {
lcd_write_data(LCD_WIDTH*2, (uint8_t *)(&row[NUM_PHASES - phase - 1]));
}
phase = (phase + 1) % NUM_PHASES;
}
// lcd_write_rows()
@ -264,81 +356,41 @@ lcd_draw_progress(void)
void
lcd_write_rows(int y, int num_rows, uint16_t *pixels)
{
send_window(0, y, LCD_WIDTH, num_rows, pixels);
#if 0
lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x)
lcd_write_cmd4(0x2b, y, LCD_HEIGHT-1); // RASET - Row address set range (y) [wrong, works]
lcd_write_cmd(0x2c); // RAMWR - memory write
lcd_write_data(num_rows * 2 * LCD_WIDTH, (uint8_t *)pixels);
#endif
}
// lcd_show()
//
// Perform simple RLE decompression, and pixel expansion.
//
void
lcd_show(const uint8_t *pixels)
{
lcd_setup();
// we are NOT fast enough to send entire screen during the
// vblanking time, so either we show torn stuff, or we flash display off a little
wait_vsync();
lcd_write_cmd(0x28); // DISPOFF
// always full update
lcd_write_cmd4(0x2a, 0, LCD_WIDTH-1); // CASET - Column address set range (x)
lcd_write_cmd4(0x2b, 0, LCD_HEIGHT-1); // RASET - Row address set range (y)
uint8_t buf[127];
uint16_t expand[sizeof(buf)*8];
const uint8_t *p = pixels;
lcd_write_cmd(0x2c); // RAMWR - memory write
while(1) {
uint8_t hdr = *(p++);
if(!hdr) break; // end marker
uint8_t len = hdr & 0x7f;
if(hdr & 0x80) {
// random bytes follow
memcpy(buf, p, len);
p += len;
} else {
// repeat same byte
memset(buf, *p, len);
p++;
}
// expand 'len' packed monochrom into BGR565 16-bit data: buf => expand
uint16_t *out = expand;
for(int i=0; i<len; i++) {
uint8_t packed = buf[i];
for(uint8_t mask = 0x80; mask; mask >>= 1, out++) {
if(packed & mask) {
*out = COL_FOREGROUND;
} else {
*out = COL_BLACK;
}
}
}
lcd_write_data(len*8*2, (uint8_t *)expand);
}
lcd_write_cmd(0x29); // DISPON
}
// lcd_animate()
//
// Called at LCD frame rate, when we have control over LCD.
//
void
lcd_animate(void)
{
take_control();
lcd_draw_progress();
if(lcd_state.activity_bar) {
lcd_draw_progress();
}
phase = (phase + 1) % NUM_PHASES;
if(lcd_state.solid_cursor || lcd_state.outline_cursor) {
static int cur_phase;
if(cur_phase == 0) {
cursor_draw(lcd_state.cursor_x, lcd_state.cursor_y,
lcd_state.outline_cursor, lcd_state.cur_flash, lcd_state.dbl_wide);
lcd_state.cur_flash = !lcd_state.cur_flash;
}
cur_phase = (cur_phase+1) % 32;
}
release_control();
}

View File

@ -3,6 +3,19 @@
*/
#pragma once
typedef struct {
bool activity_bar:1;
bool solid_cursor:1;
bool outline_cursor:1;
bool dbl_wide:1;
bool cur_flash:1; // clear when changing pos/type/enable
uint8_t cursor_x, cursor_y;
} lcd_state_t;
extern lcd_state_t lcd_state;
void lcd_setup(void);
void lcd_animate(void);

View File

@ -278,4 +278,12 @@ mainloop(void)
//return 0;
}
// fatal_error()
//
void __attribute__((noreturn))
fatal_error(const char *msg)
{
while(1) ;
}
// EOF