summaryrefslogtreecommitdiffstats
path: root/app/oled.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/oled.c')
-rw-r--r--app/oled.c281
1 files changed, 281 insertions, 0 deletions
diff --git a/app/oled.c b/app/oled.c
new file mode 100644
index 0000000..51cfd9a
--- /dev/null
+++ b/app/oled.c
@@ -0,0 +1,281 @@
+#include "project.h"
+
+
+#define DMA_BUF_SZ (2 + (SSD1306_VRAM_SIZE))
+
+uint8_t dma_buf[DMA_BUF_SZ];
+uint8_t update_buf[DMA_BUF_SZ];
+static int dma_in_progress = 0;
+static int refresh_enabled = 0;
+static uint32_t refresh_wdt = 0;
+
+
+int
+ssd1306_cmds (uint8_t * buf, size_t len, int delay)
+{
+
+ while (i2c_lock ());
+
+ i2cp_start_transaction (SSD1306_I2C_ADDRESS, I2C_WRITE);
+ i2cp_send (SSD1306_COMMAND);
+
+ while (len--)
+ i2cp_send (*(buf++));
+
+ i2cp_stop ();
+
+ if (delay)
+ delay_us (delay);
+ i2c_unlock ();
+
+ return 0;
+}
+
+int
+ssd1306_cmd (uint8_t cmd, int delay)
+{
+ return ssd1306_cmds (&cmd, 1, delay);
+}
+
+
+
+static void
+start_dma (void)
+{
+ uint8_t cmds[] = {
+ SSD1306_SET_PAGE_ADDR, 0, 0xff,
+ SSD1306_SET_COLUMN_ADDR, 0, SSD1306_WIDTH - 1
+ };
+
+ if (dma_in_progress)
+ return;
+
+ refresh_wdt = 0;
+
+ dma_in_progress = 1;
+
+ memcpy (dma_buf, update_buf, DMA_BUF_SZ);
+
+ ssd1306_cmds (cmds, sizeof (cmds), 0);
+
+ i2cp_start_transaction (SSD1306_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
+oled_refresh_wdt (void)
+{
+ if (!refresh_enabled)
+ return;
+
+ 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 void
+dma_generate_stream (void)
+{
+ memset (update_buf, 0, DMA_BUF_SZ);
+ update_buf[0] = SSD1306_DATA_CONTINUE;
+}
+
+
+
+void
+oled_enable_refresh (void)
+{
+ refresh_enabled = 1;
+ start_dma ();
+}
+
+
+void
+oled_disable_refresh (void)
+{
+ refresh_enabled = 0;
+ while (dma_in_progress);
+}
+
+void
+oled_cls (void)
+{
+ memset (update_buf + 1, 0, SSD1306_VRAM_SIZE);
+}
+
+
+void
+oled_on (void)
+{
+ ssd1306_cmd (SSD1306_DISPLAY_ON, 0);
+
+}
+
+
+void
+oled_off (void)
+{
+ ssd1306_cmd (SSD1306_DISPLAY_OFF, 0);
+}
+
+
+
+void
+oled_squirt (void)
+{
+ unsigned i;
+ uint8_t cmds[] = {
+ SSD1306_SET_PAGE_ADDR, 0, 0xff,
+ SSD1306_SET_COLUMN_ADDR, 0, SSD1306_WIDTH - 1
+ };
+
+ ssd1306_cmds (cmds, sizeof (cmds), 0);
+
+
+ while (i2c_lock ());
+
+ i2cp_start_transaction (SSD1306_I2C_ADDRESS, I2C_WRITE);
+
+ for (i = 0; i < DMA_BUF_SZ; ++i)
+ i2cp_send (update_buf[i]);
+
+ i2cp_stop ();
+
+ i2c_unlock ();
+}
+
+void
+oled_reset (void)
+{
+ uint8_t init[] = {
+ SSD1306_DISPLAY_OFF,
+ SSD1306_SET_DISPLAY_CLOCK_DIV_RATIO, 0x80,
+ SSD1306_SET_MULTIPLEX_RATIO, SSD1306_HEIGHT - 1,
+ SSD1306_SET_DISPLAY_OFFSET, 0x0,
+ SSD1306_SET_START_LINE | 0x0,
+ SSD1306_CHARGE_PUMP, 0x14,
+ SSD1306_MEMORY_ADDR_MODE, 0x00,
+ SSD1306_SET_SEGMENT_REMAP | 0x1,
+ SSD1306_COM_SCAN_DIR_DEC,
+ //SSD1306_SET_COM_PINS, 0x12,
+ SSD1306_SET_COM_PINS, 0x02,
+ //SSD1306_SET_CONTRAST_CONTROL, 0xCF,
+ SSD1306_SET_CONTRAST_CONTROL, 0x8F,
+ SSD1306_SET_PRECHARGE_PERIOD, 0xF1,
+ SSD1306_SET_VCOM_DESELECT, 0x40,
+ SSD1306_DISPLAY_ALL_ON_RESUME,
+ SSD1306_NORMAL_DISPLAY,
+ SSD1306_DISPLAY_ON,
+ };
+
+ ssd1306_cmds (init, sizeof (init), 0);
+}
+
+
+void
+oled_const_strip (int x, int xe, uint8_t and, uint8_t or)
+{
+ uint8_t r;
+
+ for (x++; x <= xe; ++x)
+ {
+ r = update_buf[x] & and;
+ update_buf[x] = r | or;
+ }
+}
+
+
+void
+oled_blit_strip (int x, int xe, uint8_t mask, int shift, uint8_t * src)
+{
+ uint8_t r, s;
+
+
+ if (shift > 0)
+ {
+ for (x++; x <= xe; ++x)
+ {
+ s = *(src++);
+ s <<= shift;
+ s &= mask;
+ r = update_buf[x] & ~mask;
+ update_buf[x] = r | s;
+
+ }
+ }
+ else if (shift < 0)
+ {
+ shift = -shift;
+ for (x++; x <= xe; ++x)
+ {
+ s = *(src++);
+ s >>= shift;
+ s &= mask;
+ r = update_buf[x] & ~mask;
+ update_buf[x] = r | s;
+ }
+ }
+ else
+ {
+ for (x++; x <= xe; ++x)
+ {
+ s = *(src++);
+ s &= mask;
+ r = update_buf[x] & ~mask;
+ update_buf[x] = r | s;
+ }
+ }
+}
+
+
+
+void
+oled_init ()
+{
+ oled_reset ();
+
+ delay_us (100);
+
+ dma_generate_stream ();
+
+ nvic_enable_irq (NVIC_DMA1_CHANNEL6_IRQ);
+ oled_enable_refresh ();
+}
+
+
+void
+oled_shutdown (void)
+{
+ oled_disable_refresh ();
+ oled_off ();
+}