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

ST7920 on Raspberry Pi (HW SPI/WiringPi) #457

Closed
ribasco opened this issue Dec 13, 2017 · 5 comments
Closed

ST7920 on Raspberry Pi (HW SPI/WiringPi) #457

ribasco opened this issue Dec 13, 2017 · 5 comments
Labels

Comments

@ribasco
Copy link

ribasco commented Dec 13, 2017

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;
}
@olikraus
Copy link
Owner

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.

@olikraus
Copy link
Owner

no further comment, closing...

@ribasco
Copy link
Author

ribasco commented Jun 27, 2018

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.

@olikraus
Copy link
Owner

Wow, thanks a lot for your detailed feedback.

@olikraus
Copy link
Owner

Also thanks for the updates to https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants