olikraus / u8g2

U8glib library for monochrome displays, version 2
Other
5.1k stars 1.05k forks source link

U8G2 Library from Oliver Kraus using with PCD8544 (Nokia5110) connected to I2C Bus #528

Closed stevie72 closed 6 years ago

stevie72 commented 6 years ago

Hello Friends,

at first please let me say thank you to Oliver Kraus for the U8G2 lib and to maxint-rd for the PCD8544 -I2C Library. Both Great Libraries !

Thanks Guys - Great Work!

OK - I found this great topic on github: ... here

It´s about connecting a PCD8544 with the I2C Bus - using 4 Wires and a PCF8574 using VCC, GND, SDA and SCL.

I did a test of this Project and I was impressed with the results. Thanks to maxint-rd !

I thought by my self - What about of melting those two libs together and having both combined in one ? A PCD8544 Display with IC2 Interface with the great U8G2 Library.

EDIT: another great feature is on my Mind: using the MCP23017 .. that would offer the chance to add a short Keyboard to the Display.

What do you think about this?

Cheers, Stefan

olikraus commented 6 years ago

One approach might be to use the C API and the SW SPI init function together with a custom GPIO and Delay callback as described here: https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform

The SW SPI interface will call the GPIO and Delay callback with individual signals. These signals are then translated in the custom callback into suitable I2C commands for the target I2C GPIO expander.

The init function is mentioned here: https://github.com/olikraus/u8g2/wiki/u8g2setupc#pcf8812-96x65

u8g2_t u8g2;
u8g2_Setup_pcf8812_96x65_1(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, my_gpio_and_delay_cb)

So only the my_gpio_and_delay_cb has to be implemented according to the template on the wiki page.

stevie72 commented 6 years ago

Sorry, I have a problem, I'm stuck ...

I´ve tried to write a *.ino File with the Information of the I2C Expander...

Here my first buggy results:


/*********************************************************************
  This is an example sketch for our Monochrome Nokia 5110 LCD Displays

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/338

  These displays use SPI to communicate, 4 or 5 pins are required to
  interface

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada  for Adafruit Industries.
  BSD license, check license.txt for more information
  All text above, and the splash screen must be included in any redistribution

  Example sketch adapted by Maxint R&D to demonstrate I2C driven display
  https://github.com/maxint-rd/I2C-PCF8574-PCD8544-Nokia-5110-LCD
*********************************************************************/

#include <PCF8574_PCD8544.h>     // Those Libs are using Adafruit_GFX

#include <U8g2lib.h>             // We want to use U8G2 
#include <U8x8lib.h>                

// I2C to SPI via PCF8574 interface (slower updates, less pins):
// Address (LCD Interface: 0x20-0x27 selectable by connecting pads, all open=0x27)
// pcf-P7 - 5. Serial clock out (SCLK, CLK)
// pcf-P6 - 4. Serial data out (DIN / MISO)
// pcf-P5 - 3. Data/Command select (D/C, DC)
// pcf-P4 - 2. LCD chip select (CS, CE), can be set to -1 if not used (tie line low)
// pcf-P2 - 1. LCD reset (RST), can be set to -1 if not used (tie line high or to reset of MCU)
// pcf-P3 - 7. Backlight control (LIGHT), not used in i2c display constructor

// PCF8574_PCD8544 display = PCF8574_PCD8544(0x20, 7, 6, 5, 4, 2);       // Adress 0x20  = A0,A1,A2 = GND

// *******************************************************************************************************************************************************

// Callback Funtion     somewhere her integrate the PCF8574_PCD8544

#include "u8x8.h"

static const uint8_t u8x8_d_pcd8544_84x48_init_seq[] = {

  U8X8_START_TRANSFER(),               /* enable chip, delay is part of the transfer start */

  U8X8_C(0x021),                  /* activate chip (PD=0), horizontal increment (V=0), enter extended command set (H=1) */
  U8X8_C(0x006),                    /* temp. control: b10 = 2  */
  U8X8_C(0x013),                    /* bias system 1:48 */
  U8X8_C(0x0c0),                    /* medium Vop  */

  U8X8_C(0x020),                    /* activate chip (PD=0), horizontal increment (V=0), enter normal command set (H=0) */
  U8X8_C(0x008),        /* blank */
  U8X8_C(0x024),                    /* power down (PD=1), horizontal increment (V=0), enter normal command set (H=0) */

  U8X8_END_TRANSFER(),              /* disable chip */
  U8X8_END()                  /* end of sequence */
};

static const uint8_t u8x8_d_pcd8544_84x48_powersave0_seq[] = {
  U8X8_START_TRANSFER(),              /* enable chip, delay is part of the transfer start */
  U8X8_C(0x020),                    /* activate chip (PD=0), horizontal increment (V=0), enter normal command set (H=0) */
  U8X8_C(0x00c),        /* display on */
  U8X8_END_TRANSFER(),              /* disable chip */
  U8X8_END()                  /* end of sequence */
};

static const uint8_t u8x8_d_pcd8544_84x48_powersave1_seq[] = {
  U8X8_START_TRANSFER(),              /* enable chip, delay is part of the transfer start */
  U8X8_C(0x020),                    /* activate chip (PD=0), horizontal increment (V=0), enter normal command set (H=0) */
  U8X8_C(0x008),        /* blank */
  U8X8_C(0x024),                    /* power down (PD=1), horizontal increment (V=0), enter normal command set (H=0) */
  U8X8_END_TRANSFER(),              /* disable chip */
  U8X8_END()                  /* end of sequence */
};

static const u8x8_display_info_t u8x8_pcd8544_84x48_display_info =
{
  /* chip_enable_level = */ 0,
  /* chip_disable_level = */ 1,

  /* post_chip_enable_wait_ns = */ 5,
  /* pre_chip_disable_wait_ns = */ 5,
  /* reset_pulse_width_ms = */ 2, 
  /* post_reset_wait_ms = */ 2, 
  /* sda_setup_time_ns = */ 12,   
  /* sck_pulse_width_ns = */ 75,  /* half of cycle time (100ns according to datasheet), AVR: below 70: 8 MHz, >= 70 --> 4MHz clock */
  /* sck_clock_hz = */ 4000000UL, /* since Arduino 1.6.0, the SPI bus speed in Hz. Should be  1000000000/sck_pulse_width_ns */
  /* spi_mode = */ 0,   /* active high, rising edge */
  /* i2c_bus_clock_100kHz = */ 4,
  /* data_setup_time_ns = */ 30,
  /* write_pulse_width_ns = */ 40,
  /* tile_width = */ 11,    /* width of 11*8=88 pixel */
  /* tile_hight = */ 6,
  /* default_x_offset = */ 0,
  /* flipmode_x_offset = */ 0,
  /* pixel_width = */ 84,
  /* pixel_height = */ 48
};

uint8_t u8x8_d_pcd8544_84x48(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  uint8_t x, c;
  uint8_t *ptr;
  switch(msg)
  {
    case U8X8_MSG_DISPLAY_SETUP_MEMORY:
      u8x8_d_helper_display_setup_memory(u8x8, &u8x8_pcd8544_84x48_display_info);
      break;
    case U8X8_MSG_DISPLAY_INIT:
      u8x8_d_helper_display_init(u8x8);
      u8x8_cad_SendSequence(u8x8, u8x8_d_pcd8544_84x48_init_seq);
      break;
    case U8X8_MSG_DISPLAY_SET_POWER_SAVE:
      if ( arg_int == 0 )
  u8x8_cad_SendSequence(u8x8, u8x8_d_pcd8544_84x48_powersave0_seq);
      else
  u8x8_cad_SendSequence(u8x8, u8x8_d_pcd8544_84x48_powersave1_seq);
      break;
    // case U8X8_MSG_DISPLAY_SET_FLIP_MODE:
    //    break;  NOT SUPPORTED

#ifdef U8X8_WITH_SET_CONTRAST
    case U8X8_MSG_DISPLAY_SET_CONTRAST:
      u8x8_cad_StartTransfer(u8x8);
      u8x8_cad_SendCmd(u8x8, 0x021 ); /* command mode, extended function set */
      u8x8_cad_SendCmd(u8x8, 0x080 | (arg_int >> 1) );
      u8x8_cad_EndTransfer(u8x8);
      break;
#endif
    case U8X8_MSG_DISPLAY_DRAW_TILE:
      u8x8_cad_StartTransfer(u8x8);

      x = ((u8x8_tile_t *)arg_ptr)->x_pos;
      x *= 8;
      x += u8x8->x_offset;
      u8x8_cad_SendCmd(u8x8, 0x020 ); /* activate chip (PD=0), horizontal increment (V=0), enter normal command set (H=0) */
      u8x8_cad_SendCmd(u8x8, 0x080 | (x) ); /* set X address */
      u8x8_cad_SendCmd(u8x8, 0x040 | (((u8x8_tile_t *)arg_ptr)->y_pos) ); /* set Y address */

      ptr = ((u8x8_tile_t *)arg_ptr)->tile_ptr;
      c = ((u8x8_tile_t *)arg_ptr)->cnt;
      c *= 8; 
      do
      {
  if ( c + x > 84u )
  {
    if ( x >= 84u )
      break;
    c = 84u;
    c -= x;
  }
  u8x8_cad_SendData(u8x8, c, ptr);  /* note: SendData can not handle more than 255 bytes */
  x += c;
  arg_int--;
      } while( arg_int > 0 );

      u8x8_cad_EndTransfer(u8x8);
      break;
    default:
      return 0;
  }
  return 1;
}

// *******************************************************************************************************************************************************

u8g2_t u8g2;   // a structure which will contain all the data for one display

u8g2_Setup_pcf8544_48x48_1(&u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, my_gpio_and_delay_PCF8574_PCD8544), u8g2_SetPowerSave(&u8g2, 0);       //init u8g2 and wake up display 

// *******************************************************************************************************************************************************

class U8G2_PCF8574_PCD8544_84X48_F_4W_HW_SPI : public U8G2 {
  public: U8G2_PCD8544_84X48_F_4W_HW_SPI(const u8g2_cb_t *rotation, uint8_t cs, uint8_t dc, uint8_t reset = U8X8_PIN_NONE) : U8G2() {
      u8g2_Setup_pcd8544_84x48_f(&u8g2, rotation, u8x8_byte_arduino_hw_spi, U8G2_PCF8574_PCD8544_I2C);

    }
};

// *******************************************************************************************************************************************************

void setup()
{
  //pinMode(2, OUTPUT);         // schaltet LED als Ausgang
  // For ESP-01 board: Disable Serial.begin and call display.begin specifying GPIO 1 and 3.

  display.begin();           // regular begin() using default settings and high speeed (1MHz on ESP8266, 400kHz on others)

  display.setContrast(60);
  display.clearDisplay();   // clears the screen and buffer
  display.display(); // show splashscreen

  // draw the first ~12 characters in the font
  testdrawchar();
  display.display();

  // *********************************************************************************************************************************

  void loop() {
    // Nothing to do here...
  }

  // *********************************************************************************************************************************

  void testdrawchar(void) {               // schreibt das Display voll mit Zeichen / Buchstaben
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0, 0);

    for (uint8_t i = 0; i < 168; i++) {
      if (i == '\n') continue;
      //display.writeChar(i);
      display.write(i);
      //if ((i > 0) && (i % 14 == 0))
      //display.println();
    }
    display.display();
  }
olikraus commented 6 years ago

Oh.. No No, you really only have to write the gpio can... It is much simpler then your attempt. I am still busy with other things. Hope I find time for this later...

olikraus commented 6 years ago

Ok, then, everything after the following code should be correct:

class U8G2_PCF8574_PCD8544_84X48_F_4W_HW_SPI : public U8G2 {
  public: U8G2_PCD8544_84X48_F_4W_HW_SPI(const u8g2_cb_t *rotation, uint8_t cs, uint8_t dc, uint8_t reset = U8X8_PIN_NONE) : U8G2() {
      u8g2_Setup_pcd8544_84x48_f(&u8g2, rotation, u8x8_byte_arduino_hw_spi, U8G2_PCF8574_PCD8544_I2C);
    }
};

However, you probably do not need any arguments and you want to use SW SPI:

class U8G2_PCF8574_PCD8544_84X48_F : public U8G2 {
  public: U8G2_PCF8574_PCD8544_84X48_F(const u8g2_cb_t *rotation) : U8G2() {
      u8g2_Setup_pcd8544_84x48_f(&u8g2, rotation, u8x8_byte_arduino_sw_spi, my_gpio_cb);
    }
};

Now, you need to define my_gpio_cb. I take the template code from the wiki page and already removed some of the not required messages:

uint8_t my_gpio_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  switch(msg)
  {
    case U8X8_MSG_GPIO_AND_DELAY_INIT:  
      break;                            // Setup your port expander
    case U8X8_MSG_DELAY_NANO:           // not required
      break;    
    case U8X8_MSG_DELAY_100NANO:        // delay arg_int * 100 nano seconds
      break;
    case U8X8_MSG_DELAY_10MICRO:        // delay arg_int * 10 micro seconds
      break;
    case U8X8_MSG_DELAY_MILLI:          // delay arg_int * 1 milli second
      break;
    case U8X8_MSG_GPIO_D0:              // D0 or SPI clock pin: Output level in arg_int
    //case U8X8_MSG_GPIO_SPI_CLOCK:
      // set clock line of the gpio expander to level given in arg_int
      break;
    case U8X8_MSG_GPIO_D1:              // D1 or SPI data pin: Output level in arg_int
    //case U8X8_MSG_GPIO_SPI_DATA:
      // set data line of the gpio expander to level given in arg_int
      break;
    case U8X8_MSG_GPIO_CS:              // CS (chip select) pin: Output level in arg_int
      // set cs line of the gpio expander to level given in arg_int
      break;
    case U8X8_MSG_GPIO_DC:              // DC (data/cmd, A0, register select) pin: Output level in arg_int
      // set dc line of the gpio expander to level given in arg_int
      break;
    case U8X8_MSG_GPIO_RESET:           // Reset pin: Output level in arg_int
      // set reset line of the gpio expander to level given in arg_int
      break;
    default:
      u8x8_SetGPIOResult(u8x8, 1);          // default return value
      break;
  }
  return 1;
}

Hope you are able to set the levels of the i/o expander. We can later think on how to optimize this, but as of now, best is just to set the levels directly as mentioned in the comments above.

olikraus commented 6 years ago

no further feedback, closing...