Menu

Video on GLCD test.

5 days ago
4 days ago
  • Roger Jönsson

    Roger Jönsson - 5 days ago

    I was curious to see if it would be possible to show video on a small and cheap 1" GLCD screen.
    I chose the SSD1331, since it was both cheap and had working routines in GC-basic where I could modify some coordinates to get the screen in sequential mode. This way addresses does not have to be sent for each pixel, speeding up the process. In sequential mode, 16bit 565 colourmode, I could achieve 30fps using a PIC18F27Q83 @64MHz. When I also fetch the pixels from picture in a Data Block (or Table), it slows down to 15fps for this demo. My initial goal was 10fps, so my idea of streaming to it via serial-USB seems doable (later on). If anyone has some ideas improving or making modifications, feel free to dig in.

    The demo displays a photo that is rotating vertically one line per frame.
    6144 pixels are fetched and sent to the GLCD for every frame. 15 frames per second.

     
  • Anobium

    Anobium - 4 days ago

    Use DMA chip. That could improve operations.

    The is really good work. Impressed.

     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    Yes, DMA is there somewhere in the future. At this time it is beyond my capabilities.
    Actually measuring the fps I was only getting about 13,5 fps.
    After sleeping on it I tried spooling it over to an Array and is now getting 17fps!

    The reason for choosing PIC18F27Q83 is that is has 13kB RAM, enough to hold a complete frame. I am thinking: Serial streaming (computer -> PIC) , store the whole frame in an array, then transfer it to the GLCD.

     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    I was glad to find out that the array holds a complete frame of 12288 bytes!
    (The help says it is limited to 10,000 bytes for 18F)

     
  • Anobium

    Anobium - 4 days ago

    The max array is the max available RAM or 64k.


    Optimization Description Code Changes Estimated FPS Gain Difficulty
    Burst SPI Transfer (Highest Impact) Copy the library write operations to your program. Then, in your new method. Set DC high and CS low once per frame, then stream all bytes via raw SPI writes, toggling CS/DC only at the end. Eliminates per-byte overhead. Replace Do...Loop with burst code using SSPBUF and SSPSTAT. Add defines if needed. Verify pin states in glcd.h. +50–100% (to 22–30 FPS) Low
    Loop Unrolling or Assembly Inline Use Repeat...Until with manual increment instead of For...Next. Replace For...Next with Repeat...Until Offset > 12287+192. Or hand-optimize ASM. +5–15% (to 16–18 FPS, stacks with burst) Medium
    DMA Transfer (Advanced) Use DMA channel for auto-transfer to SSPBUF on SPI TX. Enable DMA in MCC; add init/start/wait code in loop. +200–500% (to 40–80 FPS) High
    System Clock Tweaks Ensure 64 MHz stable; switch to internal FRC+PLL if needed. Add #CONFIG FEXTOSC = OFF, FRCPLL before #CHIP. +0–5% (if underclocked) Low

    Untested DMA.

    Do
      ' Set display to data mode and select chip
      GLCD_DC = 1
      GLCD_CS = 0
    
      ' Load first byte manually to kick off SPI master clock
      SSP1BUF = Arrayframe(0)
    
      ' Set source addr and size (reload for each frame; assumes rolling wraps manually if needed)
      DMA1SSA = @Arrayframe + 1  ' Start after first byte
      DMA1SSIZ = 12479  ' Remaining bytes
    
      ' Enable/start DMA (EN=1, SIRQEN=1)
      DMA1CON0 = 0xC0
    
      ' Wait for DMA complete (poll dest count flag; clears on read)
      While !DMA1CON0bits.DMA1DCNTIF
        ' Optional: Add timeout or NOPs for stability
      Wend
      DMA1CON0bits.DMA1DCNTIF = 0  ' Clear flag
    
      ' End transfer
      GLCD_CS = 1
      PORTF.3 = !PORTF.3  ' Frame toggle
    Loop
    
     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    Testing it I get a bunch of errors. Of the things I thought I understood is that it complains about "Do without matching Loop" and it thinks Wend is syntax error. I see the Loop at the end. If I remove the While-Wend part, then it stops complaining about missing Loop. About the missing declares I don't know what to do.

     

    Last edit: Roger Jönsson 4 days ago
  • Anobium

    Anobium - 4 days ago

    My code was not tested. You should study examples and adapt from C:\GCstudio\gcbasic\demos\DMA_Solutions\

    The best place to start would be C:\GCstudio\gcbasic\demos\DMA_Solutions\18f27q43_send_bigtable_dma_to_serial.gcb then adapt to SPI.

    The While-Wend. That should have been WAIT While ...


    The real code that send SPI data is:

    ;Source: hwspi.h (979)
    // FASTHWSPITRANSFER
    ;Master mode only
    ;One byte transfer count
    SPI1TCNTL = 1
    SPI1TXB = Arrayframe( address)
    wait while SPI1RXIF = SPI_RX_IN_PROGRESS
    SPI1RXB = SPI1RXB
    

    The real quick way send bytes via SPI could be be adapt that to send many bytes. The datasheet will reveal how to send multiple bytes.


    But, you seem to happy having interesting coding going on there!

     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    As said, I am not yet skilled enough to follow this. I will save the info for later explorations.
    (At this point in time it would be way easier to use two chips to share chores via an 8-bit bus, but no where near as impressive as getting it done as quickly one chip).
    Still, I wonder what the compiler means by:
    Error: Variable SSP1BUF was not explicitly declared
    SSP1BUF is a register, right? On another chip? Is that why the compiler says "was not explicitly declared"?

     
  • Anobium

    Anobium - 4 days ago

    SSP1BUF is the generic name for the SSP buffer on channel 1. Each chip family may have a different real name. The GCBASIC libraries manage the mapping for you.

    In the older chips the hardware SPI the output port was called SSP1BUF. In the modern chips is far more complex ( again this is in the GCBASIC library to make things easy to use ).
    Below is a new method for you to try. I have lifted the FASTHWSPITRANSFER code and placed in-line within you solution ( this is the main body, the rest remains as-is).

    This shows that SSP1BUF is actually called SPI1TXB and there are other registers needed to send data ( old chips do not have these other additional registers ).

    This, if it works, will send the DATA block a lor faster. Wh ? No subs calls.
    But, you need to set up the GLCD control lines ( CS and DC )

    It wil be interesting to see the fps improvement!

    Evan

    // Program starts here:
    DIM ii as Word
    DIM ARRAYframe(12288) as Byte
    Dim value as byte
     Dim SPI1RXB as Byte = 0
    
    For ii = 0 to 12287
    ProgramRead (@BLOCK0 + ii, value)
    Arrayframe(ii)=value
    next
    
    DIR PORTF.3 OUT   
    Dim offset as word
    // Dim value as byte
    
        SendCommand_SSD1331(SSD1331_Set_Column_Address) ' Column addr set
        //Adds borders for the sequential mode:
        SendCommand_SSD1331 0 //GLCDX start coordinate (left)
        SendCommand_SSD1331 95 //GLCDX stop coordinate (right)
    
        SendCommand_SSD1331(SSD1331_Set_Row_Address) ' Row addr set
        SendCommand_SSD1331 0 //GLCDY start coordinate (up)
        SendCommand_SSD1331 63 //GLCDY stop coordinate (down)
    
    
     Do  
    
        // At this point we need to select the chip and tell the glcd data is coming
        set SSD1331_CS OFF
        set SSD1331_DC ON
    
        For Offset = 0 to 12287+192 '2x96 pixels = one line extra to make it roll
            // ProgramRead (@BLOCK0 + Offset, value) '2 bytes = two loop rounds needed (each pixel is 16 bits)
    
            ;Source: hwspi.h (979)
            // FASTHWSPITRANSFER
            ;Master mode only
            ;One byte transfer count
            SPI1TCNTL = 1
            SPI1TXB = (Arrayframe(offset))
            wait while SPI1RXIF = SPI_RX_IN_PROGRESS
            ;You MUST read the incoming byte to complete the SPI process. Do not remove!
            SPI1RXB = SPI1RXB
    
            // SendData_SSD1331 (Arrayframe(offset))
        next
    
        PORTF.3 = !PORTF.3 'changes state once per frame
    
        // Deselect the GLCD
        set SSD1331_CS ON
    
    Loop
    
     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    WOW!
    We are down to one error, the compiler did not like; Dim SPI1RXB as Byte = 0:
    Invalid variable type: BYTE = 0

     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    The compiler didn't like
    Dim SPI1RXB as Byte = 0
    so I changed it to:
    Dim SPI1RXB as Byte
    SPI1RXB=0

    It updates at 30fps, but only draws one pixel (X0 Y0).

     
    • Anobium

      Anobium - 4 days ago

      Yes.

      You have just come across a new feature to be added in the next release!!

      Dim SPI1RXB as Byte = 0

      In the new release you can DIMension a variable and initialise on one line! You can do this on all variables soon.

      This enriches the GCBASIC language, and, it will make code more readable.

      An idea by me, implemented by Angel!


      Re the one byte. I do not have the datasheet to hand but the logic ( based on your result ) would be toggle the DC.

      Do  
      
          // At this point we need to select the chip and tell the glcd data is coming
          set SSD1331_CS OFF
      
          For Offset = 0 to 12287+192 '2x96 pixels = one line extra to make it roll
              // ProgramRead (@BLOCK0 + Offset, value) '2 bytes = two loop rounds needed (each pixel is 16 bits)
      
              set SSD1331_DC ON
      
              ;Source: hwspi.h (979)
              // FASTHWSPITRANSFER
              ;Master mode only
              ;One byte transfer count
              SPI1TCNTL = 1
              SPI1TXB = (Arrayframe(offset))
              wait while SPI1RXIF = SPI_RX_IN_PROGRESS
              ;You MUST read the incoming byte to complete the SPI process. Do not remove!
              SPI1RXB = SPI1RXB
      
              set SSD1331_DC OFF
      
              // SendData_SSD1331 (Arrayframe(offset))
          next
      
          PORTF.3 = !PORTF.3 'changes state once per frame
      
          // Deselect the GLCD
          set SSD1331_CS ON
      
      Loop
      
       

      Last edit: Anobium 4 days ago
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    The screeen is now all black.
    ...
    "DIMension a variable and initialise on one line!"
    This is great! I have missed this in GCbasic.
    My first thought seing you used it:"-Oh, so I can do this after all", but nah. -Well soon, then. -Looking forward to it.

     
    • Anobium

      Anobium - 4 days ago

      Re the next release.

      You will be stunned by what is coming!

       
  • Anobium

    Anobium - 4 days ago

    Black. How odd.

    This may work. Latches the data. Is this does not work.
    Then, this maybe a timing issue where the previous logic would work with a delay.

     Dim SPI1RXB as Byte = 0   
    
     Do  
    
        // At this point we need to tell the glcd data is coming
    
        set SSD1331_DC ON
    
        For Offset = 0 to 12287+192 '2x96 pixels = one line extra to make it roll
            // ProgramRead (@BLOCK0 + Offset, value) '2 bytes = two loop rounds needed (each pixel is 16 bits)
    
            ;Source: hwspi.h (979)
            // FASTHWSPITRANSFER
            ;Master mode only
            ;One byte transfer count
            SPI1TCNTL = 1
    
            set SSD1331_CS OFF
                SPI1TXB = (Arrayframe(offset))
                wait while SPI1RXIF = SPI_RX_IN_PROGRESS
                SPI1RXB = SPI1RXB
            // Latch the transmission ?
            set SSD1331_CS On
    
            // SendData_SSD1331 (Arrayframe(offset))
        next
    
        PORTF.3 = !PORTF.3 'changes state once per frame
    
        // Deselect the GLCD
        set SSD1331_CS ON
    
    Loop
    
     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    Still black.
    If I put Wait 1 ms (or 10 ms) after SPI1RXB = SPI1RXB then I get a bright dot at X0Y0.
    The same if I put wait 1 ms between all instructions in your code. One bright dot at X0Y0.
    In all cases it updates the PORTF.3 around 30 times per s, so it does not get stuck.

     
  • Anobium

    Anobium - 4 days ago

    OK. This feels like a timing issue. I would put a protocol analyser to see what was happening on the wire.

     Dim SPI1RXB as Byte = 0   
    
     Do  
    
        For Offset = 0 to 12287+192 '2x96 pixels = one line extra to make it roll
    
            set SSD1331_CS OFF
    
            set SSD1331_DC ON
    
            ;Source: hwspi.h (979)
            // FASTHWSPITRANSFER
            ;Master mode only
            ;One byte transfer count
            SPI1TCNTL = 1
    
            SPI1TXB = (Arrayframe(offset))
            wait while SPI1RXIF = SPI_RX_IN_PROGRESS
            SPI1RXB = SPI1RXB
            // To prevent deselecting the GLCD before the GLCD has physically written the data byte.  May need more or less NOPs
            NOP
            NOP
            NOP
            NOP
            set SSD1331_CS ON
    
            // SendData_SSD1331 (Arrayframe(offset))
        next
    
        PORTF.3 = !PORTF.3 'changes state once per frame
    
        // Deselect the GLCD
        set SSD1331_CS ON
    
    Loop
    
     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    Using my old analogue oscilloscope on DataOut PORTC.5:
    I went back and ran your first test that I could compile. I didn't see it when I tested it then, but it flickers RF3 at 30fps, but then pauses off briefly every 1.5 seconds and then starts again. The data out pin is stuck high apart from a single downwards spike (that could be a byte) and then goes 0 when it pauses.
    Then I took your last code "Black how odd". DO just stays high it seems. A few nop after SPI1RXB = SPI1RXB does not change anything.
    Your last code DO just stays high. F.3 flickers at about the previous speed.

    Ok, I got the white dot when inserting wait 1ms, but also one frame per 12s. So maybe the white dot is about something timing out. Sorry about that.

    Running my last demo (the one with array) there is heavy traffic on DO.

     
  • Anobium

    Anobium - 4 days ago

    This is really odd.

    What have I missed?

    SendData_SSD1331 (Arrayframe(offset) calls SendData_SSD1331() which then calls FASTHWSPITRANSFER. All the new code is meant to do is to miss the call to SendData_SSD1331() to improve speed. The method SendData_SSD1331() sets the DC and CS then that calls FASTHWSPITRANSFER.

    So, check do this work still? If it does then rebuild removing SendData_SSD1331 and replacing with the optimised code.

     Do  
    
        For Offset = 0 to 12287+192 '2x96 pixels = one line extra to make it roll
            SendData_SSD1331 (Arrayframe(offset))
        next
    
        PORTF.3 = !PORTF.3 'changes state once per frame
    
    Loop
    
     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    SendData_SSD1331 (Arrayframe(offset)) -Works.

    Replacing that line with one of the two later testcodes, then DO goes high and stays high.
    F3 flickers as expected.

     
  • Anobium

    Anobium - 4 days ago

    Ok. I cannot, at this time, get this GLCD hooked up.

    So, time to hack. Edit the SSD1331 library and change the called library to a macro, and, do the same in hwspi library for FASTSPITRANSFER. This will put both methods inline with no calls. So, this will increase code size but it should improve speed.

    If this works then we have proven the approach. Best to revert the hacked library's.

    Give this a go.

     
  • Roger Jönsson

    Roger Jönsson - 4 days ago

    Hm. I am sorry, not able to grasp how to do what you are suggesting.
    But I found this. Putting some delays in there I could again get the white spot in the upper left corner. So there is some issue with timing also. I deliberately slowed it down to about 1-2 fps.
    When doing that I can control that corner pixel by changing the first two bytes of the Data Block. 0xF8 0x00 becomes bright red, which is according to the 565 16bit format and another value in the lower byte changes the colour, so it seems to read and send the two first bytes correctly. Since there is no visible traffic on DO over time, it is not stuck at sending these two bytes over and over.
    It appears that something is not incrementing. It just sends one pair of bytes and then does nothing. Weird.
    Removing SPI1RXB = SPI1RXB does not seem to have any effect.
    Ah, well. 17fps is ok and can maybe be improved later.

    ........
    //waits like this:

    Do
    // At this point we need to tell the glcd data is coming
    set SSD1331_DC ON
    wait 10 us
    For Offset = 0 to 12287+192 '2x96 pixels = one line extra to make it roll
    // ProgramRead (@BLOCK0 + Offset, value) '2 bytes = two loop rounds needed (each pixel is 16 bits)

        ;Source: hwspi.h (979)
        // FASTHWSPITRANSFER
        ;Master mode only
        ;One byte transfer count
        wait 10 us
        SPI1TCNTL = 1
        wait 10 us
        set SSD1331_CS OFF
        wait 10 us
            SPI1TXB = (Arrayframe(offset))
            wait 10 us
            wait while SPI1RXIF = SPI_RX_IN_PROGRESS
            wait 10 us
           SPI1RXB = SPI1RXB
           wait 10 us
        // Latch the transmission ?
        set SSD1331_CS On
    
        // SendData_SSD1331 (Arrayframe(offset))
    next
    
    PORTF.3 = !PORTF.3 'changes state once per frame
    
    // Deselect the GLCD
    set SSD1331_CS ON
    

    Loop

     
  • Anobium

    Anobium - 4 days ago

    OK. Let me send you two adapted files. Tomorrow.

     

Log in to post a comment.