# External Camera with SPI bus

**URL:** <https://forums.openmv.io/t/external-camera-with-spi-bus/1139>\
**Category:** OpenMV Boards\
**Created:** [February 18, 2019, 3:33am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139 "2019-02-18T03:33:52Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [February 18, 2019, 3:33am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/1 "2019-02-18T03:33:52Z")

</div>

Hi,  
This is my first contact with OpenMV, it is so powerful.  
But for my purpose that I want using an external camera (maybe with SPI bus) to replace embedded camera.  
It means I hope I can control my external camera with OpenMV IDE just like embedded camera (using “sensor” class to control it, and show the frames in framebuffer window).

So far, all I known is I need to modify the firmware, add a new driver to implement all functions in sensor\_t, and init my driver in sensor\_init (sensor.c).  
Is it enough to fit my purpose? Have I missed anything?

Thanks for everyone reply me, and welcome for any suggestions. ^.^

---

<div class="post-metadata">

**Author:** ![kwagyeman](https://yyz1.discourse-cdn.com/flex029/user_avatar/forums.openmv.io/kwagyeman/32/4_2.png) [@kwagyeman](https://forums.openmv.io/u/kwagyeman)\
**Post date:** [February 18, 2019, 3:44am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/2 "2019-02-18T03:44:31Z")

</div>

Hi, we’ve made this easier in our next firmware release. You will now be able to create images and then draw on them in python and then send these to the IDE. So, you need to edit our C code unless you need the speed. SPI can be done in python too.

If you want to do things in C it’s pretty easy to. Sensor.c shows off how to deal with multiple cameras. In fact, on the H7 we have a SPI bus to the camera connector for you to use for this (and an I2C bus there too along with the camera bus).

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [February 18, 2019, 4:14am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/3 "2019-02-18T04:14:46Z")

</div>

> Hi, we’ve made this easier in our next firmware release. You will now be able to create images and then draw on them in python and then send these to the IDE. So, you need to edit our C code unless you need the speed. SPI can be done in python too.

Are you mean I can read image (frame) for my camera (with SPI) and send the image to framebuffer or another speical way? Actually my camera FPS is only… maybe 10 ~ 30, not so fast.  
Would you please get me more information about this? Thanks so much.

> If you want to do things in C it’s pretty easy to. Sensor.c shows off how to deal with multiple cameras.

Yes, I saw you pick the camera in sensor\_init, and the driver implement functions that defined in sensor\_t, I was plan to follow that to implement my driver…

> In fact, on the H7 we have a SPI bus to the camera connector for you to use for this (and an I2C bus there too along with the camera bus).

I am sorry I not really understand this part, do you mean there is already have the API to access SPI? Do you have any example to show that?  
(BTW, my device is OpenMV3 R2 OV7725-M7)

Thanks for your quick reply, it is helpful to me.

---

<div class="post-metadata">

**Author:** ![kwagyeman](https://yyz1.discourse-cdn.com/flex029/user_avatar/forums.openmv.io/kwagyeman/32/4_2.png) [@kwagyeman](https://forums.openmv.io/u/kwagyeman)\
**Post date:** [February 19, 2019, 7:00am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/4 "2019-02-19T07:00:55Z")

</div>

Hi,

> Are you mean I can read image (frame) for my camera (with SPI) and send the image to framebuffer or another speical way? Actually my camera FPS is only… maybe 10 ~ 30, not so fast.  
> Would you please get me more information about this? Thanks so much.

So, like:

```python
img = Image(30, 40, sensor.GRAYSCALE, copy_to_fb=True)
data = spi.read(30 * 40)
for y in range(40):
    for x in range(30):
        img.set_pixel(data[(y*30)+x])

```

If you put code like that in a loop with our latest firmware (not yet released however) then you can manipulate the image in python. That said, python is kinda slow. So, a large image will not work fast. You have to go to C then.

> Yes, I saw you pick the camera in sensor\_init, and the driver implement functions that defined in sensor\_t, I was plan to follow that to implement my driver…

That’s the proper way in C.

```python
I am sorry I not really understand this part, do you mean there is already have the API to access SPI? Do you have any example to show that? 
(BTW, my device is OpenMV3 R2 OV7725-M7)

```

No, the hardware is just there. But, you have to write C code for whatever you want to do.

Note that the OpenMV Cam is quite a nice to use ARM platform. It has SWD support and can be programmed via USB using OpenMV IDE. With my dev environment re-flashing is simple.

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [February 21, 2019, 7:31am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/5 "2019-02-21T07:31:06Z")

</div>

> So, like:
> 
> ```python
> 
> ```

img = Image(30, 40, sensor.GRAYSCALE, copy\_to\_fb=True)  
data = spi.read(30 \* 40)  
for y in range(40):  
for x in range(30):  
img.set\_pixel(data[(y\*30)+x])

````auto
>

Wow, it looks great!!
I saw the spi API is already prepared, I guess the unfinished part is to flush Image's data to framebuffer, is it?

```python
img = Image(30, 40, sensor.GRAYSCALE, copy_to_fb=True)

````

==========================================================================

> No, the hardware is just there. But, you have to write C code for whatever you want to do.

I think I can reference sdcard\_spi.c to study how to use SPI API, do you have any better reference document/source code/suggestion?

```python
static BYTE spi_send(BYTE out)
...
static bool spi_send_buff(const BYTE *buff, uint32_t size)
...
static bool spi_recv_buff(BYTE *buff, uint32_t size)
...

```

Thank you!!

---

<div class="post-metadata">

**Author:** ![kwagyeman](https://yyz1.discourse-cdn.com/flex029/user_avatar/forums.openmv.io/kwagyeman/32/4_2.png) [@kwagyeman](https://forums.openmv.io/u/kwagyeman)\
**Post date:** [February 22, 2019, 2:50am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/6 "2019-02-22T02:50:56Z")

</div>

Um, just use the PYB SPI module: [class SPI – a master-driven serial protocol — MicroPython 1.9.3 documentation](http://docs.micropython.org/en/v1.9.3/pyboard/library/pyb.SPI.html)

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [February 22, 2019, 3:51am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/7 "2019-02-22T03:51:00Z")

</div>

> [@kwagyeman](#):
>
> Um, just use the PYB SPI module: [class SPI – a master-driven serial protocol — MicroPython 1.9.3 documentation](http://docs.micropython.org/en/v1.9.3/pyboard/library/pyb.SPI.html)

Ya, I have checked it before, when your new firmware is release, I will use it first.

But I should still write a C driver because my camera maybe support to FHD.  
So I just like to make sure what my understand is correct, the main SPI APIs in C are as follows,

```python
HAL_SPI_Init()
HAL_SPI_Receive()
HAL_SPI_TransmitReceive()
HAL_SPI_Transmit()

```

Where can I find the document/references for those APIs?

---

<div class="post-metadata">

**Author:** ![kwagyeman](https://yyz1.discourse-cdn.com/flex029/user_avatar/forums.openmv.io/kwagyeman/32/4_2.png) [@kwagyeman](https://forums.openmv.io/u/kwagyeman)\
**Post date:** [February 22, 2019, 5:29am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/8 "2019-02-22T05:29:16Z")

</div>

You have to read this:

> <https://github.com/openmv/micropython/blob/20dec3960516a082e27b812827ffcaf72d2c6916/ports/stm32/spi.c>

As for the STM32 HAL. They put the documentation in the c file. Not the h file. So, read the HAL C file.

> <https://github.com/micropython/stm32lib/blob/90b9961963b625db0f2aabfe8e5e508b1f4fb7f1/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_spi.c>

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [February 22, 2019, 8:42am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/9 "2019-02-22T08:42:38Z")

</div>

> [@kwagyeman](#):
>
> You have to read this:
> 
> [https://github.com/openmv/micropython/blob/20dec3960516a082e27b812827ffcaf72d2c6916/ports/stm32/spi.c](https://github.com/openmv/micropython/blob/20dec3960516a082e27b812827ffcaf72d2c6916/ports/stm32/spi.c)
> 
> As for the STM32 HAL. They put the documentation in the c file. Not the h file. So, read the HAL C file.
> 
> [https://github.com/micropython/stm32lib/blob/90b9961963b625db0f2aabfe8e5e508b1f4fb7f1/STM32F7xx\_HAL\_Driver/Src/stm32f7xx\_hal\_spi.c](https://github.com/micropython/stm32lib/blob/90b9961963b625db0f2aabfe8e5e508b1f4fb7f1/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_spi.c)

Got it, thank you.

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [March 11, 2019, 2:30am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/10 "2019-03-11T02:30:21Z")

</div>

**UPDATE** -  
I found out **MICROPY\_BEGIN\_ATOMIC\_SECTION()** maybe the key point of this behavior.  
If I remove **MICROPY\_BEGIN\_ATOMIC\_SECTION()** & **MICROPY\_END\_ATOMIC\_SECTION** , my code is not block anymore.  
But **HAL\_SPI\_Transmit()** return **HAL\_TIMEOUT** …

But I don’t know why it is happen. I just follow the procedure of spi.c, why does it blocking here?  
Would you please point out what I am wrong or miss? Thank you.

===========================================================  
I make 2 test and use the oscilloscope to observe its behavior.  
But one of them is failed, when I send data with SPI API, it seems block.  
Do you have any suggestion about how to debug in this situation or any idea about it?

Below is the detail. Thanks.

Test 1, control SPI and CS pin (with GPIO) in Python.  
It works great.

Test 2, control SPI and CS pin in C.  
I use **HAL\_GPIO\_Init** & **HAL\_GPIO\_WritePin** to control CS pin, it works.  
And I init SPI with **HAL\_SPI\_Init** , it return HAL\_OK, but when I send the data with **HAL\_SPI\_Transmit** , it seems block (Not crash, just like block in that API, OpenMV IDE show the device is busy.).

Here is my code, and I put the code in ov7725.c **get\_gain\_db()** to let me can easy call it with current API **sensor.get\_gain\_db()** … (you known, just a test…)  
One more strange thing, because my log “ooxx” is not show in terminal, that is why I thought it is block in the API…

```python
    SPIHandle.Instance = SPI2;
    SPIHandle.Init.Mode = SPI_MODE_MASTER;
    SPIHandle.Init.Direction = SPI_DIRECTION_2LINES;
    SPIHandle.Init.DataSize = SPI_DATASIZE_8BIT;
    SPIHandle.Init.CLKPolarity = SPI_POLARITY_HIGH;
    SPIHandle.Init.CLKPhase = SPI_CR1_CPHA;
    SPIHandle.Init.NSS = SPI_NSS_SOFT;
    SPIHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    SPIHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
    SPIHandle.Init.TIMode = SPI_TIMODE_DISABLED;
    SPIHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
    SPIHandle.Init.CRCPolynomial = 0;

    if (HAL_SPI_Init(&SPIHandle) != HAL_OK) 
    {
        /* Initialization Error */
        printf("HAL_SPI_Init() failed.");
        BREAK();
    }
    
    GPIO_InitStructure.Pin = CS_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(CS_PORT, &GPIO_InitStructure);

    W_CS_HIGH();

```

```python
    [b]mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();[/b]
    W_CS_LOW();
    printf("ooxx\n");
    bool res = (HAL_SPI_Transmit(
        &SPIHandle, data, 1, SPI_TIMEOUT) == HAL_OK);
    W_CS_HIGH();
    [b]MICROPY_END_ATOMIC_SECTION(atomic_state);[/b]
    return res;

```

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [March 14, 2019, 1:51am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/11 "2019-03-14T01:51:40Z")

</div>

Well, I found the root cause…  
I thought that SPI GPIO will be configured after call **HAL\_SPI\_Init()**, but when I checked its detail, I found out that **HAL\_SPI\_MspInit** is not implemented.  
So I need to manually configure the GPIO.

Below is my initial code, after that, my SPI2 controls are work fine.  
(Post here because I think that may help some people…)

```python
    // Init GPIO for SPI2.
    GPIO_InitTypeDef GPIO_InitStructure;

    __HAL_RCC_SPI2_CLK_ENABLE();

    GPIO_InitStructure.Pin = MISO_PIN | MOSI_PIN | SCLK_PIN;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStructure.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(SPI_PORT, &GPIO_InitStructure);

    // Init SPI
    memset(&SPIHandle, 0, sizeof(SPIHandle));
    SPIHandle.Instance = SPI2;
    SPIHandle.Init.Mode = SPI_MODE_MASTER;
    SPIHandle.Init.Direction = SPI_DIRECTION_2LINES;
    SPIHandle.Init.DataSize = SPI_DATASIZE_8BIT;
    SPIHandle.Init.CLKPolarity = SPI_POLARITY_HIGH;
    SPIHandle.Init.CLKPhase = SPI_CR1_CPHA;
    SPIHandle.Init.NSS = SPI_NSS_SOFT;
    SPIHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
    SPIHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
    SPIHandle.Init.TIMode = SPI_TIMODE_DISABLED;
    SPIHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
    SPIHandle.Init.CRCPolynomial = 0;
    if (HAL_SPI_Init(&SPIHandle) != HAL_OK) 
    {
        /* Initialization Error */
        printf("HAL_SPI_Init() failed.\n");
        BREAK();
    }

    // Init GPIO for SPI SS(CS) pin, software control.
    GPIO_InitStructure.Pin = CS_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(CS_PORT, &GPIO_InitStructure);

```

---

<div class="post-metadata">

**Author:** ![kwagyeman](https://yyz1.discourse-cdn.com/flex029/user_avatar/forums.openmv.io/kwagyeman/32/4_2.png) [@kwagyeman](https://forums.openmv.io/u/kwagyeman)\
**Post date:** [March 14, 2019, 3:54pm UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/12 "2019-03-14T15:54:41Z")

</div>

Hi, thanks for posting!

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [April 12, 2019, 6:49am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/13 "2019-04-12T06:49:32Z")

</div>

Hi kwagyeman and everyone,  
I got a good news, it is my SPI camera is workable… but bad news is, its performance is terrible… (sad)

So, I plan to speed my SPI r/w with SPI-DMA.  
My question is can I implement it directly without any HW change? (via SPI2)

If the answer is yes, where can I find the example or reference to study how to do it?  
I have checked the lepton.c, but it seems base on stm32H7 not F7…(there are couple APIs different…)

If the answer is no, how can I do to let DMA R/W SPI2?

Thanks for the any help…

---

<div class="post-metadata">

**Author:** ![kwagyeman](https://yyz1.discourse-cdn.com/flex029/user_avatar/forums.openmv.io/kwagyeman/32/4_2.png) [@kwagyeman](https://forums.openmv.io/u/kwagyeman)\
**Post date:** [April 12, 2019, 4:13pm UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/14 "2019-04-12T16:13:12Z")

</div>

Hi, the default MP spi read method uses DMA to suck the data in. It’s quite fast. What’s the SPI camera protocol that it requires a lot of interaction? Generally, the data transfer should move quite quickly. The FLIR Lepton was very complicated because the VOSPI protocol is complex. Otherwise, it should move fast.

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [April 15, 2019, 6:17am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/15 "2019-04-15T06:17:00Z")

</div>

> What’s the SPI camera protocol that it requires a lot of interaction?

I am not R/W SPI via MicroPython.  
I implement a new driver to control my camera with SPI, replace ovxxxx.c, and in my current implementation, I does not use DMA, so I just want to improve it.  
  
  
And thank you, I reference spi.c to study how to R/W SPI with DMA. But there is a little part that I don’t really understand what it means.  
In spi.c, I found a condition ‘!DMA\_BUFFER(src/dst)’, is it means I need allocate the buffer with a special API? Otherwise how can I make sure my buffer is end by 000011b?

```python
    #define DMA_BUFFER(p) ((uint32_t)p & 3)
     ...
     
        if (len == 1 || query_irq() == IRQ_STATE_DISABLED || !DMA_BUFFER(src)) {
	    // Read data directly
        } else {
            // Read data with DMA

```

---

<div class="post-metadata">

**Author:** ![iabdalkader](https://yyz1.discourse-cdn.com/flex029/user_avatar/forums.openmv.io/iabdalkader/32/1169_2.png) [@iabdalkader](https://forums.openmv.io/u/iabdalkader)\
**Post date:** [April 15, 2019, 4:08pm UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/16 "2019-04-15T16:08:00Z")

</div>

> [@](#):
>
> In spi.c, I found a condition ‘!DMA\_BUFFER(src/dst)’, is it means I need allocate the buffer with a special API? Otherwise how can I make sure my buffer is end by 000011b?
> 
> ```python
> #define DMA_BUFFER(p) ((uint32_t)p & 3)
> ...
>      
> if (len == 1 || query_irq() == IRQ_STATE_DISABLED || !DMA_BUFFER(src)) {
> // Read data directly
> } else {
> // Read data with DMA
> 
> ```

You need to reserve memory in the linker script that’s accessible by the DMA. This is different from chip to chip (see the datasheets).

EDIT: Using DMA is faster, but this is probably not the only issue, maybe check the SPI clock.

If you don’t mind, which SPI cam you’re using ? Link ?

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [April 22, 2019, 5:34am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/17 "2019-04-22T05:34:17Z")

</div>

Hi, sorry for late reply.

> You need to reserve memory in the linker script that’s accessible by the DMA.

Would you like to teach me how to set up it (my device is OpenMV3 R2)? I want R/W my SPI sensor with DMA.

And, a little question, according kwagyeman’s reply, OpenMV MicroPython SPI module is uses DMA, I think it means there is already reserve a memory for that.  
Why can I not just use the that memory? Is it because they will conflict?

> If you don’t mind, which SPI cam you’re using ? Link ?

The sensor is for internal research, not public.  
Thanks for your help.

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [April 22, 2019, 5:58am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/18 "2019-04-22T05:58:53Z")

</div>

After checked, can I use the following DMA configuration to implement my sensor driver?

```python
const dma_descr_t dma_SPI_2_RX = { DMA1_Stream3, DMA_CHANNEL_0, DMA_PERIPH_TO_MEMORY, dma_id_3, &dma_init_struct_spi_i2c };
const dma_descr_t dma_SPI_2_TX = { DMA1_Stream4, DMA_CHANNEL_0, DMA_MEMORY_TO_PERIPH, dma_id_4, &dma_init_struct_spi_i2c };

```

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [April 22, 2019, 6:25am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/19 "2019-04-22T06:25:40Z")

</div>

Excuse me, one more question…  
I still don’t understand ‘DMA\_BUFFER(src/dst)’. How is it be defined? Why 0011b?

```python
#define DMA_BUFFER(p) ((uint32_t)p & 3)

```

---

<div class="post-metadata">

**Author:** ![lak4cyut](https://avatars.discourse-cdn.com/v4/letter/l/87869e/32.png) [@lak4cyut](https://forums.openmv.io/u/lak4cyut)\
**Post date:** [April 30, 2019, 6:50am UTC](https://forums.openmv.io/t/external-camera-with-spi-bus/1139/20 "2019-04-30T06:50:12Z")

</div>

Hi iabdalkader & kwagyeman,  
Is there exists a bug about “DMA\_BUFFER” macro? I think it missing a ! (not).

After a little research, I guess this macro is used for check the align (4 bytes.)  
But it will return 0 (false) when target address is aligned on 4 bytes.

And your check condition is below, then means, DMA will only available when address NOT aligned on 4 bytes.  
I think that is a little strange. FYI.

```python
        if (len == 1 || query_irq() == IRQ_STATE_DISABLED || !DMA_BUFFER(src)) {
	    // Read data directly
        } else {
            // Read data with DMA
        }

```

BTW, I finish my implementation, succeed in read SPI via DMA.

[Next page](https://forums.openmv.io/t/external-camera-with-spi-bus/1139.md?page=2)
