olikraus / u8g2

U8glib library for monochrome displays, version 2
Other
5.21k stars 1.06k forks source link

u8g2_SetupDisplay #196

Closed freedaun closed 7 years ago

freedaun commented 7 years ago

I am integrating u8g2 into an STM8 project, and use the C codebase.

The compiler trips up on u8g2_Setup_ssd1306_i2c_128x64_noname_1(). Within it (any many other similar functions) is a call to u8g2_SetupDisplay(). But strangely no such function can be found in the (single) u8g2 folder, nor a line explaining that it should be defined. Google could not find a definition either. From the last few search results I deduced that it should be a #define. A define of what? That led me to https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform, and that seems like a lot of complexity just to get a buffer transferred to the display.

So the question is: why doesn't u8g2 use an API of a few functions (or #defines) to send data to the screen and be done with it? Like http://www.embeddedlightning.com/ugui/ does? Is this some sort of Arduino tax?

olikraus commented 7 years ago

The comparison with ugui is not fair. ugui does not support a single display out of the box. Instead it leaves the complexity of talking to display, of mapping the buffer to the target display, of doing the proper communication and of doing the display setup to the user. Each single display usually comes with 50 to 100 pages of documentation. A TFT easily has 100 to 150 pages. Do you really think it is that easy to transfer data to a display? Maybe you have read section 2.3 of the ugui user manual and you feel that this is simple. But believe me, setting up a TFT display, doing the communication to it, is a huge work and there no information at all in the ugui user manual. Instead this all is done by u8g2 for you. Ugui claims to support ePaper and others, but i did not find a single code for any of these displays. Whereas i can say, that u8g2 at least supports some ePaper devices. Each one did cost me several weeks for making it work.

Maybe you also think that https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform looks complicated and huge. Well, it just became complete and detailed (well it could be still improved). In the case of driving a I2C display, you just need to implement the gpio&delay callback and handle three (maybe four) messages. Thats it...

Anyhow, i am happy to support here and you are right, u8g2_SetupDisplay is a function-like makro which is defined in u8g2.h. So if you include u8g2.h everything should be fine.

Maybe you let me know your target display, and i will try to guide you though the porting process, which is not complicated at all.

freedaun commented 7 years ago

Hi Oli(ver?). I didn't expect this kind of involvement, that's fantastic. Give me some time to get back on your (welcome) update.

Your lib is the only lib out there specifically for monochrome, it is therefore preferred.

My display initially is the SSD1306 I2C (could also prove to be a SH1106). The final LCD probably has a IST3615 SPI interface. I tried to provide a link, but I couldn't find a single reference.

I hope the driver will be straightforward, as the datasheet says "It provides a high-flexible display section due to 1-to-1 correspondence between on-chip display data RAM bits and LCD panel pixels". I know, datasheets can be hell. Code would be much clearer.

olikraus commented 7 years ago

Maybe you mean IST3613 (http://www.ist4u.com/web/goods/goods.jsp#a123)? So there will be two tasks (1) port to STM8 and (2) getting the IST361x based display to work. ... sounds interessting.

I do not know the datasheet of your display, but if it belongs to the "ST7565" type of displays, then the porting will be simple: https://github.com/olikraus/u8g2/blob/master/doc/controller_cmds.txt

Oliver

freedaun commented 7 years ago

I hope the display will be similar. I think it's a relatively new design ("65 x 132 STN LCD Driver. Preliminary. Effective Date: 02/03/2016"), for an FSTN display. But for now, to kickstart development, it's the usual OLED.

I still don't understand the justification for using u8x8_gpio_and_delay_template(), as it amounts to a set of externs (ex: extern u8g2_delay_ns(int ns)) which are dispatched from a single function and a common interface. A call would be direct, from u8g2 to the extern and back, with only the required parms.

There is no need to go through the concept of messages by default (the user could still add this functionality himself) and really, the compiler would signal "missing messages" if they were calls.

Messages like U8X8_MSG_GPIO_D6 raise question marks. What is the purpose of setting D6? Why not set all of the byte at once? Who uses D6 anyway?? Indeed in the example that follows you omit its definition, it is not needed for I2C. But a new user like me does not know when to omit a message, and its inclusion in the interface raises questions.

What I'd like to see is to say that I'll be using I2C, and then have the compiler (or linker), in response, accuse the missing functions, needed to fully implement the I2C interface. For example the function to set SDA. But I'd still prefer a function to send a full (bit banged or otherwise) byte over configuring u8g2 to have it manipulate pins on my behalf. Sincerely, I would think this pin-manipulation is none of u8g2's business, though default implementations would sure help.

None of the points I raise detract from the merit of having this code, these points are after all only peripheral issues. With a little more understanding I could easily customize the code and be done with it.

Thanks for the improvements!

olikraus commented 7 years ago

I think there are two questions:

  1. Why is the interface like this?
  2. What is required to make it work?

Most people ask the second question, and the wiki page tries to address the second question. But sure, some people also ask the first question.

I do not know whether I really should try to justify the interface and for sure, it could be implemented in a different way, but fine, i will try to justify my code:

Let me start with a very simple point: U8g2 has to support displays with SPI, I2C and parallel interfaces.

Now there is one user who wants to support a SPI display in a very small 8 bit uC. Does he want the I2C and parallel code inside? Probably not. It would be a waste of Flash-ROM resources.

What I'd like to see is to say that I'll be using I2C, and then have the compiler (or linker), in response, accuse the missing functions, needed to fully implement the I2C interface.

Sounds good, but how can the linker know this? Your SSD1306 OLED supports all three modes and as a consequence the linker will request all three interfaces (SPI, I2C and paralllel). Your linker does NOT know, that you have ordered an OLED with I2C only (in fact it has all three interfaces, but only I2C is available at the PCB).

Of course i could throw away the SPI and parallel code from u8g2. Then you would be happy and a lot of other people would complain. So i guess this is not a solution.

As a conclusion: You have to define your target interface, by handling the interface, which is required in your case. So you handle I2C, but you will ignore SPI and parallel messages.

For the software it means this: How can i write an interface which allows optional code. There are basically two concepts in C:

One more concept is introduced in C++: Inheritance

I have used a combination of function pointers and messages to allow a flexible implementation of the desired communication.

Messages like U8X8_MSG_GPIO_D6 raise question marks.

It is part of the parallel interface implementation.

Why not set all of the byte at once?

Because there is no generic way to set a byte.

Who uses D6 anyway?

All users with a parallel interface to their display.

But I'd still prefer a function to send a full (bit banged or otherwise) byte over configuring u8g2 to have it manipulate pins on my behalf.

Well, this is not exactly what you want, but maybe a starting point and you can at least see something on your display:

uint8_t u8x8_gpio_and_delay_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  switch(msg)
  {
    case U8X8_MSG_GPIO_AND_DELAY_INIT:
      /* setup I2C pins */
      break;
    case U8X8_MSG_DELAY_MILLI:
       /* delay by arg_int milli seconds */
      break;
    case U8X8_MSG_DELAY_I2C:
      /* delay by 5 us when arg_int is 1, or 1.25us otherwise */
      break;
    case U8X8_MSG_GPIO_I2C_CLOCK:
      /* output 0 if arg_int is 0, make the pin input if arg_int is 1 */
      break;
    case U8X8_MSG_GPIO_I2C_DATA:
      /* output 0 if arg_int is 0, make the pin input if arg_int is 1 */
      break;
    default:
      break;
  }
  return 1;
}

and this init code:

 u8g2_Setup_ssd1306_i2c_128x64_noname_1(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay_i2c);
 u8g2_InitDisplay(&u8g2);
 u8g2_SetPowerSave(&u8g2, 0);

If you want to sent a byte directly, then the gpio/delay function will be even simpler:

uint8_t u8x8_gpio_and_delay_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  switch(msg)
  {
    case U8X8_MSG_GPIO_AND_DELAY_INIT:
      /* setup I2C pins */
      break;
    case U8X8_MSG_DELAY_MILLI:
       /* delay by arg_int milli seconds */
      break;
    default:
      break;
  }
  return 1;
}

However, you need your own byte transfer function: my_i2c_byte_transfer. A template is given here: https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform

After this, your setup must include your new byte transfer function:

 u8g2_Setup_ssd1306_i2c_128x64_noname_1(&u8g2, U8G2_R0, my_i2c_byte_transfer, u8x8_gpio_and_delay_i2c);
 u8g2_InitDisplay(&u8g2);
 u8g2_SetPowerSave(&u8g2, 0);

U8g2 has a hardware abstraction layer, which supports:

All these interfaces can be implemented in two ways:

Of course you can still ask: I do not need this, so why is this there? But the answer is simple: It is needed by others.

freedaun commented 7 years ago

Ok, thank you. No more questions from me ;)

olikraus commented 7 years ago

oh.. i still was about/fix to update my previous post...

so... i leave this issue open for some more time?

freedaun commented 7 years ago

the byte transfer is probably the way to go for me. thanks once again.