olikraus / u8g2

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

U8g2 with STM32L432KCU6 and ERC240120 LCD (UC1608 driver) SPI #1388

Closed Basti3DB closed 3 years ago

Basti3DB commented 3 years ago

Hello all,

I am working on a project including the ERC240120 LCD. I already did this with my Arduino MEGA and everything works perfektly fine. Now i am struggling with Flash and Ram so i decided to switch to the STM32 platform (STM32L432KCU6). -My setup is 3 Wire SPI, S9 mode

I tried a lot and red many other issues taken by other people but did not get it working... Then i compared the "old" SPI clock from the arduino VS the new SPI and tweaked it, so the new looks the same now, but still nothing. (surprisingly the old SPI clock did not match the LCD specs(ec. SCK default low, 8 Bit ...). but still works. No clue why) Also the CS and RST line have the right level.

Here is my code: main.txt

You saw errors? Or need more detailed infos just say. Any help appreciated. Thanks in advance. Basti ;)

olikraus commented 3 years ago

At least some delay cases are not implemented. At least for testing you should use some delay instead of NOP. 3wire is also a little bit complicated. I personally would first test with 4wire spi.

olikraus commented 3 years ago

Regarding the 3wire interface: It is the responsibility of the byte procedure to convert the 9 bit stream into a byte stream. It may look like this: https://github.com/olikraus/u8g2/blob/master/csrc/u8x8_byte.c#L279 Another implementation (which will use the Arduino SPI interface) is here: https://github.com/olikraus/u8g2/blob/3c6460a73f7f310c665cef0af1d2bac49bf6c655/cppsrc/U8x8lib.cpp#L758-L765 this includes a call to arduino_hw_spi_3w_sendbyte where most of the additional conversion magic is done.

Another option is to ask your SPI hardware subsystem to send 9 instead of 8 bytes, if this is supported by your SPI hardware. Especially the line 'hspi1.Init.DataSize = SPI_DATASIZE_8BIT;' needs to be different I assume. Additionally you still have to construct the 9 bit token and provide the same to your hardware subsystem, so the BYTE_SEND code will be more complex:

      case U8X8_MSG_BYTE_SEND:
          //HAL_SPI_Transmit(&hspi1,(uint8_t *) arg_ptr, arg_int, 1000);
          HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *) arg_ptr, arg_int);
          break;

As a conclusion, your code can not work, because the 9 to 8 bit conversion is missing.

Basti3DB commented 3 years ago

So in order to make things simpler, i did change the display connection to use 4 Wire SPI. That means the CD Bit is now a seperate wire. Also the SPI data length is no more 9 bit, but 8 bit.

In the delay fuctions i changed the nop to 1 ms delay, but in debug mode i noticed they were not called. Only the "U8X8_MSG_DELAY_MILLI" is called.

uint8_t u8g2_gpio_and_delay_stm32(U8X8_UNUSED u8x8_t *u8x8,U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int,
        U8X8_UNUSED void *arg_ptr) {
    switch (msg) {
    case U8X8_MSG_GPIO_AND_DELAY_INIT:  //Initialize SPI peripheral
        // HAL initialization contains all what we need so we can skip this part.
        HAL_Delay(1);
        break;
    case U8X8_MSG_DELAY_MILLI:      //Function which delay arg_int * 1ms
        HAL_Delay(arg_int);
        break;
    case U8X8_MSG_DELAY_10MICRO:    //Function which delay 10us
        HAL_Delay(1);
        //__NOP();
        break;
    case U8X8_MSG_DELAY_100NANO:    //Function which delay 100ns
        HAL_Delay(1);
        //__NOP();
        break;
    case U8X8_MSG_DELAY_NANO:       //Function which delay 1ns
        HAL_Delay(1);
        //__NOP();
        break;
        //Function to define the logic level of the clockline
    case U8X8_MSG_GPIO_SPI_CLOCK:
        if (arg_int)
            HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, RESET);
        else
            HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, SET);
        break;
        //Function to define the logic level of the data line to the display
    case U8X8_MSG_GPIO_SPI_DATA:
        if (arg_int)
            HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, SET);
        else
            HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, RESET);
        break;
        // Function to define the logic level of the CS line
    case U8X8_MSG_GPIO_CS:
        if (arg_int)
            HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, RESET);
        else
            HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, SET);
        break;
        //Function to define the logic level of the Data/ Command line
    case U8X8_MSG_GPIO_DC:
            if (arg_int) HAL_GPIO_WritePin(LCD_CD_GPIO_Port, LCD_CD_Pin, SET);
            else HAL_GPIO_WritePin(LCD_CD_GPIO_Port, LCD_CD_Pin, RESET);
        break;
        //Function to define the logic level of the RESET line
    case U8X8_MSG_GPIO_RESET:
        if (arg_int)
            HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, SET);
        else
            HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, SET);
        break;
    default:
        return 0; //A message was received which is not implemented, return 0 to indicate an error
    }
    return 1; // command processed successfully.
}

Ony my Oszi i can see the Data is not changing very often and the clock is not seperated into 8 bit bursts.

My send function now looks like this:

uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr) {
    switch (msg) {
    case U8X8_MSG_BYTE_SEND:
        HAL_SPI_Transmit(&hspi1,(uint8_t *) arg_ptr, arg_int, 100);
        //HAL_SPI_Transmit_DMA(&hspi1, (uint8_t*) arg_ptr, arg_int);
        break;
    case U8X8_MSG_BYTE_INIT:
        break;
    case U8X8_MSG_BYTE_SET_DC:
        HAL_GPIO_WritePin(LCD_CD_GPIO_Port, LCD_CD_Pin, arg_int);
      break;
    case U8X8_MSG_BYTE_START_TRANSFER:
        HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, SET);
        //__NOP();
        break;
    case U8X8_MSG_BYTE_END_TRANSFER:
        //__NOP();
        HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, RESET);
        break;
    default:
        return 0;
    }

return 1;
}
olikraus commented 3 years ago

Did you also change the Interface type of the display to 4 wire SPI?

Basti3DB commented 3 years ago

You mean in my init function?

  printf("********LCD SETUP BEGIN********\n");
    HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, RESET);
    HAL_Delay(5);
    HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, SET);
    HAL_Delay(2);

    //u8g2_Setup_st7920_p_128x64_1(&u8g2, U8G2_R0, u8x8_byte_3wire_sw_spi, u8g2_gpio_and_delay_stm32);
    //u8g2_Setup_uc1608_erc240120_f(&u8g2, U8G2_R0, u8x8_byte_3wire_sw_spi, u8g2_gpio_and_delay_stm32);
    u8g2_Setup_uc1608_erc240120_f(&u8g2, U8G2_R2, u8x8_byte_4wire_hw_spi,u8g2_gpio_and_delay_stm32);
    HAL_Delay(500);
    LCD_setup();
    HAL_Delay(500);
  printf("********LCD SETUP END********\n");

Yes i changed it and also checked with debug and its called.

Is it right to only change the argumet in this setup (u8g2_Setup_uc1608_erc240120_f)?

olikraus commented 3 years ago

No, the display itself.

Basti3DB commented 3 years ago

Yes i did. I rewired the BM0 and BM1 pins to GND. Also the D7 pin is HIGH, D6 LOW. So S8 mode is selected. grafik

Basti3DB commented 3 years ago

i wonder if its corret, in the HAL_SPI_Transmit function call:

uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr) {
    switch (msg) {
    case U8X8_MSG_BYTE_SEND:
        if(HAL_SPI_Transmit(&hspi1,(uint8_t *) arg_ptr, arg_int, 100) != HAL_OK) printf("SPI error\n");
        //HAL_SPI_Transmit_DMA(&hspi1, (uint8_t*) arg_ptr, arg_int);
        break;
    case U8X8_MSG_BYTE_INIT:
        break;
    case U8X8_MSG_BYTE_SET_DC:
        HAL_GPIO_WritePin(LCD_CD_GPIO_Port, LCD_CD_Pin, arg_int);
      break;
    case U8X8_MSG_BYTE_START_TRANSFER:
        HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, SET);
        //__NOP();
        break;
    case U8X8_MSG_BYTE_END_TRANSFER:
        //__NOP();
        HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, RESET);
        break;
    default:
        return 0;
    }
    return 1;
}

there the input arguments are: HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout) but when i debug and watch the arg_int(which is the size) variable i see it is mostly 1(dec) and a few times it is 240(dec). In my opinion it make sense if it is a 8, because of the 8 bit SPI data?

olikraus commented 3 years ago

The size is provided in bytes. So 1 means 1 byte.

Basti3DB commented 3 years ago

so i now have debugged a lot and can now see the data beeing send to the display, but still shows nothing. In this spi routing i added the "while" statement to wait til previous data is send, but no improvement

uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr) {
    switch (msg) {
    case U8X8_MSG_BYTE_SEND:
        while(!__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE));
        if(HAL_SPI_Transmit(&hspi1,(uint8_t *) arg_ptr, arg_int, 100) != HAL_OK) printf("SPI error\n");
        //HAL_SPI_Transmit_DMA(&hspi1, (uint8_t*) arg_ptr, arg_int);
        break;
....
Basti3DB commented 3 years ago

in the SPI data i see, the Clock, CS and DC signal to be ok. there is only data been send while dc is low, so it means only instructions are send. when dc is high (so screen data) there is no "movement" on the data line.

Do you have any idea why this occurs?

olikraus commented 3 years ago

I do not know anything on your HAL functions, but

            if (arg_int) HAL_GPIO_WritePin(LCD_CD_GPIO_Port, LCD_CD_Pin, SET);
            else HAL_GPIO_WritePin(LCD_CD_GPIO_Port, LCD_CD_Pin, RESET);

did look better to me. One more point: Did you init the CD (DC) pin corrently as output pin? You had added this pin during the change from 3wire to 4wire...

Basti3DB commented 3 years ago

I changed those to lines, but nothing on the display.

Yes, i think the CD pin is init correctly, those init functions are generated automatic from the CUBE IDE.

While debugging i noticed, the arg_ptr variable, which should hold the data to send, actually holds the right data. Think this because the one variable counts up, which is also in the arg_ptr...

void LCD_send_screen(void) {
    yPos++;
    if(yPos > 100)yPos = 0;
    u8g2_ClearBuffer(&u8g2);
    u8g2_DrawLine(&u8g2, 50,yPos, 100, yPos);
    u8g2_SetFont(&u8g2, u8g2_font_lastapprenticebold_tr);
    u8g2_DrawStr(&u8g2, 10, 20, "Uff");
    u8g2_SendBuffer(&u8g2);
}

with arg_ptr i mean this:

uint8_t *txdata;

uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr) {
    switch (msg) {
    case U8X8_MSG_BYTE_SEND:
        txdata = (uint8_t*)arg_ptr;
        printf("arg_ptr: %x\n",txdata);  // THIS VARIABLE
        while(!__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE));
        if(HAL_SPI_Transmit(&hspi1,(uint8_t *) &arg_ptr, arg_int, 100) != HAL_OK) printf("SPI error\n");
        //HAL_SPI_Transmit_DMA(&hspi1, (uint8_t*) arg_ptr, arg_int);
        break;

in the Inline debuggin i see that at the pointer memory addr. is the "changing" data but i dont think, the "real" variable data is transferd, i think, what the debug prints, is only the memory addr. of this variable. How can i change this behavior?

olikraus commented 3 years ago

yes arg_ptr is a memory location with arg_int bytes in case of MSG_BYTE_SEND.

Your question is more regarding HAL_SPI_Transmit, which I do not know. I can just guess, that you have to provide a memory location as second argument. In this case you must not apply & to arg_ptr, because arg_ptr already is the memory location (and it does not make sense to calculate the memoy location of the variable which holds the memory location), so

HAL_SPI_Transmit(&hspi1,(uint8_t *) arg_ptr, arg_int, 100)

would be correct. However, reading https://community.st.com/s/question/0D50X00009kKdg5/halspitransmit-size-is-it-defined-as-a-byte-or-a-word seems to say, that the second argument actually expects an array of words instead of an array of bytes. If this is the case, than you need to convert the array of bytes (which is provided in arg_ptr) into an array of words for your HAL function.

Maybe as a first step try to transfer only one byte at a time in a loop:

while( arg_int > 0 ) {
   HAL_SPI_Transmit(&hspi1,arg_ptr++, 1, 100);
   arg_int--;
}
Basti3DB commented 3 years ago

I finally got it working!

As a conclusion i can say:

Many thanks for your great support and helping me out ;)

olikraus commented 3 years ago

:-)