diff --git a/examples/board_ssd1306_i2c/Makefile b/examples/board_ssd1306_i2c/Makefile new file mode 100644 index 000000000..ba93bdab8 --- /dev/null +++ b/examples/board_ssd1306_i2c/Makefile @@ -0,0 +1,57 @@ +# +# Copyright 2008-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 . + +target= ssd1306demo +firm_src = ${wildcard at*${board}.c} +firmware = ${firm_src:.c=.axf} +simavr = ../../ + +IPATH = . +IPATH += ../shared +IPATH += ../parts +IPATH += ${simavr}/include +IPATH += ${simavr}/simavr/sim +IPATH += ${simavr}/simavr/sim +IPATH += ${simavr}/simavr/sim + +VPATH = . +VPATH += ../shared +VPATH += ../parts + +LDFLAGS += -lpthread + +include ../Makefile.opengl + +all: obj atmega32_ssd1306.axf ${target} + +atmega32_ssd1306.axf: atmega32_ssd1306.c ssd1306.c images.c twimaster.c + +include ${simavr}/Makefile.common + +board = ${OBJ}/${target}.elf + +${board} : ${OBJ}/ac_input.o +${board} : ${OBJ}/ssd1306_virt.o +${board} : ${OBJ}/ssd1306_glut.o +${board} : ${OBJ}/${target}.o + +${target}: ${board} + @echo $@ done + +clean: clean-${OBJ} + rm -rf *.hex *.a *.axf ${target} *.vcd .*.swo .*.swp .*.swm .*.swn diff --git a/examples/board_ssd1306_i2c/README b/examples/board_ssd1306_i2c/README new file mode 100644 index 000000000..4a5378570 --- /dev/null +++ b/examples/board_ssd1306_i2c/README @@ -0,0 +1,10 @@ +board_ssd1306 + +(C) 2014 Doug Szumski + +This sample code demonstrates the use of the SSD1306 OLED driver. It +runs identically in real life. + +The part has been tested using only the supplied driver. + +In the future it could be extended to allow parallel addressing. diff --git a/examples/board_ssd1306_i2c/atmega32_ssd1306.c b/examples/board_ssd1306_i2c/atmega32_ssd1306.c new file mode 100644 index 000000000..f932fa986 --- /dev/null +++ b/examples/board_ssd1306_i2c/atmega32_ssd1306.c @@ -0,0 +1,172 @@ +/* + atmega32_ssd1306.c + + 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 + +#undef F_CPU +#define F_CPU 7380000 + +#include "avr_mcu_section.h" +AVR_MCU(F_CPU, "atmega32"); + +#include "ssd1306.h" +#include "images.h" +#include "../shared/twimaster.h" + +#define DEFAULT_DELAY 10000 + +void +spi_init (void) +{ + DDRB |= (1 << PB4) | (1 << PB5) | (1 << PB7) | (1 << PB3) | (1 << PB1); + SPCR |= (1 << SPE) | (1 << MSTR); + // Double the speed + SPSR |= (1 << SPI2X); +} + +void +demo_set_contrast (void) +{ + //do while loop needed to iterate through full range of uint8_t + uint8_t contrast=0; + do { + ssd1306_set_contrast (contrast); + _delay_ms (DEFAULT_DELAY/200); + contrast++; + } while (contrast>0); +} + +void +demo_show_image (void) +{ + ssd1306_write_image_fb (logo); + ssd1306_display_fb (); + _delay_ms (DEFAULT_DELAY); +} + +/* Draw some dots by writing directly to the VRAM */ +void +demo_set_byte_direct (void) +{ + ssd1306_clear_screen (); + uint8_t x = 0; + for (uint8_t page = 0; page < SSD1306_PIXEL_PAGES; ++page) + { + for (x = 0; x < SSD1306_X_PIXELS; x += 2) + { + ssd1306_write_byte (x, page, 0xAA); + } + } + _delay_ms (DEFAULT_DELAY); +} + +/* Draw some stripes by setting individual pixels on the frame buffer */ +void +demo_set_pixels (void) +{ + ssd1306_clear_fb (); + uint8_t x = 0; + for (uint8_t y = 0; y < SSD1306_Y_PIXELS; ++y) + { + for (x = 0; x < SSD1306_X_PIXELS; x += 2) + { + ssd1306_set_pixel_fb (x, y, PIXEL_STATE_ON); + } + } + ssd1306_display_fb (); + _delay_ms (DEFAULT_DELAY); +} + +void +demo_clear_screen (void) +{ + // Turn all pixels on + memset (ssd1306_frame_buffer_g, 0xFF, SSD1306_PIXEL_BYTES); + ssd1306_display_fb (); + _delay_ms (DEFAULT_DELAY); + + // Clear screen + ssd1306_clear_screen (); + _delay_ms (DEFAULT_DELAY); +} + +void +demo_set_power_state (void) +{ + ssd1306_set_power_state (POWER_STATE_SLEEP); + _delay_ms (DEFAULT_DELAY); + ssd1306_set_power_state (POWER_STATE_ON); + _delay_ms (DEFAULT_DELAY); +} + +void +demo_rotate_display (void) +{ + for (uint8_t i = DISP_ORIENT_NORMAL; + i <= DISP_ORIENT_UPSIDE_DOWN_MIRRORED; ++i) + { + ssd1306_set_display_orientation (i); + ssd1306_write_image_fb (logo); + ssd1306_display_fb (); + _delay_ms (DEFAULT_DELAY); + } +} + +void +demo_invert_image () +{ + ssd1306_set_display_orientation (DISP_ORIENT_NORMAL); + for (uint8_t i = DISPLAY_MODE_NORMAL; i <= DISPLAY_MODE_INVERTED; ++i) + { + ssd1306_set_display_mode (i); + ssd1306_write_image_fb (logo); + ssd1306_display_fb (); + // Check inverted contrast works + demo_set_contrast (); + } +} + +int +main () +{ + i2c_init (); + + /* + * Demonstrate the virtual part functionality. Runs approx 10 times + * faster on an i7-3740QM CPU @ 2.70GHz than in real life. + */ + for (;;) + { + ssd1306_init_display (); + + demo_show_image (); + demo_set_power_state (); + demo_set_contrast (); + demo_set_byte_direct (); + demo_set_pixels (); + demo_clear_screen (); + demo_rotate_display (); + demo_invert_image (); + } + +} diff --git a/examples/board_ssd1306_i2c/images.c b/examples/board_ssd1306_i2c/images.c new file mode 100644 index 000000000..354f62c23 --- /dev/null +++ b/examples/board_ssd1306_i2c/images.c @@ -0,0 +1,61 @@ +/* + images.c + + 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 "images.h" + +/* 128x64 run length encoded simavr logo */ +const uint8_t logo[] PROGMEM = +{ + 0x0, 0x0, 0xff, 0x0, 0x0, 0x7, 0xfe, 0xfe, 0x4, 0xe, 0x6, 0x6, 0x8, 0xe, 0xfe, + 0xfe, 0x3, 0xfc, 0x0, 0x0, 0x5, 0x54, 0xfe, 0xfe, 0x2, 0x0, 0x0, + 0x3, 0xfe, 0xfe, 0x2, 0xff, 0x6, 0x1e, 0x7e, 0xfc, 0xe0, 0xc0, + 0x0, 0x80, 0xc0, 0xf0, 0x7c, 0x3e, 0xe, 0x5e, 0xff, 0xfe, 0xfe, + 0x2, 0xff, 0xfe, 0xfe, 0x2, 0x0, 0x0, 0x6, 0xfe, 0xfe, 0x3, 0x6, + 0x6, 0xa, 0x2e, 0xfe, 0xfe, 0x3, 0x0, 0x0, 0x2, 0xfe, 0xff, + 0xba, 0x0, 0x0, 0x7, 0xf4, 0xff, 0xff, 0x5, 0xfe, 0x0, 0x0, 0x2, + 0xfe, 0xff, 0xff, 0x2, 0xf, 0x7, 0x7, 0xa, 0xff, 0xff, 0x3, 0x0, + 0x0, 0xf, 0x7f, 0x7f, 0x4, 0x70, 0x60, 0x60, 0x8, 0xe0, 0xe0, + 0x2, 0xe1, 0xe1, 0x2, 0xc0, 0x0, 0x0, 0x2, 0x80, 0x80, 0x3, + 0xe5, 0xff, 0xff, 0x2, 0x0, 0x0, 0x3, 0xff, 0xff, 0x3, 0x0, 0x0, + 0x3, 0x1, 0x7, 0x1f, 0x1f, 0x2, 0xf, 0x7, 0x1, 0x0, 0x0, 0x3, + 0x55, 0xff, 0xff, 0x6, 0x0, 0x0, 0x2, 0x80, 0x80, 0x3, 0xc0, + 0xff, 0xff, 0x3, 0xf0, 0x60, 0x60, 0x8, 0xf0, 0xfd, 0xff, 0xff, + 0x3, 0x0, 0x0, 0x2, 0xff, 0xff, 0x2, 0xbb, 0x0, 0x0, 0x7, 0xf7, + 0xff, 0xff, 0x6, 0x0, 0x0, 0x2, 0xff, 0xff, 0x3, 0xf0, 0xf0, + 0x2, 0xe0, 0xe0, 0x3, 0x60, 0x60, 0x4, 0x70, 0xf0, 0xff, 0xff, + 0x3, 0xf8, 0xe0, 0xe0, 0x3, 0x0, 0x0, 0xb, 0xf0, 0xf8, 0xf8, + 0x5, 0xe0, 0x0, 0x0, 0x7, 0xff, 0xff, 0x4, 0x0, 0x0, 0x2, 0xff, + 0xff, 0x6, 0x0, 0x0, 0x3, 0xff, 0xff, 0x3, 0x0, 0x0, 0x3, 0x40, + 0xf8, 0xf8, 0x5, 0xf0, 0x0, 0x0, 0x3, 0xb5, 0xff, 0xff, 0x6, + 0x0, 0x0, 0x2, 0xff, 0xff, 0x7, 0x0, 0x0, 0xa, 0x77, 0xff, 0xff, + 0x3, 0x0, 0x0, 0x2, 0x7, 0x1f, 0x3f, 0xf8, 0xe0, 0xc0, 0x0, 0x0, + 0x4, 0xf7, 0xff, 0xff, 0x4, 0x7f, 0x3f, 0x0, 0x0, 0x2, 0xff, + 0xff, 0x7, 0x0, 0x0, 0x6, 0x1, 0xff, 0xff, 0x7, 0x0, 0x0, 0xb, + 0x7, 0xf, 0xf, 0x6, 0xe, 0xe, 0x7, 0xf, 0xf, 0x3, 0x7, 0x0, 0x0, + 0x2, 0xf, 0xf, 0x6, 0x0, 0x0, 0x3, 0xf, 0xf, 0x2, 0x7, 0x0, 0x0, + 0x3, 0x3, 0xf, 0xf, 0x6, 0x0, 0x0, 0x3, 0x2, 0xf, 0xf, 0x6, 0x0, + 0x0, 0x2, 0x7, 0xf, 0xf, 0x5, 0x7, 0x0, 0x0, 0xa, 0x3, 0xf, 0xf, + 0x3, 0x0, 0x0, 0x6, 0x3, 0x7, 0xf, 0xe, 0xe, 0x2, 0xf, 0x7, 0x3, + 0x3, 0x2, 0x1, 0x0, 0x0, 0x5, 0x7, 0xf, 0xf, 0x5, 0x7, 0x0, 0x0, + 0x7, 0x7, 0xf, 0xf, 0x5, 0x7, 0x0, 0x0, 0xff, 0x0, 0x0, 0x6 +}; + + diff --git a/examples/board_ssd1306_i2c/images.h b/examples/board_ssd1306_i2c/images.h new file mode 100644 index 000000000..3534adecb --- /dev/null +++ b/examples/board_ssd1306_i2c/images.h @@ -0,0 +1,30 @@ +/* + images.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 IMAGES_H_ +#define IMAGES_H_ + +#include +#include + +extern const uint8_t logo[] PROGMEM; + +#endif /* IMAGES_H_ */ diff --git a/examples/board_ssd1306_i2c/ssd1306.c b/examples/board_ssd1306_i2c/ssd1306.c new file mode 100644 index 000000000..e9a372f26 --- /dev/null +++ b/examples/board_ssd1306_i2c/ssd1306.c @@ -0,0 +1,261 @@ +/* + ssd1306.c + + SSD1306 display driver (TWI mode) + + Copyright 2014 Doug Szumski + + Inspired by the work of Gabriel Anzziani. + + This program 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. + + This program 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 this program. If not, see + */ + +#include + +#include +#include +#include + +#include "ssd1306.h" +#include "../shared/twimaster.h" + +uint8_t ssd1306_frame_buffer_g[SSD1306_PIXEL_PAGES][SSD1306_X_PIXELS]; + +void +ssd1306_reset_display (void) +{ + PORTC |= (1 << SSD1306_RESET_PIN); + _delay_us (3); + PORTC &= ~(1 << SSD1306_RESET_PIN); + _delay_us (3); + PORTC |= (1 << SSD1306_RESET_PIN); +} + +void +ssd1306_write_data (const uint8_t byte) +{ + while(i2c_write(SSD1306_TWI_DATA)); + i2c_write(byte); +} + +void +ssd1306_write_instruction (const uint8_t byte) +{ + i2c_write(SSD1306_TWI_INSTRUCTION); + i2c_write(byte); +} + +/* Initialise display mostly as per p64 of the datasheet */ +void +ssd1306_init_display (void) +{ + ssd1306_reset_display (); + i2c_start_wait(SSD1306_TWI_ADDRESS << 1); + + ssd1306_set_power_state (POWER_STATE_SLEEP); + + ssd1306_write_instruction (SSD1306_SET_MULTIPLEX_RATIO); + i2c_write(0x3F); + + ssd1306_write_instruction (SSD1306_SET_VERTICAL_OFFSET); + i2c_write(0x00); + + ssd1306_write_instruction (SSD1306_SET_DISP_START_LINE); + + ssd1306_set_display_orientation(DISP_ORIENT_NORMAL); + + ssd1306_write_instruction (SSD1306_SET_WIRING_SCHEME); + i2c_write(0x12); + + ssd1306_set_contrast (SSD1306_DEFAULT_CONTRAST); + + ssd1306_write_instruction (SSD1306_RESUME_TO_RAM_CONTENT); + + ssd1306_set_display_mode (DISPLAY_MODE_NORMAL); + + // Horizontal memory addressing mode + ssd1306_write_instruction (SSD1306_MEM_ADDRESSING); + i2c_write(0x00); + + ssd1306_write_instruction (SSD1306_SET_DISP_CLOCK); + i2c_write(0x80); + + ssd1306_write_instruction (SSD1306_CHARGE_PUMP_REGULATOR); + i2c_write(SSD1306_CHARGE_PUMP_ON); + + ssd1306_set_power_state (POWER_STATE_ON); +} + +void +ssd1306_set_display_orientation (const disp_orient_t disp_orient) +{ + switch (disp_orient) + { + case DISP_ORIENT_NORMAL: + ssd1306_write_instruction (SSD1306_SET_SEG_REMAP_0); + ssd1306_write_instruction (SSD1306_SET_COM_SCAN_NORMAL); + break; + case DISP_ORIENT_NORMAL_MIRRORED: + // The display is mirrored from the upper edge + ssd1306_write_instruction (SSD1306_SET_SEG_REMAP_0); + ssd1306_write_instruction (SSD1306_SET_COM_SCAN_INVERTED); + break; + case DISP_ORIENT_UPSIDE_DOWN: + ssd1306_write_instruction (SSD1306_SET_SEG_REMAP_127); + ssd1306_write_instruction (SSD1306_SET_COM_SCAN_INVERTED); + break; + case DISP_ORIENT_UPSIDE_DOWN_MIRRORED: + // The upside down display is mirrored from the upper edge + ssd1306_write_instruction (SSD1306_SET_SEG_REMAP_127); + ssd1306_write_instruction (SSD1306_SET_COM_SCAN_NORMAL); + break; + default: + break; + } +} + +/* Move the cursor to the start */ +static void +ssd1306_reset_cursor (void) +{ + ssd1306_write_instruction (SSD1306_SET_PAGE_START_ADDR); + ssd1306_write_instruction (SSD1306_SET_COL_HI_NIBBLE); + ssd1306_write_instruction (SSD1306_SET_COL_LO_NIBBLE); +} + +void +ssd1306_set_contrast (const uint8_t contrast) +{ + ssd1306_write_instruction (SSD1306_SET_CONTRAST); + i2c_write(contrast); +} + +void +ssd1306_set_display_mode(const display_mode_t display_mode) +{ + switch (display_mode) { + case DISPLAY_MODE_NORMAL: + ssd1306_write_instruction (SSD1306_DISP_NORMAL); + break; + case DISPLAY_MODE_INVERTED: + ssd1306_write_instruction (SSD1306_DISP_INVERTED); + break; + default: + ssd1306_write_instruction (SSD1306_DISP_NORMAL); + break; + } +} + +void +ssd1306_set_power_state (const power_state_t power_state) +{ + switch (power_state) + { + case POWER_STATE_ON: + ssd1306_write_instruction (SSD1306_DISP_ON); + break; + case POWER_STATE_SLEEP: + ssd1306_write_instruction (SSD1306_DISP_SLEEP); + break; + default: + break; + } +} + +void +ssd1306_write_byte (const uint8_t x, const uint8_t page, const uint8_t byte) +{ + ssd1306_write_instruction (SSD1306_SET_PAGE_START_ADDR | page); + ssd1306_write_instruction (SSD1306_SET_COL_LO_NIBBLE | (x & 0xF)); + ssd1306_write_instruction (SSD1306_SET_COL_HI_NIBBLE | (x >> 4)); + ssd1306_write_data(byte); +} + +void +ssd1306_clear_screen (void) +{ + ssd1306_reset_cursor (); + + for (uint16_t byte = 0; byte < SSD1306_PIXEL_BYTES; byte++) + { + ssd1306_write_data (0x00); + } +} + +/* Transfer display buffer to LCD */ +void +ssd1306_display_fb (void) +{ + ssd1306_reset_cursor (); + + for (uint8_t page = 0; page < SSD1306_PIXEL_PAGES; page++) + { + for (uint8_t column = 0; column < SSD1306_X_PIXELS; column++) + { + ssd1306_write_data (ssd1306_frame_buffer_g[page][column]); + } + } +} + +void +ssd1306_clear_fb (void) +{ + memset(ssd1306_frame_buffer_g, 0, SSD1306_PIXEL_BYTES); +} + +void +ssd1306_set_pixel_fb (const uint8_t x, const uint8_t y, const pixel_state_t pixel_state) +{ + switch (pixel_state) + { + case PIXEL_STATE_ON: + ssd1306_frame_buffer_g[y / SSD1306_PIXEL_PAGES][x] |= (1 << y % SSD1306_PIXEL_PAGES); + break; + case PIXEL_STATE_OFF: + ssd1306_frame_buffer_g[y / SSD1306_PIXEL_PAGES][x] &= ~(1 << y % SSD1306_PIXEL_PAGES); + break; + default: + break; + } +} + +/* Writes a run length encoded image to the display buffer */ +void +ssd1306_write_image_fb (const uint8_t * image) +{ + uint8_t image_byte = 0, next_image_byte, write_byte_count = 0; + + for (uint8_t page = 0; page < SSD1306_PIXEL_PAGES; page++) + { + for (uint8_t column = 0; column < SSD1306_X_PIXELS; column++) + { + if (!write_byte_count) + { + image_byte = pgm_read_byte_near (image++); + next_image_byte = pgm_read_byte_near (image++); + if (image_byte == next_image_byte) + { + write_byte_count = pgm_read_byte_near (image++); + } + else + { + write_byte_count = 1; + image--; + } + } + write_byte_count--; + ssd1306_frame_buffer_g[page][column] = image_byte; + } + } +} diff --git a/examples/board_ssd1306_i2c/ssd1306.h b/examples/board_ssd1306_i2c/ssd1306.h new file mode 100644 index 000000000..9b7b814f7 --- /dev/null +++ b/examples/board_ssd1306_i2c/ssd1306.h @@ -0,0 +1,144 @@ +/* + ssd1306.h + + SSD1306 display driver (TWI mode) + + Copyright 2014 Doug Szumski + + Inspired by the work of Gabriel Anzziani. + + This program 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. + + This program 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 this program. If not, see + */ + +#ifndef SSD1306_H +#define SSD1306_H + +#include + +// TWI pin config +#define SSD1306_RESET_PIN PC2 + +#define SSD1306_X_PIXELS 128 +#define SSD1306_Y_PIXELS 64 +#define SSD1306_PIXEL_PAGES (SSD1306_Y_PIXELS / 8) +#define SSD1306_PIXEL_BYTES (SSD1306_X_PIXELS * SSD1306_PIXEL_PAGES) + + +// Default settings +#define SSD1306_DEFAULT_CONTRAST 0x7F + +//TWI Control bytes +#define SSD1306_TWI_ADDRESS 0x3c +#define SSD1306_TWI_INSTRUCTION 0x00 +#define SSD1306_TWI_DATA 0x40 + +// Fundamental commands +#define SSD1306_CHARGE_PUMP_REGULATOR 0x8D +#define SSD1306_CHARGE_PUMP_ON 0x14 +#define SSD1306_SET_CONTRAST 0x81 +#define SSD1306_RESUME_TO_RAM_CONTENT 0xA4 +#define SSD1306_IGNORE_RAM_CONTENT 0xA5 +#define SSD1306_DISP_NORMAL 0xA6 +#define SSD1306_DISP_INVERTED 0xA7 +#define SSD1306_DISP_SLEEP 0xAE +#define SSD1306_DISP_ON 0xAF + +// Scroll commands +#define SSD1306_SCROLL_RIGHT 0x26 +#define SSD1306_SCROLL_LEFT 0x27 +#define SSD1306_SCROLL_VERTICAL_RIGHT 0x29 +#define SSD1306_SCROLL_VERTICAL_LEFT 0x2A +#define SSD1306_SCROLL_OFF 0x2E +#define SSD1306_SCROLL_ON 0x2F +#define SSD1306_VERT_SCROLL_AREA 0xA3 + +// Address setting commands +#define SSD1306_SET_COL_LO_NIBBLE 0x00 +#define SSD1306_SET_COL_HI_NIBBLE 0x10 +#define SSD1306_MEM_ADDRESSING 0x20 +#define SSD1306_SET_COL_ADDR 0x21 +#define SSD1306_SET_PAGE_ADDR 0x22 +#define SSD1306_SET_PAGE_START_ADDR 0xB0 + +// Hardware configuration +#define SSD1306_SET_DISP_START_LINE 0x40 +#define SSD1306_SET_SEG_REMAP_0 0xA0 +#define SSD1306_SET_SEG_REMAP_127 0xA1 +#define SSD1306_SET_MULTIPLEX_RATIO 0xA8 +#define SSD1306_SET_COM_SCAN_NORMAL 0xC0 +#define SSD1306_SET_COM_SCAN_INVERTED 0xC8 +#define SSD1306_SET_VERTICAL_OFFSET 0xD3 +#define SSD1306_SET_WIRING_SCHEME 0xDA +#define SSD1306_SET_DISP_CLOCK 0xD5 +#define SSD1306_SET_PRECHARGE_PERIOD 0xD9 +#define SSD1306_SET_VCOM_DESELECT_LEVEL 0xDB +#define SSD1306_NOP 0xE3 + +typedef enum +{ + DISPLAY_MODE_NORMAL, DISPLAY_MODE_INVERTED +} display_mode_t; + +typedef enum +{ + POWER_STATE_SLEEP, POWER_STATE_ON +} power_state_t; + +typedef enum +{ + PIXEL_STATE_OFF, PIXEL_STATE_ON +} pixel_state_t; + +typedef enum +{ + DISP_ORIENT_NORMAL, + DISP_ORIENT_NORMAL_MIRRORED, + DISP_ORIENT_UPSIDE_DOWN, + DISP_ORIENT_UPSIDE_DOWN_MIRRORED +} disp_orient_t; + +extern uint8_t ssd1306_frame_buffer_g[SSD1306_PIXEL_PAGES][SSD1306_X_PIXELS]; + +void +ssd1306_write_data (const uint8_t); +void +ssd1306_write_instruction (const uint8_t); +void +ssd1306_reset_display (void); +void +ssd1306_init_display (void); +void +ssd1306_set_display_orientation (const disp_orient_t disp_orient); +void +ssd1306_set_contrast (const uint8_t contrast); +void +ssd1306_set_power_state (const power_state_t power_state); +void +ssd1306_set_display_mode (const display_mode_t display_mode); +void +ssd1306_write_byte (const uint8_t x, const uint8_t page, const uint8_t byte); +void +ssd1306_clear_screen (void); + +/* Frame buffer operations */ +void +ssd1306_display_fb (void); +void +ssd1306_set_pixel_fb (const uint8_t x, const uint8_t y, const pixel_state_t pixel_state); +void +ssd1306_clear_fb (void); +void +ssd1306_write_image_fb (const uint8_t * image); + +#endif diff --git a/examples/board_ssd1306_i2c/ssd1306demo.c b/examples/board_ssd1306_i2c/ssd1306demo.c new file mode 100644 index 000000000..7227b5e82 --- /dev/null +++ b/examples/board_ssd1306_i2c/ssd1306demo.c @@ -0,0 +1,177 @@ +/* + charlcd.c + + Copyright Luki + 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_avr.h" +#include "avr_ioport.h" +#include "sim_elf.h" +#include "sim_gdb.h" + +#if __APPLE__ +#include +#else +#include +#endif + +#include + +#include "ssd1306_glut.h" + +int window_identifier; + +avr_t * avr = NULL; +ssd1306_t ssd1306; + +int win_width, win_height; + +static void * +avr_run_thread (void * ignore) +{ + while (1) + { + avr_run (avr); + } + return NULL; +} + +/* Called on a key press */ +void +keyCB (unsigned char key, int x, int y) +{ + switch (key) + { + case 'q': + exit (0); + break; + } +} + +/* Function called whenever redisplay needed */ +void +displayCB (void) +{ + const uint8_t seg_remap_default = ssd1306_get_flag ( + &ssd1306, SSD1306_FLAG_SEGMENT_REMAP_0); + const uint8_t seg_comscan_default = ssd1306_get_flag ( + &ssd1306, SSD1306_FLAG_COM_SCAN_NORMAL); + + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Set up projection matrix + glMatrixMode (GL_PROJECTION); + // Start with an identity matrix + glLoadIdentity (); + glOrtho (0, win_width, 0, win_height, 0, 10); + // Apply vertical and horizontal display mirroring + glScalef (seg_remap_default ? 1 : -1, seg_comscan_default ? -1 : 1, 1); + glTranslatef (seg_remap_default ? 0 : -win_width, seg_comscan_default ? -win_height : 0, 0); + + // Select modelview matrix + glMatrixMode (GL_MODELVIEW); + glPushMatrix (); + // Start with an identity matrix + glLoadIdentity (); + ssd1306_gl_draw (&ssd1306); + glPopMatrix (); + glutSwapBuffers (); +} + +// gl timer. if the lcd is dirty, refresh display +void +timerCB (int i) +{ + // restart timer + glutTimerFunc (1000 / 64, timerCB, 0); + glutPostRedisplay (); +} + +int +initGL (int w, int h, float pix_size) +{ + win_width = w * pix_size; + win_height = h * pix_size; + + // Double buffered, RGB disp mode. + glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE); + glutInitWindowSize (win_width, win_height); + window_identifier = glutCreateWindow ("SSD1306 128x64 OLED"); + + // Set window's display callback + glutDisplayFunc (displayCB); + // Set window's key callback + glutKeyboardFunc (keyCB); + + glutTimerFunc (1000 / 24, timerCB, 0); + + ssd1306_gl_init (pix_size, SSD1306_GL_WHITE); + + return 1; +} + +int +main (int argc, char *argv[]) +{ + elf_firmware_t f; + const char * fname = "atmega32_ssd1306.axf"; + char path[256]; + sprintf (path, "%s/%s", dirname (argv[0]), fname); + printf ("Firmware pathname is %s\n", path); + elf_read_firmware (fname, &f); + + printf ("firmware %s f=%d mmcu=%s\n", fname, (int) f.frequency, f.mmcu); + + avr = avr_make_mcu_by_name (f.mmcu); + if (!avr) + { + fprintf (stderr, "%s: AVR '%s' not known\n", argv[0], f.mmcu); + exit (1); + } + + avr_init (avr); + avr_load_firmware (avr, &f); + + ssd1306_init (avr, &ssd1306, 128, 64); + + // SSD1306 wired to the TWI bus, with the following additional pins: + ssd1306_wiring_t wiring = + { + .reset.port = 'C', + .reset.pin = 2, + }; + + ssd1306_connect_twi (&ssd1306, &wiring); + + printf ("SSD1306 display demo\n Press 'q' to quit\n"); + + // Initialize GLUT system + glutInit (&argc, argv); + initGL (ssd1306.columns, ssd1306.rows, 2.0); + + pthread_t run; + pthread_create (&run, NULL, avr_run_thread, NULL); + + glutMainLoop (); +} diff --git a/examples/parts/ssd1306_virt.c b/examples/parts/ssd1306_virt.c index 84d25aa62..3a55e788e 100644 --- a/examples/parts/ssd1306_virt.c +++ b/examples/parts/ssd1306_virt.c @@ -159,7 +159,6 @@ ssd1306_update_command_register (ssd1306_t *part) case SSD1306_VIRT_MULTIPLEX: case SSD1306_VIRT_SET_OFFSET: case SSD1306_VIRT_MEM_ADDRESSING: - case SSD1306_VIRT_SET_LINE: case SSD1306_VIRT_SET_PADS: case SSD1306_VIRT_SET_CHARGE: case SSD1306_VIRT_SET_VCOM: @@ -174,6 +173,7 @@ ssd1306_update_command_register (ssd1306_t *part) case SSD1306_VIRT_SCROLL_ON: case SSD1306_VIRT_SCROLL_OFF: case SSD1306_VIRT_RESUME_TO_RAM_CONTENT: + case SSD1306_VIRT_SET_LINE: SSD1306_CLEAR_COMMAND_REG(part); return; default: @@ -284,7 +284,8 @@ ssd1306_twi_hook (struct avr_irq_t * irq, uint32_t value, void * param) if (v.u.twi.msg & TWI_COND_START) { p->twi_selected = 0; - p->twi_index = 0; + p->twi_control = 0; + p->twi_continue = 0; if (((v.u.twi.addr>>1) & SSD1306_I2C_ADDRESS_MASK) == SSD1306_I2C_ADDRESS) { p->twi_selected = v.u.twi.addr; avr_raise_irq(p->irq + IRQ_SSD1306_TWI_IN, @@ -297,13 +298,12 @@ ssd1306_twi_hook (struct avr_irq_t * irq, uint32_t value, void * param) avr_raise_irq(p->irq + IRQ_SSD1306_TWI_IN, avr_twi_irq_msg(TWI_COND_ACK, p->twi_selected, 1)); - if (p->twi_index == 0) { // control byte + if (!p->twi_control) { // control byte if ((v.u.twi.data & (~(1<<6))) != 0) { - printf("%s COND_WRITE %x\n", __FUNCTION__, v.u.twi.data); - printf("%s ALERT: unhandled Co bit\n", __FUNCTION__); - abort(); + p->twi_continue=1; } p->di_pin = v.u.twi.data ? SSD1306_VIRT_DATA : SSD1306_VIRT_INSTRUCTION; + p->twi_control=1; } else { p->spi_data = v.u.twi.data; @@ -311,16 +311,19 @@ ssd1306_twi_hook (struct avr_irq_t * irq, uint32_t value, void * param) { case SSD1306_VIRT_DATA: ssd1306_write_data (p); + if (!p->twi_continue) + p->twi_control=0; break; case SSD1306_VIRT_INSTRUCTION: ssd1306_write_command (p); + if (!p->command_register && !p->twi_continue) + p->twi_control=0; break; default: // Invalid value break; } } - p->twi_index++; } // SSD1306 doesn't support read on serial interfaces @@ -329,7 +332,6 @@ ssd1306_twi_hook (struct avr_irq_t * irq, uint32_t value, void * param) uint8_t data = 0; avr_raise_irq(p->irq + IRQ_SSD1306_TWI_IN, avr_twi_irq_msg(TWI_COND_READ, p->twi_selected, data)); - p->twi_index++; } } } diff --git a/examples/parts/ssd1306_virt.h b/examples/parts/ssd1306_virt.h index fdf810379..d6cd83f8a 100644 --- a/examples/parts/ssd1306_virt.h +++ b/examples/parts/ssd1306_virt.h @@ -162,7 +162,8 @@ typedef struct ssd1306_t enum ssd1306_addressing_mode_t addr_mode; uint8_t twi_selected; - uint8_t twi_index; + uint8_t twi_control; + uint8_t twi_continue; } ssd1306_t; typedef struct ssd1306_pin_t