You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
6.0 KiB
294 lines
6.0 KiB
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include <pico/stdlib.h>
|
|
|
|
#include "pinout.h"
|
|
#include "lcd.h"
|
|
|
|
// ------------------ PORTING ----------------------
|
|
|
|
#define lcd_mode_instr(bb) gpio_put(PIN_LCD_DC, 0)
|
|
#define lcd_mode_data(bb) gpio_put(PIN_LCD_DC, 1)
|
|
|
|
#define lcd_mode_write(bb) gpio_put(PIN_LCD_RW, 0)
|
|
#define lcd_mode_read(bb) gpio_put(PIN_LCD_RW, 1)
|
|
|
|
//// Write utilities
|
|
//#define lcd_write_nibble(nib) \
|
|
// gpio_put_masked((1 << PIN_LCD_D7) | (1 << PIN_LCD_D6) | (1 << PIN_LCD_D5) | (1 << PIN_LCD_D4), (nib) << PIN_LCD_D4)
|
|
|
|
|
|
// Write utilities
|
|
#define lcd_write_nibble(nib) do { \
|
|
gpio_put(PIN_LCD_D7, nib & 0b1000); \
|
|
gpio_put(PIN_LCD_D6, nib & 0b0100); \
|
|
gpio_put(PIN_LCD_D5, nib & 0b0010); \
|
|
gpio_put(PIN_LCD_D4, nib & 0b0001); \
|
|
} while (0)
|
|
|
|
/** Wait until the device is ready */
|
|
static void lcd_wait_ready()
|
|
{
|
|
sleep_us(160);
|
|
}
|
|
|
|
/** Send a pulse on the ENABLE line */
|
|
static inline void lcd_clk()
|
|
{
|
|
gpio_put(PIN_LCD_CLK, 1);
|
|
sleep_us(1);
|
|
gpio_put(PIN_LCD_CLK, 0);
|
|
sleep_us(1);
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// --- Commands ---
|
|
|
|
// Clear screen (reset)
|
|
#define LCD_CLEAR 0b00000001
|
|
// Move cursor to (0,0), unshift...
|
|
#define LCD_HOME 0b00000010
|
|
|
|
// Set mode: Increment + NoShift
|
|
#define LCD_MODE_INC 0b00000110
|
|
// Set mode: Increment + Shift
|
|
#define LCD_MODE_INC_SHIFT 0b00000111
|
|
|
|
// Set mode: Decrement + NoShift
|
|
#define LCD_MODE_DEC 0b00000100
|
|
// Set mode: Decrement + Shift
|
|
#define LCD_MODE_DEC_SHIFT 0b00000101
|
|
|
|
// Disable display (data remains untouched)
|
|
#define LCD_DISABLE 0b00001000
|
|
|
|
// Disable cursor
|
|
#define LCD_CURSOR_NONE 0b00001100
|
|
// Set cursor to still underscore
|
|
#define LCD_CURSOR_BAR 0b00001110
|
|
// Set cursor to blinking block
|
|
#define LCD_CURSOR_BLINK 0b00001101
|
|
// Set cursor to both of the above at once
|
|
#define LCD_CURSOR_BOTH (LCD_CURSOR_BAR | LCD_CURSOR_BLINK)
|
|
|
|
// Move cursor
|
|
#define LCD_MOVE_LEFT 0b00010000
|
|
#define LCD_MOVE_RIGHT 0b00010100
|
|
|
|
// Shift display
|
|
#define LCD_SHIFT_LEFT 0b00011000
|
|
#define LCD_SHIFT_RIGHT 0b00011100
|
|
|
|
// Set iface to 5x7 font, 1-line
|
|
#define LCD_IFACE_4BIT_1LINE 0b00100000
|
|
#define LCD_IFACE_8BIT_1LINE 0b00110000
|
|
// Set iface to 5x7 font, 2-line
|
|
#define LCD_IFACE_4BIT_2LINE 0b00101000
|
|
#define LCD_IFACE_8BIT_2LINE 0b00111000
|
|
|
|
/** Write an instruction byte */
|
|
static void lcd_command(uint8_t bb);
|
|
|
|
/** Write a data byte, waiting for ready and handling CRLF if in text mode */
|
|
static void lcd_write(uint8_t bb);
|
|
|
|
/** Write a byte (handles just the data pins and clk) */
|
|
static void lcd_write_byte(uint8_t bb);
|
|
|
|
#define lcd_write_low(bb) lcd_write_nibble((bb) & 0x0F)
|
|
#define lcd_write_high(bb) lcd_write_nibble(((bb) & 0xF0) >> 4)
|
|
|
|
static struct {
|
|
uint8_t x;
|
|
uint8_t y;
|
|
} lcd_pos;
|
|
|
|
static enum {
|
|
LCD_ADDRTYPE_TEXT = 0,
|
|
LCD_ADDRTYPE_CG = 1
|
|
} lcd_addrtype;
|
|
|
|
static uint8_t lcd_old_cursor = CURSOR_NONE;
|
|
static bool lcd_enabled = false;
|
|
|
|
// Start address of rows
|
|
const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54};
|
|
|
|
/** Initialize the display */
|
|
void lcd_init()
|
|
{
|
|
// Magic sequence to invoke Cthulhu (or enter 4-bit mode)
|
|
sleep_ms(100);
|
|
lcd_write_nibble(0b0011);
|
|
lcd_clk();
|
|
sleep_ms(5);
|
|
lcd_clk();
|
|
sleep_ms(1);
|
|
lcd_clk();
|
|
sleep_ms(1);
|
|
lcd_write_nibble(0b0010);
|
|
lcd_clk();
|
|
sleep_us(100);
|
|
|
|
// Configure the display
|
|
lcd_command(LCD_IFACE_4BIT_2LINE);
|
|
lcd_command(LCD_DISABLE);
|
|
lcd_command(LCD_CLEAR);
|
|
lcd_command(LCD_MODE_INC);
|
|
|
|
// mark as enabled
|
|
lcd_enable();
|
|
|
|
lcd_pos.x = 0;
|
|
lcd_pos.y = 0;
|
|
lcd_addrtype = LCD_ADDRTYPE_TEXT;
|
|
}
|
|
|
|
/** Write an instruction byte */
|
|
void lcd_command(uint8_t bb)
|
|
{
|
|
lcd_wait_ready();
|
|
lcd_mode_instr();
|
|
lcd_write_byte(bb); // send instruction byte
|
|
}
|
|
|
|
|
|
/** Write a data byte */
|
|
void lcd_write(uint8_t bb)
|
|
{
|
|
if (lcd_addrtype == LCD_ADDRTYPE_TEXT) {
|
|
if (bb == '\r') {
|
|
// CR
|
|
lcd_pos.x = 0;
|
|
lcd_xy(lcd_pos.x, lcd_pos.y);
|
|
return;
|
|
}
|
|
|
|
if (bb == '\n') {
|
|
// LF
|
|
lcd_pos.y++;
|
|
lcd_xy(lcd_pos.x, lcd_pos.y);
|
|
return;
|
|
}
|
|
|
|
lcd_pos.x++;
|
|
}
|
|
|
|
lcd_wait_ready();
|
|
lcd_mode_data();
|
|
lcd_write_byte(bb); // send data byte
|
|
}
|
|
|
|
|
|
/** Write a byte using the 4-bit interface */
|
|
static void lcd_write_byte(uint8_t bb)
|
|
{
|
|
lcd_write_high(bb);
|
|
lcd_clk();
|
|
|
|
lcd_write_low(bb);
|
|
lcd_clk();
|
|
}
|
|
|
|
|
|
/** Send a string to LCD */
|
|
void lcd_puts(char *str)
|
|
{
|
|
char c;
|
|
while ((c = *str++)) {
|
|
lcd_putc(c);
|
|
}
|
|
}
|
|
|
|
/** Sedn a char to LCD */
|
|
void lcd_putc(const char c)
|
|
{
|
|
lcd_write(c);
|
|
}
|
|
|
|
/** Set cursor position */
|
|
void lcd_xy(const uint8_t x, const uint8_t y)
|
|
{
|
|
lcd_pos.x = x;
|
|
lcd_pos.y = y;
|
|
lcd_addr(LCD_ROW_ADDR[y] + (x));
|
|
}
|
|
|
|
/** Set LCD cursor. If not enabled, only remember it. */
|
|
void lcd_set_cursor(uint8_t type)
|
|
{
|
|
lcd_old_cursor = (type & CURSOR_BAR_BLINK);
|
|
|
|
if (lcd_enabled) { lcd_command(LCD_CURSOR_NONE | lcd_old_cursor); }
|
|
}
|
|
|
|
|
|
/** Disable display (preserving cursor) */
|
|
void lcd_disable()
|
|
{
|
|
lcd_command(LCD_DISABLE);
|
|
lcd_enabled = false;
|
|
}
|
|
|
|
|
|
/** Enable display (restoring cursor) */
|
|
void lcd_enable()
|
|
{
|
|
lcd_enabled = true;
|
|
lcd_set_cursor(lcd_old_cursor);
|
|
}
|
|
|
|
|
|
/** Go home */
|
|
void lcd_home()
|
|
{
|
|
lcd_command(LCD_HOME);
|
|
lcd_pos.x = 0;
|
|
lcd_pos.y = 0;
|
|
lcd_addrtype = LCD_ADDRTYPE_TEXT;
|
|
}
|
|
|
|
|
|
/** Clear the screen */
|
|
void lcd_clear()
|
|
{
|
|
lcd_command(LCD_CLEAR);
|
|
lcd_pos.x = 0;
|
|
lcd_pos.y = 0;
|
|
lcd_addrtype = LCD_ADDRTYPE_TEXT;
|
|
}
|
|
|
|
|
|
/** Define a glyph */
|
|
void lcd_glyph(const uint8_t index, const uint8_t *array)
|
|
{
|
|
lcd_addr_cg(index * 8);
|
|
for (uint8_t i = 0; i < 8; ++i) {
|
|
lcd_write(array[i]);
|
|
}
|
|
|
|
// restore previous position
|
|
lcd_xy(lcd_pos.x, lcd_pos.y);
|
|
lcd_addrtype = LCD_ADDRTYPE_TEXT;
|
|
}
|
|
|
|
|
|
/** Set address in CGRAM */
|
|
void lcd_addr_cg(const uint8_t acg)
|
|
{
|
|
lcd_addrtype = LCD_ADDRTYPE_CG;
|
|
lcd_command(0b01000000 | ((acg) & 0b00111111));
|
|
}
|
|
|
|
|
|
/** Set address in DDRAM */
|
|
void lcd_addr(const uint8_t add)
|
|
{
|
|
lcd_addrtype = LCD_ADDRTYPE_TEXT;
|
|
lcd_command(0b10000000 | ((add) & 0b01111111));
|
|
}
|
|
|