RP2040 C++ SDK 환경에서 iperf 전송율 테스트 결과 전송 속도는 잘 나오지만 웹서버를 위한 라리브러리가 불편한점이 많다.
웹서버 라이브러리 (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 개발환경에서도 많이 개선된것 같다.
속도는 조금 느리지만 편리한 라이브러리를 사용하면 쉽고 빠르게 웹서버를 구현할수 있을것 같다.
반응형