#include #include #include #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)); }