olikraus / u8g2

U8glib library for monochrome displays, version 2
Other
4.91k stars 1.02k forks source link

t6963 f Data Hold Time #2427

Open carlk3 opened 2 months ago

carlk3 commented 2 months ago

Thank you for providing an excellent library!

We're doing some extreme testing with a Newhaven NHD-240128WG-BTMI-VZ# LCD, using a 10' long shielded 20-conductor cable to connect the display. It worked well when we were using a 200 mm ribbon cable but we got dropped pixels with the 10' cable. (We're not really going to use a 10' cable in production but we are trying to build in some margin for robustness.) We found that the rise time of the E/WR signal was so long that the data lines were changing before getting latched with E/WR. We were able to get it to work well by adding a Data Hold delay in csrc/u8x8_byte.c:

--- a/csrc/u8x8_byte.c
+++ b/csrc/u8x8_byte.c
@@ -35,6 +35,7 @@
 */

 #include "u8x8.h"
+#include "micros.h"

 uint8_t u8x8_byte_SetDC(u8x8_t *u8x8, uint8_t dc)
 {
@@ -237,6 +238,8 @@ uint8_t u8x8_byte_8bit_8080mode(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void
        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);
        u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->write_pulse_width_ns);
        u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 1);
+
+       delay_us(1); // Data hold time
       }
       break;

This is what it looks like now: scope_82

Note: we are using a 3.3 V STM32 MCU, and to do the level shifting to the 5 V Newhaven we are using Open Collector GPIO outputs with 5 k Ω pull-ups. The data lines suffer the same extended rise times as E/WR so data transitions from 0 to 1 were OK, but transitions from 1 to 0 are driven low very quickly.

I don't like to modify your library, and I'm wondering if there is a better way to do this.

olikraus commented 2 months ago

modifing u8g2 should be fine. As an alternative, you could replace the byte procedure, like for example here: https://github.com/olikraus/u8g2/blob/master/sys/arduino/u8g2_page_buffer/I2CLCDBoard/I2CLCDBoard.ino

carlk3 commented 2 months ago

As an alternative, you could replace the byte procedure...

Thanks. That looks like a good method. That way I won't lose track of it in my source code control system.

olikraus commented 2 months ago

Not tested, but this should replicate the same default behavior. With this setup you could simply write your own optimized byte procedure.

U8G2 u8g2;              // create a plain U8g2 object

uint8_t u8x8_byte_8bit_8080mode(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  uint8_t i, b;
  uint8_t *data;

  switch(msg)
  {
    case U8X8_MSG_BYTE_SEND:
      data = (uint8_t *)arg_ptr;
      while( arg_int > 0 )
      {
    b = *data;
    data++;
    arg_int--;
    for( i = U8X8_MSG_GPIO_D0; i <= U8X8_MSG_GPIO_D7; i++ )
    {
      u8x8_gpio_call(u8x8, i, b&1);
      b >>= 1;
    }    

    u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->data_setup_time_ns);
    u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 0);
    u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->write_pulse_width_ns);
    u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 1);
      }
      break;

    case U8X8_MSG_BYTE_INIT:
      /* disable chipselect */
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);    
      /* ensure that the enable signal is high */
      u8x8_gpio_call(u8x8, U8X8_MSG_GPIO_E, 1);
      break;
    case U8X8_MSG_BYTE_SET_DC:
      u8x8_gpio_SetDC(u8x8, arg_int);
      break;
    case U8X8_MSG_BYTE_START_TRANSFER:
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level);  
      u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);
      break;
    case U8X8_MSG_BYTE_END_TRANSFER:
      u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);
      break;
    default:
      return 0;
  }
  return 1;
}

void setup(void) 
{
  u8g2_Setup_t6963_240x128_2(u8g2.getU8g2(), U8G2_R0, u8x8_byte_8bit_8080mode, u8x8_gpio_and_delay_arduino);
}
olikraus commented 2 months ago

Setup procedures are described here: https://github.com/olikraus/u8g2/wiki/u8g2setupc

olikraus commented 2 months ago

Inside Arduino Env you could use void u8x8_SetPin_8Bit_8080(u8x8_t *u8x8, 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 wr, uint8_t cs, uint8_t dc, uint8_t reset); to assign the pins.

carlk3 commented 2 months ago

Thanks. I implemented the private byte procedure and it works fine. I'm actually using the STM32CubeIDE, not Arduino.