본문 바로가기

RaspberryPi/RP2040

[RP2040_W5500] Arduino IDE에서 Iperf TCP Throughput 측정 테스트 (Ethernet_Generic 라이브러리 수정)

 

RP2040 C++ SDK 환경에서 iperf 전송율 테스트 결과 전송 속도는 잘 나오지만 웹서버를 위한 라리브러리가 불편한점이 많다. 

 

[RP2040_W5500] RP2040 C++ SDK 환경에서 iperf 를 이용한 W5500 TCP 전송률 테스트

. Arduino IDE 환경에서 TCP전송률 테스트 결과 12Mbps 정도로 측정이 되서 다른 MCU들의 iperf 테스트 결과와 비교해 느린것 같아 SDK 환경에서 테스트 해볼 필요가 있을것 같다. RP2040 C/C++ SDK 개발환경 예

nexp.tistory.com

웹서버 라이브러리 (https://github.com/khoih-prog/EthernetWebServer)는 쉽고 간단하게 사용할 수 있어 편리 하지만 이더넷 전송 속도가 느리다.

 

그래서 이 라이브러리를 수정해서 이더넷 전송 속도를 RP2040 C++ SDK 환경과 비슷한 속도로 올려보자.

W5500은 SPI를 사용하기 때문에 RP2040의 SPI DMA를 사용하면 될것 같다.

 

우선 왜 속도가 느린지 파악해 보자

 

iperf 전송률 테스트코드는 아래와 같이 작성했다.

#include <SPI.h>
#include "Ethernet_Generic.h"
#include "config.h"

// gateway and subnet are optional:
byte mac[] = {
  0x00, 0x08, 0xDC, 0x00, 0x00, 0x00
};

IPAddress ip(192, 168, 11, 25);
IPAddress gateway(192, 168, 10, 1);
IPAddress dns_server(8, 8, 8, 8);
IPAddress subnet(255, 255, 254, 0);


EthernetServer server(5001);


void setup() {
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  
  Serial.println("SPI INIT");
  pinMode(USE_THIS_SS_PIN, OUTPUT);
  digitalWrite(USE_THIS_SS_PIN, 1);
  
  pinMode(13, OUTPUT);
  digitalWrite(13, 1);
  
  Ethernet.init (USE_THIS_SS_PIN);

  Ethernet.begin(mac, ip, dns_server, gateway, subnet);
  //Ethernet.begin(mac);
  Serial.println("...");
  
  // start listening for clients
  
  server.begin();
  Serial.print("Iperf server address : ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  byte buf[2048];
  unsigned int len = 0;
  
  // wait for a new client:
  EthernetClient client = server.available();

    if (client) {
    Serial.println("Here is new client for check arduino performance");

  while (client.connected())
    {
      len = client.available();
      if (len)
      {
       client.read(buf, 2048);
      }
   }
    // close the connection:
    client.stop();
    Serial.println("client disonnected");
  }
}

 

 

Ethernet_Generic 라이브러리(https://github.com/khoih-prog/Ethernet_Generic)를 사용할 경우 SPI1 으로 출력이 나온다.
#define ETHERNET_USE_RPIPICO      true
를 활성화 하니 정상 동작 한다.

 

 

W5500의 SPI 출력 부분을 처리 하는 부분은 write(), read() 함수 이다.

\Documents\Arduino\libraries\Ethernet_Generic\src\utility\w5100_Impl.h 

SPI전송을 transfer 함수로 전송수량 만큼 루프를 돌기 때문에 속도가 느리다. RP2040 SPI 테스트 참고(https://nexp.tistory.com/3683)
uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len)
{
:

 else
  {
    // chip == w5500
    setSS();
 :
    {
      pCUR_SPI->transfer(cmd, 3);
#ifdef SPI_HAS_TRANSFER_BUF
      pCUR_SPI->transfer(buf, NULL, len);
#else

      // TODO: copy 8 bytes at a time to cmd[] and block transfer
      for (uint16_t i = 0; i < len; i++)
      {
        pCUR_SPI->transfer(buf[i]);
      }

#endif
    }

    resetSS();
  }

  return len;
}



uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len)
{
  uint8_t cmd[4];

:

      // receive buffers
      cmd[0] = addr >> 8;
      cmd[1] = addr & 0xFF;
#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
      cmd[2] = 0x18;                       // 16K buffers
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
      cmd[2] = ((addr >> 8) & 0x20) | 0x18; // 8K buffers
#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
      cmd[2] = ((addr >> 7) & 0x60) | 0x18; // 4K buffers
#else
      cmd[2] = ((addr >> 6) & 0xE0) | 0x18; // 2K buffers
#endif
    }

    pCUR_SPI->transfer(cmd, 3);
    memset(buf, 0, len);
    pCUR_SPI->transfer(buf, len);
    resetSS();
  }

  return len;
}

 

 

버퍼모드로 설정하면 좀더 빨라 질까? RP2040 SPI테스트 결과로 봤을때 많이 개선되지는 않을것 같은데... 일단 버퍼 모드로 변경해 보자
#define SPI_HAS_TRANSFER_BUF      true
에러가 발생한다
In file included from c:\Users\Documents\Arduino\libraries\Ethernet_Generic\src/Ethernet_Generic_Impl.h:59,
                 from c:\Users\Documents\Arduino\libraries\Ethernet_Generic\src/Ethernet_Generic.h:85,
                 from D:\WORK\arduino\RP2040\w5500_iperf_generric\w5500_iperf_generric.ino:20:
c:\Users\Documents\Arduino\libraries\Ethernet_Generic\src/utility/w5100_Impl.h: In static member function 'static uint16_t W5100Class::write(uint16_t, const uint8_t*, uint16_t)':
c:\Users\Documents\Arduino\libraries\Ethernet_Generic\src/utility/w5100_Impl.h:935:38: error: no matching function for call to 'arduino::HardwareSPI::transfer(const uint8_t*&, NULL, uint16_t&)'
  935 |     pCUR_SPI->transfer(buf, NULL, len);
      |                                      ^
In file included from C:\Users\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\2.5.0\libraries\SPI\src/SPI.h:24,
                 from D:\WORK\arduino\RP2040\w5500_iperf_generric\w5500_iperf_generric.ino:4:
C:\Users\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\2.5.0\cores\rp2040/api/HardwareSPI.h:109:21: note: candidate: 'virtual uint8_t arduino::HardwareSPI::transfer(uint8_t)'
  109 |     virtual uint8_t transfer(uint8_t data) = 0;

 

#if ETHERNET_USE_RPIPICO 를 추가 해서 에러 없이 컴파일 된다.
uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len)
{

    else
    {
      pCUR_SPI->transfer(cmd, 3);
#ifdef SPI_HAS_TRANSFER_BUF
	#if ETHERNET_USE_RPIPICO
	pCUR_SPI->transfer((uint8_t*)buf, len);  
	#else
      pCUR_SPI->transfer(buf, NULL, len);  
    #endif
	  
#else

      // TODO: copy 8 bytes at a time to cmd[] and block transfer
      for (uint16_t i = 0; i < len; i++)
      {
        pCUR_SPI->transfer(buf[i]);
      }

#endif

 

하지만 iperf 전송율은 4Mbps로 느리다.

 


SPI를 DMA로 변경해 보자. RP2040 SPI DMA 사용하기 참고

 

#define SPI_X spi0
#define ETHERNET_USE_RPIPICO_DMA  true


uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len) 
{ 
    { 
       
#ifdef SPI_HAS_TRANSFER_BUF 
	#if ETHERNET_USE_RPIPICO 
		#if ETHERNET_USE_RPIPICO_DMA 
        spi_write_buf(cmd, 3); 
		spi_write_buf((uint8_t*)buf, len); 
  		#else 
		pCUR_SPI->transfer(cmd, 3); 
		pCUR_SPI->transfer((uint8_t*)buf, len);   
		#endif 
	#else 
	pCUR_SPI->transfer(cmd, 3); 
      pCUR_SPI->transfer(buf, NULL, len);   
    #endif 
	   
#else 
		pCUR_SPI->transfer(cmd, 3); 
      // TODO: copy 8 bytes at a time to cmd[] and block transfer 
      for (uint16_t i = 0; i < len; i++) 
      { 
        pCUR_SPI->transfer(buf[i]); 
      } 
#endif 


uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len) 
{ 
  uint8_t cmd[4]; 
    else 
    { 
	#if ETHERNET_USE_RPIPICO 
		#if ETHERNET_USE_RPIPICO_DMA 
        spi_write_buf(cmd, 3); 
		memset(buf, 0, len); 
		spi_read_buf(buf, len);  		 
		#else 
		pCUR_SPI->transfer(cmd, 3); 
		memset(buf, 0, len); 
		pCUR_SPI->transfer(buf, len); 
		#endif 
	#else 
    pCUR_SPI->transfer(cmd, 3); 
    memset(buf, 0, len); 
    pCUR_SPI->transfer(buf, len); 
    #endif

 

SPI를 DMA로 전송하도록 수정후 iperf TCP 전송률 테스트를 해 보면 12Mbps 로 RP2040 C++ SDK 개발환경에서의 속도만큼은 아지만 Aruino 개발환경에서도 많이 개선된것 같다.

 

속도는 조금 느리지만 편리한 라이브러리를 사용하면 쉽고 빠르게 웹서버를 구현할수 있을것 같다.

반응형