Jump to content

Using ugfx on a Teensy 3.1 or Teensy LC


crteensy

Recommended Posts

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

  • Replies 51
  • Created
  • Last Reply

Top Posters In This Topic

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

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 size
width = gdispGetWidth();
height = gdispGetHeight();

// Code Here
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);

DSC_0237_small.thumb.JPG.77a278c6a6f0e65

Link to comment
Share on other sites

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

To summarize:

Files 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_H

gdisp_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_DRAWPIXEL
LLDSPEC 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_WRITE
LLDSPEC 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_GDISP

ssd1351_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_H

board_ssd1351_teensy.h:

#ifndef BOARD_SSD1351_TEENSY_H
#define BOARD_SSD1351_TEENSY_H

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

extern 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_H

board_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_H

And 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.1
  • write a suitably flexible board file for the different Teensys

I'm now having a look at speed and fonts.

Link to comment
Share on other sites

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

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 I

  • rename "ugfx/boards/addons/gdisp/board_SSD1351_teensy.h" to "ugfx/boards/addons/gdisp/board_SSD1351.h" or
  • change the include in "gdisp_lld_SSD1351.c" to be "board_SSD1351_teensy.h" (probably not...) or
  • create 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

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 __cplusplus
extern "C" {
#endif // __cplusplus

extern 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" prototypes
extern "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

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

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

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

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

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

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

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

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 account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...