Interfacing DMA with SPI save camera frames to RAM buffer

Status
Not open for further replies.

kdharbert

Well-known member
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:
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/camera-breakout-board/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.
 
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.
 
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?
 
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.
 
Re: Teensy & OV5642 Any Progress?

kdharbert - How's your progress? Success?
I need to also interface OV5642/SPI/I2C & Teensy 4.1. Any chance of you sharing your code?
 
Will try standard library

kdharbert I am trying to stream out the Ethernet and hoping to get 10Fps or higher. As per your suggestion I will give the library a try and reach out is that approach isn't sufficient.
Thanks.
 
That will NOT work. Those units are incredibly frustrating in hiding the fact that they cannot be used like this. I switched to using the OV7670. It should be able to accomplish close to what you're looking for...as long as you're not using MATLAB to read the packets. It apparently has a throughput cap on ethernet...you can get frames, just not as fast as the OS ethernet and PC support.
 
Status
Not open for further replies.
Back
Top