summaryrefslogtreecommitdiffstats
path: root/app/not/lcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/not/lcd.c')
-rw-r--r--app/not/lcd.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/app/not/lcd.c b/app/not/lcd.c
new file mode 100644
index 0000000..d6ee5c7
--- /dev/null
+++ b/app/not/lcd.c
@@ -0,0 +1,477 @@
+#include "project.h"
+
+
+#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(n);
+ i2cp_send(LINE_EN | n);
+ i2cp_send(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 (i2c_lock ());
+ i2cp_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE);
+ send_command (cmd);
+ i2cp_stop ();
+ if (delay)
+ delay_ms (delay);
+ i2c_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 (i2c_lock ());
+ i2cp_start_transaction (PCF8574_I2C_ADDRESS, I2C_WRITE);
+ i2cp_send(backlight ? LINE_BACKLIGHT : 0);
+ i2cp_stop ();
+ i2c_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 (i2c_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 ();
+ i2c_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 ();
+}