Jump to content

SSD1306 driver


goeck

Recommended Posts

Hey everyone

I am happy to release my first version of a SSD1306 display driver with nice smooth scrolling.

I packed everything together and uploaded it with this post. It was developed using the 1,3" OLED board by adafruit hooked up over I2C.

Notes:

- Driver works only with SSD1306 hooked up over I2C (at least SPI lld needs to be added)

- Driver is written for 128x64 pixel displays (128x32 are only partly suppoerted and need small further work)

- after using GFX subsystem gdisp_lld_diaply() hast to be called "by hand" to push over framebuffer to display

- for nice scrolling GWIN_CONSOLE_USE_CLEAR_LINES has to be set to FALSE in file console.c

Please someone give it a try.

Cheers

Stefan

SSD1306.zip

Link to comment
Share on other sites

Hello goeck,

Thank you very much for sharing you driver. I took a look and these are the points I have to mention:

  • I see that you have the ChibiOS/RT logo in the framebuffer per default. Since this project is now completely separated from ChibiOS, it's a big no-go. I recommend you using an empty frame buffer.
  • In the source file, what is this?
    /* Fulfill Adafruit license.*/
    gfxSleepMilliseconds(1000);


    The driver source must be as generic as possible and really just LCD controller dependent - not LCD panel or LCD module (adafruit) dependent.

  • In the source file, after the scroll implementation which ends at line 534, you start giving an interface to control several things such as the LCD controllers power states and start addresses. These are things that need to be declared as static and only usable inside your drivers source file. Things like backlight and contrast changes are meant to be made through the gdisp_lld_control() routine. Here's an example.
  • I'd recommend to name the BLACK and WHITE macros in the SSD1306.h a different way, such as SSD1306_BLACK and SSD1306_WHITE so there are no confusions between the rest of the library.
  • The board file mess has been cleaned up in the meantime. All the #ifdef stuff in line 43 of the source file became obsolet. It's now appropriate to just #include gdisp_lld_board.g there.

Beside these things, your driver works seems to be very solid and consistent. Good work!

~ Tectu

Link to comment
Share on other sites

Hey,

thanks for the feedback. Though I was kind of wondering, what semed to have been happening after I started developing the driver. I just took the templates from that time and worked upon them..anyway, I guess I now have changed almost everything accordingly but left the most stuff undone in gdisp_lld_control for now.

Cheers

Stefan

SSD1306.zip

Link to comment
Share on other sites

I guess adding SPI support shouldn't be that much of an issue. I will do some untested coding, since I don't actually want to rewire my board. But maybe I even do this, because update rates can be much higher using SPI (at least tons of youtube videos point that way).

_____________________________________

EDIT 1: I just hacked a little and we some support for SPI now, please give it a try. I didn't test it, as I would have to reassemble the wiring then.

Cheers

SSD1306.zip

Link to comment
Share on other sites

Okay, I finally got the time to look at your new driver. It looks a lot better now! There's only one more thing: Could you please split the I²C and SPI interface into two separate board files named gdisp_lld_board_example_spi.h and gdisp_lld_board_example_i2c.h? If you don't want to waste your time with that, I can do it as soon as I add the driver to the repository.

Keep going the good work!

~ Tectu

Link to comment
Share on other sites

Oh, sorry...I am in some kind of vacation state since Friday actually.

I could for sure do that split, but not earlier than in two weeks when beeing back in office.

I didn't know about the physical layer extraction into board files, that's why I did it with the PHY_LAYER_xxx define.

So that's not needed anymore, How exactly am I gonna do it now?

Two board files one assuming SPI is used and one assuming I2C is used, right?

Thanks for the hints!

Cheers

Stefan

Link to comment
Share on other sites

  • 3 weeks later...
  • 1 month later...

Hey everyone,

I have tuned the driver a little more. This is the small changelog:

+ added a thread priority lift for the time data gets stuffed into the display, thus updating display goes faster and looks smoother

* now spi and i2c init are done after the pads functions are set

Hope this makes it into trunk.

Cheers

goeck

SSD1306.rar

Link to comment
Share on other sites

I have integrated the changes into the GDISPStreaming branch of development (which will soon become version 2.0 of uGFX).

There has been significant changes to the GDISP low level driver interface to enable some major new features such as support for multiple displays, a formal gdispFlush() call and even automatic flushing.

Can you please test the GDISPStreaming branch for your controller as I don't have equivalent hardware myself.

One change you will need to be aware of is that the gdisp_lld_board.h in your project directory should now be called board_SSD1306.h. The interface has also changed so start from one of the template ones provided.

Thanks for your contribution. It is appreciated.

I also notice that you have started adding a GTimer to your code (although it does nothing yet). I presume this is so you could do flushing using the timer. I really like the idea and now that flushing is officially supported by the new GDISP framework, I think I will add that concept to the framework itself so it can be used by any driver that needs flushing.

Link to comment
Share on other sites

Hey,

I just took some time to test the new adapted driver and found some issues. It's not working properly because the write_data() implementation has a bug.

The display wants to receive one data stream without interruption (i2c Stop condition followed by a new i2c start condition) beginning with DATA indicating start byte and the next consequently following bytes shall be the frame buffer content in x-many pieces.

So it has to look like this, this actually works fine.


static inline void write_data(GDisplay *g, uint8_t* data, uint16_t length) {
// uint8_t command[1];
(void) g;
//
// command[0] = 0x40; // Co = 0, D/C = 1
//
// i2cStart(&I2CD1, &i2cconfig);
// i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, 1, NULL, 0, MS2ST(10));
// i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, data, length, NULL, 0 , MS2ST(10));
// i2cStop(&I2CD1);

uint8_t command[length+1],
txLength = length+1;

command[0] = 0x40; // Co = 0, D/C = 1
memmove(&command[1], data, length);

i2cStart(&I2CD1, &i2cconfig);
i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, txLength, NULL, 0, MS2ST(10));
i2cStop(&I2CD1);
}

The point is, how to make this more memory optimized, so to append the 0x40 at the beginning. This would mess up the data array if we don't do a temp. copy to work on. All that is needed here, is a way to control the sending of an i2c start/stop condition with hte higl level i2c driver.

By now, that's the best and most simple way to do it.

Another thing is. I tried to implement native scrolling on top, I activated GDISP_NEED_SCROLL but the scrolling routine doesn't get called.

Keep it up.

cheers

Stefan

Link to comment
Share on other sites

Thanks for the testing.

I thought that might be the case with the board file.

Does that also apply to the spi interface?

I will see if I find another way of including the extra byte as it seems a shame to allocate a whole new buffer and copy the line particularly as stack seems to be a critical resource for most processors. I should have the fix up later today.

The GDISP_NEED_SCROLL turns on the gdispVerticalScroll() function - in other words the GDISP API for scrolling. GDISP_HARDWARE_SCROLL says that the controller supports hardware accelerated scrolling. Note with the new code that GDISP_NEED_SCROLL now works even if there is no hardware support for scrolling provided the low level driver provides a read pixel routine (and yours does).

With the new code, provided there is a read pixel routine (which there is for your driver) you don't need to specifically implement a scrolling routine any more as the high level code will do it for you.

If you want to define a hardware accelerated version you need to specify GDISP_HARDWARE_SCROLL in the gdisp_lld_config.h file for the driver. Note that most controllers only support full screen width scrolling which is not sufficient to support the GDISP vertical scroll method. I think I glanced at the datasheet for your controller and that was the case for the SSD1306 as well (although don't quote me on that - you know the controller much better than I) and if so then the only possible implementation is via the high level code using the read pixel routine.

This was actually why I added the vertical scroll handling to the higher level code - it is a common operation but few controllers seem to support a variable width vertical scroll natively.

Link to comment
Share on other sites

Hey,

I have not had the chance to test the fix. I have some weird hardware flaws since today in my testing system. I can't get it to work until Monday unfortunately.

Actually you're just right. It's mess with the temp. buffer, but sending (at least over I2C) in more packets takes more time to transmit the whole frame buffer. That's why I tried to send in as few packages as possible which has shown to be two transmissions each containing half of the frame buffer. I like thy slick way you took a chance on that problem though *thumbs up*

When I have the chance to view this with my saleae logic I'll post some performance infos about that topic.

Maybe it's the best tradeoff between performance and memory optimization anyways.

I'll keep you posted.

Cheers

Link to comment
Share on other sites

I took a quick look at the board_SSD1306_spi.h file of the current GDISPStreaming branch:


static const SPIConfig spi1config = {
NULL,
/* HW dependent part.*/
SSD1306_MISO_PORT,
SSD1306_MISO_PIN,
0
//SPI_CR1_BR_0
};

This is actually the CS, not the MISO port and pin.

I don't want to touch file in case of inmarket is still working on it - could you please fix this as well?

Also note that there's a missing (void)g; in the write_cmd() call.

~ Tectu

Link to comment
Share on other sites

  • 2 weeks later...

Hey everyone,

No after some days of bedtime due to a heavy cold, I was able to fix my hardware issue and test the memory-fixed SSD1306 driver, which worked like a charm (except scrolling was done in software).

Now I updated my local repository clone and found the old code within the driver. Could you, inmarket, please update the branch with the memory size oriented improvements you made? Then I was able to retest this and implement the scrolling again.

Cheers

Stefan

Link to comment
Share on other sites

The master branch is at a point where we have locked it off and stopped making any changes. We are about a week away from merging the GDISPStreaming branch into the master and releasing it as v2.0. The code in the GDISPStreaming branch is ready to go now - Tectu is just busy this week and he needs a bit of time to write release documents and update the website to match.

If you have any updates and changes relative to the GDISPStreaming branch I will be happy to incorporate them.

I am guessing from your previous post that there are a few changes to be made relative to the spi board file in the GDISPStreaming branch. Can you please post the updated SPI board file and I will integrate it immediately.

Note the location for the example board files has now changed. They are now stored in the boards/addons/gdisp directory if they are not specific to a particular base board.

Thanks for your work. It is very much appreciated.

Link to comment
Share on other sites

OH, now I know what happened. I had a SSD1306_board.h file left in the drivers source folder, where in the GDISPStreaming branch all those board files have been moved, as you just said. I guess I still had it in the editor and accidently saved in nack to the location where it originally was loaded from. There was some older code in it.

Now, in the boards/addons/gdisp folder the code is the latest, the version that I just tested as good.

So, after trying to compile the latest version in GDISPStreaming I get a compiler error in gdisp_lld_SSD1306.c, the board file is not found.

fatal error: board_SSD1306.h: No such file or directory

Also, I am a little stuck. How can I implement vertical scrolling again. Adding a gdisp_lld_vertical_scroll() doesn't change anything. Please, I need a quick advice on this topic.

EDIT: OK, I just had to add GDISP_HARDWARE_SCROLL TRUE to gdisp_lld_config.h to let uGFX run into that function. No I am getting that guy...

EDIT 2: Almost there, the only issue that I'm having is: it looks like scrolling only happens if the page is fully written, not after each line has been written, so that I could scroll one line up. That's how my scrolling routine is written ( i use g->p.y1 to grab y height which is to be scroll up).

EDIT3: OK, done. That was because I didn't call the flushing routine but just set the need_flush flag. So scrolling was done but the display would just have been updated after "a while"... No that's through.

Here's my working implementation (part of gdisp_lld_SSD1306.c) of scrolling for the v2.0 GDISPStreaming branch (Can't push a pull request since I don't have a bitbucket account)


#if GDISP_NEED_SCROLL
/**
* @brief Scroll vertically a section of the screen.
* @note Optional.
* @note If x,y + cx,cy is off the screen, the result is undefined.
* @note If lines is >= 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
*/
LLDSPEC void gdisp_lld_vertical_scroll(GDisplay *g) {

#ifdef GFX_USE_OS_CHIBIOS
int32_t thdPriority = (int32_t)chThdGetPriority();
chThdSetPriority(HIGHPRIO);
#endif

/* See datasheet table T10-1 for this*/
uint8_t fHeight = g->p.y1;
write_cmd2(g, SSD1306_SETDISPLAYOFFSET, fHeight-2);
write_cmd2(g, SSD1306_SETMULTIPLEX, (GDISP_SCREEN_HEIGHT - fHeight+1));

/* Scrolling animation.*/
for(int i=0; i write_cmd(g, SSD1306_SETSTARTLINE | i);
gfxSleepMilliseconds(20);
}

/* Shift buffer up a font line.*/
for (int i = 0; i < SSD1306_PAGE_WIDTH*(GDISP_SCREEN_HEIGHT/8-1); i++) {
if(i % SSD1306_PAGE_WIDTH){
RAM(g)[i] = RAM(g)[i+SSD1306_PAGE_WIDTH*(fHeight/8)] >> fHeight % 8;
RAM(g)[i] |= RAM(g)[i+SSD1306_PAGE_WIDTH*(fHeight/8 + 1)] << (8 - fHeight%8);
}
}

/* Clear second last page.*/
for(uint8_t i=0; i<2; i++)
memset( RAM(g) + (GDISP_SCREEN_HEIGHT/8 -1)*SSD1306_PAGE_WIDTH - GDISP_SCREEN_WIDTH, 0, GDISP_SCREEN_WIDTH);

#ifdef GFX_USE_OS_CHIBIOS
chThdSetPriority(thdPriority);
#endif

/* Update display.*/
gdisp_lld_flush(g);
}
#endif // GDISP_NEED_SCROLL

Link to comment
Share on other sites

OK, here we go.

I did change to SPI and made the SPI board working. It's blazing fast, but some scrolling artefacts are not affected by this improvements. I was hoping to get rid of those two flicks every time a line gets scrolled by doing faster communication.

Anyway, I hereby have the spi board file and the tweaked "main" file gdisp_lld_SSD1360.c

Maybe it's not yet to late to incorporate this in v2.0.

Cheers

Stefan

Can't upload files?!

I have therefor set up an repository on Github, which is my fav. GIT hoster. Please have a look at https://github.com/goeck/SSD1306/ .

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...