Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New display procedure for graphic screens (non 8x8) [SED1520/SBN1661] #324

Closed
RobbertHob opened this issue Aug 1, 2017 · 12 comments
Closed
Milestone

Comments

@RobbertHob
Copy link

Is there an example of how to implement a new non 8x8 display. Or is this the same as the example for an 8x8 display, as shown on the wiki? New Display Procedure

@olikraus
Copy link
Owner

olikraus commented Aug 1, 2017

All displays are based on the 8x8 code.
Which controller? Which display? Any datasheet?

@RobbertHob
Copy link
Author

Thanks for your quick response.

It seems to be an SED1520-equivalent with 2 enable lines (see Similar datasheet) Since there are not enough pins on the ESP-12 to directly support the 6800-bus, I'm forced to use a 16-bit I/O expander (currently a MCP23017). .

My understanding of your library is, that a display that is used only for text, starts with U8x8, and do not need to use a full buffer, while display that is used for graphics, starts with U8g2, and requires a full buffer.
The example in the wiki shows for example "U8X8_MSG_DISPLAY_DRAW_TILE" is this also used for U8g2 displays?

@olikraus
Copy link
Owner

olikraus commented Aug 1, 2017

From outside it seems that there are two libraries (u8x8 and u8g2). However it is more like this: U8g2 is based on u8x8, I mean u8g2 extends u8x8 by added buffer procedures and high level graphics procedures.
U8g2 only talks to u8x8, while u8x8 talks to your display.

For the SED1520: I have not implemented this, because it requires a complete new interface (because of the two enable lines). So the effort would be more than just adding a new display.
Well. maybe i could reuse the KS0108 interface.

What i will not do, is supporting an additional I/O expander. However you can find some hints in the Arduino Forum on this. I think someone did this for a KS0108 display.
Question: Do you need the library to be useable with your display inside the Arduino IDE?

All in all, adding support for SED1520 will be a good amount of effort for both of us. Are you really sure, that you want to use a SED1520 display? For example ST7920 displays are also very cheap and support SPI interface (3 I/O lines).

@RudyFiero
Copy link

If the SED1520 must be used then maybe it might be easier to use a library that already supports it. Possibly the one at http://en.radzio.dxp.pl/sed1520/

@RobbertHob
Copy link
Author

Aha, it is slowly getting clearer for me now. But maybe it looks too simple for me now. Do I need, besides a proper setup/initialization, more than a reimplementation for U8X8_MSG_DISPLAY_DRAW_TILE? Which can be expected to be more complicated due to the 2 enable lines, and the I/O-expander.

(@RudyFiero) Wouldn't it be great if one could use an already familiar library, with a lot of features, for all his displays?

@zerog2k
Copy link

zerog2k commented Aug 2, 2017

sadly, I have also been researching this. I have a device which i have reverse engineered and attempting to repurpose, which has 240x160 screen, using ST7586S controller.
This controller is 2-bit greyscale, but can also do mono. It has built in frame-buffer (ddram) interface, however the biggest problem is that it has the oddest pixel format I have ever seen.
It packs 1- (mono) or 2- (4-level greyscale) bits for 3 pixels into one byte. This makes any hope of alignment on something nice like 8x8 tiles really impossible, unless I'm missing something. I didn't notice any drivers which are doing something like read-modify-write on controller's framebuffer - maybe this is an option.
I'm trying to understand if I can bypass implementation of 8x8 (U8X8_MSG_DISPLAY_DRAW_TILE handling), and go straight to some single-framebuffer (as I only have 16k ram to work with on this mcu, nrf51822)

@olikraus
Copy link
Owner

olikraus commented Aug 2, 2017

Library Support
Also my old library (u8glib) did support SED1520. However it did not support any port expander and it is even more complicated to support any port expander with the old u8glib.

U8g2 Software Architecture
The software architecture for u8g2 is more clean and powerful compared to u8glib. It would allow port expander support.

U8g2 software abstraction layers are:

  • U8g2 interface, which calls u8x8 interface (using U8X8_MSG_DISPLAY_DRAW_TILE)
  • U8x8 interface, which calls the display/controller procedure
  • Display/controller procedure, which calls the byte level procedures (actually there is another layer in between, called "cad")
  • Byte level procedures, which call the I/O procedures.
  • I/O procedures, which call the uC specific I/O procedures (like Arduino digitalWrite)

Any of these layers can be implemented/exchanged almost independently.

So the problem of supporting the SED1520 with port expanders can be split into two problems:

  1. Write a new display/controller procedure
  2. Write a new I/O procedure which programs the port expander

The u8g2 software architecture (starting with the u8x8 layer) is documented here:
https://github.com/olikraus/u8g2/blob/master/doc/u8g2_software_architecture.odg

Port Expander
All you need is to write a new "GPIO and Delay" procedure. This is explained in more detail in the u8g2 porting guide (because the this task is always needed when porting u8g2 to a none-Arduino platform)

https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform

I also gave some advice on port expanders here #256 : The suggestion is to just extend the extisting Arduino GPIO and Delay procedure by adding support to a port expander by modifing the Arduino Code.

SED1520
Support for the SED1520 is not a big problem. Code for the SED1520 is just not yet
ported to u8g2 from u8glib. For the u8g2 support we need a new display/controller software layer to support the SED1520. Indeed this would mean, that the U8X8_MSG_DISPLAY_DRAW_TILE message is handled by the new SED1520 device (i usually call this kind of software layer a "device").

Odd controller dram pixel patterns
I would say, this is a typical problem.
Example 1: The LC7981 has a reverted bit order in the internal RAM (compared to most other displays). So the LC7981 will use the existing code, bit revert the bit order:
https://github.com/olikraus/u8g2/blob/master/csrc/u8x8_d_lc7981.c#L126

Example 2: The UC1610 does not include a monochrome mode, so u8g2 has to convert the b/w pattern into a graylevel pixmap. This is done here:
https://github.com/olikraus/u8g2/blob/master/csrc/u8x8_d_uc1610.c#L190

@zerog2k If you want more discussions on the ST7586, better create a new issue for this.

@olikraus olikraus changed the title New display procedure for graphic screens (non 8x8) New display procedure for graphic screens (non 8x8) [SED1520] Aug 4, 2017
@olikraus
Copy link
Owner

olikraus commented Aug 4, 2017

#144

@RobbertHob
Copy link
Author

I've managed to get my screen working! I took one of ks0108 displays as a template, adjusted the parameters (size, and tiles), and add the code to write a complete line inside 8X8_MSG_DISPLAY_DRAW_TILE. (See code)

While writing this, I slowly understood more of the original code, and how ingenious the original code for the ks0108 was setup. So that probably answers the original question for me.

Things that could be done to make the code nicer.

  • Adjust buffer to correct size.
  • Intern all initialization
  • Use u8g2 i2c abstraction
  • Abstract away the I/O expander (I think this could hurt the performance)
  • Use clever tiled approach of the original ks0108 code, not just write a full buffer

Somehow I cannot attach the code as a file, so here it is:

#ifndef MY_LCD_h
#define MY_LCD_h


#include <U8g2lib.h>

static const u8x8_display_info_t u8x8_MyDisplay_display_info =
{
  /* chip_enable_level = */ 0,    /* KS0108: Not used */
  /* chip_disable_level = */ 1,   /* KS0108: Not used */
  
  /* post_chip_enable_wait_ns = */ 100,
  /* pre_chip_disable_wait_ns = */ 20,
  /* reset_pulse_width_ms = */ 1, 
  /* post_reset_wait_ms = */ 6,     /* could be faster for the KS0108 */
  /* sda_setup_time_ns = */ 12,   
  /* sck_pulse_width_ns = */ 75,  /* KS0108: Not used */
  /* sck_clock_hz = */ 4000000UL, /* KS0108: Not used */
  /* spi_mode = */ 0,       /* active high, rising edge */
  /* i2c_bus_clock_100kHz = */ 4, /* KS0108: Not used */
  /* data_setup_time_ns = */ 200,
  /* write_pulse_width_ns = */ 200, /* KS0108: actially 450 ns */
  /* tile_width = */ 16,    /* width of 16*8=128 pixel */
  /* tile_hight = */ 4,
  /* default_x_offset = */ 0,
  /* flipmode_x_offset = */ 0,
  /* pixel_width = */ 122,
  /* pixel_height = */ 32
};

struct u8x8_MyDisplay_vars
{
  uint8_t *ptr;
  uint8_t x;
  uint8_t c;
  uint8_t arg_int;
};
/* ... */
//uint8_t u8x8_d_MyDisplay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
/* east rising (buydisplay.com) ERM19264 */
/* left: 011, middle: 101, right: 110, no chip select: 111 */
uint8_t u8x8_d_MyDisplay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
  struct u8x8_MyDisplay_vars v;
  int ii = 0;
  switch(msg)
  {
    case U8X8_MSG_DISPLAY_SETUP_MEMORY:
      u8x8_d_helper_display_setup_memory(u8x8, &u8x8_MyDisplay_display_info);
      break;
    case U8X8_MSG_DISPLAY_INIT:
      u8x8_d_helper_display_init(u8x8);
    
//      u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 3, NULL);
//      u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_init_seq);
//      u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
//    
//      u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 5, NULL);
//      u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_init_seq);
//      u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
//    
//      u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 6, NULL);
//      u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_init_seq);
//      u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
      break;
    case U8X8_MSG_DISPLAY_SET_POWER_SAVE:
      
      if ( arg_int == 0 )
      {
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 3, NULL);
//  u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_powersave0_seq);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
//
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 5, NULL);
//  u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_powersave0_seq);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
//
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 6, NULL);
//  u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_powersave0_seq);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
//  
      }
      else
      {
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 3, NULL);
//  u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_powersave1_seq);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
//
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 5, NULL);
//  u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_powersave1_seq);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
//
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 6, NULL);
//  u8x8_cad_SendSequence(u8x8, u8x8_d_ks0108_powersave1_seq);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
  
      }
      break;
// The KS0108 can not mirror the cols and rows, use U8g2 for rotation
//    case U8X8_MSG_DISPLAY_SET_FLIP_MODE:
//      break;
// The KS0108 has no internal contrast command
//    case U8X8_MSG_DISPLAY_SET_CONTRAST:
//      break;
    case U8X8_MSG_DISPLAY_DRAW_TILE:

      v.ptr = ((u8x8_tile_t *)arg_ptr)->tile_ptr;
      v.x = ((u8x8_tile_t *)arg_ptr)->x_pos;
      v.c = ((u8x8_tile_t *)arg_ptr)->cnt;
      v.arg_int = arg_int;
      /* To see what changes here */
      Serial.print((int)v.ptr);
      Serial.print(" - ");
      Serial.print(v.x);
      Serial.print(" - ");
      Serial.print(v.c); 
      Serial.print(" - ");
      Serial.print(v.arg_int);
      Serial.print(" - ");
      Serial.print(((u8x8_tile_t *)arg_ptr)->y_pos);
      Serial.println(" - ;");
      
      /*  Write a full row here 
       *  v.x is assumed 0
       *  
      */
          WriteCommand(0xB8 | ((u8x8_tile_t *)arg_ptr)->y_pos);
          WriteCommand(0x00 | v.x);
          for (int Row=0;Row < 62;Row++) {
            Write(*(v.ptr++));
          }
          WriteCommandA(0xB8 | ((u8x8_tile_t *)arg_ptr)->y_pos);
          WriteCommandA(0x00);
          for (int Row=0;Row < 62;Row++) {
            WriteA(*(v.ptr++));
          }

      
      if ( v.x < 8 )
      {
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 3, NULL);
//  u8x8_ks0108_out(u8x8, &v, arg_ptr);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
      }
      if ( v.x < 16 )
      {
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 5, NULL);
//  u8x8_ks0108_out(u8x8, &v, arg_ptr);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
      }
      if ( v.x < 24 )
      {
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_START_TRANSFER, 6, NULL);
//  u8x8_ks0108_out(u8x8, &v, arg_ptr);
//  u8x8->cad_cb(u8x8, U8X8_MSG_CAD_END_TRANSFER, 7, NULL);
      }    
      break;
    default:
      return 0;
  }
  return 1;
}


uint8_t *u8g2_m_MyDisplay_24_f(uint8_t *page_cnt)
{
  static uint8_t buf[1536];
  *page_cnt = 8;
  return buf;
}


//void u8g2_Setup_MyDisplay_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb);
/* ks0108 f */
void u8g2_Setup_MyDisplay_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
{
  uint8_t tile_buf_height;
  uint8_t *buf;
  u8g2_SetupDisplay(u8g2, u8x8_d_MyDisplay, u8x8_cad_001, byte_cb, gpio_and_delay_cb);
  buf = u8g2_m_MyDisplay_24_f(&tile_buf_height);
  u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}

class U8G2_MYDISPLAY_F : public U8G2 {
  public: U8G2_MYDISPLAY_F(const u8g2_cb_t *rotation) : U8G2() {
//  public: U8G2_MYDISPLAY_F(const u8g2_cb_t *rotation, uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7, uint8_t enable, uint8_t dc, uint8_t cs0, uint8_t cs1, uint8_t cs2, uint8_t reset = U8X8_PIN_NONE) : U8G2() {
    u8g2_Setup_MyDisplay_f(&u8g2, rotation, u8x8_byte_arduino_ks0108, u8x8_gpio_and_delay_arduino);
    //u8x8_SetPin_MyDisplay(getU8x8(), d0, d1, d2, d3, d4, d5, d6, d7, enable, dc, cs0, cs1, cs2, reset);
  }
};

#endif

@olikraus
Copy link
Owner

olikraus commented Aug 4, 2017

Thanks for. I will try to add support for this display with the next release. Your code will definitely help.

@olikraus olikraus changed the title New display procedure for graphic screens (non 8x8) [SED1520] New display procedure for graphic screens (non 8x8) [SED1520/SDN1661] Aug 5, 2017
@olikraus
Copy link
Owner

olikraus commented Aug 9, 2017

KS0108 can not be used --> new com procedure created
--> codebuild pending.

@olikraus olikraus changed the title New display procedure for graphic screens (non 8x8) [SED1520/SDN1661] New display procedure for graphic screens (non 8x8) [SED1520/SBN1661] Aug 11, 2017
olikraus added a commit that referenced this issue Aug 11, 2017
@olikraus
Copy link
Owner

implemented...

Beta release: https://github.com/olikraus/U8g2_Arduino/archive/master.zip

@olikraus olikraus added this to the 2.16 milestone Aug 11, 2017
@olikraus olikraus mentioned this issue Aug 11, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants