From b3df20a08974584076e802d5f7061a8a8cb96930 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 16 Jul 2015 08:35:16 +0100 Subject: initial commit --- app/lcd.c | 478 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 478 insertions(+) create mode 100644 app/lcd.c (limited to 'app/lcd.c') diff --git a/app/lcd.c b/app/lcd.c new file mode 100644 index 0000000..e4a91ab --- /dev/null +++ b/app/lcd.c @@ -0,0 +1,478 @@ +#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 20 +#define LCD_W_MASK (LCD_RS -1 ) +#define LCD_H 4 +#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 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; + +/*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 (void) +{ + refresh_enabled = 1; + start_dma (); +} + + +void +lcd_disable_refresh (void) +{ + refresh_enabled = 0; + while (dma_in_progress); +} + + + +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 (); +} -- cgit v1.2.3