ak-kr-project/Core/Src/lcd.c

182 lines
3.8 KiB
C
Raw Normal View History

#include "main.h"
#include "lcd.h"
#include "generic_macros.h"
static char display_framebuffer[16*2*DISPLAY_FRAMES_AVAILABLE];
size_t display_current_frame;
static size_t display_framebuffer_mode;
static struct Display_emu_state des;
void display_init(void)
{
// prepare virtual framebuffer
display_current_frame = 0;
display_framebuffer_mode = 0;
des.cursor_offset = 0;
des.next = 0;
memset(display_framebuffer, 0x20, 16*2*DISPLAY_FRAMES_AVAILABLE);
// switch to 4-bit 2-line mode
display_write_instruction_byte(0x28);
// clear display
display_write_instruction_byte(0x01);
// enable display
display_write_instruction_byte(0x0C);
// move cursor to first line
display_write_instruction_byte(0x80);
}
void display_to_framebuffer(void)
{
display_framebuffer_mode = 1;
}
void display_to_direct(void)
{
display_framebuffer_mode = 0;
}
static uint8_t display_read_status(void)
{
// make sure GPIOE is in correct mode
GPIOE->MODER = 0x00504000;
if (GPIOE->MODER != 0x00504000)
PANIC(0x4000);
uint8_t status = 0;
GPIOE->ODR = 0x0;
GPIOE->BSRR = DISPLAY_RW;
GPIOE->BSRR = DISPLAY_ENA;
status |= (GPIOE->IDR & 0xF000) >> 8;
GPIOE->BSRR = (DISPLAY_ENA << 16);
GPIOE->BSRR = DISPLAY_ENA;
status |= (GPIOE->IDR & 0xF000) >> 12;
GPIOE->BSRR = (DISPLAY_ENA << 16);
GPIOE->ODR = 0x0;
return status;
}
static void display_write_instruction_byte_direct(uint8_t code)
{
DISPLAY_POLL_UNTIL_READY;
// make sure GPIOE is in correct mode
GPIOE->MODER = 0x55504000;
if (GPIOE->MODER != 0x55504000)
PANIC(0x4000);
GPIOE->ODR = (code & 0xF0) << 8;
GPIOE->BSRR = DISPLAY_ENA;
GPIOE->BSRR = (DISPLAY_ENA << 16);
GPIOE->ODR = (code & 0x0F) << 12;
GPIOE->BSRR = DISPLAY_ENA;
GPIOE->BSRR = (DISPLAY_ENA << 16);
}
static void display_write_instruction_byte_framebuffer(uint8_t code)
{
// emulate physical display behavior on receiving instructions
if (code & 0x80) {
// decode new cursor offset
size_t offset = ((code & 0x40) >> 2) | (code & 0xF);
des.cursor_offset = offset;
} else if (code == 0x01) {
// reset screen
memset(&(display_framebuffer[16*2*display_current_frame]), 0x20, 16*2);
des.cursor_offset = 0;
des.next = 0;
} else if (code == 0x06) {
// set increment mode
des.next = 0;
} else if (code == 0x04) {
// set decrement mode
des.next = 1;
}
}
void display_write_instruction_byte(uint8_t code)
{
if (display_framebuffer_mode) {
display_write_instruction_byte_framebuffer(code);
} else {
display_write_instruction_byte_direct(code);
}
}
static void display_write_data_byte_direct(uint8_t code)
{
DISPLAY_POLL_UNTIL_READY;
// make sure GPIOE is in correct mode
GPIOE->MODER = 0x55504000;
if (GPIOE->MODER != 0x55504000)
PANIC(0x4000);
GPIOE->ODR = ((code & 0xF0) << 8) | (DISPLAY_RS);
GPIOE->BSRR = DISPLAY_ENA;
GPIOE->BSRR = DISPLAY_ENA << 16;
GPIOE->ODR = ((code & 0x0F) << 12) | (DISPLAY_RS);
GPIOE->BSRR = DISPLAY_ENA;
GPIOE->BSRR = DISPLAY_ENA << 16;
}
static void display_write_data_byte_framebuffer(uint8_t code)
{
if (display_current_frame >= DISPLAY_FRAMES_AVAILABLE)
return;
display_framebuffer[16*2*display_current_frame + des.cursor_offset] = (char) code;
des.cursor_offset += des.next ? -1 : 1;
}
void display_write_data_byte(uint8_t code)
{
if (display_framebuffer_mode) {
display_write_data_byte_framebuffer(code);
} else {
display_write_data_byte_direct(code);
}
}
void display_write_data_seq(char *codes)
{
for (size_t i = 0; i < 16; i++) {
if (codes[i])
display_write_data_byte(codes[i]);
else
break;
}
}
void display_load(uint32_t frame_no)
{
if (display_framebuffer_mode)
return;
DISPLAY_CLEAR;
for (uint32_t i = 0; i < 16; i++) {
display_write_data_byte_direct(display_framebuffer[16*2*display_current_frame + i]);
}
DISPLAY_SET_CURSOR(1, 0);
for (uint32_t i = 0; i < 16; i++) {
display_write_data_byte_direct(display_framebuffer[16*2*display_current_frame + i + 16]);
}
}