inmarket Posted March 31, 2015 Report Share Posted March 31, 2015 It looks like your gdisp_lld_init routine is not filling in important fields in the GDriver structure.To help I have added a quick driver for the SSD1351 to the master repository mostly based on your code. I have not done any debugging on it as I do not have this hardware.I also added an example board file and matching cpp file to the boards/addons directory.You will of course need to rename board_ssd1351_teensy.h to board_ssd1351.h and include those two files into your project.You will see that I have used driver specific routine names and externally defined them in both the board h file and in the cpp file. This saves having to mix any Arduino and any ugfx header files and will therefore prevent the compile type errors you were seeing. That issue we can save for another day to solve.Please test and get back to us on any corrections needed.Hope that helps. Link to comment Share on other sites More sharing options...
crteensy Posted March 31, 2015 Author Report Share Posted March 31, 2015 Oooooooh alright! I simply missed the part where the other drivers fill in stuff in the GDisplay struct! Works now with this gdisp_lld_init():LLDSPEC bool_t gdisp_lld_init(GDisplay *g){ init_board_pins(g); acquire_bus(g); write_cmd(g, SSD1351_SET_COMMAND_LOCK); write_data(g, 0x12); // unlock OLED driver IC write_cmd(g, SSD1351_SET_COMMAND_LOCK); write_data(g, 0xB1); // make commands A1, B1, B3, BB, BE, C1 accesible in unlocked state write_cmd(g, SSD1351_SET_SLEEP_ON); write_cmd(g, SSD1351_CLOCKDIV_OSCFREQ); write_data(g, 0xF1); // Osc = 0xF; div = 2 write_cmd(g, SSD1351_SET_MUX_RATIO); write_data(g, 127); write_cmd(g, SSD1351_SET_REMAP); write_data(g, 0b01110100); // [0] : address increment (0: horizontal, 1: vertical, reset 0) // [1] : column remap (0: 0..127, 1: 127..0, reset 0) // [2] : color remap (0: A->B->C, 1: C->B->A, reset 0) // [3] : reserved // [4] : column scan direction (0: top->down, 1: bottom->up, reset 0) // [5] : odd/even split COM (0: disable, 1: enable, reset 1) // [6..7] : color depth (00,01: 65k, 10: 262k, 11: 262k format 2) write_cmd(g, SSD1351_SET_COLUMN_ADDRESS); write_data(g, 0x00); // start write_data(g, 0x7F); // end write_cmd(g, SSD1351_SET_ROW_ADDRESS); write_data(g, 0x00); // start write_data(g, 0x7F); // end write_cmd(g, SSD1351_SET_DISPLAY_START); write_data(g, 0x00); // 0 write_cmd(g, SSD1351_SET_DISPLAY_OFFSET); write_data(g, 0x00); // 0 write_cmd(g, SSD1351_SET_GPIO); write_data(g, 0x00); // both HiZ, input disabled write_cmd(g, SSD1351_SET_FUNCTION_SELECT); write_data(g, 0x01); // enable internal VDD regulator write_cmd(g, SSD1351_SET_RESET_PRECHARGE); write_data(g, 0x32); // phase 2: 3 DCLKs, phase 1: 5 DCLKs write_cmd(g, SSD1351_SET_VCOMH); write_data(g, 0x05); // 0.82*Vcc write_cmd(g, SSD1351_SET_PRECHARGE); write_data(g, 0x17); // 0.6*Vcc write_cmd(g, SSD1351_SET_DISPLAY_MODE_RESET); write_cmd(g, SSD1351_SET_CONTRAST); write_data(g, 0xC8); write_data(g, 0x80); write_data(g, 0xC8); write_cmd(g, SSD1351_MASTER_CONTRAST_CURRENT_CONTROL); write_data(g, 0x0F); // no change write_cmd(g, SSD1351_SET_VSL); write_data(g, 0xA0); // external VSL write_data(g, 0xB5); // hard value write_data(g, 0x55); // hard value write_cmd(g, SSD1351_SET_SECOND_PRECHARGE); write_data(g, 0x01); // 1 DCLKs write_cmd(g, SSD1351_SET_SLEEP_OFF); write_cmd(g, SSD1351_WRITE_RAM); // clear display uint16_t i = 0; for (i = 0; i < 128*128; i++) { write_data(g, 0x80); write_data(g, 0); } release_bus(g); g->g.Width = GDISP_SCREEN_WIDTH; g->g.Height = GDISP_SCREEN_HEIGHT; g->g.Orientation = GDISP_ROTATE_0; g->g.Powermode = powerOn; g->g.Backlight = GDISP_INITIAL_BACKLIGHT; g->g.Contrast = GDISP_INITIAL_CONTRAST; return TRUE;}I will adapt my C/C++ separation to the model you suggested (I've already seen the commit, thank you!) and report if it works.Thank you!Christoph Link to comment Share on other sites More sharing options...
crteensy Posted March 31, 2015 Author Report Share Posted March 31, 2015 Does the output shown in the attached picture match these commands with height = width = 128? I'm not yet too familiar with the geometry arguments for the various drawing functions so I thought I might ask...// Get the screen sizewidth = gdispGetWidth();height = gdispGetHeight();// Code HeregdispDrawBox(10, 10, width/2, height/2, Yellow);gdispFillArea(width/2, height/2, width/2-10, height/2-10, Blue);gdispDrawLine(5, 30, width-50, height-40, Red);for(i = 5, j = 0; i < width && j < height; i += 7, j += i/20) gdispDrawPixel(i, j, White); Link to comment Share on other sites More sharing options...
inmarket Posted March 31, 2015 Report Share Posted March 31, 2015 The yellow box and the red line look right but the blue filled box doesn't.Are you using the setpixel code (your original version) or the window based driver from the repository? Link to comment Share on other sites More sharing options...
inmarket Posted March 31, 2015 Report Share Posted March 31, 2015 By the way - nice work on getting it going! Link to comment Share on other sites More sharing options...
crteensy Posted March 31, 2015 Author Report Share Posted March 31, 2015 I found out why the blue box was not drawn correctly, but I also tried your suggestion of splitting C and C++. Now I've got no output any more, but I'll try to fix that. board init seems to get called, but it doesn't clear the display any more...something in there is wrong. Link to comment Share on other sites More sharing options...
Joel Bodenmann Posted March 31, 2015 Report Share Posted March 31, 2015 I like where this is going. It would be neat if we could provide official Teensy (3.1) support at the end of this ~ Tectu Link to comment Share on other sites More sharing options...
crteensy Posted March 31, 2015 Author Report Share Posted March 31, 2015 To summarize:I have now successfully split C and C++ by adding extern "C" declarations at the top of the C++ file as suggested in this commit: https://bitbucket.org/Tectu/ugfx/commits/9f38cbc445e95162ad583d55dbcd36b6381fe5a0#chg-boards/addons/gdisp/board_SSD1351_teensy.cppThe code still doesn't link for the Teensy 3.1 (undefined reference to _ebss)Speed can certainly be improved with more implemented point-and-block functions, better loops, usage of SPI features, and DMAFiles as they are now:os.c:#include #include systemticks_t gfxSystemTicks(void){ return millis();}systemticks_t gfxMillisecondsToTicks(delaytime_t ms){ return ms;}gdisp_lld_config.h:#ifndef GDISP_LLD_CONFIG_H#define GDISP_LLD_CONFIG_H#if GFX_USE_GDISP/*===========================================================================*//* Driver hardware support. *//*===========================================================================*/#define GDISP_HARDWARE_DRAWPIXEL TRUE#define GDISP_HARDWARE_STREAM_WRITE TRUE#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB565#endif /* GFX_USE_GDISP */#endif // GDISP_LLD_CONFIG_Hgdisp_lld_ssd1351.c:#include #if GFX_USE_GDISP#define GDISP_DRIVER_VMT GDISPVMT_SSD1351#include "gdisp_lld_config.h"#include "src/gdisp/gdisp_driver.h"#include "ssd1351_commands.h"#include "board_ssd1351_teensy.h"/*===========================================================================*//* Driver local definitions. *//*===========================================================================*/#ifndef GDISP_SCREEN_HEIGHT #define GDISP_SCREEN_HEIGHT 128#endif#ifndef GDISP_SCREEN_WIDTH #define GDISP_SCREEN_WIDTH 128#endif#ifndef GDISP_INITIAL_CONTRAST #define GDISP_INITIAL_CONTRAST 100#endif#ifndef GDISP_INITIAL_BACKLIGHT #define GDISP_INITIAL_BACKLIGHT 100#endif#define GDISP_FLG_NEEDFLUSH (GDISP_FLG_DRIVER<<0)/*===========================================================================*//* Driver local functions. *//*===========================================================================*/// none/*===========================================================================*//* Driver exported functions. *//*===========================================================================*/LLDSPEC bool_t gdisp_lld_init(GDisplay *g){ init_board_pins(g); acquire_bus(g); write_cmd(SSD1351_SET_COMMAND_LOCK); write_data(0x12); // unlock OLED driver IC write_cmd(SSD1351_SET_COMMAND_LOCK); write_data(0xB1); // make commands A1, B1, B3, BB, BE, C1 accesible in unlocked state write_cmd(SSD1351_SET_SLEEP_ON); write_cmd(SSD1351_CLOCKDIV_OSCFREQ); write_data(0xF1); // Osc = 0xF; div = 2 write_cmd(SSD1351_SET_MUX_RATIO); write_data(127); write_cmd(SSD1351_SET_REMAP); write_data(0b01110100); // [0] : address increment (0: horizontal, 1: vertical, reset 0) // [1] : column remap (0: 0..127, 1: 127..0, reset 0) // [2] : color remap (0: A->B->C, 1: C->B->A, reset 0) // [3] : reserved // [4] : column scan direction (0: top->down, 1: bottom->up, reset 0) // [5] : odd/even split COM (0: disable, 1: enable, reset 1) // [6..7] : color depth (00,01: 65k, 10: 262k, 11: 262k format 2) write_cmd(SSD1351_SET_COLUMN_ADDRESS); write_data(0x00); // start write_data(0x7F); // end write_cmd(SSD1351_SET_ROW_ADDRESS); write_data(0x00); // start write_data(0x7F); // end write_cmd(SSD1351_SET_DISPLAY_START); write_data(0x00); // 0 write_cmd(SSD1351_SET_DISPLAY_OFFSET); write_data(0x00); // 0 write_cmd(SSD1351_SET_GPIO); write_data(0x00); // both HiZ, input disabled write_cmd(SSD1351_SET_FUNCTION_SELECT); write_data(0x01); // enable internal VDD regulator write_cmd(SSD1351_SET_RESET_PRECHARGE); write_data(0x32); // phase 2: 3 DCLKs, phase 1: 5 DCLKs write_cmd(SSD1351_SET_VCOMH); write_data(0x05); // 0.82*Vcc write_cmd(SSD1351_SET_PRECHARGE); write_data(0x17); // 0.6*Vcc write_cmd(SSD1351_SET_DISPLAY_MODE_RESET); write_cmd(SSD1351_SET_CONTRAST); write_data(0xC8); write_data(0x80); write_data(0xC8); write_cmd(SSD1351_MASTER_CONTRAST_CURRENT_CONTROL); write_data(0x0F); // no change write_cmd(SSD1351_SET_VSL); write_data(0xA0); // external VSL write_data(0xB5); // hard value write_data(0x55); // hard value write_cmd(SSD1351_SET_SECOND_PRECHARGE); write_data(0x01); // 1 DCLKs write_cmd(SSD1351_SET_SLEEP_OFF); write_cmd(SSD1351_WRITE_RAM); // clear display uint16_t i = 0; for (i = 0; i < 128*128; i++) { write_data(0x80); write_data(0); } release_bus(g); g->g.Width = GDISP_SCREEN_WIDTH; g->g.Height = GDISP_SCREEN_HEIGHT; g->g.Orientation = GDISP_ROTATE_0; g->g.Powermode = powerOn; g->g.Backlight = GDISP_INITIAL_BACKLIGHT; g->g.Contrast = GDISP_INITIAL_CONTRAST; return TRUE;}#if GDISP_HARDWARE_DRAWPIXELLLDSPEC void gdisp_lld_draw_pixel(GDisplay *g){ coord_t x = g->p.x; coord_t y = g->p.y; color_t c = g->p.color; acquire_bus(g); write_cmd(SSD1351_SET_COLUMN_ADDRESS); write_data(x); write_data(127); write_cmd(SSD1351_SET_ROW_ADDRESS); write_data(y); write_data(127); write_cmd(SSD1351_WRITE_RAM); write_data(c >> 8); write_data(c & 0xFF); release_bus(g);}#endif // GDISP_HARDWARE_DRAWPIXEL#if GDISP_HARDWARE_STREAM_WRITELLDSPEC void gdisp_lld_write_start(GDisplay* g){ coord_t x = g->p.x; coord_t y = g->p.y; coord_t sx = g->p.cx; coord_t sy = g->p.cy; acquire_bus(g); write_cmd(SSD1351_SET_COLUMN_ADDRESS); write_data(x); write_data(x + sx - 1); write_cmd(SSD1351_SET_ROW_ADDRESS); write_data(y); write_data(y + sy - 1); write_cmd(SSD1351_WRITE_RAM);}LLDSPEC void gdisp_lld_write_color(GDisplay* g){ color_t c = g->p.color; write_data(c >> 8); write_data(c & 0xFF);}LLDSPEC void gdisp_lld_write_stop(GDisplay* g){ release_bus(g);}#endif // GDISP_HARDWARE_STREAM_WRITE#endif // GFX_USE_GDISPssd1351_commands.h:#ifndef SSD1351_COMMANDS_H#define SSD1351_COMMANDS_H#define SSD1351_SET_COLUMN_ADDRESS 0x15#define SSD1351_SET_ROW_ADDRESS 0x75#define SSD1351_WRITE_RAM 0x5C#define SSD1351_READ_RAM 0x5D#define SSD1351_SET_REMAP 0xA0#define SSD1351_SET_DISPLAY_START 0xA1#define SSD1351_SET_DISPLAY_OFFSET 0xA2#define SSD1351_SET_DISPLAY_MODE_ALL_OFF 0xA4#define SSD1351_SET_DISPLAY_MODE_ALL_ON 0xA5#define SSD1351_SET_DISPLAY_MODE_RESET 0xA6#define SSD1351_SET_DISPLAY_MODE_INVERT 0xA7#define SSD1351_SET_FUNCTION_SELECT 0xAB#define SSD1351_SET_SLEEP_ON 0xAE#define SSD1351_SET_SLEEP_OFF 0xAF#define SSD1351_SET_RESET_PRECHARGE 0xB1#define SSD1351_DISPLAY_ENHANCEMENT 0xB2#define SSD1351_CLOCKDIV_OSCFREQ 0xB3#define SSD1351_SET_VSL 0xB4#define SSD1351_SET_GPIO 0xB5#define SSD1351_SET_SECOND_PRECHARGE 0xB6#define SSD1351_LUT_GRAYSCALE 0xB8#define SSD1351_USE_BUILTIN_LUT 0xB9#define SSD1351_SET_PRECHARGE 0xBB#define SSD1351_SET_VCOMH 0xBE#define SSD1351_SET_CONTRAST 0xC1#define SSD1351_MASTER_CONTRAST_CURRENT_CONTROL 0xC7#define SSD1351_SET_MUX_RATIO 0xCA#define SSD1351_SET_COMMAND_LOCK 0xFD#endif // SSD1351_COMMANDS_Hboard_ssd1351_teensy.h:#ifndef BOARD_SSD1351_TEENSY_H#define BOARD_SSD1351_TEENSY_H#ifdef __cplusplusextern "C" {#endif // __cplusplusextern void init_board_pins();extern void acquire_bus();extern void release_bus();extern void write_cmd(uint8_t cmd);extern void write_data(uint8_t data);extern void write_datap(uint8_t* data, uint16_t length);static inline void post_init_board(){}#ifdef __cplusplus}#endif // __cplusplus#endif // BOARD_SSD1351_TEENSY_Hboard_ssd1351_teensy.cpp:#include #include #include "board_ssd1351_pins.h"extern "C" void init_board_pins();extern "C" void acquire_bus();extern "C" void release_bus();extern "C" void write_cmd(uint8_t cmd);extern "C" void write_data(const uint8_t data);extern "C" void write_datap(const uint8_t* data, uint16_t length);static SPISettings settings(12000000, MSBFIRST, SPI_MODE0);void setpin_reset(int state){ if(state) { digitalWriteFast(SSD1351_R, 0); } else { digitalWriteFast(SSD1351_R, 1); }}void select(){ digitalWriteFast(SSD1351_CS, 0);}void deselect(){ digitalWriteFast(SSD1351_CS, 1);}void command(){ digitalWriteFast(SSD1351_DC, 0);}void data(){ digitalWriteFast(SSD1351_DC, 1);}void init_board_pins(){ Serial.println("board init"); pinMode(SSD1351_R, OUTPUT); setpin_reset(1); pinMode(SSD1351_CS, OUTPUT); deselect(); pinMode(SSD1351_DC, OUTPUT); delay(50); setpin_reset(0); delay(50);}void acquire_bus(){ SPI.beginTransaction(settings); select();}void release_bus(){ deselect(); SPI.endTransaction();}void write_cmd(uint8_t cmd){ command(); SPI.transfer(cmd); data();}void write_data(uint8_t data){ SPI.transfer(data);}//void write_data(const uint8_t* data, uint16_t length)//{// for (uint16_t i = 0; i < length; i++)// {// SPI.transfer(data[i]);// }//}board_ssd1351_pins.h:#ifndef BOARD_SSD1351_PINS_H#define BOARD_SSD1351_PINS_H#define SSD1351_DC 14#define SSD1351_R 15#define SSD1351_CS 16#endif // BOARD_SSD1351_PINS_HAnd the error in my streaming code (see image above) is now also fixed.Steps to "official" teensyduino support would probably be:turn ugfx into an arduino library (I'm using Code::Blocks to build, and doing this with arduino is a totally different thing)get rid of that linking error for the Teensy 3.1write a suitably flexible board file for the different TeensysI'm now having a look at speed and fonts. Link to comment Share on other sites More sharing options...
inmarket Posted March 31, 2015 Report Share Posted March 31, 2015 I just got myself a tiny circuits smart watch. This is arduino based and so I will play with ugfx on that soon.Well done on getting it going.Can you please play with the driver and interface files in the repository and see if you can get them going. That way we will have official support in the repository. Link to comment Share on other sites More sharing options...
crteensy Posted April 1, 2015 Author Report Share Posted April 1, 2015 I've pulled the current master branch and will see if it works, or how to make it work. Link to comment Share on other sites More sharing options...
crteensy Posted April 1, 2015 Author Report Share Posted April 1, 2015 Well the code looks good, but I'm not sure how it's supposed to be used. As it is, in "ugfx/drivers/gdisp/SSD1351/gdisp_lld_SSD1351.c:25" the include file "board_SSD1351.h" is not found, because it doesn't exist anywhere in the include paths. There is "ugfx/boards/addons/gdisp/board_SSD1351_teensy.h" which has all the necessary declarations and macros, but that's board-specific.Should Irename "ugfx/boards/addons/gdisp/board_SSD1351_teensy.h" to "ugfx/boards/addons/gdisp/board_SSD1351.h" orchange the include in "gdisp_lld_SSD1351.c" to be "board_SSD1351_teensy.h" (probably not...) orcreate a new header "board_SSD1351.h" that simply includes "board_SSD1351_teensy.h" somewhere in my include paths?I've now picked the third option for debugging and placed it in my main project directory, which works fine:#ifndef BOARD_SSD1351_H#define BOARD_SSD1351_H#include "board_SSD1351_teensy.h"#endif // BOARD_SSD1351_H Also, where should gfxMillisecondsToTicks() and gfxSystemTicks() be defined? They are part of arduino, so should arduino be regarded as an OS? It is not an OS, but bare metal is no OS either. I could also place them in the board cpp file, where they might fight with other definitions if other board files also provide these functions. My current code has them in a separate file. Link to comment Share on other sites More sharing options...
crteensy Posted April 1, 2015 Author Report Share Posted April 1, 2015 That was fairly easy! Notes:Pin numbers are currently hard-wired in board_SSD1351_teensy.cpp. Other users might use different pins and they should be free to do so. What would be the appropriate place for that? I think board_SSD1351_teensy.h would be better, but I'd prefer a place that's totally outside of the ugfx source tree.Reading from the display is not possible in SPI mode. I'm not sure if the dummy read functions should be there. If optional parallel interfacing was supported, how would one switch the driver to that mode? It would probably be a totally different driver, wouldn't it?what exactly are, from ugfx's point of view, the different sleep modes? I could experiment with hardware control to enable those features, but I need to know what is expected from each mode.How are the orientations defined? Again, rotating is not really a problem.I'm not sure how to create a pull request for you, so here are my files:ugfx/boards/addons/gdisp/board_SSD1351_teensy.h, changed write_data(g,d):/* * 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#ifdef __cplusplusextern "C" {#endif // __cplusplusextern void ssd1351_init_board(void);extern void ssd1351_setpin_reset(int state);extern void ssd1351_acquire_bus(void);extern void ssd1351_release_bus(void);extern void ssd1351_write_cmd(unsigned char index);extern void ssd1351_write_data(unsigned char data);#define init_board(g) ssd1351_init_board()#define post_init_board(g)#define setpin_reset(g, s) ssd1351_setpin_reset(s)#define set_backlight(g, p)#define acquire_bus(g) ssd1351_acquire_bus()#define release_bus(g) ssd1351_release_bus()#define write_cmd(g, i) ssd1351_write_cmd(i)#define write_data(g, d) ssd1351_write_data(d)#ifdef __cplusplus}#endif // __cplusplus#endif /* _GDISP_LLD_BOARD_H */ugfx/boards/addons/gdisp/board_SSD1351_teensy.cpp (unchaged I think):/* * 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 */#include #include // pin numbers#define SSD1351_DC 14#define SSD1351_R 15#define SSD1351_CS 16// Predefine the routine with "C" prototypesextern "C" void ssd1351_init_board(void);extern "C" void ssd1351_setpin_reset(int state);extern "C" void ssd1351_acquire_bus(void);extern "C" void ssd1351_release_bus(void);extern "C" void ssd1351_write_cmd(unsigned char index);extern "C" void ssd1351_write_data(unsigned char data);static SPISettings settings(12000000, MSBFIRST, SPI_MODE0);void ssd1351_init_board(void) { pinMode(SSD1351_R, OUTPUT); pinMode(SSD1351_CS, OUTPUT); pinMode(SSD1351_DC, OUTPUT); digitalWriteFast(SSD1351_R, 1); digitalWriteFast(SSD1351_CS, 1); digitalWriteFast(SSD1351_DC, 1);}void ssd1351_setpin_reset(int state) { if (state) digitalWriteFast(SSD1351_R, 0); else digitalWriteFast(SSD1351_R, 1);}void ssd1351_acquire_bus(void) { SPI.beginTransaction(settings); digitalWriteFast(SSD1351_CS, 0);}void ssd1351_release_bus(void) { digitalWriteFast(SSD1351_CS, 1); SPI.endTransaction();}void ssd1351_write_cmd(unsigned char index) { digitalWriteFast(SSD1351_DC, 0); SPI.transfer(index); digitalWriteFast(SSD1351_DC, 1);}void ssd1351_write_data(unsigned char data) { SPI.transfer(data);}ugfx/drivers/gdisp/SSD1351/gdisp_lld_config.h:/* * 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_CONFIG_H#define _GDISP_LLD_CONFIG_H#if GFX_USE_GDISP/*===========================================================================*//* Driver hardware support. *//*===========================================================================*/#define GDISP_HARDWARE_STREAM_WRITE TRUE//#define GDISP_HARDWARE_STREAM_READ TRUE//#define GDISP_HARDWARE_CONTROL TRUE#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB565#endif /* GFX_USE_GDISP */#endif /* _GDISP_LLD_CONFIG_H */ugfx/drivers/gdisp/SSD1351/gdisp_lld_SSD1351.c:/* * 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 */#include "gfx.h"#if GFX_USE_GDISP#if defined(GDISP_SCREEN_HEIGHT) #warning "GDISP: This low level driver does not support setting a screen size. It is being ignored." #undef GISP_SCREEN_HEIGHT#endif#if defined(GDISP_SCREEN_WIDTH) #warning "GDISP: This low level driver does not support setting a screen size. It is being ignored." #undef GDISP_SCREEN_WIDTH#endif#define GDISP_DRIVER_VMT GDISPVMT_SSD1351#include "gdisp_lld_config.h"#include "src/gdisp/gdisp_driver.h"#include "board_SSD1351.h"/*===========================================================================*//* Driver local definitions. *//*===========================================================================*/#ifndef GDISP_SCREEN_HEIGHT #define GDISP_SCREEN_HEIGHT 128#endif#ifndef GDISP_SCREEN_WIDTH #define GDISP_SCREEN_WIDTH 128#endif#ifndef GDISP_INITIAL_CONTRAST #define GDISP_INITIAL_CONTRAST 100#endif#ifndef GDISP_INITIAL_BACKLIGHT #define GDISP_INITIAL_BACKLIGHT 100#endif#include "SSD1351.h"/*===========================================================================*//* Driver local functions. *//*===========================================================================*/// Some common routines and macros#define dummy_read(g) { volatile uint16_t dummy; dummy = read_data(g); (void) dummy; }#define write_reg(g, reg, data) { write_cmd(g, reg); write_data(g, data); }/*===========================================================================*//* Driver exported functions. *//*===========================================================================*/LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { // No private area for this controller g->priv = 0; // Initialise the board interface init_board(g); // Hardware reset setpin_reset(g, TRUE); gfxSleepMilliseconds(20); setpin_reset(g, FALSE); gfxSleepMilliseconds(20); // Get the bus for the following initialisation commands acquire_bus(g); write_reg(g, SSD1351_SET_COMMAND_LOCK, 0x12); // unlock OLED driver IC write_reg(g, SSD1351_SET_COMMAND_LOCK, 0xB1); // make commands A1, B1, B3, BB, BE, C1 accesible in unlocked state write_cmd(g, SSD1351_SET_SLEEP_ON); // sleep mode ON (display off) write_reg(g, SSD1351_CLOCKDIV_OSCFREQ, 0xF1); // Front clock divider / osc freq - Osc = 0xF; div = 2 write_reg(g, SSD1351_SET_MUX_RATIO, 127); // set MUX ratio write_reg(g, SSD1351_SET_REMAP, 0b01110100); // Set re-map / color depth // [0] : address increment (0: horizontal, 1: vertical, reset 0) // [1] : column remap (0: 0..127, 1: 127..0, reset 0) // [2] : color remap (0: A->B->C, 1: C->B->A, reset 0) // [3] : reserved // [4] : column scan direction (0: top->down, 1: bottom->up, reset 0) // [5] : odd/even split COM (0: disable, 1: enable, reset 1) // [6..7] : color depth (00,01: 65k, 10: 262k, 11: 262k format 2) write_cmd(g, SSD1351_SET_COLUMN_ADDRESS); // Set Column address write_data(g, 0x00); // start write_data(g, GDISP_SCREEN_WIDTH-1); // end write_cmd(g, SSD1351_SET_ROW_ADDRESS); // set row address write_data(g, 0x00); // start write_data(g, GDISP_SCREEN_WIDTH-1); // end write_reg(g, SSD1351_SET_DISPLAY_START, 0x00); // set display start line - 0 write_reg(g, SSD1351_SET_DISPLAY_OFFSET, 0x00); // set display offset - 0 write_reg(g, SSD1351_SET_GPIO, 0x00); // set GPIO - both HiZ, input disabled write_reg(g, SSD1351_SET_FUNCTION_SELECT, 0x01); // enable internal VDD regulator write_reg(g, SSD1351_SET_RESET_PRECHARGE, 0x32); // set reset / pre-charge period - phase 2: 3 DCLKs, phase 1: 5 DCLKs write_reg(g, SSD1351_SET_VCOMH, 0x05); // set VComH voltage - 0.82*Vcc write_reg(g, SSD1351_SET_PRECHARGE, 0x17); // set pre-charge voltage - 0.6*Vcc write_cmd(g, SSD1351_SET_DISPLAY_MODE_RESET); // set display mode: reset to normal display write_cmd(g, SSD1351_SET_CONTRAST); // set contrast current for A,B,C write_data(g, 0xC8); write_data(g, 0x80); write_data(g, 0xC8); write_reg(g, SSD1351_MASTER_CONTRAST_CURRENT_CONTROL, 0x0F); // master contrast current control - no change write_cmd(g, SSD1351_SET_VSL); // set segment low voltage write_data(g, 0xA0); // external VSL write_data(g, 0xB5); // hard value write_data(g, 0x55); // hard value write_reg(g, SSD1351_SET_SECOND_PRECHARGE, 0x01); // set second pre-charge period - 1 DCLKs write_cmd(g, SSD1351_SET_SLEEP_OFF); // sleep mode OFF (display on) write_cmd(g, SSD1351_WRITE_RAM); // write to RAM uint16_t i = 0; for (i = 0; i < 128*128; i++) { write_data(g, 0); write_data(g, 0); } release_bus(g); // Finish Init post_init_board(g); // Release the bus release_bus(g); /* Turn on the back-light */ set_backlight(g, GDISP_INITIAL_BACKLIGHT); /* Initialise the GDISP structure */ g->g.Width = GDISP_SCREEN_WIDTH; g->g.Height = GDISP_SCREEN_HEIGHT; g->g.Orientation = GDISP_ROTATE_0; g->g.Powermode = powerOn; g->g.Backlight = GDISP_INITIAL_BACKLIGHT; g->g.Contrast = GDISP_INITIAL_CONTRAST; return TRUE;}#if GDISP_HARDWARE_STREAM_WRITE LLDSPEC void gdisp_lld_write_start(GDisplay *g) { acquire_bus(g); write_cmd(g, SSD1351_SET_COLUMN_ADDRESS); write_data(g, g->p.x); write_data(g, g->p.x + g->p.cx - 1); write_cmd(g, SSD1351_SET_ROW_ADDRESS); write_data(g, g->p.y); write_data(g, g->p.y + g->p.cy - 1); write_cmd(g, SSD1351_WRITE_RAM); } LLDSPEC void gdisp_lld_write_color(GDisplay *g) { LLDCOLOR_TYPE c; c = gdispColor2Native(g->p.color); write_data(g, c >> 8); write_data(g, c & 0xFF); } LLDSPEC void gdisp_lld_write_stop(GDisplay *g) { release_bus(g); }#endif#if GDISP_HARDWARE_STREAM_READ #error "SSD1351 - Stream Read is not supported yet" LLDSPEC void gdisp_lld_read_start(GDisplay *g) { acquire_bus(g); //set_viewport(g); //write_index(g, 0x2E); setreadmode(g); //dummy_read(g); } LLDSPEC color_t gdisp_lld_read_color(GDisplay *g) { uint16_t data; data = read_data(g); return gdispNative2Color(data); } LLDSPEC void gdisp_lld_read_stop(GDisplay *g) { setwritemode(g); release_bus(g); }#endif#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL #error "SSD1351 - Hardware control is not supported yet" LLDSPEC void gdisp_lld_control(GDisplay *g) { switch(g->p.x) { case GDISP_CONTROL_POWER: if (g->g.Powermode == (powermode_t)g->p.ptr) return; switch((powermode_t)g->p.ptr) { case powerOff: case powerSleep: case powerDeepSleep: acquire_bus(g); //TODO release_bus(g); break; case powerOn: acquire_bus(g); //TODO release_bus(g); break; default: return; } g->g.Powermode = (powermode_t)g->p.ptr; return; case GDISP_CONTROL_ORIENTATION: if (g->g.Orientation == (orientation_t)g->p.ptr) return; switch((orientation_t)g->p.ptr) { case GDISP_ROTATE_0: acquire_bus(g); //TODO release_bus(g); g->g.Height = GDISP_SCREEN_HEIGHT; g->g.Width = GDISP_SCREEN_WIDTH; break; case GDISP_ROTATE_90: acquire_bus(g); //TODO release_bus(g); g->g.Height = GDISP_SCREEN_WIDTH; g->g.Width = GDISP_SCREEN_HEIGHT; break; case GDISP_ROTATE_180: acquire_bus(g); //TODO release_bus(g); g->g.Height = GDISP_SCREEN_HEIGHT; g->g.Width = GDISP_SCREEN_WIDTH; break; case GDISP_ROTATE_270: acquire_bus(g); //TODO release_bus(g); g->g.Height = GDISP_SCREEN_WIDTH; g->g.Width = GDISP_SCREEN_HEIGHT; break; default: return; } g->g.Orientation = (orientation_t)g->p.ptr; return; case GDISP_CONTROL_BACKLIGHT: if ((unsigned)g->p.ptr > 100) g->p.ptr = (void *)100; set_backlight(g, (unsigned)g->p.ptr); g->g.Backlight = (unsigned)g->p.ptr; return; //case GDISP_CONTROL_CONTRAST: default: return; } }#endif#endif /* GFX_USE_GDISP */and finally ugfx/drivers/gdisp/SSD1351/SSD1351.h remains unchanged. Link to comment Share on other sites More sharing options...
crteensy Posted April 1, 2015 Author Report Share Posted April 1, 2015 My controller doesn't seem to like rotation commands. The only rotation command that has an effect is that for 180°, but it mirrors the display along the x axis instead of rotating. Here's my gdisp_lld_control():#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL// #error "SSD1351 - Hardware control is not supported yet"LLDSPEC void gdisp_lld_control(GDisplay *g) { switch(g->p.x) { case GDISP_CONTROL_POWER: if (g->g.Powermode == (powermode_t)g->p.ptr) return; switch((powermode_t)g->p.ptr) { case powerOff: case powerSleep: case powerDeepSleep: acquire_bus(g); write_cmd(g, SSD1351_SET_SLEEP_ON); release_bus(g); break; case powerOn: acquire_bus(g); write_cmd(g, SSD1351_SET_SLEEP_OFF); release_bus(g); break; default: return; } g->g.Powermode = (powermode_t)g->p.ptr; return; case GDISP_CONTROL_ORIENTATION: if (g->g.Orientation == (orientation_t)g->p.ptr) return; switch((orientation_t)g->p.ptr) { case GDISP_ROTATE_0: acquire_bus(g); write_reg(g, SSD1351_SET_REMAP, 0b01110100); // [0] : address increment (0: horizontal, 1: vertical, reset 0) // [1] : column remap (0: 0..127, 1: 127..0, reset 0) // [2] : color remap (0: A->B->C, 1: C->B->A, reset 0) // [3] : reserved // [4] : column scan direction (0: top->down, 1: bottom->up, reset 0) // [5] : odd/even split COM (0: disable, 1: enable, reset 1) // [6..7] : color depth (00,01: 65k, 10: 262k, 11: 262k format 2) g->g.Height = GDISP_SCREEN_HEIGHT; g->g.Width = GDISP_SCREEN_WIDTH; release_bus(g); break; case GDISP_ROTATE_90: acquire_bus(g); write_reg(g, SSD1351_SET_REMAP, 0b01100101); // Set re-map / color depth g->g.Height = GDISP_SCREEN_WIDTH; g->g.Width = GDISP_SCREEN_HEIGHT; release_bus(g); break; case GDISP_ROTATE_180: acquire_bus(g); write_reg(g, SSD1351_SET_REMAP, 0b01100110); // Set re-map / color depth g->g.Height = GDISP_SCREEN_HEIGHT; g->g.Width = GDISP_SCREEN_WIDTH; release_bus(g); break; case GDISP_ROTATE_270: acquire_bus(g); write_reg(g, SSD1351_SET_REMAP, 0b01110111); // Set re-map / color depth g->g.Height = GDISP_SCREEN_WIDTH; g->g.Width = GDISP_SCREEN_HEIGHT; release_bus(g); break; default: return; } g->g.Orientation = (orientation_t)g->p.ptr; return; case GDISP_CONTROL_BACKLIGHT: if ((unsigned)g->p.ptr > 100) g->p.ptr = (void *)100; set_backlight(g, (unsigned)g->p.ptr); g->g.Backlight = (unsigned)g->p.ptr; return; //case GDISP_CONTROL_CONTRAST: default: return; }}#endif Link to comment Share on other sites More sharing options...
inmarket Posted April 1, 2015 Report Share Posted April 1, 2015 To answer your questions...The files in the boards/addons directory are not ready to run as-is (although they should be close). They are effectively example files designed to be added to your project and modified to suit. In your case the board_ssd1351_teensy.h would be copied to the project and renamed board_ssd1351.h. The corresponding cpp file would also be copied to your project and the pin definitions modified to suit your hardware.The files in the boards/base directory however are designed to exactly match a particular supported board configuration and the appropriate board makefile can be included directly into the users project to get all supported hardware on that board without any modification.Based on the confusion you experienced I may rename the boards/addons to boards/examples or something like that (suggestions welcome).Yes, there are extra support routines required for the arduino when using OS_RAW32. I may write a full os layer later for the arduino bare metal but probably the best solution in the short term is that we add the arduino support routines into a file that gets put into the boards/addons directory so other people can also make use of it.If you wanted to swap to a parallel interface instead of a SPI interface, normally there are some jumpers on the lcd which tell it how to expect to be talked to on power up. As far as software is concerned - the only change would be to the board_ssd1351.h file (or in your case the support board_ssd1351.cpp file) to talk via a parallel interface rather than SPI. That is the whole purpose of the board file layer - to separate the electrical interface from the details of the driver code. It also allows the driver to be completely operating system and pin assignment independent.I will have to look at the SSD1351 data sheet but some interface modes on the chip may allow reading. The SSD1351 is very similar to most embedded controllers in that you cannot read back from them. The ones you can read back from are the exception rather than the rule. Certainly if GDISP_HARDWARE_STREAMREAD or GDISP_HARDWARE_READPIXEL are not set, the read routines in the board file will never be used and do not need to be defined (in fact in board_ssd1351_teensy.h you will see that they are not defined even though the template currently says they should. All of that is something however that we can clean up later when I get the datasheet (and time).Orientation, sleep modes, contrast and backlight level are all optional and are handled by the gdisp_lld_control() function (but only if GDISP_HARDWARE_CONTROL is set in gdisp_lld_config.h). Again this is something that can be done when I have the datasheet and is not generally difficult. Just look at the ILI9341 as a reasonable example. As to what the various sleep modes mean - that is entirely up to the driver writer. Most sleep modes should be fairly self-explanatory just by their name. Rotation at the driver level is GDISP_ROTATE_0, GDISP_ROTATE_90, GDISP_ROTATE_180 and GDISP_ROTATE_270 (Ithink those are the correct labels off the top of my head). ROTATE_0 is obviously the native rotation for the display, everything else is relative to that in a anti-clockwise direction. ROTATE_90 then has the "top" to the left and the "bottom" to the right etc.I will compare the code to the repository a bit later (I need to go get some food and sleep now). Thanks for all your work! Link to comment Share on other sites More sharing options...
inmarket Posted April 1, 2015 Report Share Posted April 1, 2015 Looking at your comments there, it looks like bits 0 and bits 4 will be the ones to change to do the orientation. Bit 0 will be clear for R0 and R180, set for R90 and R270.Bit 4 will be clear for R0 and R90, set for R180 and R270. I may have bit 4 back to front for R90 and R270. Link to comment Share on other sites More sharing options...
crteensy Posted April 1, 2015 Author Report Share Posted April 1, 2015 That's what I thought as well but whatever I tried I could never get 90° or 270°. That's not a desaster to me (I don't need to rotate the screen in my current application) but it would have been nice to add that. Link to comment Share on other sites More sharing options...
crteensy Posted April 1, 2015 Author Report Share Posted April 1, 2015 Wow thanks for the online font converter - that was unbelievably easy to use! Link to comment Share on other sites More sharing options...
crteensy Posted April 2, 2015 Author Report Share Posted April 2, 2015 Got it compile for the Teensy 3.1 as well, I had an error in my linker settings. Link to comment Share on other sites More sharing options...
inmarket Posted April 2, 2015 Report Share Posted April 2, 2015 Congrats. Can you post your project as I am wanting to start playing with my new arduino tinyscreen? It will make a nice sideline project over easter. Thanks. Link to comment Share on other sites More sharing options...
crteensy Posted April 2, 2015 Author Report Share Posted April 2, 2015 Of course, I can do that, but:TinyScreen is a different platform (ATMega328P - mine is Cortex-M0+ and Cortex-M4)I don't exactly know which driver chip TinyScreen uses - is it the SSD1351?There is no ugfx library port for arduino - I'd be happy to see one, though.If TinyScreen uses the SSD1351 then you already have my driver code. The board file will be different. My own board files won't work for you without modification. Maybe I can be of help when setting up the AVR toolchain in a non-arduino environment, but I think that's it. Link to comment Share on other sites More sharing options...
inmarket Posted April 2, 2015 Report Share Posted April 2, 2015 Yes I realised the chips and boards were different, I am just not use to the arduino way of doing things and was hoping your project with mods would form a shortcut to building a project from scratch on a platform I am unfamiliar with. Even if it isn't close enough to form a project base the sameness and differences would be useful in determining how to create a supported arduino port for ugfx.Send me whatever you think might be useful given that my two aims are 1. To get a project working on tinyscreen, and more importantly 2. Make arduino a supported platform for ugfx. Link to comment Share on other sites More sharing options...
crteensy Posted April 2, 2015 Author Report Share Posted April 2, 2015 Well, arduino is a wrap-up of a couple of things:They started by giving people a simple piece of hardware: an AVR board with a given pinout, geometry and power supply. That was not special by itself. There were also so-called "shields" which can be stacked on top of that board, e.g. for motor drivers, display, and so on. Still nothing extraordinary.Next is the arduino core library, which exposes a simple interface to the hardware, with functions like pinMode() which is used to set the I/O properties of a pin. Pins are numbered consecutively and the library figures out which port registers to manipulate in order to a certain pin to a certain mode. Many libraries were added that simply wrap internal peripherals of the controller and provide a simple to use interface for most common use cases (PWM, SPI, ADC, ...)There's also the arduino IDE which controls the build process. The core and all libraries are compiled as archives and then linked together.The boards I use are Cortex-M based (M0+ and M4), so there's a totally rewritten core library that exposes the common arduino interface, but all arduino libraries can be compiled "on top" of this. Most libraries are portable between platforms unless they provide an interface for a very specific MCU peripheral (DMA or something).So here's what I suggest: Install the arduino IDE and the files provided for the TinyScreen. It looks like these two should be enough to get you going. Get used to the arduino IDE, their build process and your new hardware, and then we'll see how an arduino-ized ugfx library can be written. To be honest I have no idea how exactly that could work, because I hardly understand how the bits of ugfx work together when it comes to compilation. Bear in mind that arduino is not an OS. In fact, an OS can run on top of arduino.To complete my code, here's my main C++ file that I used to test my SSD1351 driver:#include #include #include #include void draw();void setup(){ Serial.println("Start"); SPI.begin(); coord_t width, height; coord_t i, j; // Initialize and clear the display gfxInit(); draw();}void draw(){ coord_t width, height; coord_t i, j; width = gdispGetWidth(); height = gdispGetHeight(); elapsedMicros us_timer; gdispClear(Black);// gdispDrawBox(10, 10, width/2, height/2, Yellow);// gdispFillArea(width/2, height/2, width/2-10, height/2-10, Blue);// gdispDrawLine(5, 30, width-50, height-40, Red);//// for(i = 5, j = 0; i < width && j < height; i += 7, j += i/20)// gdispDrawPixel(i, j, White); font_t font; uint8_t y = 0; font = gdispOpenFont("fixed_5x8"); y += font->height; gdispDrawString(0, y, "fixed_5x8", font, White); font = gdispOpenFont("fixed_7x14"); y += font->height; gdispDrawString(0, y, "fixed_7x14", font, White); font = gdispOpenFont("Pirulen Regular 12"); y += font->height; gdispDrawString(0, y, "Pirulen Rg 12", font, White); uint32_t us = us_timer; char str[20]; snprintf(str, 20, "dt = % " PRIu32, us); y += font->height; gdispDrawString(0, y, str, font, White);}void loop(){ static const uint16_t heartbeat_period = 1000; static elapsedMillis heartbeat_timer = heartbeat_period; static uint8_t heartbeat_counter = 0; static uint8_t rotation; if (heartbeat_timer >= heartbeat_period) { heartbeat_timer = 0; Serial.printf("heartbeat: %" PRIu8 "\r", heartbeat_counter); switch(rotation) { case 0: gdispSetOrientation(GDISP_ROTATE_0); draw(); break; case 1: gdispSetOrientation(GDISP_ROTATE_90); draw(); break; case 2: gdispSetOrientation(GDISP_ROTATE_180); draw(); break; case 3: gdispSetOrientation(GDISP_ROTATE_270); draw(); break; default: rotation = 0; } rotation++; if (rotation == 4) { rotation = 0; }// rotation &= 0x01; }}All other files are in this thread already. Link to comment Share on other sites More sharing options...
inmarket Posted April 2, 2015 Report Share Posted April 2, 2015 Thanks. That is a great help. Link to comment Share on other sites More sharing options...
crteensy Posted April 7, 2015 Author Report Share Posted April 7, 2015 So did you try the TinyScreen? Link to comment Share on other sites More sharing options...
inmarket Posted April 7, 2015 Report Share Posted April 7, 2015 Yes. I have been playing with it over the Easter weekend.It was hard to initially get the arduino IDE up and running as the Tinyduino board file is not easily found. In the end I wrote my own (fairly simple once I knew what was going on).I have written a driver for the SSD1331 for the TinyScreen which was nearly completed testing when my TinyScreen died (the display not the cpu). Nevertheless I have put it up in the repository along with the necessary board files (in the board/addon's directory) and I think it should work as it is. It is a great controller as it supports accelerated commands including the ones necessary to support generalised scrolling. Like most small controllers it doesn't support read over SPI.I have also added arduino as a full GOS layer (even though it is really just a variant of the RAW32). This enables arduino support with no extra support routines being required.I am currently working on making ugfx as an "arduino IDE library". There are some issues with this due to differences in the directory layouts given that the arduino IDE does not support a separate include path for libraries. I have most of the issues sorted out but it is not ready for integration with the ugfx repository yet.It has also raised some interesting issues with respect to GTimer. GTimer is an essential module for most of the other modules (except GDISP). Unfortunately it uses a separate thread for implementation. Whilst this is supported by the GOS emulation layer even for arduino, the real issue is the amount of RAM that the stack for a separate thread requires - particularly one that may be drawing on a screen. 1K for a stack on a CPU that only has 2K RAM is a bit too much. I have some ideas to remove the dependence on threads for GTimer but that will take some time to implement and is a fairly major change so that will come later. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now