ST7920_SPI_master library not using delays for glcd128x64

Hi,

By datasheet this glcd 128x64 using ST7920 driver need at least 72us for each transaction except the clear command which needs 1.6ms.

I'm doing the time delays in my library and it's working ok, but what is interesting is that there's a master library by cbm80amiga which is found here:

cbm80amiga / ST7920_SPI

Is not using delays ! when I compile the examples from this library, it works.

So I tried to remove the delays from my library but it failed !

So what's I'm missing ?

The master library init function is this:

void ST7920_SPI::init()
{
  scrWd=SCR_WD/8;
  scrHt=SCR_HT;
  isNumberFun = &isNumber;
  cr = 0;
  cfont.font = NULL;
  dualChar = 0;
  pinMode(csPin, OUTPUT);
  digitalWrite(csPin, LOW);
  SPI.begin();
  sendCmd(LCD_BASIC);
  sendCmd(LCD_BASIC);
  sendCmd(LCD_CLS); delay(2);
  sendCmd(LCD_ADDRINC);
  sendCmd(LCD_DISPLAYON);
  setGfxMode(true);
}

My init function is this:

void glcd_init(void){
	CO_BEGIN;
	// #1
	pinMode(CS_PIN, OUTPUT);
	pinMode(CLK_PIN, OUTPUT);
	pinMode(MOSI_PIN, OUTPUT);
	digitalWrite(CS_PIN, LOW);
	SPI.begin();						CO_DELAY(100000);
	// #2
	digitalWrite(CS_PIN, HIGH);			CO_DELAY(40000);
	// #3
	glcd_cmd(FUNCTION_SET_BASIC);		CO_DELAY(72);
	glcd_cmd(FUNCTION_SET_BASIC);		CO_DELAY(72);
	glcd_cmd(DISPLAY_CLEAR);			CO_DELAY(1600);
	glcd_cmd(ENTERY_MODE);				CO_DELAY(72);	
	glcd_cmd(DISPLAY_CONTROL);			CO_DELAY(72);
	
	glcd_task_flag = FINISHED;
	CO_END;
}

As you notice I must use different delays to get the glcd to work.

Are you running your SPI display at 5V or 3.3V? I bought this ST7920 module from buydisplay.com in China, but for 3.3V.

ST7920 Module

Datasheet timing specs may be different than your display module, depending on how it was built. I noticed from the ST7920 datasheet, that there is an external resistor across the controller's osc pins and it recommend different values for 5V and 3.3V. I think mine has a 27K ohm resistor (R7), while the datasheet recommends 33K for 5V and 18K ohm for 3.3V.

1 Like

I have exactly like the one provided in the link and I checked the resistor and it's 27k ohms.

The datasheet says 72us at least after each transaction but I used 30us and it's working fine.

But the library I linked in the main post doesn't even use any delays except for one 2ms for the clear command. And when I upload the library examples, they work fine.

I want to know how the programmer do it ? How he managed to not using delays ? Where I couldn't get lower than 30us to get a stable writes to the glcd ?

If you dig into the source code, you'll see something like this

void ST7920_SPI::sendCmd(byte b)
{
  SPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE3));
  digitalWrite(csPin, HIGH);
  SPI.transfer(0xF8);
  SPI.transfer(b & 0xF0);
  SPI.transfer(b << 4);
  digitalWrite(csPin, LOW);
  SPI.endTransaction();
}

Maybe there's enough clock cycle in

  SPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE3));

and

  SPI.endTransaction();

to generate the usec delay required. Not to mention, digitalWrite is slow itself

1 Like

Yes, you're right, I modified my library to this method and it worked and I think it's faster than my previous coding system.

But if I'm dealing with a faster SPI module; e.g. a TFT display, should I use this method with command/data functions ?

Like this:

void glcd_cmd(uint8_t cmd) {
  SPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE3));
  digitalWrite(CS_PIN, HIGH);
  SPI.transfer(0xF8);
  SPI.transfer(cmd & 0xF0);
  SPI.transfer(cmd << 4);
  digitalWrite(CS_PIN, LOW);
  SPI.endTransaction();
}
// ----------------------------------------------------------------

void glcd_data(uint8_t data){
  SPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE3));
  digitalWrite(CS_PIN, HIGH);
  SPI.transfer(0xFA);
  SPI.transfer(data & 0xF0);
  SPI.transfer(data << 4);
  digitalWrite(CS_PIN, LOW);
  SPI.endTransaction();
}

Or use my old method:

void glcd_cmd(uint8_t cmd){
	SPI.transfer(CMD_MSK);
	SPI.transfer(cmd & 0xf0);
	SPI.transfer(cmd << 4);
 }
void glcd_data(uint8_t data){
	SPI.transfer(DATA_MSK);
	SPI.transfer(data & 0xf0);
	SPI.transfer(data << 4);
}

I think my older method it more faster, but with the ST7920, the first method should be enough and it saved a lot of memory as well.

For the sending commands, you can get away single byte transfers. But for data, it is much faster if you can transfer multiple bytes at a time

Maybe something like

void glcd_data(uint8_t * data, uint16_t len) {
    digitalWrite(CS_PIN, HIGH);
    for(uint16_t count=0; count<len; count++) {
  	SPI.transfer(DATA_MSK);
  	SPI.transfer(data[count] & 0xf0);
  	SPI.transfer(data[count] << 4);
     }
        digitalWrite(CS_PIN, LOW);
}
1 Like

Another thing I started to notice.

When I clear the display with the current speed which is 1000000UL there are some pixels left over because the speed is still a bit fast than enough.

I used 500000 is problem is solved.

void glcd_cmd(uint8_t cmd) {
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));
  digitalWrite(CS_PIN, HIGH);
  SPI.transfer(0xF8);
  SPI.transfer(cmd & 0xF0);
  SPI.transfer(cmd << 4);
  digitalWrite(CS_PIN, LOW);
  SPI.endTransaction();
}

void glcd_data(uint8_t data){
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));
  digitalWrite(CS_PIN, HIGH);
  SPI.transfer(0xFA);
  SPI.transfer(data & 0xF0);
  SPI.transfer(data << 4);
  digitalWrite(CS_PIN, LOW);
  SPI.endTransaction();
}

My question, how to balance the best SPI speed for this glcd ?

If I put any speed in the setting function, would the Arduino adapt that exact speed ?

Like:

  1. This causes some left pixels on the display, so it's still too fast for the glcd
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE3));
  1. I used this speed and it worked fine:
SPI.beginTransaction(SPISettings(950000, MSBFIRST, SPI_MODE3));

Does it really run at 950k ?

Looks like SPI for the LCD maxes out at 1.666 MHz.

1 Like

So if I used this setting:

SPI.beginTransaction(SPISettings(950000, MSBFIRST, SPI_MODE3));

Then what is the output spi speed ? what the number means, is it in hertz or bit/s ?

I also shortened the init function to this:

void glcd_init(void){
	pinMode(CS_PIN, OUTPUT);
	SPI.begin();
	glcd_cmd(FUNCTION_SET_BASIC);
	glcd_cmd(FUNCTION_SET_BASIC);
	glcd_cmd(ENTERY_MODE);
	glcd_cmd(DISPLAY_CONTROL);
	glcd_task_flag = FINISHED;
}

I removed these two lines:

	glcd_cmd(DISPLAY_CLEAR);
	CO_DELAY(16000);

It doesn't do much of a difference, because I will call the function that clears the pixels after initialization.

God gave you Arduino Library documentation e.g. Tools->Reference

from file:///C:/Program%20Files%20(x86)/Arduino-1.8.13/reference/www.arduino.cc/en/Reference/SPISettings.html

SPISettings mySettting(speedMaximum, dataOrder, dataMode)
Note: Best when any setting is a variable''

Parameters

speedMaximum: The maximum speed of communication. For a SPI chip rated up to 20 MHz, use 20000000.

dataOrder: MSBFIRST or LSBFIRST

dataMode : SPI_MODE0, SPI_MODE1, SPI_MODE2, or SPI_MODE3

Returns

None.

Since you have a 16MHz Uno/Nano the available SPI speeds are:
8Mb/s, 4M, 2M, 1M, 500k, 250k, 125k, 62.5k.

So if you specify maximumSpeed = 950kB it will use the 950kB or next slower. i.e. 500kb/s
If you specify 20Mb/s it will use 8Mb/s on a 16MHz Nano.
However you will get 20Mb/s on an 80MHz ESP8266
Or 16Mb/s on a 64MHz STM32

There is no point is specifying 999.9kb/s or any other artificial speed.
Just follow the datasheet. If the chip is specified for 500kb/s just say 500000

From memory, cbm80amiga manipulates a full size shadow buffer in SRAM. Then blits the whole buffer in one go.
Also from memory, the ST7920 is fussy about how fast you send the SPI data. cbm80amiga achieves the "best possible throughput".

ST7920 LCD screens do not like fast updates. You can do running horse animations but they look smeary if you go too quickly.

His examples work very well on real ST7920 hardware.
I suggest that you just use his proven library.

Incidentally, you can drive a KS0108 parallel controller much faster than a ST7920 parallel controller. A bit pointless when LCD is smeary.
Driving ST7920 SPI works plenty fast enough. And it wastes LESS pins.

David.

1 Like

Thank you so much for this rich answer.

I use the master library for tests but I'm working on improving my own library too.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.