Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 5 of 5

Thread: Interfacing DMA with SPI save camera frames to RAM buffer

  1. #1
    Senior Member
    Join Date
    Oct 2019
    Posts
    131

    Interfacing DMA with SPI to save camera frames to RAM buffer

    I need to process camera frames arriving from an Arducam OV5642 via SPI. If I add both QSPI chips I can get a 16MB buffer which is large enough for to store a RAW format or a JPEG format which can then be uncompressed to a RAW file.

    The default Arducam library code writes to the SD card, but I can modify it to write to a buffer instead. This sort of buffer copying is what DMA is used for. Iíve never used the Teensy DMA features, but Iíve heard of people using DMA features to speed SPI data handling like this. Does this sound similar to anything anyone has implemented?

    Questions:
    1. Does the peripheralís SPI interface have to support DMA to be used like this?
    2. It seems like I might be able to implement DMA by turning it on before a series of repeat SPI reads of predetermined length (ie 640x480) and then disabling it afterward. This way the arriving SPI data will be ferried to the buffer without any control traffic also being transferred into the buffer and corrupting the image. Is this a close guess to how it could work?
    Last edited by kdharbert; 04-16-2021 at 02:24 AM.

  2. #2
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    9,000
    I am assuming T4.1 as you mention QSPI...

    Assuming this, than you can access that 16mb memory is in the linear address space, so you can simply use addresses in that range.
    Two ways to allocate: define variables with the keyword EXTMEM and we have some malloc (extmem_malloc()) functions

    How to read from the camera? May depend on which camera? For example they do have some like:
    https://www.arducam.com/products/cam...rd/5mp-ov5642/
    Which is like the cameras we have been playing with except more resolution. Again we played with two versions of code to read in 8 bit parallel on the T4.1
    We have DMA GPIO and we have played with code that uses the CSI sub-system using DMA.

    As for doing simple DMA SPI - Just look at the SPI library. It has code to say transfer N bytes and you can specify a send buffer and/or receive buffer and it do that many transfers.

    Or you can look at many of the different display libraries like ILI9341_t3n ILI9488_t3, ST7735_t3... and all of these have SPI DMA... Most of them mainly do transfers to the display, so you would need to setup to go the opposite direction.

    I have not studied that cameras SPI interface so don't know what you have to do to read multiple frames.

    As to how to have it stuff your memory out, there are many options: Note: 640x480x2 (assuming 16 bit colors) will not fit in DMAChannel data structure count...
    Which has a max of 32767, probably need like 10 setups. But there are ways to do so, by for example having 10 DMASetting objects, where each object is chained to the next one (replaceOnCompletion...)
    And they can cycle back to the start and keep on reading... You can also do things like tell the 10th item in the chain to stop on completion, or interrupt on completion... (actually you have the options to interrupt at the end of a setting likewise at the halfway point per setting) on any of your settings...

    There is code doing this in all of the drivers I mentioned.

  3. #3
    Senior Member
    Join Date
    Oct 2019
    Posts
    131
    It was a typo mentioning QSPI...I meant PSRAM chips for memory...QSPI was a different message that day

    Regarding memory, I have the external memory stuff working via the EXTMEM command. As you say above, I need a DMA SPI receive, which the examples are not.

    Regarding the camera, I am using the OV5642 (the mini version without the parallel output) with I2C for config and SPI for data configuration. The stuff you have above is certainly great information if I switch to that version. Presently, I'm simply trying to streamline a 1x SPI receive channel.

    In my config, the number of bytes in the payload is read via I2C and then SPI is used to read them by transferring 0x00 over and over. Sending 0x00 to execute a read is pretty standard so I suspect I can easily use a generic DMA SPI Rx example if I can find one.

  4. #4
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    9,000
    Again have no idea of your requirements. Like does your program need to do anything while the camera is reading using SPI?

    If not, it can be as simple as: SPI.transfer(nullptr, receive_buffer, count);
    And the system will transfer 0s for all of those bytes and all of the data will be put int receive_buffer. The receive buffer can be in any writable memory region including extmem.

    and again if you wish to do other things, you can use SPI library like SPI.transfer(nullptr, receive_buffer, count, event_responder);
    Which will transfer that number of bytes. This call will return immediately and when the transfer is complete the event responder code will be triggered which can be configured a few different ways to notify you...

    Or again, there are several examples out there in the release that use DMA.
    Simply search for everything that includes DMAChannel.h
    Again like ILI9341_t3n.h/cpp files
    Look for areas that are T4.x like in the header file:

    In the IMXRT section you will see:
    Code:
      DMASetting _dmasettings[3];
      DMAChannel _dmatx;
    So we have 3 setting objects: 320*240/32767= 2.34... So we need 3 transfers.

    And be ready to look into the IMXRT pdf file to understand things... Chapters 5 and 6...

    So for example in the above _dmatx in the main DMA Channel. Now again in our case we are doing transfers to the display...
    So we are doing TX...

    And again in source .cpp file file, you will see stuff like:
    Code:
        _dmasettings[0].sourceBuffer(_pfbtft, (COUNT_WORDS_WRITE)*2);
        _dmasettings[0].destination(_pimxrt_spi->TDR);
        _dmasettings[0].TCD->ATTR_DST = 1;
        _dmasettings[0].replaceSettingsOnCompletion(_dmasettings[1]);
    
        _dmasettings[1].sourceBuffer(&_pfbtft[COUNT_WORDS_WRITE],
                                     COUNT_WORDS_WRITE * 2);
        _dmasettings[1].destination(_pimxrt_spi->TDR);
        _dmasettings[1].TCD->ATTR_DST = 1;
        _dmasettings[1].replaceSettingsOnCompletion(_dmasettings[2]);
        if (_frame_callback_on_HalfDone)
          _dmasettings[1].interruptAtHalf();
        else
          _dmasettings[1].TCD->CSR &= ~DMA_TCD_CSR_INTHALF;
    
        _dmasettings[2].sourceBuffer(&_pfbtft[COUNT_WORDS_WRITE * 2],
                                     COUNT_WORDS_WRITE * 2);
        _dmasettings[2].destination(_pimxrt_spi->TDR);
        _dmasettings[2].TCD->ATTR_DST = 1;
        _dmasettings[2].replaceSettingsOnCompletion(_dmasettings[0]);
        _dmasettings[2].interruptAtCompletion();
    You can see we split the frame up into 3 equal sizes since 320*240/3 is an integer...
    Now again we are setup to output to SPI... So destination is to TDR register... Source is memory addresss.
    To input you need to change Destinationbuffer to your memory....
    And the source from SPI read register (RDR).

    You may also need to setup a DMA to TDR, with maybe a fixed memory buffer with 0 in it (like I did in SPI)...
    You will also need to then setup the _dmatx... Again more in the code...

    But the question is, do you need to go this level?

  5. #5
    Senior Member
    Join Date
    Oct 2019
    Posts
    131
    Yes the Teensy needs to do other things while the SPI is taking place. I was not aware of the approach above using eventresponder. This is really all I think I need. However, can you confirm efficiency of this method vs DMA other. Eventresponder looks like a step forward, but in the end I'll need to go to the trouble of doing whatever is fastest.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •