pigpio icon indicating copy to clipboard operation
pigpio copied to clipboard

Pi 3B+ & 4B cannot send SPI write over 128 bytes

Open haelyons opened this issue 2 years ago • 9 comments

Hello, I am using the pigpio C interface to drive an OLED, and cannot send transfers of over 128 bytes using the spi_write command. Apart from this spi_write works, as well as all other GPIO pin use for D/C and RES flags.

I can confirm this is not a hardware / OLED issue as I have a logic analyzer hooked up and increasing the transfer amount over 128 bytes results in nothing being sent.

Initialisation of the pigpio daemon: initNewGPIO = pigpio_start(NULL, NULL); // Initialise pigpio

Opening SPI interface: portNewWrite = spi_open(initNewGPIO, 0, 1000000, 0); // Channel, Baud, Flags

Regular call of the spi_write function (preceded by setting D/C flag and filling buffer with desired data): spi_write(initNewGPIO, portNewWrite, data, count); // where data[] is a char* array of 8192 bytes, and count is a sizeof(data)

The previous initialisation and calls have no output on the SPI lines at all, as can be seen in the screenshot below -- the initialisation happens, followed by no activity on the MOSI line. Anything above 128 bytes has this effect, whereas 128 bytes outputs normally.

I am relatively new to pigpio and spi-dev and would appreciate any troubleshooting tips to be able to better understand what's going on here. Is this an issue with my baud rate, or the SPI mode that I am using?

Please let me know if I should provide additional information to make the issue more clear.

Thanks, H

No Output for 8192 bytes

haelyons avatar Nov 28 '22 10:11 haelyons

There are no D/C, RES flags. I don't understand how you are using the library.

spi_flags consists of the least significant 22 bits.

21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
 b  b  b  b  b  b  R  T  n  n  n  n  W  A u2 u1 u0 p2 p1 p0  m  m

guymcswain avatar Jan 20 '23 16:01 guymcswain

I took a screenshot which didn't show sending the RES flags. I am using the pigpiod_if2 daemon to open SPI port 0, and manually setting RES and DC flags when sending the data buffer with the spi_write cmd.

Am I misunderstanding the use of SPI flags? I set them what I understood to be default (0).

haelyons avatar Jan 20 '23 16:01 haelyons

So when you say flags (RES and DC) you mean pins? I'm not familiar with that terminology associated with SPI. The standard pins are SCK, MOSI, MISO and CE. I'm still confused.

guymcswain avatar Jan 20 '23 17:01 guymcswain

Yes I mean pins! We also call them flags in SPI. Of the standard pins you mention, I am using SCK, CE, and MOSI. You can see in the screenshot that these standard pins are functioning as expected, as I am able to use the OLED, albeit sending 128 byte buffers instead of 8192.

I did not see how the flags could help, as altering the word size is not what I am looking for.

haelyons avatar Jan 20 '23 17:01 haelyons

Perhaps showing a more complete listing of your script will help.

guymcswain avatar Jan 20 '23 17:01 guymcswain

First, the framebuffer is declared, and the SPI initialisation function is run.

char *fb = (char *) calloc(8192, sizeof(char));

void NHD_initSPI() 
{
    initNewGPIO = pigpio_start(NULL, NULL); // Initialise pigpio

    if (initNewGPIO < 0) 
    {
        printf("Failed to initialise GPIO for OLED! [NHD]\n");
        printf("PIGPIO error code: %d\n", initNewGPIO);
    }
    else 
    {
        printf("Initialised GPIO for OLED [NHD]\n");
    }

    //set_mode(initNewGPIO, BUSY, PI_INPUT); // SET 24 (BUSY) to input
    set_mode(initNewGPIO, RES, PI_OUTPUT); // SET 25 (RESET) to output
    set_mode(initNewGPIO, DC, PI_OUTPUT); // SET 24 (D/C) to output
    set_mode(initNewGPIO, CS, PI_OUTPUT); // SET 8 (CS) to output
    set_mode(initNewGPIO, MOSI, PI_OUTPUT); // SET 10 (MOSI) to output
    set_mode(initNewGPIO, CLK, PI_OUTPUT); // SET 11 (CLK) to output

    set_pull_up_down(initNewGPIO, DC, PI_PUD_UP);
    set_pull_up_down(initNewGPIO, RES, PI_PUD_UP);

    portNewWrite = spi_open(initNewGPIO, 0, 1000000, 0); // Channel, Baud, Flags
    printf("Opened SPI on Channel 0 [NHD] \n");
}

Then we can write a single pixel into the framebuffer (not to screen yet). Each pixel on the SSD1322 is a nibble, so we right to the left or right nibbles based on if it is even or odd.

void NHD_FB_pixel(uint8_t x_virtual, uint8_t y) 
{
    // Convert x from a virtual address to a physical address
    // uint8_t x_physical = x_virtual >> 1;

    // [0, 1] [2, 3] [4, 5]  ----> Virtual Address space
    //    |      |      |
    //    v      v      v
    //    0      1      2    ----> Physical Address space

    // Check if the virtual address is odd or even (thanks Jack)
    if (x_virtual & 0x01U) {
        // If the virtual address is odd, set the right nibble
        fb[(y * BUFFER_WIDTH) + x_virtual >> 1U]  |= 0x0FU;
    }
    else {
        // If the virtual address is even, set the left nibble
        fb[(y * BUFFER_WIDTH) + x_virtual >> 1U]  |= 0xF0U;
    }
}

Then we can render the contents of the framebuffer to the screen by calling NHD_FB_render(), which calls SSD13222_writeBuffer(char data[], uint16_t count).

void NHD_FB_render()
{
    SSD1322_setColumns(SSD1322_COLS_MIN, SSD1322_COLS_MAX);
    SSD1322_setRows(SSD1322_ROWS_MIN, SSD1322_ROWS_MAX);
    NHD_stream();

    unsigned int i;

    //SSD1322_writeBuffer(fb, sizeof(fb));
    
    for (i = 0; i < 64; i++)
    {
        char linebuffer[128];

        for (int fill = 0; fill < 128; fill++)
        {
            //linebuffer[fill] = 0xFF;
            linebuffer[fill] = fb[(i * BUFFER_WIDTH/2) + fill];
        }
        SSD1322_writeBuffer(linebuffer, 128);
    }
}
void SSD1322_writeBuffer(char data[], uint16_t count) 
{
    //gpio_write(initNewGPIO, DC, 1);
    spi_write(initNewGPIO, portNewWrite, data, count);
}

haelyons avatar Jan 20 '23 18:01 haelyons

I expect to be able to simply use spi_write(initNewGPIO, portNewWrite, framebuffer, 8192) instead of writing my buffer line by line as shown in NHD_FB_render and SSD1322_writeBuffer.

haelyons avatar Jan 20 '23 18:01 haelyons

Ok, thanks. I can ignore DC, RES!

One thing that catches my eye is that you don't need to configure the SPI pins. pigpio does this for you. It shouldn't be a problem though configuring them twice. I'll need to verify the source to make sure.

8192 is a maximum limit, I think, without consulting the source. Have you tried 4096, 1024, etc?

I'm going off line now and won't be able to look at this again for couple of days.

guymcswain avatar Jan 20 '23 18:01 guymcswain

I've tried anything over 128 -- 256, 512, 1024, etc.

I can't get any of those working, and am really confused by where this cap could be :/

haelyons avatar Jan 20 '23 19:01 haelyons