#include #include #include #include "lcd.h" #include "pinout.h" #define LCD_D7 PIN_LCD_D7 #define LCD_D6 PIN_LCD_D6 #define LCD_D5 PIN_LCD_D5 #define LCD_D4 PIN_LCD_D4 #define LCD_RS PIN_LCD_RS #define LCD_RW PIN_LCD_RW #define LCD_E PIN_LCD_E #define set_pin(num, val) gpio_put((num), (val)) #define get_pin(num) gpio_get((num)) #define pin_high(num) set_pin((num), 1) #define pin_low(num) set_pin((num), 0) #define get_bit(a, pos) (((a) >> (pos)) & 1) #define as_output(pin) gpio_set_dir((pin), 1) #define as_input_pu(pin) do { gpio_set_dir((pin), 0); gpio_pull_up((pin)); } while (0) #define _delay_ms(ms) sleep_ms((ms)) #define _delay_us(us) sleep_us((us)) #define _delay_ns(ns) sleep_us(1 + ((ns) / 1000)) // TODO // Start address of rows const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54}; // Internal prototypes void _lcd_mode_r(); void _lcd_mode_w(); void _lcd_clk(); void _lcd_wait_bf(); void _lcd_write_byte(uint8_t bb); uint8_t _lcd_read_byte(); void lcd_command(uint8_t bb); // Write utilities #define _lcd_write_low(bb) _lcd_write_nibble((bb) & 0x0F) #define _lcd_write_high(bb) _lcd_write_nibble(((bb) & 0xF0) >> 4) #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) //#define _lcd_write_nibble(nib) do { \ // set_pin(LCD_D7, get_bit((nib), 3)); \ // set_pin(LCD_D6, get_bit((nib), 2)); \ // set_pin(LCD_D5, get_bit((nib), 1)); \ // set_pin(LCD_D4, get_bit((nib), 0)); \ //} while(0) // --- 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 // 0 W, 1 R bool _lcd_mode; struct { uint8_t x; uint8_t y; } _pos; enum { TEXT = 0, CG = 1 } _addrtype; /** Initialize the display */ void lcd_init() { // configure pins as output as_output(LCD_E); as_output(LCD_RW); as_output(LCD_RS); _lcd_mode = 1; // force data pins to output _lcd_mode_w(); // Magic sequence to invoke Cthulhu (or enter 4-bit mode) _delay_ms(16); _lcd_write_nibble(0b0011); _lcd_clk(); _delay_ms(5); _lcd_clk(); _delay_ms(5); _lcd_clk(); _delay_ms(5); _lcd_write_nibble(0b0010); _lcd_clk(); _delay_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(); _pos.x = 0; _pos.y = 0; _addrtype = TEXT; } /** Send a pulse on the ENABLE line */ void _lcd_clk() { pin_high(LCD_E); _delay_ns(450); pin_low(LCD_E); _delay_ns(450); } /** Enter READ mode */ void _lcd_mode_r() { if (_lcd_mode == 1) { return; } // already in R mode pin_high(LCD_RW); as_input_pu(LCD_D7); as_input_pu(LCD_D6); as_input_pu(LCD_D5); as_input_pu(LCD_D4); _lcd_mode = 1; } /** Enter WRITE mode */ void _lcd_mode_w() { if (_lcd_mode == 0) { return; } // already in W mode pin_low(LCD_RW); as_output(LCD_D7); as_output(LCD_D6); as_output(LCD_D5); as_output(LCD_D4); _lcd_mode = 0; } /** Read a byte */ uint8_t _lcd_read_byte() { _lcd_mode_r(); uint8_t res = 0; _lcd_clk(); res = (get_pin(LCD_D7) << 7) | (get_pin(LCD_D6) << 6) | (get_pin(LCD_D5) << 5) | (get_pin(LCD_D4) << 4); _lcd_clk(); res |= (get_pin(LCD_D7) << 3) | (get_pin(LCD_D6) << 2) | (get_pin(LCD_D5) << 1) | (get_pin(LCD_D4) << 0); return res; } /** Write an instruction byte */ void lcd_command(uint8_t bb) { _lcd_wait_bf(); pin_low(LCD_RS); // select instruction register _lcd_write_byte(bb); // send instruction byte } /** Write a data byte */ void lcd_write(uint8_t bb) { if (_addrtype == TEXT) { if (bb == '\r') { // CR _pos.x = 0; lcd_xy(_pos.x, _pos.y); return; } if (bb == '\n') { // LF _pos.y++; lcd_xy(_pos.x, _pos.y); return; } _pos.x++; } _lcd_wait_bf(); pin_high(LCD_RS); // select data register _lcd_write_byte(bb); // send data byte } /** Read BF & Address */ uint8_t lcd_read_bf_addr() { pin_low(LCD_RS); return _lcd_read_byte(); } /** Read CGRAM or DDRAM */ uint8_t lcd_read() { if (_addrtype == TEXT) { _pos.x++; } pin_high(LCD_RS); return _lcd_read_byte(); } /** Write a byte using the 4-bit interface */ void _lcd_write_byte(uint8_t bb) { _lcd_mode_w(); // enter W mode _lcd_write_high(bb); _lcd_clk(); _lcd_write_low(bb); _lcd_clk(); } /** Wait until the device is ready */ void _lcd_wait_bf() { uint8_t d = 0; while (d++ < 200 && lcd_read_bf_addr() & (1 << 7)) _delay_us(1); } /** Send a string to LCD */ void lcd_puts(char *str_p) { char c; while ((c = *str_p++)) { 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) { _pos.x = x; _pos.y = y; lcd_addr(LCD_ROW_ADDR[y] + (x)); } uint8_t _lcd_old_cursor = CURSOR_NONE; bool _lcd_enabled = false; /** Set LCD cursor. If not enabled, only remember it. */ void lcd_cursor(uint8_t type) { _lcd_old_cursor = (type & CURSOR_BOTH); if (_lcd_enabled) { lcd_command(LCD_CURSOR_NONE | _lcd_old_cursor); } } /** Display display (preserving cursor) */ void lcd_disable() { lcd_command(LCD_DISABLE); _lcd_enabled = false; } /** Enable display (restoring cursor) */ void lcd_enable() { _lcd_enabled = true; lcd_cursor(_lcd_old_cursor); } /** Go home */ void lcd_home() { lcd_command(LCD_HOME); _pos.x = 0; _pos.y = 0; _addrtype = TEXT; } /** Clear the screen */ void lcd_clear() { lcd_command(LCD_CLEAR); _pos.x = 0; _pos.y = 0; _addrtype = TEXT; _delay_ms(1); // it tends to lose the first character otherwise! } /** 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(_pos.x, _pos.y); _addrtype = TEXT; } /** Set address in CGRAM */ void lcd_addr_cg(const uint8_t acg) { _addrtype = CG; lcd_command(0b01000000 | ((acg) & 0b00111111)); } /** Set address in DDRAM */ void lcd_addr(const uint8_t add) { _addrtype = TEXT; lcd_command(0b10000000 | ((add) & 0b01111111)); }