diff --git a/drivers/gdisp/SSD1306/SSD1306.h b/drivers/gdisp/SSD1306/SSD1306.h new file mode 100644 index 00000000..38507d48 --- /dev/null +++ b/drivers/gdisp/SSD1306/SSD1306.h @@ -0,0 +1,60 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://chibios-gfx.com/license.html + */ + +#ifndef _SSD1306_H +#define _SSD1306_H + +#define SSD1306_BLACK 0 +#define SSD1306_WHITE 1 + +#define SSD1306_SETCONTRAST 0x81 +#define SSD1306_DISPLAYALLON_RESUME 0xA4 +#define SSD1306_DISPLAYALLON 0xA5 +#define SSD1306_NORMALDISPLAY 0xA6 +#define SSD1306_INVERTDISPLAY 0xA7 +#define SSD1306_DISPLAYOFF 0xAE +#define SSD1306_DISPLAYON 0xAF + +#define SSD1306_SETDISPLAYOFFSET 0xD3 +#define SSD1306_SETCOMPINS 0xDA + +#define SSD1306_SETVCOMDETECT 0xDB + +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 +#define SSD1306_SETPRECHARGE 0xD9 +#define SSD1306_ENABLE_CHARGE_PUMP 0x8D + +#define SSD1306_SETMULTIPLEX 0xA8 +#define SSD1306_SETSTARTLINE 0x40 + +#define SSD1306_MEMORYMODE 0x20 +#define SSD1306_HV_COLUMN_ADDRESS 0x21 +#define SSD1306_HV_PAGE_ADDRESS 0x22 +#define SSD1306_PAM_PAGE_START 0xB0 + +#define SSD1306_COMSCANINC 0xC0 +#define SSD1306_COMSCANDEC 0xC8 + +#define SSD1306_SEGREMAP 0xA0 + +#define SSD1306_CHARGEPUMP 0x8D + +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 + +// Scrolling #defines +#define SSD1306_SCROLL_ACTIVATE 0x2F +#define SSD1306_SCROLL_DEACTIVATE 0x2E +#define SSD1306_SCROLL_SET_VERTICAL_SCROLL_AREA 0xA3 +#define SSD1306_SCROLL_HORIZONTAL_RIGHT 0x26 +#define SSD1306_SCROLL_HORIZONTAL_LEFT 0x27 +#define SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_RIGHT 0x29 +#define SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_LEFT 0x2A + +#endif /* _SSD1306_H */ +/** @} */ + diff --git a/drivers/gdisp/SSD1306/gdisp_lld.c b/drivers/gdisp/SSD1306/gdisp_lld.c new file mode 100644 index 00000000..81fd1d66 --- /dev/null +++ b/drivers/gdisp/SSD1306/gdisp_lld.c @@ -0,0 +1,646 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://chibios-gfx.com/license.html + */ + +#include "gfx.h" + +#include "SSD1306.h" + +#if GFX_USE_GDISP || defined(__DOXYGEN__) + +/* Include the emulation code for things we don't support */ +#include "gdisp/lld/emulation.c" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#ifndef GDISP_SCREEN_HEIGHT + #define GDISP_SCREEN_HEIGHT 64 +#endif +#ifndef GDISP_SCREEN_WIDTH + #define GDISP_SCREEN_WIDTH 128 +#endif + +#define GDISP_INITIAL_CONTRAST 0xFF + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +// Include wiring specific header +#include "gdisp_lld_board_example_i2c.h" + +// Some common routines and macros +#define delay(us) gfxSleepMicroseconds(us) +#define delayms(ms) gfxSleepMilliseconds(ms) + +// The memory buffer for the display +static uint8_t gdisp_buffer[GDISP_SCREEN_HEIGHT * GDISP_SCREEN_WIDTH / 8]; + +/** Set the display to normal or inverse. + * @param[in] value 0 for normal mode, or 1 for inverse mode. + * @notapi + */ +static void invert_display(uint8_t i) { + write_cmd(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY); +} + +/** Turn the whole display off. + * Sends the display to sleep, but leaves RAM intact. + * @notapi + */ +static void display_off(){ + write_cmd(SSD1306_DISPLAYOFF); +} + +/** Turn the whole display on. + * Wakes up this display following a sleep() call. + * @notapi + */ +static void display_on() { + write_cmd(SSD1306_DISPLAYON); +} + +/** Set the vertical shift by COM. + * @param[in] value The number of rows to shift, from 0 - 63. + * @notapi +*/ +static void set_display_offset(unsigned char value) { + write_cmd(SSD1306_SETDISPLAYOFFSET); + write_cmd(value & 0x3F); +} + +/** Set the display contrast. + * @param[in] value The contrast, from 1 to 256. + * @notapi + */ +static void set_contrast(unsigned char value) { + write_cmd(SSD1306_SETCONTRAST); + write_cmd(value); +} + +/** Set the display start line. This is the line at which the display will start rendering. + * @param[in] value A value from 0 to 63 denoting the line to start at. + * @notapi + */ +static void set_display_start_line(unsigned char value) { + write_cmd(SSD1306_SETSTARTLINE | value); +} + +/** Set the segment remap state. This allows the module to be addressed as if flipped horizontally. + * NOTE: Changing this setting has no effect on data already in the module's GDDRAM. + * @param[in] value 0 = column address 0 = segment 0 (the default), 1 = column address 127 = segment 0 (flipped). + * @notapi + */ +static void set_segment_remap(unsigned char value) { + write_cmd(value ? SSD1306_SEGREMAP+1 : SSD1306_SEGREMAP); +} + +/** Set the multiplex ratio. + * @param[in] value MUX will be set to (value+1). Valid values range from 15 to 63 - MUX 16 to 64. + * @notapi + */ +static void set_multiplex_ratio(unsigned char value) { + write_cmd(SSD1306_SETMULTIPLEX); + write_cmd(value & 0x3F); +} + +/** Set COM output scan direction. If the display is active, this will immediately vertically + * flip the display. + * @param[in] value 0 = Scan from COM0 (default), 1 = reversed (scan from COM[N-1]). + * @notapi + */ +static void set_com_output_scan_direction(unsigned char value) { + write_cmd(value ? SSD1306_COMSCANDEC : SSD1306_COMSCANINC); +} + +static void set_com_pins_hardware_configuration(unsigned char sequential, unsigned char lr_remap) { + write_cmd(SSD1306_SETCOMPINS); + write_cmd(0x02 | ((sequential & 1) << 4) | ((lr_remap & 1) << 5)); +} + +/** Flip display content horizontally. + * NOTE: This only flips display content, but doesn't turn the char writing around. + * You have to unmirror everything manually. + * @param[in] value 0 = column address 0 = segment 0 (the default), 1 = column address 127 = segment 0 (flipped). + * @notapi + */ +static void flip_display(unsigned char enable) { + if( enable && GDISP.Orientation == GDISP_ROTATE_0) { + set_com_output_scan_direction(0); + set_segment_remap(0); + GDISP.Orientation = GDISP_ROTATE_0; + } + if( !enable && GDISP.Orientation == GDISP_ROTATE_180) { + set_com_output_scan_direction(1); + set_segment_remap(1); + GDISP.Orientation = GDISP_ROTATE_180; + } + else + return; +} + +/** Perform a "no operation". + * @notapi + */ +static void nop() { + write_cmd(0xE3); +} + +/** Page Addressing Mode: Set the column start address register for + * page addressing mode. + * @param[in] address The address (full byte). + * @notapi + */ +static void set_start_address_pam(unsigned char address) +{ + // "Set Lower Column Start Address for Page Addressing Mode" + write_cmd(address & 0x0F); + + // "Set Higher Column Start Address for Page Addressing Mode" + write_cmd((address << 4) & 0x0F); +} + +/** Set memory addressing mode to the given value. + * @param[in] mode 0 for Horizontal addressing mode,\n 1 for Vertical addressing mode,\n or 2 for Page addressing mode (PAM). 2 is the default. + * @notapi + */ +static void set_memory_addressing_mode(unsigned char mode) +{ + write_cmd(SSD1306_MEMORYMODE); + write_cmd(mode & 0x3); +} + +/** Set column address range for horizontal/vertical addressing mode. + * @param[in] start Column start address, 0 - 127. + * @param[in] end Column end address, 0 - 127. + * @notapi + */ +static void set_column_address_hvam(unsigned char start, unsigned char end) +{ + write_cmd(SSD1306_HV_COLUMN_ADDRESS); + write_cmd(start & 0x7F); + write_cmd(end & 0x7F); +} + +/** Set page start and end address for horizontal/vertical addressing mode. + * @param[in] start The start page, 0 - 7. + * @param[in] end The end page, 0 - 7. + * @notapi + */ +static void set_page_address_hvam(unsigned char start, unsigned char end) +{ + write_cmd(SSD1306_HV_PAGE_ADDRESS); + write_cmd(start & 0x07); + write_cmd(end & 0x07); +} + +/** Set the GDDRAM page start address for page addressing mode. + * @param[in] address The start page, 0 - 7. + * @notapi + */ +static void set_page_start_pam(unsigned char address) +{ + write_cmd(SSD1306_PAM_PAGE_START | (address & 0x07)); +} + +/** Set the display clock divide ratio and the oscillator frequency. + * @param[in] ratio The divide ratio, default is 0. + * @param[in] frequency The oscillator frequency, 0 - 127. Default is 8. + * @notapi + */ +static void set_display_clock_ratio_and_frequency(unsigned char ratio, unsigned char frequency) +{ + write_cmd(SSD1306_SETDISPLAYCLOCKDIV); + write_cmd((ratio & 0x0F) | ((frequency & 0x0F) << 4)); +} + +/** Set the precharge period. + * @param[in] phase1 Phase 1 period in DCLK clocks. 1 - 15, default is 2. + * @param[in] phase2 Phase 2 period in DCLK clocks. 1 - 15, default is 2. + * @notapi + */ +static void set_precharge_period(unsigned char phase1, unsigned char phase2) +{ + write_cmd(SSD1306_SETPRECHARGE); + write_cmd((phase1 & 0x0F) | ((phase2 & 0x0F ) << 4)); +} + +/** Set the Vcomh deselect level. + * @param[in] level @p 0 = 0.65 x Vcc, @p 1 = 0.77 x Vcc (default), @p 2 = 0.83 x Vcc. + * @notapi + */ +static void set_vcomh_deselect_level(unsigned char level) +{ + write_cmd(SSD1306_SETVCOMDETECT); + write_cmd((level & 0x03) << 4); +} + +/** Enable/disable charge pump. + * @param[in] enable 0 to disable, 1 to enable the internal charge pump. + * @notapi + */ +static void set_charge_pump(unsigned char enable) +{ + write_cmd(SSD1306_ENABLE_CHARGE_PUMP); + write_cmd(enable ? 0x14 : 0x10); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/* ---- Required Routines ---- */ +/* + The following 2 routines are required. + All other routines are optional. +*/ + +/** + * @brief Low level GDISP driver initialization. + * + * @notapi + */ +bool_t gdisp_lld_init(void) { + // Initialize your display + init_board(); + + // Hardware reset. + setpin_reset(TRUE); + delayms(1); + setpin_reset(FALSE); + delayms(10); + setpin_reset(TRUE); + + // Get the bus for the following initialization commands. + acquire_bus(); + + display_off(); + set_display_clock_ratio_and_frequency(0, 8); + #if GDISP_SCREEN_HEIGHT == 64 + set_multiplex_ratio(0x3F); // 1/64 duty + #endif + #if GDISP_SCREEN_HEIGHT == 32 + set_multiplex_ratio(0x1F); // 1/32 duty + #endif + set_precharge_period(0xF, 0x01); // + set_display_offset(0); // + set_display_start_line(0); // + set_charge_pump(1); // Enable internal charge pump. + set_memory_addressing_mode(0); // horizontal addressing mode; across then down //act like ks0108 (horizontal addressing mode) + set_segment_remap(1); // + set_com_output_scan_direction(1); // + #if GDISP_SCREEN_HEIGHT == 64 + set_com_pins_hardware_configuration(1, 0); + #endif + #if GDISP_SCREEN_HEIGHT == 32 + set_com_pins_hardware_configuration(0, 1); + #endif + set_contrast(GDISP_INITIAL_CONTRAST); // Set initial contrast. + set_vcomh_deselect_level(1); // + display_on(); // Turn on OLED panel. + invert_display(0); // Disable Inversion of display. + set_column_address_hvam(0, 127); // + set_page_address_hvam(0, 7); // + + release_bus(); + + gdisp_lld_display(); + + // Initialize the GDISP structure + GDISP.Width = GDISP_SCREEN_WIDTH; + GDISP.Height = GDISP_SCREEN_HEIGHT; + GDISP.Orientation = GDISP_ROTATE_0; + GDISP.Powermode = powerOn; + GDISP.Contrast = GDISP_INITIAL_CONTRAST; + #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP + GDISP.clipx0 = 0; + GDISP.clipy0 = 0; + GDISP.clipx1 = GDISP.Width; + GDISP.clipy1 = GDISP.Height; + #endif + return TRUE; +} + +/** + * @brief Draws a pixel on the display. + * + * @param[in] x X location of the pixel + * @param[in] y Y location of the pixel + * @param[in] color The color of the pixel + * + * @notapi + */ +void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) { + #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP + if (x < GDISP.clipx0 || y < GDISP.clipy0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return; + #endif + + if (color == SSD1306_WHITE) + gdisp_buffer[x+ (y/8)*GDISP_SCREEN_WIDTH] |= (1<= cy, it is equivalent to a area fill with bgcolor. + * + * @param[in] x, y The start of the area to be scrolled + * @param[in] cx, cy The size of the area to be scrolled + * @param[in] lines The number of lines to scroll (Can be positive or negative) + * @param[in] bgcolor The color to fill the newly exposed area. + * + * @notapi + */ + void gdisp_lld_vertical_scroll(coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) { + #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP + if (x < GDISP.clipx0) { cx -= GDISP.clipx0 - x; x = GDISP.clipx0; } + if (y < GDISP.clipy0) { cy -= GDISP.clipy0 - y; y = GDISP.clipy0; } + if (!lines || cx <= 0 || cy <= 0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return; + if (x+cx > GDISP.clipx1) cx = GDISP.clipx1 - x; + if (y+cy > GDISP.clipy1) cy = GDISP.clipy1 - y; + #endif + + /* See datasheet table T10-1 for this*/ + uint8_t fHeight = (uint8_t)gdispGetFontMetric(gwinGetDefaultFont(), fontLineSpacing); + set_multiplex_ratio(GDISP_SCREEN_HEIGHT - fHeight+1); + set_display_offset(fHeight-2); + + /* Scrolling animation.*/ + for(int i=0; i> fHeight % 8; + gdisp_buffer[i] |= gdisp_buffer[i+GDISP_SCREEN_WIDTH*(fHeight/8 + 1)] << (8 - fHeight%8); + } + + /* Clear last page.*/ + memset( &gdisp_buffer[GDISP_SCREEN_HEIGHT*GDISP_SCREEN_WIDTH/8 - GDISP_SCREEN_WIDTH*2], SSD1306_BLACK, GDISP_SCREEN_WIDTH*2); + + /* Update display.*/ + gdisp_lld_display(); + } + + /** + * @warning Implementation only fully supports left and right...some command issues here. + * Activate a scroll for rows start through stop. + * Hint, the display is 16 rows tall. To scroll the whole display, run: + * @code + * display.scrollright(0x00, 0x0F) + * @endcode + * @param[in] start The start of the area to be scrolled + * @param[in] stop The size of the area to be scrolled + * @param[in] dir direction of scrolling + * [left, right, up, down, up_right, up_left, down_left, down_right] + * @note Optional. * + * + * @notapi + */ + void gdisp_lld_start_scroll(uint8_t dir, uint8_t start, uint8_t stop, uint8_t interval){ +// if(dir == GDISP_SCROLL_RIGHT || GDISP_SCROLL_LEFT || GDISP_SCROLL_UP) { +// switch (dir) { +// case GDISP_SCROLL_RIGHT: +// write_cmd(SSD1306_SCROLL_HORIZONTAL_RIGHT); +// break; +// case GDISP_SCROLL_LEFT: +// write_cmd(SSD1306_SCROLL_HORIZONTAL_LEFT); +// break; +// } +// write_cmd(0X00); // Dummy byte. +// write_cmd(start & 0x07); // Define start page address. +// switch (interval) { // Set time interval between each scroll step (5 frames) +// case 2: write_cmd(0x07); break; // 111b +// case 3: write_cmd(0x04); break; // 100b +// case 4: write_cmd(0x05); break; // 101b +// case 5: write_cmd(0x00); break; // 000b +// case 25: write_cmd(0x06); break; // 110b +// case 64: write_cmd(0x01); break; // 001b +// case 128: write_cmd(0x02); break; // 010b +// case 256: write_cmd(0x03); break; // 011b +// default: +// // default to 2 frame interval +// write_cmd(0x07); break; +// } +// write_cmd(stop & 0x07); // Define stop page address +// write_cmd(0X01); // Set vertical scrolling offset as no row. +// write_cmd(0XFF); // Undocumented but needed. +// write_cmd(SSD1306_SCROLL_ACTIVATE); +// } +// else if(dir == GDISP_SCROLL_UP || GDISP_SCROLL_DOWN) { +// switch (dir) { +// case GDISP_SCROLL_UP: +// gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT); +// write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_RIGHT); +// break; +// +// case GDISP_SCROLL_DOWN: +// gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT); +// write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_LEFT); +// break; +// } +// write_cmd(0X00); // Dummy byte. +// write_cmd(start); // Define start page address. +// write_cmd(0X00); // Set time interval between each scroll step (5 frames) +// write_cmd(stop); // Define stop page address +// write_cmd(0X01); // Set vertical scrolling offset as no row. +// write_cmd(SSD1306_SCROLL_ACTIVATE); +// gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT-10); +// write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_RIGHT); +// write_cmd(0X00); // Dummy byte. +// write_cmd(start); // Define start page address. +// write_cmd(0X00); // Set time interval between each scroll step (5 frames) +// write_cmd(stop); // Define stop page address +// write_cmd(0X03); // Set vertical scrolling offset as no row. +// write_cmd(SSD1306_SCROLL_ACTIVATE); +// } + } + + /** + * Sets vertical scroll area of display. + * @param[in] start The start of the area to be scrolled [y coordinate] + * @param[in] stop The size of the area to be scrolled [y coordinate] + * @note Optional. * + * + * @notapi + */ + void gdisp_lld_set_vertical_scroll_area(uint8_t start, uint8_t stop){ + write_cmd(SSD1306_SCROLL_SET_VERTICAL_SCROLL_AREA); + write_cmd(start); + write_cmd(stop); + } + + /** Deactivate the continuous scroll set up with start_horizontal_scroll() or + * start_vertical_and_horizontal_scroll(). + * @see set_horizontal_scroll, set_vertical_and_horizontal_scroll + * @notapi + */ + void gdisp_lld_stop_scroll(void){ + write_cmd(SSD1306_SCROLL_DEACTIVATE); + } +#endif // GDISP_NEED_SCROLL + +#if GDISP_HARDWARE_FILLS || defined(__DOXYGEN__) + void gdisp_lld_fill_area(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { + #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP + if (x < GDISP.clipx0) { cx -= GDISP.clipx0 - x; x = GDISP.clipx0; } + if (y < GDISP.clipy0) { cy -= GDISP.clipy0 - y; y = GDISP.clipy0; } + if (cx <= 0 || cy <= 0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return; + if (x+cx > GDISP.clipx1) cx = GDISP.clipx1 - x; + if (y+cy > GDISP.clipy1) cy = GDISP.clipy1 - y; + #endif + + for(int i=x; i 100) + value = (void *)100; + if (GDISP.Contrast == (uint8_t)((float)((uint8_t)value) * 256.0/100.0) ) + return; + set_contrast((uint8_t)((float)((uint8_t)value) * 256.0/100.0) ); + GDISP.Contrast = (unsigned)value; + return; + } + } +#endif // GDISP_NEED_CONTROL + +/** + * Let the display blink several times by means of invert and invert back. + * @param num number of blink cycles to do + * @param speed milliseconds to wait between toggling inversion + * @param wait milliseconds to wait before start of all blink cycles and after finishing blink cycles + * @notapi + */ + void gdisp_lld_display_blink(uint8_t num, uint16_t speed, uint16_t wait){ + uint8_t inv = 0; + + gfxSleepMilliseconds(wait); + for(int i=0; i<2*num; i++) { + inv ^= 1; + invert_display(inv); + gfxSleepMilliseconds(speed); + } + gfxSleepMilliseconds(wait); +} + +#endif // GFX_USE_GDISP +/** @} */ + diff --git a/drivers/gdisp/SSD1306/gdisp_lld.mk b/drivers/gdisp/SSD1306/gdisp_lld.mk new file mode 100644 index 00000000..2a2e1364 --- /dev/null +++ b/drivers/gdisp/SSD1306/gdisp_lld.mk @@ -0,0 +1,5 @@ +# List the required driver. +GFXSRC += $(GFXLIB)/drivers/gdisp/SSD1306/gdisp_lld.c + +# Required include directories +GFXINC += $(GFXLIB)/drivers/gdisp/SSD1306 diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h b/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h new file mode 100644 index 00000000..0e7e153a --- /dev/null +++ b/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h @@ -0,0 +1,137 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef _GDISP_LLD_BOARD_H +#define _GDISP_LLD_BOARD_H + +#define SSD1306_RESET_PORT GPIOB +#define SSD1306_RESET_PIN 5 + +/** + * The default slave address is 0x3D, (talking about + * only the real address part here) and the slave + * address can be changed to 0x3C by soldering the + * SA0 pads on the bottom side of the module. + * + * b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 + * -------------------------------------- + * 0 | 1 | 1 | 1 | 1 | 0 |SA0 | R/W + */ +#define SSD1306_I2C_ADDRESS 0x3D +#define SSD1306_SDA_PORT GPIOB +#define SSD1306_SDA_PIN 7 +#define SSD1306_SCL_PORT GPIOB +#define SSD1306_SCL_PIN 6 +#define SET_RST palSetPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN); +#define CLR_RST palClearPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN); +int8_t vccstate; +int32_t row_offset = 0; + +// I2C configuration structure. +static I2CConfig i2cconfig; + + +/** + * @brief Initialize the board for the display. + * @notes This board definition uses GPIO and assumes exclusive access to these GPIO pins + * @notapi + */ +static inline void init_board(void) { + + // RESET pin. + palSetPadMode(SSD1306_RESET_PORT, SSD1306_RESET_PIN, PAL_MODE_OUTPUT_PUSHPULL); + + + /* + * Initializes the I2C driver 1. The I2C1 signals are routed as follows: + * PB6 - SCL. + * PB7 - SDA. + * Timing value comes from ST I2C config tool (xls): + * 0x00901D2B; // 100kHz Standard Mode + * 0x00300444; // 100kHz Fast Mode + * 0x0030020A; // 400kHz Fast Mode + * 0x00100002; // 800kHz Fast Mode + + */ + i2cconfig.timingr = 0x00100002; // 800kHz Fast Mode+ + i2cInit(); + palSetPadMode(SSD1306_SCL_PORT, SSD1306_SCL_PIN, PAL_MODE_ALTERNATE(1)); + palSetPadMode(SSD1306_SDA_PORT, SSD1306_SDA_PIN, PAL_MODE_ALTERNATE(1)); + vccstate = SSD1306_SWITCHCAPVCC; +} + +/** + * @brief Set or clear the lcd reset pin. + * @param[in] state TRUE = lcd in reset, FALSE = normal operation + * @notapi + */ +static inline void setpin_reset(bool_t state) { + if(state) + SET_RST + else + CLR_RST +} + +/** + * @brief Set the lcd back-light level. + * @param[in] percent 0 to 100% + * @notapi + */ +static inline void set_backlight(uint8_t percent) { + // Since we are on OLED no backlight needed +} + +/** + * @brief Take exclusive control of the bus + * @notapi + */ +static inline void acquire_bus(void) { + i2cAcquireBus(&I2CD1); +} + +/** + * @brief Release exclusive control of the bus + * @notapi + */ +static inline void release_bus(void) { + i2cReleaseBus(&I2CD1); +} + +/** + * @brief Send command to the display. + * @param[in] cmd The command to send * + * @notapi + */ +static inline void write_cmd(uint8_t cmd) { + uint8_t command[] = { 0x00, // Co = 0, D/C = 0 + cmd }, + txLength = sizeof(command)/sizeof(command[0]), + rxLength = 0; + i2cStart(&I2CD1, &i2cconfig); + i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, txLength, NULL, rxLength, MS2ST(10)); + i2cStop(&I2CD1); +} + +/** + * @brief Send data to the display. + * @param[in] data The data to send + * @notapi + */ +static inline void write_data(uint8_t* data, uint16_t length) { + uint8_t command[length+1], + txLength = length+1, + rxLength = 0; + command[0] = 0x40; // Co = 0, D/C = 1 + memmove(&command[1], data, length); + + i2cStart(&I2CD1, &i2cconfig); + i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, txLength, NULL, rxLength, MS2ST(10)); + i2cStop(&I2CD1); +} + +#endif /* _GDISP_LLD_BOARD_H */ +/** @} */ + diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h b/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h new file mode 100644 index 00000000..232cd58c --- /dev/null +++ b/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h @@ -0,0 +1,139 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef _GDISP_LLD_BOARD_H +#define _GDISP_LLD_BOARD_H + +#define SSD1306_RESET_PORT GPIOB +#define SSD1306_RESET_PIN 5 +#define SSD1306_MISO_PORT GPIOB +#define SSD1306_MISO_PIN 8 +#define SSD1306_MOSI_PORT GPIOB +#define SSD1306_MOSI_PIN 7 +#define SSD1306_SCK_PORT GPIOB +#define SSD1306_SCK_PIN 6 +#define SSD1306_CS_PORT GPIOB +#define SSD1306_CS_PIN 5 +#define SET_RST palSetPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN); +#define CLR_RST palClearPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN); +int8_t vccstate; +int32_t row_offset = 0; + +/* + * SPI1 configuration structure. + * Speed 42MHz, CPHA=0, CPOL=0, 8bits frames, MSb transmitted first. + * The slave select line is the pin 4 on the port GPIOA. + */ +static const SPIConfig spi1config = { + NULL, + /* HW dependent part.*/ + SSD1306_MISO_PORT, + SSD1306_MISO_PIN, + 0 + //SPI_CR1_BR_0 +}; + +/** + * @brief Initialize the board for the display. + * @notes This board definition uses GPIO and assumes exclusive access to these GPIO pins + * @notapi + */ +static inline void init_board(void) { + + // RESET pin. + palSetPadMode(SSD1306_RESET_PORT, SSD1306_RESET_PIN, PAL_MODE_OUTPUT_PUSHPULL); + + spiInit(); + palSetPadMode(SSD1306_MISO_PORT, SSD1306_MISO_PIN, PAL_MODE_ALTERNATE(1)| + PAL_STM32_OSPEED_HIGHEST); + palSetPadMode(SSD1306_MOSI_PORT, SSD1306_MOSI_PIN, PAL_MODE_ALTERNATE(1)| + PAL_STM32_OSPEED_HIGHEST); + palSetPadMode(SSD1306_SCK_PORT, SSD1306_SCK_PIN, PAL_MODE_ALTERNATE(1)| + PAL_STM32_OSPEED_HIGHEST); + palSetPad(SSD1306_CS_PORT, SSD1306_CS_PIN); + palSetPadMode(SSD1306_CS_PORT, SSD1306_CS_PIN, PAL_MODE_ALTERNATE(1)| + PAL_STM32_OSPEED_HIGHEST); + vccstate = SSD1306_SWITCHCAPVCC; +} + +/** + * @brief Set or clear the lcd reset pin. + * @param[in] state TRUE = lcd in reset, FALSE = normal operation + * @notapi + */ +static inline void setpin_reset(bool_t state) { + if(state) + SET_RST + else + CLR_RST +} + +/** + * @brief Set the lcd back-light level. + * @param[in] percent 0 to 100% + * @notapi + */ +static inline void set_backlight(uint8_t percent) { + // Since we are on OLED no backlight needed +} + +/** + * @brief Take exclusive control of the bus + * @notapi + */ +static inline void acquire_bus(void) { + spiAcquireBus(&SPID1); +} + +/** + * @brief Release exclusive control of the bus + * @notapi + */ +static inline void release_bus(void) { + spiReleaseBus(&SPID1); +} + +/** + * @brief Send command to the display. + * @param[in] cmd The command to send * + * @notapi + */ +static inline void write_cmd(uint8_t cmd) { + uint8_t command[] = { 0x00, // Co = 0, D/C = 0 + cmd }, + txLength = sizeof(command)/sizeof(command[0]), + rxLength = 0; + + spiStart(&SPID1, &spi1config); + spiSelect(&SPID1); + spiStartSend(&SPID1, txLength, command); + spiUnselect(&SPID1); + spiStop(&SPID1); +} + +/** + * @brief Send data to the display. + * @param[in] data The data to send + * @notapi + */ +static inline void write_data(uint8_t* data, uint16_t length) { + uint8_t command[length+1], + txLength = length+1; + command[0] = 0x40; // Co = 0, D/C = 1 + memmove(&command[1], data, length); + + spiStart(&SPID1, &spi1config); + spiSelect(&SPID1); + spiStartSend(&SPID1, txLength, command); + spiUnselect(&SPID1); + spiStop(&SPID1); +} + + +#endif /* _GDISP_LLD_BOARD_H */ +/** @} */ + diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_template.h b/drivers/gdisp/SSD1306/gdisp_lld_board_template.h new file mode 100644 index 00000000..48028e83 --- /dev/null +++ b/drivers/gdisp/SSD1306/gdisp_lld_board_template.h @@ -0,0 +1,74 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#ifndef _GDISP_LLD_BOARD_H +#define _GDISP_LLD_BOARD_H + +/** + * @brief Initialize the board for the display. + * @notes This board definition uses GPIO and assumes exclusive access to these GPIO pins + * @notapi + */ +static inline void init_board(void) { + +} + +/** + * @brief Set or clear the lcd reset pin. + * @param[in] state TRUE = lcd in reset, FALSE = normal operation + * @notapi + */ +static inline void setpin_reset(bool_t state) { + +} + +/** + * @brief Set the lcd back-light level. + * @param[in] percent 0 to 100% + * @notapi + */ +static inline void set_backlight(uint8_t percent) { + // Since we are on OLED no backlight needed +} + +/** + * @brief Take exclusive control of the bus + * @notapi + */ +static inline void acquire_bus(void) { + +} + +/** + * @brief Release exclusive control of the bus + * @notapi + */ +static inline void release_bus(void) { + +} + +/** + * @brief Send command to the display. + * @param[in] cmd The command to send * + * @notapi + */ +static inline void write_cmd(uint8_t cmd) { + +} + +/** + * @brief Send data to the display. + * @param[in] data The data to send + * @notapi + */ +static inline void write_data(uint8_t* data, uint16_t length) { + +} + +#endif /* _GDISP_LLD_BOARD_H */ +/** @} */ + diff --git a/drivers/gdisp/SSD1306/gdisp_lld_config.h b/drivers/gdisp/SSD1306/gdisp_lld_config.h new file mode 100644 index 00000000..158c3e15 --- /dev/null +++ b/drivers/gdisp/SSD1306/gdisp_lld_config.h @@ -0,0 +1,32 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://chibios-gfx.com/license.html + */ + +#ifndef _GDISP_LLD_CONFIG_H +#define _GDISP_LLD_CONFIG_H + +#if GFX_USE_GDISP + +/*===========================================================================*/ +/* Driver hardware support. */ +/*===========================================================================*/ + +#define GDISP_DRIVER_NAME "SSD1306" + +#define GDISP_HARDWARE_CLEARS FALSE +#define GDISP_HARDWARE_FILLS TRUE +#define GDISP_HARDWARE_BITFILLS FALSE +#define GDISP_HARDWARE_SCROLL TRUE +#define GDISP_HARDWARE_PIXELREAD FALSE +#define GDISP_HARDWARE_CONTROL TRUE + +#define GDISP_PIXELFORMAT GDISP_PIXELFORMAT_MONO + +#endif /* GFX_USE_GDISP */ + +#endif /* _GDISP_LLD_CONFIG_H */ +/** @} */ + diff --git a/drivers/gdisp/SSD1306/readme.txt b/drivers/gdisp/SSD1306/readme.txt new file mode 100644 index 00000000..6f0220be --- /dev/null +++ b/drivers/gdisp/SSD1306/readme.txt @@ -0,0 +1,27 @@ +Description: + +Driver for OLED with 4-wire serial interface and I²C/SPI interface + +Know restictions: +- Driver works only with SSD1306 hooked up over I2C or SPI (include corresponding header) +- Driver is written for 128x64 pixel displays (128x32 are only partly supported and need small further work) +- after using uGFX subsystem gdisp_lld_display() has to be called "by hand" to push framebuffer to display + +To use this driver: + +1. Add in your gfxconf.h: + a) #define GFX_USE_GDISP TRUE + + b) Any optional high level driver defines (see gdisp.h) eg: GDISP_NEED_MULTITHREAD + + c) The following are optional - define them if you are not using the defaults below: + #define GDISP_SCREEN_WIDTH 128 + #define GDISP_SCREEN_HEIGHT 64 + +2. If you are not using a known board then create a gdisp_lld_board.h file according to + given example files (or just stick with them) and ensure it is on your include path. + +3. To your makefile add the following lines: + include $(GFXLIB)/drivers/gdisp/SSD1306/gdisp_lld.mk + +4. Call gdisp_lld_display() every time you want to update display content diff --git a/releases.txt b/releases.txt index f7e35003..b657960f 100644 --- a/releases.txt +++ b/releases.txt @@ -9,6 +9,7 @@ FEATURE: GWIN list boxes. FIX: POSIX port removed, now dedicated OS-X and Linux ports FIX: Several bugfixes FEATURE: mcufont integration +FEATURE: SSD1306 driver by user goeck *** changes after 1.7 ***