olikraus / u8g2

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

Second SPI not working for ST7920 using with STM32F103C8T6 (Blue Pill) or STM32F401CCU (Black Pill) #1208

Closed delgadosouza closed 4 years ago

delgadosouza commented 4 years ago

Dear @olikraus , thank you for this great library!

I have a Blue Pill STM32F103C8T6 and also a Black Pill STM32F401CCU and I would like to use a ST7920 display on the SPI2 (pins PB12 to PB15). Any of the microcontrollers would be great. The screen only works reliably with low frequencies (<2MHz) so the second SPI is perfect. On the SPI1 I have another sensor.

I used the SW constructor on those pins (PB12 to PB15) and it works fine but slow. Unfortunately, because my project is interactive I can't use it with this speed. While with Hardware SPI I got a 55ms refresh rate, with Software SPI I got only 250ms on the BluePill and 150ms with the BlackPill.

I am using the official Arduino_Core_STM32 by stm32duino. My entire project is based on this core so changing for another core would require changing and testing everything again...

I tried the HW version:

U8G2_ST7920_128X64_F_2ND_HW_SPI disp(LCDROTATION, SS_RS_CS, RST_RESET);

and it compiles but nothing is shown on the screen.

So I tried defining SPI_INTERFACES_COUNT 2 on the beggining of the U8x8lib.h file and got the error:

\src\U8x8lib.cpp:1000:7: error: request for member 'transfer' in '(SPI_TypeDef*)((1073741824 + 65536) + 12288)', which is of pointer type 'SPI_TypeDef*' (maybe you meant to use '->' ?)

So I openned the U8x8lib.cpp file and tried to hardcode the constructor for the second SPI. By the documentation in their wiki the constructor for the second SPI would be:

SPIClass mySPI_2(uint8_t mosi, uint8_t miso, uint8_t sclk, uint8_t ssel)

Then I switched all occurrences of SPI1 to mySPI_2 and it compiled but the screen was blank.

I have sill a lot to learn so I have no ideas on how to solve this... Is it really a problem with the core I am using that don't follow the Arduino API? They claim that they follow the API and that Roger's core doesn't.

I tried using the nightly build of Arduino IDE and also the beta release (Arduino 1.9).

If the problem really is with the core, do you have any suggestions for a workaround?

Any help would be really apreciated! Thank you again.

marekche commented 4 years ago

Hi! @delgadosouza @olikraus I have the same issue! I can't use HW constructor for ST7920 U8G2_ST7920_128X64_F_HW_SPI Nothing is showing on the screen! With SW constructor works ok. I'm using Arduino Mega.

delgadosouza commented 4 years ago

Hi @marekche, I believe Arduino Mega have only one SPI that is probably what you are trying to use. Did you try to reduce the speed of the screen? Have a look at this code:

#include "U8g2lib.h" // by olikraus

#define LCDROTATION U8G2_R0
#define SCK_E_CLOCK PA5
#define MOSI_RW_DATA PA7
#define SS_RS_CS PA4
#define RST_RESET PA3

U8G2_ST7920_128X64_F_HW_SPI disp(LCDROTATION, SS_RS_CS, RST_RESET);

int ypos = 22;

void setup(){
  disp.begin();
  delay(100);
  disp.setBusClock(500000); // Try increasing until the screen is fluid but without artifacts
  delay(100);
}

void loop(){
  // Show something
  disp.clearBuffer();
  disp.setFontMode(1);
  disp.setDrawColor(1);
  disp.setFont(u8g2_font_pcsenior_8u);
  disp.drawStr(22,ypos,"HELLO WORLD");
  disp.sendBuffer();
  delay(50);
  ypos++;
  if(ypos>50){
    ypos=22;
  }
}

About the SPI2 I asked the people on the Arduino_Core_STM32 and they say that the SPI implementations is fine...

delgadosouza commented 4 years ago

Oh, you must set the correct pins, of course. Also try using other constructors like: U8G2_ST7920_128X64_1_HW_SPI U8G2_ST7920_128X64_2_HW_SPI The F option will use the higest amount of memory but will be the smoothest option.

marekche commented 4 years ago

Thanks @delgadosouza I tried, but without successful :( Always nothing in the screen. I have the SS_RS_CS to RS of LCD ST7920. I want to do real time graph and with SW_SPI take around 40ms to draw a pixel! Do you know if with HW_SPI is more faster than SW_SFI?

delgadosouza commented 4 years ago

It sure is!

For me it takes around 55ms to refresh the entire screen on hardware SPI. With software SPI it goes to 250ms but that is using STM32F103 which is faster than Arduino Mega. The speed for hardware SPI should be similar for both boards though.

I believe 40ms for a pixel is too much, even for software SPI. How many pixels are on the screen?

delgadosouza commented 4 years ago

Maybe I can help, can you give me more of your code?

marekche commented 4 years ago

Yes, sure! This is a part of the code! Thanks a lot again... I measured with only one pixel and take 35ms around.

` #include

include

    #include "Constants.h"
    #include "Utilities.h"
    #ifdef U8X8_HAVE_HW_SPI
    #include <SPI.h>
    #endif
    #include <i2cSimpleTransfer.h>
    #include <Wire.h>

    using namespace utils;

    U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, 10, 9, 8, 5);

    int bufferArray[123];
    int pointer = 0;
    int xPoint = 4;
    int yPoint;

    void setup(void) {
      Serial.begin(SERIAL_BAUD_RATE);
      Wire.begin(I2C_SLAVE);    // i2c Slave address
      Wire.onRequest(requestEvent);     // when the Master makes a request, run this function
      Wire.onReceive (receiveEvent);
      u8g2.begin(/*Select=*/ ROTARY_BUTTON_PIN, /*Right/Next=*/ ROTARY_PIN1, /*Left/Prev=*/ ROTARY_PIN2, /*Up=*/ U8X8_PIN_NONE, /*Down=*/ U8X8_PIN_NONE, /*Home/Cancel=*/ U8X8_PIN_NONE); // Arduboy 10 (Production)
      u8g2.setFont(u8g2_font_6x12_tr);
      attachInterrupt(digitalPinToInterrupt(ROTARY_BUTTON_PIN), rotaryInterruptButton, CHANGE);
      u8g2.setFontMode(0);

    }

    void loop(void) {

      if(mainMenu){
        u8g2.setFont(u8g2_font_6x12_tr);
        current_selection = u8g2.userInterfaceSelectionList(
        "Menu",
        current_selection, 
        MENU_LIST);
      }

      switch(current_selection){
        case 0:
          if(abnormalStop){
            current_selection = 5;
            mainMenu = false;
          }
        break;

        case 2:
          mainMenu = false;
          drawState();
        break;

        case 3:
          mainMenu = false;
          if(enteringMenu){
            //drawAxis();
            enteringMenu = false;
          }else{
            drawGraph();
          }
        break;

      }

      delay(1);

    }

    void drawAxis(){

      u8g2.firstPage();
      do {
          u8g2.setFont(u8g2_font_micro_tr);
          u8g2.clearBuffer();
          u8g2.drawHLine(0,58,128);
          u8g2.drawVLine(4,0,64);
           for (int i = 0; i < 8; i++) {
            u8g2.setCursor(0, y);
            u8g2.print(value[i]);
            y = y + 6;
          }
          u8g2.drawStr(50, 64, "CYCLE");
          u8g2.sendBuffer();
          enteringMenu = false;
          y = 6;
      } while ( u8g2.nextPage() );
    }

    void drawGraph(){

      bufferArray[pointer] = map(readPressure(),0,100,58,0);;
      u8g2.firstPage();
      do {
        u8g2.setFont(u8g2_font_micro_tr);
        u8g2.drawHLine(0,58,128);
        u8g2.drawVLine(4,0,64);
         for (int i = 0; i < 8; i++) {
          u8g2.setCursor(0, y);
          u8g2.print(value[i]);
          y = y + 6;
        }
        u8g2.drawStr(50, 64, "CYCLE");
        enteringMenu = false;
        y = 6;

        for (int i = 0; i < pointer+1; i++) {
          u8g2.drawPixel(4+i, bufferArray[i]);
        }

      } while ( u8g2.nextPage() );

      xPoint++;
      pointer++;
      if(xPoint > 127){
        xPoint = 4;
        pointer = 0;
        memset(bufferArray, 0, sizeof(bufferArray));
      }

    }

}`

delgadosouza commented 4 years ago

Hmm, for the hardware SPI you must use specific pins to work. Did you use these pins:

SCK_E_CLOCK 52
MOSI_RW_DATA 51
SS_RS_CS 53
RESET 5

They are the green marked pins on this picture: image

They are at the bottom of the board. The MISO won't be used because the screen don't send anything to the arduino.

I believe what is happening is that the pins you conected the LCD are the pins 10, 9, 8 and 5 so software SPI works because any pin will work. But with hardware SPI the library is using pins 52, 51 and 53.

The reset pin is free for you to choose, so I kept 5. I believe that the SS pin you can choose too.

delgadosouza commented 4 years ago

Regarding the time, it will take a minimum time to refresh the screen. So even printing nothing on the screen it may take the 35ms you are measuring just to send the command to the screen. If you print more pixels the time increases linearly? What about no pixel?

marekche commented 4 years ago

No, not increase lenearly! If a draw the axis for example, it take 40-45ms around. I'm cheking the connection that you said. About the graph, maybe I need change my point of view! I will not be able to graph pixel by pixel (123 that is my width) in less than around 5s...

delgadosouza commented 4 years ago

The approach looks ok to me... I once printed a QR code composed of 49x49 pixels, pixel by pixel inside 2 nested loops. Together I printed a bar plot composed of 30 bars. It took about 200ms.

I believe that the time to send the data to the screen is fixed and only depends on the communication (HW SPI, SW SPI, frequency clock, etc).

The remaining of the time is the time for the code to run the computations and fill the buffer to be sent to the screen.

marekche commented 4 years ago

Thanks a lot @delgadosouza It was my mistake about the connection with arduino! It seems that take same time to draw...

marekche commented 4 years ago

Its take around 48-50ms every refresh! I need findout other way to do the graph in realtime! Thank you again for your time!

AnHardt commented 4 years ago

Essentially a display update in u8g2 has two components. a) Draw the pixels into the frame buffer. The time this lasts, depends on what you draw. The more pixels you set the longer it takes. b) Sending the frame buffer to the display. This is independent from what you draw. The complete screen is transferred. Here the time it last depends on the method of transferring the data. Usually the software SPI is slower than the hardware SPI. (There can be exceptions.)

If not using the 'F'-constructors u8g2 divides the screen into N pages. When updating the screen it is done in the picture-loop. a) When executing the part inside the loop the drawing into the framebuffer is done. It's done N times. For the part u8g2 is doing this is not N times slower than with the F constructor because of chipping - but a bit. But what you are doing to prepare the content, calculations, biulding/selecting strings, inside the loop is done N times. It makes sense to have the screen content ready before you enter the picture loop. b) When transferring the frame buffer (nextPage() ) each time only 1/N of the picture is transferred (plus a bit of overhead). So the transfer time over the loop is nearly constant but a single nextPage() lasts only 1/N of that.

If you know in what page you are and draw only in that page you can speed up drawing (user clipping). (f.e. Don't try draw icons not at the current page)

If you have time critical tasks, like when a buffer is running over when it is not polled often enough if done outside the picture loop - pull that into the loop - the polling interval will than be ~ 1/N. Just be sure the polling will not alter the displays content.

delgadosouza commented 4 years ago

Interesting... so if I don't use the F constructor I can monitor a sensor more frequently without afecting the refresh rate for the entire screen? I will try that!

@AnHardt do you have any suggestions about my problem with hardware SPI2 on STM32F103 or STM32F401?

marekche commented 4 years ago

@AnHardt Thanks a lot for your explanation! It was very helpful.

olikraus commented 4 years ago

Hi All Thanks everyone for contribution. Currently my time is very much limited and there is almost no time left for me to work on u8g2 and answer questions here :-(

I can not say much regarding the STM32 implementation. If the second SPI is implemented in the same way as for standard Arduino libs, then it should work. If not then it is probably difficult, because I do not want to create an exception for each and every other lib.

HW SPI in Arduino Mega should work. If not, then often there are wiring issues because the HW SPI pins are fixed on Arduino Mega