본문 바로가기

RaspberryPi/RP2350

[RP2350_W6100] Pi Pico C/C++ SDK에서 SPI DMA 테스트

Pi Pico C/C++ SDK 환경에서 GPIO토글 속도 측정 결과와 비교해서 Arduino 환경보다 빠른것 같은데 SPI 속도도 측정해서 비교해 보자

 

W6100과 같은 SPI의 전송 속도가 이더넷 전송률에 영향을 미치는 어플리케이션을 위해 SDK환경에서 SPI 테스트를 해 둘 필요가 있을것 같다.

 

SDK 환경에서 SPI 전송 테스트를 해보면 SPI Bye 전송 지연은 680ns로 Arduino 환경과 비슷하게 측정이 된다.

 

 

#include <stdio.h>
#include "pico/binary_info.h"
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/uart.h"

// SPI Defines
// We are going to use SPI 0, and allocate it to the following GPIO pins
// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments
#define SPI_PORT spi0
#define PIN_MISO 16
#define PIN_CS   17
#define PIN_SCK  18
#define PIN_MOSI 19


void spi_initialize(void)
{
    spi_init(SPI_PORT, 37.5 * 1000 * 1000);
    
    gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
    gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
    gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);

    // make the SPI pins available to picotool
    bi_decl(bi_3pins_with_func(PIN_MISO, PIN_MOSI, PIN_SCK, GPIO_FUNC_SPI));
}


int main()
{
    unsigned char  buffer[10];
    stdio_init_all();

    // SPI initialisation.
    spi_initialize();
    
    // Chip select is active-low, so we'll initialise it to a driven-high state
    gpio_init(PIN_CS);
    gpio_set_dir(PIN_CS, GPIO_OUT);
    gpio_put(PIN_CS, 1);

    gpio_init(25);
    gpio_set_dir(25, GPIO_OUT);
    gpio_put(25, 0);   

    while (true) {
        spi_write_blocking(SPI_PORT, buffer, 2);
        spi_write_blocking(SPI_PORT, buffer, 2);
    }
}

 

 

옵티마이즈 옵션을 최대로 해서 측정해 보자

 

# Add executable. Default name is the project name, version 0.1

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")

 

 

 

컴파일 옵티마이즈후 SPI Byte전송 지연이 48ns로 빨라 졌다. Arduino 환경에서  DMA를 사용한것과 거의 동일하게 빨라 졌다.

 

 

그렇다면 DMA를 사용한다면 더 빨라 지려나?

RP2350에서 SPI DMA를 이용한 전송 테스트를 해 보자

#include <stdio.h>
#include "pico/binary_info.h"
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/uart.h"


// SPI Defines
// We are going to use SPI 0, and allocate it to the following GPIO pins
// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments
#define SPI_PORT spi0
#define PIN_MISO 16
#define PIN_CS   17
#define PIN_SCK  18
#define PIN_MOSI 19


#define USE_SPI_DMA


#ifdef USE_SPI_DMA
#include "hardware/dma.h"

static uint dma_tx;
static uint dma_rx;
static dma_channel_config dma_channel_config_tx;
static dma_channel_config dma_channel_config_rx;

void spi_write_dma(uint8_t *pBuf, uint16_t len)
{
    uint8_t dummy_data;


    channel_config_set_read_increment(&dma_channel_config_tx, true);
    channel_config_set_write_increment(&dma_channel_config_tx, false);
    dma_channel_configure(dma_tx, &dma_channel_config_tx,
                          &spi_get_hw(SPI_PORT)->dr, // write address
                          pBuf,                      // read address
                          len,                       // element count (each element is of size transfer_data_size)
                          false);                    // don't start yet


    channel_config_set_read_increment(&dma_channel_config_rx, false);
    channel_config_set_write_increment(&dma_channel_config_rx, false);
    dma_channel_configure(dma_rx, &dma_channel_config_rx,
                          &dummy_data,               // write address
                          &spi_get_hw(SPI_PORT)->dr, // read address
                          len,                       // element count (each element is of size transfer_data_size)
                          false);                    // don't start yet


    dma_start_channel_mask((1u << dma_tx) | (1u << dma_rx));
    dma_channel_wait_for_finish_blocking(dma_rx);
}
#endif


void spi_initialize(void)
{
    spi_init(SPI_PORT, 37.5 * 1000 * 1000);
   
    gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
    gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
    gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);


    // make the SPI pins available to picotool
    bi_decl(bi_3pins_with_func(PIN_MISO, PIN_MOSI, PIN_SCK, GPIO_FUNC_SPI));


    #ifdef USE_SPI_DMA
    dma_tx = dma_claim_unused_channel(true);
    dma_rx = dma_claim_unused_channel(true);


    dma_channel_config_tx = dma_channel_get_default_config(dma_tx);
    channel_config_set_transfer_data_size(&dma_channel_config_tx, DMA_SIZE_8);
    channel_config_set_dreq(&dma_channel_config_tx, DREQ_SPI0_TX);


    // We set the inbound DMA to transfer from the SPI receive FIFO to a memory buffer paced by the SPI RX FIFO DREQ
    // We coinfigure the read address to remain unchanged for each element, but the write
    // address to increment (so data is written throughout the buffer)
    dma_channel_config_rx = dma_channel_get_default_config(dma_rx);
    channel_config_set_transfer_data_size(&dma_channel_config_rx, DMA_SIZE_8);
    channel_config_set_dreq(&dma_channel_config_rx, DREQ_SPI0_RX);
    channel_config_set_read_increment(&dma_channel_config_rx, false);
    channel_config_set_write_increment(&dma_channel_config_rx, true);
    #endif    
}


int main()
{
    unsigned char  buffer[10];
    stdio_init_all();


    // SPI initialisation.
    spi_initialize();
   
    // Chip select is active-low, so we'll initialise it to a driven-high state
    gpio_init(PIN_CS);
    gpio_set_dir(PIN_CS, GPIO_OUT);
    gpio_put(PIN_CS, 1);


    gpio_init(25);
    gpio_set_dir(25, GPIO_OUT);
    gpio_put(25, 0);  
   
    while (true) {
        #ifdef USE_SPI_DMA        
        spi_write_dma(buffer, 2);  
        spi_write_dma(buffer, 2);
        #else
        spi_write_blocking(SPI_PORT, buffer, 2);
        spi_write_blocking(SPI_PORT, buffer, 2);
        #endif
       
        sleep_ms(10);              
    }
}

 

 

 

DMA 사용시 48ns로 10배이상 빨라진다.




옵티마이즈 했을 경우 DMA 블럭 전송 지연은 440ns로 측정이 된다.



옵티마이즈 옵션을 사용하지 않으면 6200ns로 느려진다.

 

반응형