From: Doug Szumski Date: Tue, 5 Aug 2014 10:43:25 +0000 (+0100) Subject: parts: Added SSD1306 OLED driver virtual part X-Git-Tag: v1.3~78^2~1 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=b59997c36d5cec94732c17c04ab858c279731eaa;p=sx%2Fsimavr.git parts: Added SSD1306 OLED driver virtual part --- diff --git a/examples/parts/ssd1306_glut.c b/examples/parts/ssd1306_glut.c new file mode 100644 index 0000000..9846aa5 --- /dev/null +++ b/examples/parts/ssd1306_glut.c @@ -0,0 +1,193 @@ +/* + ssd1306_glut.c + + Copyright 2014 Doug Szumski + + Based on the hd44780 part: + Copyright Luki + Copyright 2011 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include +#include "ssd1306_glut.h" + +#if __APPLE__ +#include +#else +#include +#endif + +ssd1306_colour_t oled_colour_g; +float pix_size_g = 1.0; +float pix_gap_g = 0.0; + +// Keep colours in sync with enum in header. +float ssd1306_colours[][3] = +{ + { 1.0f, 1.0f, 1.0f }, // White + { 0.5f, 0.9f, 1.0f }, // Blue +}; + +void +ssd1306_gl_init (float pix_size, ssd1306_colour_t oled_colour) +{ + pix_size_g = pix_size; + oled_colour_g = oled_colour; +} + +void +ssd1306_gl_set_colour (uint8_t invert, float opacity) +{ + if (invert) + { + glColor4f (ssd1306_colours[oled_colour_g][0], + ssd1306_colours[oled_colour_g][1], + ssd1306_colours[oled_colour_g][2], + opacity); + } else + { + glColor4f (0.0f, 0.0f, 0.0f, 1.0f); + } +} + +float +ssd1306_gl_get_pixel_opacity (uint8_t contrast) +{ + // Typically the screen will be clearly visible even at 0 contrast + return contrast / 512.0 + 0.5; +} + +uint8_t +ssd1306_gl_reverse_byte (uint8_t byte) +{ + byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4; + byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2; + byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1; + return byte; +} + +void +ssd1306_gl_put_pixel_column (uint8_t block_pixel_column, float pixel_opacity, + int invert) +{ + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBegin (GL_QUADS); + + ssd1306_gl_set_colour (!invert, pixel_opacity); + + for (int i = 0; i < 8; ++i) + { + if (block_pixel_column & (1 << i)) + { + glVertex2f (pix_size_g, pix_size_g * (i + 1)); + glVertex2f (0, pix_size_g * (i + 1)); + glVertex2f (0, pix_size_g * i); + glVertex2f (pix_size_g, pix_size_g * i); + } + } + glEnd (); +} + +/* + * Controls the mapping between the VRAM and the display. + */ +static uint8_t +ssd1306_gl_get_vram_byte (ssd1306_t *part, uint8_t page, uint8_t column) +{ + uint8_t seg_remap_default = ssd1306_get_flag ( + part, SSD1306_FLAG_SEGMENT_REMAP_0); + uint8_t seg_comscan_default = ssd1306_get_flag ( + part, SSD1306_FLAG_COM_SCAN_NORMAL); + + if (seg_remap_default && seg_comscan_default) + { + // Normal display + return part->vram[page][column]; + } else if (seg_remap_default && !seg_comscan_default) + { + // Normal display, mirrored from upper edge + return ssd1306_gl_reverse_byte ( + part->vram[part->pages - 1 - page][column]); + } + + else if (!seg_remap_default && !seg_comscan_default) + { + // Upside down display + return ssd1306_gl_reverse_byte ( + part->vram[part->pages - 1 - page][part->columns - 1 - column]); + } else if (!seg_remap_default && seg_comscan_default) + { + // Upside down display, mirrored from upper edge + return part->vram[page][part->columns - 1 - column]; + } + + return 0; +} + +static void +ssd1306_gl_draw_pixels (ssd1306_t *part, float opacity, uint8_t invert) +{ + for (int p = 0; p < part->pages; p++) + { + glPushMatrix (); + for (int c = 0; c < part->columns; c++) + { + uint8_t vram_byte = ssd1306_gl_get_vram_byte (part, p, c); + ssd1306_gl_put_pixel_column (vram_byte, opacity, invert); + // Next column + glTranslatef (pix_size_g + pix_gap_g, 0, 0); + } + glPopMatrix (); + // Next page + glTranslatef (0, + (part->rows / part->pages) * pix_size_g + pix_gap_g, + 0); + } +} + +void +ssd1306_gl_draw (ssd1306_t *part) +{ + ssd1306_set_flag (part, SSD1306_FLAG_DIRTY, 0); + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBegin (GL_QUADS); + + // Draw background + float opacity = ssd1306_gl_get_pixel_opacity (part->contrast_register); + int invert = ssd1306_get_flag (part, SSD1306_FLAG_DISPLAY_INVERTED); + ssd1306_gl_set_colour (invert, opacity); + + glTranslatef (0, 0, 0); + glBegin (GL_QUADS); + glVertex2f (part->rows, 0); + glVertex2f (0, 0); + glVertex2f (0, part->columns); + glVertex2f (part->rows, part->columns); + glEnd (); + + // Draw pixels + if (ssd1306_get_flag (part, SSD1306_FLAG_DISPLAY_ON)) + { + ssd1306_gl_draw_pixels (part, opacity, invert); + } +} diff --git a/examples/parts/ssd1306_glut.h b/examples/parts/ssd1306_glut.h new file mode 100644 index 0000000..4a51d1a --- /dev/null +++ b/examples/parts/ssd1306_glut.h @@ -0,0 +1,39 @@ +/* + ssd1306_glut.h + + Copyright 2014 Doug Szumski + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#ifndef __SSD1306_GLUT_H__ +#define __SSD1306_GLUT_H__ + +#include "ssd1306_virt.h" + +// Keep colours in sync with array +typedef enum +{ + SSD1306_GL_WHITE, SSD1306_GL_BLUE +} ssd1306_colour_t; + +void +ssd1306_gl_draw (ssd1306_t *part); + +void +ssd1306_gl_init (float pix_size, ssd1306_colour_t oled_colour); + +#endif diff --git a/examples/parts/ssd1306_virt.c b/examples/parts/ssd1306_virt.c new file mode 100644 index 0000000..24d2372 --- /dev/null +++ b/examples/parts/ssd1306_virt.c @@ -0,0 +1,326 @@ +/* + ssd1306_virt.c + + Copyright 2011 Michel Pollet + Copyright 2014 Doug Szumski + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include +#include "sim_time.h" + +#include "ssd1306_virt.h" +#include "avr_spi.h" +#include "avr_ioport.h" + +/* + * Write a byte at the current cursor location and then scroll the cursor. + */ +static void +ssd1306_write_data (ssd1306_t *part) +{ + part->vram[part->cursor.page][part->cursor.column] = part->spi_data; + + // Scroll the cursor + if (++(part->cursor.column) >= SSD1306_VIRT_COLUMNS) + { + part->cursor.column = 0; + if (++(part->cursor.page) >= SSD1306_VIRT_PAGES) + { + part->cursor.page = 0; + } + } + + ssd1306_set_flag (part, SSD1306_FLAG_DIRTY, 1); +} + +/* + * Called on the first command byte sent. For setting single + * byte commands and initiating multi-byte commands. + */ +void +ssd1306_update_command_register (ssd1306_t *part) +{ + switch (part->spi_data) + { + case SSD1306_VIRT_SET_CONTRAST: + part->command_register = part->spi_data; + //printf ("SSD1306: CONTRAST SET COMMAND: 0x%02x\n", part->spi_data); + return; + case SSD1306_VIRT_DISP_NORMAL: + ssd1306_set_flag (part, SSD1306_FLAG_DISPLAY_INVERTED, + 0); + ssd1306_set_flag (part, SSD1306_FLAG_DIRTY, 1); + //printf ("SSD1306: DISPLAY NORMAL\n"); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_DISP_INVERTED: + ssd1306_set_flag (part, SSD1306_FLAG_DISPLAY_INVERTED, + 1); + ssd1306_set_flag (part, SSD1306_FLAG_DIRTY, 1); + //printf ("SSD1306: DISPLAY INVERTED\n"); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_DISP_SUSPEND: + ssd1306_set_flag (part, SSD1306_FLAG_DISPLAY_ON, 0); + ssd1306_set_flag (part, SSD1306_FLAG_DIRTY, 1); + //printf ("SSD1306: DISPLAY SUSPENDED\n"); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_DISP_ON: + ssd1306_set_flag (part, SSD1306_FLAG_DISPLAY_ON, 1); + ssd1306_set_flag (part, SSD1306_FLAG_DIRTY, 1); + //printf ("SSD1306: DISPLAY ON\n"); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_SET_PAGE_START_ADDR + ... SSD1306_VIRT_SET_PAGE_START_ADDR + + SSD1306_VIRT_PAGES - 1: + part->cursor.page = part->spi_data + - SSD1306_VIRT_SET_PAGE_START_ADDR; + //printf ("SSD1306: SET PAGE ADDRESS: 0x%02x\n", part->spi_data); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_SET_COLUMN_LOW_NIBBLE + ... SSD1306_VIRT_SET_COLUMN_LOW_NIBBLE + 0xF: + part->spi_data -= SSD1306_VIRT_SET_COLUMN_LOW_NIBBLE; + part->cursor.column = (part->cursor.column & 0xF0) + | (part->spi_data & 0xF); + //printf ("SSD1306: SET COLUMN LOW NIBBLE: 0x%02x\n",part->spi_data); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_SET_COLUMN_HIGH_NIBBLE + ... SSD1306_VIRT_SET_COLUMN_HIGH_NIBBLE + 0xF: + part->spi_data -= SSD1306_VIRT_SET_COLUMN_HIGH_NIBBLE; + part->cursor.column = (part->cursor.column & 0xF) + | ((part->spi_data & 0xF) << 4); + //printf ("SSD1306: SET COLUMN HIGH NIBBLE: 0x%02x\n", part->spi_data); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_SET_SEG_REMAP_0: + ssd1306_set_flag (part, SSD1306_FLAG_SEGMENT_REMAP_0, + 1); + //printf ("SSD1306: SET COLUMN ADDRESS 0 TO OLED SEG0 to \n"); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_SET_SEG_REMAP_127: + ssd1306_set_flag (part, SSD1306_FLAG_SEGMENT_REMAP_0, + 0); + //printf ("SSD1306: SET COLUMN ADDRESS 127 TO OLED SEG0 to \n"); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_SET_COM_SCAN_NORMAL: + ssd1306_set_flag (part, SSD1306_FLAG_COM_SCAN_NORMAL, + 1); + //printf ("SSD1306: SET COM OUTPUT SCAN DIRECTION NORMAL \n"); + SSD1306_CLEAR_COMMAND_REG(part); + return; + case SSD1306_VIRT_SET_COM_SCAN_INVERTED: + ssd1306_set_flag (part, SSD1306_FLAG_COM_SCAN_NORMAL, + 0); + //printf ("SSD1306: SET COM OUTPUT SCAN DIRECTION REMAPPED \n"); + SSD1306_CLEAR_COMMAND_REG(part); + return; + default: + // Unknown command + return; + } +} + +/* + * Multi-byte command setting + */ +void +ssd1306_update_setting (ssd1306_t *part) +{ + switch (part->command_register) + { + case SSD1306_VIRT_SET_CONTRAST: + part->contrast_register = part->spi_data; + ssd1306_set_flag (part, SSD1306_FLAG_DIRTY, 1); + SSD1306_CLEAR_COMMAND_REG(part); + //printf ("SSD1306: CONTRAST SET: 0x%02x\n", part->contrast_register); + return; + default: + // Unknown command + return; + } +} + +/* + * Determines whether a new command has been sent, or + * whether we are in the process of setting a multi- + * byte command. + */ +static void +ssd1306_write_command (ssd1306_t *part) +{ + if (!part->command_register) + { + // Single byte or start of multi-byte command + ssd1306_update_command_register (part); + } else + { + // Multi-byte command setting + ssd1306_update_setting (part); + } +} + +/* + * Called when a SPI byte is sent + */ +static void +ssd1306_spi_in_hook (struct avr_irq_t * irq, uint32_t value, void * param) +{ + ssd1306_t * part = (ssd1306_t*) param; + + // Chip select should be pulled low to enable + if (part->cs_pin) + return; + + part->spi_data = value & 0xFF; + + switch (part->di_pin) + { + case SSD1306_VIRT_DATA: + ssd1306_write_data (part); + break; + case SSD1306_VIRT_INSTRUCTION: + ssd1306_write_command (part); + break; + default: + // Invalid value + break; + } +} + +/* + * Called when chip select changes + */ +static void +ssd1306_cs_hook (struct avr_irq_t * irq, uint32_t value, void * param) +{ + ssd1306_t * p = (ssd1306_t*) param; + p->cs_pin = value & 0xFF; + //printf ("SSD1306: CHIP SELECT: 0x%02x\n", value); + +} + +/* + * Called when data/instruction changes + */ +static void +ssd1306_di_hook (struct avr_irq_t * irq, uint32_t value, void * param) +{ + ssd1306_t * part = (ssd1306_t*) param; + part->di_pin = value & 0xFF; + //printf ("SSD1306: DATA / INSTRUCTION: 0x%08x\n", value); +} + +/* + * Called when a RESET signal is sent + */ +static void +ssd1306_reset_hook (struct avr_irq_t * irq, uint32_t value, void * param) +{ + //printf ("SSD1306: RESET\n"); + ssd1306_t * part = (ssd1306_t*) param; + if (irq->value && !value) + { + // Falling edge + memset (part->vram, 0, part->rows * part->pages); + part->cursor.column = 0; + part->cursor.page = 0; + part->flags = 0; + part->command_register = 0x00; + part->contrast_register = 0x7F; + ssd1306_set_flag (part, SSD1306_FLAG_COM_SCAN_NORMAL, 1); + ssd1306_set_flag (part, SSD1306_FLAG_SEGMENT_REMAP_0, 1); + } + +} + +static const char * irq_names[IRQ_SSD1306_COUNT] = +{ [IRQ_SSD1306_SPI_BYTE_IN] = "=ssd1306.SDIN", [IRQ_SSD1306_RESET + ] = "avr, AVR_IOCTL_SPI_GETIRQ(0), + SPI_IRQ_OUTPUT), + part->irq + IRQ_SSD1306_SPI_BYTE_IN); + + avr_connect_irq ( + avr_io_getirq (part->avr, + AVR_IOCTL_IOPORT_GETIRQ( + wiring->chip_select.port), + wiring->chip_select.pin), + part->irq + IRQ_SSD1306_ENABLE); + + avr_connect_irq ( + avr_io_getirq (part->avr, + AVR_IOCTL_IOPORT_GETIRQ( + wiring->data_instruction.port), + wiring->data_instruction.pin), + part->irq + IRQ_SSD1306_DATA_INSTRUCTION); + + avr_connect_irq ( + avr_io_getirq (part->avr, + AVR_IOCTL_IOPORT_GETIRQ( + wiring->reset.port), + wiring->reset.pin), + part->irq + IRQ_SSD1306_RESET); +} + +void +ssd1306_init (struct avr_t *avr, struct ssd1306_t * part, int width, int height) +{ + if (!avr || !part) + return; + + memset (part, 0, sizeof(*part)); + part->avr = avr; + part->columns = width; + part->rows = height; + part->pages = height / 8; // 8 pixels per page + + /* + * Register callbacks on all our IRQs + */ + part->irq = avr_alloc_irq (&avr->irq_pool, 0, IRQ_SSD1306_COUNT, + irq_names); + + avr_irq_register_notify (part->irq + IRQ_SSD1306_SPI_BYTE_IN, + ssd1306_spi_in_hook, part); + avr_irq_register_notify (part->irq + IRQ_SSD1306_RESET, + ssd1306_reset_hook, part); + avr_irq_register_notify (part->irq + IRQ_SSD1306_ENABLE, + ssd1306_cs_hook, part); + avr_irq_register_notify (part->irq + IRQ_SSD1306_DATA_INSTRUCTION, + ssd1306_di_hook, part); + + printf ("SSD1306: %duS is %d cycles for your AVR\n", 37, + (int) avr_usec_to_cycles (avr, 37)); + printf ("SSD1306: %duS is %d cycles for your AVR\n", 1, + (int) avr_usec_to_cycles (avr, 1)); +} diff --git a/examples/parts/ssd1306_virt.h b/examples/parts/ssd1306_virt.h new file mode 100644 index 0000000..8ad4b35 --- /dev/null +++ b/examples/parts/ssd1306_virt.h @@ -0,0 +1,184 @@ +/* + ssd1306_virt.h + + Copyright 2011 Michel Pollet + Copyright 2014 Doug Szumski + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +/* + * This "Part" simulates the SSD1306 OLED display driver. + * + * The following functions are currently supported: + * + * > Display reset + * > Display on / suspend + * > Setting of the contrast + * > Inversion of the display + * > Rotation of the display + * > Writing to the VRAM using horizontal addressing mode + * + * It has been tested on a "JY MCU v1.5 OLED" in 4 wire SPI mode + * with the E/RD and R/W lines hard wired low as per the datasheet. + * + */ + +#ifndef __SSD1306_VIRT_H__ +#define __SSD1306_VIRT_H__ + +#include "sim_irq.h" + +#define SSD1306_VIRT_DATA 1 +#define SSD1306_VIRT_INSTRUCTION 0 + +#define SSD1306_VIRT_PAGES 8 +#define SSD1306_VIRT_COLUMNS 128 + +/* Fundamental commands. */ +#define SSD1306_VIRT_SET_CONTRAST 0x81 +#define SSD1306_VIRT_RESUME_TO_RAM_CONTENT 0xA4 +#define SSD1306_VIRT_IGNORE_RAM_CONTENT 0xA5 +#define SSD1306_VIRT_DISP_NORMAL 0xA6 +#define SSD1306_VIRT_DISP_INVERTED 0xA7 +#define SSD1306_VIRT_DISP_SUSPEND 0xAE +#define SSD1306_VIRT_DISP_ON 0xAF + +/* Scrolling commands */ +#define SSD1306_VIRT_SCROLL_RIGHT 0x26 +#define SSD1306_VIRT_SCROLL_LEFT 0x27 +#define SSD1306_VIRT_SCROLL_VR 0x29 +#define SSD1306_VIRT_SCROLL_VL 0x2A +#define SSD1306_VIRT_SCROLL_OFF 0x2E +#define SSD1306_VIRT_SCROLL_ON 0x2F +#define SSD1306_VIRT_VERT_SCROLL_A 0xA3 + +/* Address setting commands */ +#define SSD1306_VIRT_SET_COLUMN_LOW_NIBBLE 0x00 +#define SSD1306_VIRT_SET_COLUMN_HIGH_NIBBLE 0x10 +#define SSD1306_VIRT_MEM_ADDRESSING 0x20 +#define SSD1306_VIRT_SET_COL_ADDR 0x21 +#define SSD1306_VIRT_SET_PAGE_ADDR 0x22 +#define SSD1306_VIRT_SET_PAGE_START_ADDR 0xB0 + +/* Hardware config. commands */ +#define SSD1306_VIRT_SET_LINE 0x40 +#define SSD1306_VIRT_SET_SEG_REMAP_0 0xA0 +#define SSD1306_VIRT_SET_SEG_REMAP_127 0xA1 +#define SSD1306_VIRT_MULTIPLEX 0xA8 +#define SSD1306_VIRT_SET_COM_SCAN_NORMAL 0xC0 +#define SSD1306_VIRT_SET_COM_SCAN_INVERTED 0xC8 +#define SSD1306_VIRT_SET_OFFSET 0xD3 +#define SSD1306_VIRT_SET_PADS 0xDA + +/* Timing & driving scheme setting commands */ +#define SSD1306_VIRT_SET_RATIO_OSC 0xD5 +#define SSD1306_VIRT_SET_CHARGE 0xD9 +#define SSD1306_VIRT_SET_VCOM 0xDB +#define SSD1306_VIRT_NOP 0xE3 + +/* Charge pump command table */ +#define SSD1306_VIRT_CHARGE_PUMP 0x8D +#define SSD1306_VIRT_PUMP_ON 0x14 + +#define SSD1306_CLEAR_COMMAND_REG(part) part->command_register = 0x00 + +enum +{ + //IRQ_SSD1306_ALL = 0, + IRQ_SSD1306_SPI_BYTE_IN, + IRQ_SSD1306_ENABLE, + IRQ_SSD1306_RESET, + IRQ_SSD1306_DATA_INSTRUCTION, + //IRQ_SSD1306_INPUT_COUNT, + IRQ_SSD1306_ADDR, // << For VCD + IRQ_SSD1306_COUNT +//TODO: Add IRQs for VCD: Internal state etc. +}; + +enum +{ + SSD1306_FLAG_DISPLAY_INVERTED = 0, + SSD1306_FLAG_DISPLAY_ON, + SSD1306_FLAG_SEGMENT_REMAP_0, + SSD1306_FLAG_COM_SCAN_NORMAL, + + /* + * Internal flags, not SSD1306 + */ + SSD1306_FLAG_BUSY, // 1: Busy between instruction, 0: ready + SSD1306_FLAG_REENTRANT, // 1: Do not update pins + SSD1306_FLAG_DIRTY, // 1: Needs redisplay... +}; + +/* + * Cursor position in VRAM + */ +struct ssd1306_virt_cursor_t +{ + uint8_t page; + uint8_t column; +}; + +typedef struct ssd1306_t +{ + avr_irq_t * irq; + struct avr_t * avr; + uint8_t columns, rows, pages; + struct ssd1306_virt_cursor_t cursor; + uint8_t vram[SSD1306_VIRT_PAGES][SSD1306_VIRT_COLUMNS]; + uint16_t flags; + uint8_t command_register; + uint8_t contrast_register; + uint8_t cs_pin; + uint8_t di_pin; + uint8_t spi_data; +} ssd1306_t; + +typedef struct ssd1306_pin_t +{ + char port; + uint8_t pin; +} ssd1306_pin_t; + +typedef struct ssd1306_wiring_t +{ + ssd1306_pin_t chip_select; + ssd1306_pin_t data_instruction; + ssd1306_pin_t reset; +} ssd1306_wiring_t; + +void +ssd1306_init (struct avr_t *avr, struct ssd1306_t * b, int width, int height); + +static inline int +ssd1306_set_flag (ssd1306_t *b, uint16_t bit, int val) +{ + int old = b->flags & (1 << bit); + b->flags = (b->flags & ~(1 << bit)) | (val ? (1 << bit) : 0); + return old != 0; +} + +static inline int +ssd1306_get_flag (ssd1306_t *b, uint16_t bit) +{ + return (b->flags & (1 << bit)) != 0; +} + +void +ssd1306_connect (ssd1306_t * part, ssd1306_wiring_t * wiring); + +#endif