使用menuconfig配置了w5500的驱动,开启了SPI+DMA并且在IDE中使用了Release配置,但使用iperf工具或之间使用tcp程序进行速度测试,包括在任务管理器中看到的速率都很低。
iperf的输出:
[680] 9.0-10.0 sec 168 KBytes 1376 Kbits/sec
rtconfig的配置:
#define RT_USING_PIN
#define RT_USING_SPI
#define RT_USING_SAL
#define SAL_INTERNET_CHECK
#define SAL_USING_POSIX
#define RT_USING_NETDEV
#define PKG_USING_NETUTILS
#define PKG_NETUTILS_IPERF
#define PKG_NETUTILS_TELNET
#define PKG_USING_NETUTILS_LATEST_VERSION
#define PKG_NETUTILS_VER_NUM 0x99999
#define PKG_USING_WIZNET
#define WIZ_USING_W5500
#define WIZ_USING_PING
#define PKG_USING_WIZNET_V200
#define SOC_STM32H743II
#define BSP_USING_W5500
#define BSP_USING_SPI
#define BSP_USING_SPI4
#define BSP_SPI4_TX_USING_DMA
分割线
我初步找到问题了,是STM32H7的SPI4相关时钟配置存在问题。之前认为SPI框架是成熟的,没有怀疑过时钟频率,今天用逻辑分析仪测了SCK频率只有3M左右的频率,原因是SPI驱动对时钟源取值时没有考虑时钟源Mux选择器的情况,需要手动修改时钟频率。
对此我添加了下面的代码
static uint32_t stm32_spi_get_clock(SPI_HandleTypeDef *spi_handle)
{
SPI_TypeDef *SPIx = spi_handle->Instance;
#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
return HAL_RCC_GetPCLK1Freq();
#elif defined(SOC_SERIES_STM32H7)
if (SPIx == SPI1 || SPIx == SPI2 || SPIx == SPI3)
{
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI123);
}
else if (SPIx == SPI4 || SPIx == SPI5)
{
uint32_t sel = __HAL_RCC_GET_SPI45_SOURCE();
if (sel == RCC_SPI45CLKSOURCE_D2PCLK1)
{
return HAL_RCC_GetPCLK2Freq();
}
else if (sel == RCC_SPI45CLKSOURCE_PLL2)
{
PLL2_ClocksTypeDef PLL2_Clocks;
HAL_RCCEx_GetPLL2ClockFreq(&PLL2_Clocks);
return PLL2_Clocks.PLL2_Q_Frequency;
}
else if (sel == RCC_SPI45CLKSOURCE_PLL3)
{
PLL3_ClocksTypeDef PLL3_Clocks;
HAL_RCCEx_GetPLL3ClockFreq(&PLL3_Clocks);
return PLL3_Clocks.PLL3_Q_Frequency;
}
else if (sel == RCC_SPI45CLKSOURCE_HSI)
{
return HSI_VALUE;
}
else if (sel == RCC_SPI45CLKSOURCE_CSI)
{
return CSI_VALUE;
}
else if (sel == RCC_SPI45CLKSOURCE_HSE)
{
return HSE_VALUE;
}
}
else
{
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI6);
}
#else
return HAL_RCC_GetPCLK2Freq();
#endif
return HAL_RCC_GetSysClockFreq();
}
现在iperf测试相对正常了,但该测试只对PC向MCU的下行速率进行了测试。
我编写了MCU向PC上传数据的测试代码,测试结果还是不理想,只有2.1Mbps。经过测量,发现SPI通讯线每隔8ms左右才发送一次2048字节的包,两个包中间分散着少量通讯,可能是在进行忙状态查询,不知道如何优化。
#include <sys/socket.h>
#include <netdb.h>
#define BUFSZ (2048)
/* 发送用到的数据 */
static const char send_data1[] = "AD7980 Ready\n";
static const char send_data2[] = "AD7980 NotFound\n";
#define TEST_SIZE 10000000 // 10MB
static char test_buf[BUFSZ];
static const char *send_data;
static void tcpserv(void param)
{
/ ---------------------AD7980 Data--------------------- /
rt_device_t drv_adc = rt_device_find("ad7980");
if (drv_adc == RT_NULL)
send_data = send_data2;
send_data = send_data1;
for (int i = 0; i < BUFSZ; i++)
test_buf[i] = i;
/ ---------------------Completion--------------------- */
rt_completion_init(&cpl_rx);
rt_device_set_rx_indicate(drv_adc, ad7980_rx_ind);
char recv_data; / 用于接收的指针,后面会做一次动态分配以请求可用内存 /
socklen_t sin_size;
int sock, connected, bytes_received;
struct sockaddr_in server_addr, client_addr;
rt_bool_t stop = RT_FALSE; / 停止标志 /
int ret;
recv_data = rt_malloc(BUFSZ + 1); / 分配接收用的数据缓冲 /
if (recv_data == RT_NULL)
{
rt_kprintf("No memory\n");
return;
}
/ 一个socket在使用前,需要预先创建出来,指定SOCK_STREAM为TCP的socket /
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/ 创建失败的错误处理 /
rt_kprintf("Socket error\n");
/ 释放已分配的接收缓冲 /
rt_free(recv_data);
return;
}
/ 初始化服务端地址 /
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5000); / 服务端工作的端口 /
server_addr.sin_addr.s_addr = INADDR_ANY;
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/ 绑定socket到服务端地址 */
if (bind(sock, (struct sockaddr )&server_addr, sizeof(struct sockaddr)) == -1)
{
/ 绑定失败 /
rt_kprintf("Unable to bind\n");
/ 释放已分配的接收缓冲 /
rt_free(recv_data);
return;
}
/ 在socket上进行监听 /
if (listen(sock, 5) == -1)
{
rt_kprintf("Listen error\n");
/ release recv buffer /
rt_free(recv_data);
return;
}
rt_kprintf("\nTCPServer Waiting for client on port 5000...\n");
while (stop != RT_TRUE)
{
sin_size = sizeof(struct sockaddr_in);
/ 接受一个客户端连接socket的请求,这个函数调用是阻塞式的 */
connected = accept(sock, (struct sockaddr )&client_addr, &sin_size);
/ 返回的是连接成功的socket /
if (connected < 0)
{
rt_kprintf("accept connection failed! errno = %d\n", errno);
continue;
}
/ 接受返回的client_addr指向了客户端的地址信息 /
rt_kprintf("I got a connection from (%s , %d)\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
/ 客户端连接的处理 /
while (1)
{
/ 发送数据到connected socket /
ret = send(connected, send_data, strlen(send_data), 0);
if (ret < 0)
{
/ 发送失败,关闭这个连接 /
closesocket(connected);
rt_kprintf("\nsend error,close the socket.\r\n");
break;
}
else if (ret == 0)
{
/ 打印send函数返回值为0的警告信息 /
rt_kprintf("\n Send warning,send function return 0.\r\n");
}
int sz = TEST_SIZE; // 10MB
rt_tick_t t0 = rt_tick_get_millisecond();
while (sz > 0)
{
/ 发送数据到connected socket /
ret = send(connected, test_buf, sizeof(test_buf), 0);
if (ret < 0)
{
/ 发送失败,关闭这个连接 /
closesocket(connected);
rt_kprintf("\nsend error,close the socket.\r\n");
break;
}
else if (ret == 0)
{
/ 打印send函数返回值为0的警告信息 /
rt_kprintf("\n Send warning,send function return 0.\r\n");
}
sz -= BUFSZ;
}
t0 = rt_tick_get_millisecond() - t0;
rt_kprintf("\n Send finish, time: %d s, avgspd: %d KB/s\r\n", (t0 + 500) / 1000, TEST_SIZE / t0);
/ 从connected socket中接收数据,接收buffer是1024大小,但并不一定能够收到1024大小的数据 /
bytes_received = recv(connected, recv_data, BUFSZ, 0);
if (bytes_received < 0)
{
/ 接收失败,关闭这个connected socket /
closesocket(connected);
break;
}
else if (bytes_received == 0)
{
/ 打印recv函数返回值为0的警告信息 /
rt_kprintf("\nReceived warning,recv function return 0.\r\n");
closesocket(connected);
break;
}
/ 有接收到数据,把末端清零 /
recv_data[bytes_received] = '\0';
if (strcmp(recv_data, "q") == 0 || strcmp(recv_data, "Q") == 0)
{
/ 如果是首字母是q或Q,关闭这个连接 /
closesocket(connected);
break;
}
else if (strcmp(recv_data, "exit") == 0)
{
/ 如果接收的是exit,则关闭整个服务端 /
closesocket(connected);
stop = RT_TRUE;
break;
}
else
{
/ 在控制终端显示收到的数据 /
rt_kprintf("RECEIVED DATA = %s \n", recv_data);
}
}
}
/ 退出服务 /
closesocket(sock);
/ 释放接收缓冲 */
rt_free(recv_data);
return;
}
输出结果 Send finish, time: 39 s, avgspd: 250 KB/s