#include "project.h" #ifndef SLIM #define FOO do { printf("lcd:%x\r\n",__LINE__); usart1_drain(); } while (0) #define PCF8574_I2C_ADDRESS 0x27 #define LINE_RS 0x1 #define LINE_RnW 0x2 #define LINE_EN 0x4 #define LINE_BACKLIGHT 0x8 #define LCD_CLEAR 0x1 #define LCD_HOME 0x2 #define LCD_DISP 0x8 #define LCD_DISP_ON 0x4 #define LCD_DISP_CURSOR 0x2 #define LCD_DISP_CURSOR_BLINK 0x1 #define LCD_FUNC 0x20 #define LCD_FUNC_8BIT 0x10 #define LCD_FUNC_4BIT 0x00 #define LCD_FUNC_2ROWS 0x08 #define LCD_FUNC_1ROW 0x00 #define LCD_FUNC_5x10 0x04 #define LCD_FUNC_5X7 0x00 #define LCD_SET_DDRAM_ADDR 0x80 #define LCD_H_SHIFT 5 #define LCD_RS (1 << LCD_H_SHIFT) #define LCD_W 16 #define LCD_W_MASK (LCD_RS -1 ) #define LCD_H 2 #define LCD_SZ (LCD_H << LCD_H_SHIFT) #define LCD_POS(c,r) (((r) << LCD_H_SHIFT ) +(c)) #define BYTES_PER_BYTE 6 #define DMA_BUF_SZ (BYTES_PER_BYTE * ( (1 + LCD_W) *LCD_H + 1)) static int backlight; uint8_t fb[LCD_H][LCD_W]; uint8_t shadow[LCD_H][LCD_W]; static int pos; static void clock_nibble (uint8_t n) { if (backlight) n |= LINE_BACKLIGHT; i2c_bb_send_data (n); i2c_bb_send_data (LINE_EN | n); i2c_bb_send_data (n); } static void write_reg (uint8_t c, int r) { uint8_t b; b = c & 0xf0; if (r) b |= LINE_RS; clock_nibble (b); b = (c & 0xf) << 4; if (r) b |= LINE_RS; clock_nibble (b); } static void send_data (uint8_t c) { write_reg (c, 1); } static void send_command (uint8_t c) { write_reg (c, 0); } static void send_one_command (uint8_t cmd, int delay) { i2c_bb_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE); send_command (cmd); i2c_bb_stop (); if (delay) delay_ms (delay); } static void cls (void) { send_one_command (LCD_CLEAR, 2); } #if 0 static void home (void) { send_one_command (LCD_HOME, 2); } #endif static void on (void) { send_one_command (LCD_DISP | LCD_DISP_ON /* | LCD_DISP_CURSOR */ , 4); } static void off (void) { send_one_command (LCD_DISP, 4); } static int lcd_addr (int x, int y) { return x + ((y & 1) << 6) + ((y & 2) ? 20 : 0); } #if 0 static void move (uint8_t c, uint8_t r) { send_one_command (LCD_SET_DDRAM_ADDR | lcd_addr (c, r), 2); } #endif void lcd_refresh (void) { int c, r; int addr; i2c_bb_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE); for (r = 0; r < LCD_H; ++r) { for (c = 0; c < LCD_W; ++c) { if (shadow[r][c] != fb[r][c]) { addr = lcd_addr (c, r); if (addr != pos) { send_command (LCD_SET_DDRAM_ADDR | addr); pos = addr; } send_data (shadow[r][c]); fb[r][c] = shadow[r][c]; pos++; } } } i2c_bb_stop (); } void lcd_tick (void) { static int u; u++; if (u < 100) return; u = 0; if (!memcmp (shadow, fb, sizeof (fb))) return; lcd_refresh (); } void lcd_write_char (uint8_t c, int x, int y) { shadow[y][x] = c; } void lcd_erase (int x, int y, int w) { uint8_t *ptr = &shadow[y][x]; while (w--) *ptr = ' '; } void lcd_erase_line (int w, int y) { lcd_erase (0, y, w); } void lcd_erase_all (void) { int y; for (y = 0; y < LCD_H; ++y) { lcd_erase (0, 0, LCD_W); } } void lcd_write (char *c, int x, int y) { while (*c) { lcd_write_char (*(c++), x++, y); if (x == LCD_W) break; } } #if 0 static void lcd_scroll (uint8_t * p) { int i; for (i = 0; i < 3; ++i) { memcpy (p, p + LCD_RS, LCD_W); p += LCD_RS; } memset (p, ' ', LCD_W); } void lcd_putc (uint8_t c) { switch (c) { case '\r': pos &= ~LCD_W_MASK; break; case '\n': pos &= ~LCD_W_MASK; pos += LCD_RS; break; default: buf[pos] = c; pos++; } if ((pos & LCD_W_MASK) == LCD_W) { pos &= ~LCD_W_MASK; pos += LCD_RS; } if (pos == LCD_SZ) { lcd_scroll (buf); pos -= LCD_RS; } } #endif void lcd_backlight (int i) { backlight = i; i2c_bb_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE); i2c_bb_send_data (backlight ? LINE_BACKLIGHT : 0); i2c_bb_stop (); } void lcd_reset (void) { i2c_bb_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE); clock_nibble (0x30); delay_ms (5); clock_nibble (0x30); delay_us (64); clock_nibble (0x30); delay_us (64); clock_nibble (0x20); send_command (LCD_FUNC | LCD_FUNC_4BIT | LCD_FUNC_2ROWS | LCD_FUNC_5X7); i2c_bb_stop (); on (); cls (); } void lcd_init (void) { lcd_backlight (0); lcd_reset (); lcd_backlight (1); } void lcd_shutdown (void) { lcd_backlight (0); off (); } #endif