U8g2: ST7920 on Raspberry Pi (HW SPI/WiringPi)

Created on 13 Dec 2017  路  5Comments  路  Source: olikraus/u8g2

Need help making this library work for my RPI. I'm having issues drawing a simple "Hello World" string but nothing is being shown on the display. Am I missing an implementation in the code below?

Pin Config

| ST7920 | SPI | Wiring Pi Pin # | BCM Pin # |
|-------- |------ |----------------- |----------- |
| RS | CS | 10 | 8 |
| R/W | MOSI | 12 | 10 |
| EN | CLK | 14 | 11 |
| RST | | 22 | 6 |
| PSB | | GND | |

Test Code:

int main()
{
  u8g2_t u8g2;
  u8g2_Setup_st7920_128x64_f(&u8g2, U8G2_R0, u8g2_rpi_spi_byte_cb, u8g2_rpi_gpio_and_delay_cb);

  u8g2_InitDisplay(&u8g2);
  u8g2_SetPowerSave(&u8g2, 0); 

  u8g2_ClearBuffer(&u8g2);
  u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
  u8g2_DrawStr(&u8g2, 0, 15, "Hello World");
  u8g2_SendBuffer(&u8g2);

  return 0;
}

HAL - SPI

uint8_t u8g2_rpi_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
  switch(msg) {
    case U8X8_MSG_BYTE_INIT: {
        if (wiringPiSetup() == -1) {
           printf("WiringPi Error\n");
           exit(1);
        }
        if (wiringPiSPISetup(0, 100000) < 0) {
        printf("Unable to open SPI device 0: %s\n", strerror(errno));
        exit(1);
        }
        printf("Initialized SPI Hardware\n");
        break;
    }
    case U8X8_MSG_BYTE_SET_DC: {
        break;
    }
    case U8X8_MSG_BYTE_START_TRANSFER: {
      break;
    }
    case U8X8_MSG_BYTE_END_TRANSFER: {
      break;
    }    
    case U8X8_MSG_BYTE_SEND: {
        wiringPiSPIDataRW(0, arg_ptr, arg_int);
        break;
    }
    default: {
      return 0;
    }
  }
  return 1;
}

HAL - GPIO:

uint8_t u8g2_rpi_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
  switch(msg)
  {
    case U8X8_MSG_GPIO_AND_DELAY_INIT:  // called once during init phase of u8g2/u8x8
      //printf("U8X8_MSG_GPIO_AND_DELAY_INIT: %d\n", arg_int);
      if (u8g2_rpi_hal.dc != U8G2_RPI_HAL_UNDEFINED) {
        //printf("Set OUTPUT mode to pin DC/RS\n");
        pinMode(u8g2_rpi_hal.dc, OUTPUT);
      }
      if (u8g2_rpi_hal.reset != U8G2_RPI_HAL_UNDEFINED) {
        //printf("Set OUTPUT mode to pin RESET\n");
        pinMode(u8g2_rpi_hal.reset, OUTPUT);
      }
      break;                            // can be used to setup pins
    case U8X8_MSG_DELAY_NANO:           // delay arg_int * 1 nano second
      break;
    case U8X8_MSG_DELAY_100NANO:        // delay arg_int * 100 nano seconds
      break;
    case U8X8_MSG_DELAY_10MICRO:        // delay arg_int * 10 micro seconds
      delayMicroseconds(10);
      break;
    case U8X8_MSG_DELAY_MILLI:          // delay arg_int * 1 milli second
      delay(arg_int);
      break;
    case U8X8_MSG_GPIO_RESET:           // Reset pin: Output level in arg_int
      if (u8g2_rpi_hal.reset != U8G2_RPI_HAL_UNDEFINED) {
        digitalWrite(u8g2_rpi_hal.reset, arg_int);
      }
      break;
  return 1;
}
question

Most helpful comment

Apologies for not being able to get back at you regarding this issue.

Good news, I FINALLY managed to make everything work (both hardware and software SPI implementations) and I would like to share my findings here.

TLDR; For the complete solution/demo, I have uploaded the cpp project here

Couple of issues I have encountered:

  1. SPI HW not working due to incorrect pin mode settings

The SPI HW communication of the raspberry pi was not working (Confirmed through test between RPI and Arduino slave)

After reviewing the gpio status via the gpio tool, I realized that the MOSI, CLOCK and CS pins were set to OUT instead of ALT0.

Solution:

By explicitly setting the pin modes to ALT0 during initialization, I was able to establish proper SPI HW communication. The wiring pi spi library does not set this during initialization so you have to do it yourself.

    pinModeAlt(u8g2_rpi_hal.mosi, 0b100);
    pinModeAlt(u8g2_rpi_hal.clk, 0b100);
    pinModeAlt(u8g2_rpi_hal.cs, 0b100);
  1. The wiringPiSPIDataRW() transfer of Wiring Pi library is not working as expected

Even after fixing the SPI HW issue and copying the example from the port guide , replaced the arduino SPI methods with it's Wiring Pi equivalent, I was still unable to make it work (no response/activity from the display).

I then stumbled into a comment of your code found in 'u8x8_byte_arduino_hw_spi':

U8x8lib.cpp: u8x8_byte_arduino_hw_spi

      // 1.6.5 offers a block transfer, but the problem is, that the
      // buffer is overwritten with the incoming data
      // so it can not be used...
      // SPI.transfer((uint8_t *)arg_ptr, arg_int);

This comment led me to investigate the inner workings of the Wiring Pi SPI library. From the developer's website, I found that the 'wiringPiSPIDataRW' also overwrites the data in the buffer in the same manner of what the comment above described and It uses ioctl system calls for full-duplex SPI transfer operations.

*int wiringPiSPIDataRW (int channel, unsigned char *data, int len) :
>

This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus.

I also found out from the linux kernel docs that Chip Select is automatically set/unset for full-duplex operations.

Standard read() and write() operations are obviously only half-duplex, and
the chipselect is deactivated between those operations. Full-duplex access,
and composite operation without chipselect de-activation, is available using
the SPI_IOC_MESSAGE(N) request.

Unfortunately, the Wiring Pi SPI library does not provide any other methods of transfer and is only limited to full-duplex transfer operations, so I ended up writing my own spi library which supports block transfers for half-duplex operations with the intention to have u8g2 regain control of the Chip Select pin.

With this, after replacing the wiring pi methods I was able to make everything work.

All 5 comments

Both procedures are incomplete.
For the byte procedure, have a look here: https://github.com/olikraus/u8g2/blob/master/csrc/u8x8_byte.c#L88
For SPI with the 7920 indeed the dc line is not required, but you need to handle the chip select signal (u8x8_gpio_SetCS). The chip select message also needs to be handled in the GPIO and delay procedures.

no further comment, closing...

Apologies for not being able to get back at you regarding this issue.

Good news, I FINALLY managed to make everything work (both hardware and software SPI implementations) and I would like to share my findings here.

TLDR; For the complete solution/demo, I have uploaded the cpp project here

Couple of issues I have encountered:

  1. SPI HW not working due to incorrect pin mode settings

The SPI HW communication of the raspberry pi was not working (Confirmed through test between RPI and Arduino slave)

After reviewing the gpio status via the gpio tool, I realized that the MOSI, CLOCK and CS pins were set to OUT instead of ALT0.

Solution:

By explicitly setting the pin modes to ALT0 during initialization, I was able to establish proper SPI HW communication. The wiring pi spi library does not set this during initialization so you have to do it yourself.

    pinModeAlt(u8g2_rpi_hal.mosi, 0b100);
    pinModeAlt(u8g2_rpi_hal.clk, 0b100);
    pinModeAlt(u8g2_rpi_hal.cs, 0b100);
  1. The wiringPiSPIDataRW() transfer of Wiring Pi library is not working as expected

Even after fixing the SPI HW issue and copying the example from the port guide , replaced the arduino SPI methods with it's Wiring Pi equivalent, I was still unable to make it work (no response/activity from the display).

I then stumbled into a comment of your code found in 'u8x8_byte_arduino_hw_spi':

U8x8lib.cpp: u8x8_byte_arduino_hw_spi

      // 1.6.5 offers a block transfer, but the problem is, that the
      // buffer is overwritten with the incoming data
      // so it can not be used...
      // SPI.transfer((uint8_t *)arg_ptr, arg_int);

This comment led me to investigate the inner workings of the Wiring Pi SPI library. From the developer's website, I found that the 'wiringPiSPIDataRW' also overwrites the data in the buffer in the same manner of what the comment above described and It uses ioctl system calls for full-duplex SPI transfer operations.

*int wiringPiSPIDataRW (int channel, unsigned char *data, int len) :
>

This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus.

I also found out from the linux kernel docs that Chip Select is automatically set/unset for full-duplex operations.

Standard read() and write() operations are obviously only half-duplex, and
the chipselect is deactivated between those operations. Full-duplex access,
and composite operation without chipselect de-activation, is available using
the SPI_IOC_MESSAGE(N) request.

Unfortunately, the Wiring Pi SPI library does not provide any other methods of transfer and is only limited to full-duplex transfer operations, so I ended up writing my own spi library which supports block transfers for half-duplex operations with the intention to have u8g2 regain control of the Chip Select pin.

With this, after replacing the wiring pi methods I was able to make everything work.

Wow, thanks a lot for your detailed feedback.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mattogodoy picture mattogodoy  路  9Comments

hilsonp picture hilsonp  路  3Comments

walenw picture walenw  路  8Comments

Bonnie12 picture Bonnie12  路  6Comments

idreamsi picture idreamsi  路  7Comments