#include "project.h" #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 uint8_t dma_buf[DMA_BUF_SZ]; static uint8_t update_buf[DMA_BUF_SZ]; static volatile int dma_in_progress = 0; static int refresh_enabled = 0; static int backlight; static int pos; static void clock_nibble (uint8_t n) { if (backlight) n |= LINE_BACKLIGHT; i2cp_send_data (n); i2cp_send_data (LINE_EN | n); i2cp_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_command (uint8_t c) { write_reg (c, 0); } static void send_one_command (uint8_t cmd, int delay) { while (i2cp_lock ()); i2cp_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE); send_command (cmd); i2cp_stop (); if (delay) delay_ms (delay); i2cp_unlock (); } 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 uint32_t refresh_wdt = 0; static void start_dma (void) { if (dma_in_progress) return; refresh_wdt = 0; dma_in_progress = 1; memcpy (dma_buf, update_buf, DMA_BUF_SZ); i2cp_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE); i2cp_start_dma (dma_buf, DMA_BUF_SZ); } void dma1_channel6_isr (void) { if (dma_in_progress) { i2cp_stop_dma (); i2cp_stop (); dma_in_progress = 0; } if (refresh_enabled) start_dma (); } void lcd_refresh_wdt (void) { refresh_wdt++; if (refresh_wdt < 1000) return; refresh_wdt = 0; if (!refresh_enabled) return; /*No refresh for 1s, restart everything */ i2cp_stop_dma (); i2cp_stop (); dma_in_progress = 0; start_dma (); } static inline void dma_clock_nibble (uint8_t * ptr, uint8_t n) { if (backlight) n |= LINE_BACKLIGHT; *(ptr++) = n; *(ptr++) = n | LINE_EN; *(ptr++) = n; } static inline int dma_write_reg (uint8_t * ptr, uint8_t c, int r) { uint8_t b; b = c & 0xf0; if (r) b |= LINE_RS; dma_clock_nibble (ptr, b); ptr += 3; b = (c & 0xf) << 4; if (r) b |= LINE_RS; dma_clock_nibble (ptr, b); return 6; } static inline int dma_command (uint8_t * ptr, uint8_t c) { return dma_write_reg (ptr, c, 0); } static inline int dma_data (uint8_t * ptr, uint8_t c) { return dma_write_reg (ptr, c, 1); } static void dma_generate_stream (void) { uint8_t *dma_ptr; int x, y, i; dma_ptr = update_buf; /* 6 bytes per character, 6 bytes to move position, 5 moves + 20*4 chars => 510*/ for (y = 0; y < LCD_H; ++y) { dma_ptr += dma_command (dma_ptr, LCD_SET_DDRAM_ADDR | lcd_addr (0, y)); i = y << LCD_H_SHIFT; for (x = 0; x < LCD_W; ++x, i++) { dma_ptr += dma_data (dma_ptr, ' '); } } dma_ptr += dma_command (dma_ptr, LCD_SET_DDRAM_ADDR | lcd_addr (pos & LCD_W_MASK, pos >> LCD_H_SHIFT)); } static void dma_change_backlight (void) { int len = DMA_BUF_SZ; uint8_t *p = update_buf; cm_disable_interrupts (); if (backlight) while (p++, len--) *p |= LINE_BACKLIGHT; else while (p++, len--) *p &= ~LINE_BACKLIGHT; cm_enable_interrupts (); } static int dma_offset (int x, int y) { return BYTES_PER_BYTE * (1 + x + y * (LCD_W + 1)); } static void _lcd_write_char (uint8_t c, int x, int y) { cm_disable_interrupts (); dma_data (update_buf + dma_offset (x, y), c); cm_enable_interrupts (); } void lcd_write_char (uint8_t c, int x, int y) { cm_disable_interrupts (); _lcd_write_char (c, x, y); cm_enable_interrupts (); } void lcd_erase (int x, int y, int w) { uint8_t *dma_ptr = update_buf + dma_offset (x, y); cm_disable_interrupts (); while (w--) dma_ptr += dma_data (dma_ptr, ' '); cm_enable_interrupts (); } 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) { cm_disable_interrupts (); while (*c) { _lcd_write_char (*(c++), x++, y); if (x == LCD_W) break; } cm_enable_interrupts (); } #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; dma_change_backlight (); if (refresh_enabled) return; while (i2cp_lock ()); i2cp_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE); i2cp_send_data (backlight ? LINE_BACKLIGHT : 0); i2cp_stop (); i2cp_unlock (); } void lcd_enable_refresh () { refresh_enabled = 1; start_dma (); } int lcd_disable_refresh (void) { int ret; if (!refresh_enabled) return 0; ret=refresh_enabled; refresh_enabled = 0; while (dma_in_progress); return ret; } void lcd_reset (void) { while (i2cp_lock ()); i2cp_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); i2cp_stop (); i2cp_unlock (); on (); cls (); } void lcd_init (void) { lcd_backlight (0); lcd_reset (); lcd_backlight (1); dma_generate_stream (); nvic_enable_irq (NVIC_DMA1_CHANNEL6_IRQ); lcd_enable_refresh (); } void lcd_shutdown (void) { lcd_disable_refresh (); lcd_backlight (0); off (); }