Files
STM32F4-Base/Drivers/BSP/CH395F/ch395f_test.c

577 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称CH395F Driver Test Suite
* 模块功能CH395F 驱动各功能模块的测试入口,分阶段组织测试用例
* 适用平台STM32F4 系列CH395F 以太网芯片)
* 作者:王建锋
* 创建日期2026-07-19
* 修改记录:
* 2026-07-19 Phase 1/2/3 implemented
*/
/* 头文件包含区 */
#include "ch395f_test.h"
#include "ch395f.h"
#include "net_socket.h"
#define DBG_TAG "[CH395T]"
#include "dbg_log.h"
#include <string.h>
#include <stdio.h>
/*
* Phase 2 配置默认值
*/
#ifndef PHASE2_REMOTE_IP
#define PHASE2_REMOTE_IP "192.168.1.2"
#endif
#ifndef PHASE2_REMOTE_PORT
#define PHASE2_REMOTE_PORT 8081
#endif
#ifndef PHASE2_LOCAL_PORT
#define PHASE2_LOCAL_PORT 50000
#endif
#ifndef PHASE2_LOOP_COUNT
#define PHASE2_LOOP_COUNT 3
#endif
#ifndef PHASE2_TIMEOUT_MS
#define PHASE2_TIMEOUT_MS 10000
#endif
/*
* Phase 3 配置默认值
*/
#ifndef PHASE3_REMOTE_IP
#define PHASE3_REMOTE_IP "192.168.1.2"
#endif
#ifndef PHASE3_REMOTE_PORT
#define PHASE3_REMOTE_PORT 8082
#endif
#ifndef PHASE3_LOCAL_PORT
#define PHASE3_LOCAL_PORT 60000
#endif
#ifndef PHASE3_ECHO_COUNT
#define PHASE3_ECHO_COUNT 20
#endif
#ifndef PHASE3_TIMEOUT_MS
#define PHASE3_TIMEOUT_MS 30000
#endif
/*
* 恢复 TCP Server 多连接模式的数据 Socket 配置
*/
static void restore_data_sockets(uint16_t port)
{
int i;
for (i = NET_TCP_SERVER_DATA_SOCK_START; i < NET_MAX_SOCKETS; i++)
{
ch395f_set_proto_type((uint8_t)i, CH395F_PROTO_TYPE_TCP);
ch395f_set_sour_port((uint8_t)i, port);
}
DBG_INFO("data socks restored (port %d)", port);
}
/*
* ============================ Phase 1 ============================
*/
void ch395f_phase1_tests(void)
{
uint8_t i;
int ver;
DBG_INFO("=== CH395F Phase 1 Tests ===");
/* Test 1: GET_VERSION */
ver = ch395f_get_version();
DBG_INFO("GET_VERSION: 0x%02X (ver=%d)", ver, ver & 0x3F);
/* Test 2: GET_CMD_STATUS */
uint8_t cmd_st = ch395f_get_cmd_status();
DBG_INFO("GET_CMD_STATUS: 0x%02X (%s)",
cmd_st,
cmd_st == CH395F_ERR_SUCCESS ? "SUCCESS" :
cmd_st == CH395F_ERR_BUSY ? "BUSY" : "OTHER");
/* Test 3: GET_IP_INF */
uint8_t ip_info[20];
ch395f_get_ip_inf(ip_info);
DBG_INFO("GET_IP_INF:");
DBG_INFO(" IP: %d.%d.%d.%d", ip_info[0], ip_info[1], ip_info[2], ip_info[3]);
DBG_INFO(" GW: %d.%d.%d.%d", ip_info[4], ip_info[5], ip_info[6], ip_info[7]);
DBG_INFO(" MASK: %d.%d.%d.%d", ip_info[8], ip_info[9], ip_info[10], ip_info[11]);
DBG_INFO(" DNS1: %d.%d.%d.%d", ip_info[12], ip_info[13], ip_info[14], ip_info[15]);
DBG_INFO(" DNS2: %d.%d.%d.%d", ip_info[16], ip_info[17], ip_info[18], ip_info[19]);
DBG_INFO(" IP match: %s", (ip_info[0]==192 && ip_info[1]==168 && ip_info[2]==1 && ip_info[3]==100) ? "YES" : "NO");
DBG_INFO(" GW match: %s", (ip_info[4]==192 && ip_info[5]==168 && ip_info[6]==1 && ip_info[7]==1) ? "YES" : "NO");
DBG_INFO(" MASK match: %s", (ip_info[8]==255 && ip_info[9]==255 && ip_info[10]==255 && ip_info[11]==0) ? "YES" : "NO");
/* Test 4: GINT_STATUS_ALL (2-byte version) */
uint16_t gint_all = ch395f_get_glob_int_status_all();
DBG_INFO("GINT_STATUS_ALL: 0x%04X (lo=0x%02X hi=0x%02X)", gint_all, gint_all & 0xFF, (gint_all >> 8) & 0xFF);
/* Test 5: GINT_STATUS (1-byte version) — read-clear check */
uint8_t gint_1b = ch395f_get_glob_int_status();
DBG_INFO("GINT_STATUS_1byte (after clear): 0x%02X (expect 0x00 after read-clear)", gint_1b);
/* Test 6: SOCK_STATUS(0) */
{
uint8_t st[2];
ch395f_get_socket_status(0, st);
DBG_INFO("SOCK_STATUS(0): sock=0x%02X(OPEN), tcp=0x%02X(LISTEN)", st[0], st[1]);
}
/* Test 7: SET_ARP */
ch395f_set_arp(10, 5);
DBG_INFO("SET_ARP(period=10*100ms, cnt=5): OK");
/* Test 8: SET_TTL */
ch395f_set_ttl(0, 64);
DBG_INFO("SET_TTL(sock=0, ttl=64): OK");
/* Test 9: CLEAR_RECV_BUF + SOCK_INT poll */
ch395f_clear_recv_buf(0);
DBG_INFO("CLEAR_RECV_BUF(sock=0): OK");
for (i = 0; i < 8; i++)
{
uint8_t si = ch395f_get_sock_int_status(i);
DBG_INFO("SOCK_INT(%d): 0x%02X", i, si);
}
DBG_INFO("=== Phase 1 Tests Complete ===");
}
/*
* ============================ Phase 2 ============================
*/
void ch395f_phase2_tests(void)
{
uint8_t sock;
uint8_t status;
uint8_t sock_int;
uint8_t rx_buf[300];
int i;
uint32_t tick;
uint16_t int_st;
DBG_INFO("=== CH395F Phase 2 Tests (TCP Client - Direct Driver) ===");
/* 解析目标 IP */
uint8_t ip_arr[4];
{
uint32_t a[4];
sscanf(PHASE2_REMOTE_IP, "%lu.%lu.%lu.%lu", &a[0], &a[1], &a[2], &a[3]);
ip_arr[0] = (uint8_t)a[0];
ip_arr[1] = (uint8_t)a[1];
ip_arr[2] = (uint8_t)a[2];
ip_arr[3] = (uint8_t)a[3];
}
DBG_INFO("Target: %s:%d", PHASE2_REMOTE_IP, PHASE2_REMOTE_PORT);
sock = (uint8_t)(NET_MAX_SOCKETS - 1);
/* 一次性配置 */
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_TCP);
ch395f_set_send_buf(sock, 28, 2);
ch395f_set_recv_buf(sock, 30, 2);
ch395f_set_des_ip(sock, ip_arr);
ch395f_set_des_port(sock, PHASE2_REMOTE_PORT);
for (i = 0; i < PHASE2_LOOP_COUNT; i++)
{
DBG_INFO("--- Iteration %d/%d ---", i + 1, PHASE2_LOOP_COUNT);
ch395f_set_sour_port(sock, PHASE2_LOCAL_PORT + i);
status = ch395f_open_socket(sock);
if (status != CH395F_ERR_SUCCESS) { DBG_ERROR("open failed"); goto phase2_end; }
status = ch395f_tcp_connect(sock);
if (status != CH395F_ERR_SUCCESS) { DBG_ERROR("connect failed"); ch395f_close_socket(sock); goto phase2_end; }
/* 等待 CONNECT */
tick = HAL_GetTick();
while (HAL_GetTick() - tick < PHASE2_TIMEOUT_MS)
{
int_st = ch395f_get_glob_int_status_all();
if (int_st & (1U << (sock + 4)))
{
sock_int = ch395f_get_sock_int_status(sock);
if (sock_int & CH395F_SINT_STAT_CONNECT) { DBG_INFO("CONNECT"); break; }
}
HAL_Delay(10);
}
if (HAL_GetTick() - tick >= PHASE2_TIMEOUT_MS) { DBG_ERROR("connect timeout"); ch395f_close_socket(sock); goto phase2_end; }
HAL_Delay(100);
/* 发送测试数据 */
{
uint8_t test_buf[64];
const char *msg = "HelloFromCH395F_TCP_Client! This is a 64-byte test message.";
int p, mlen = (int)strlen(msg);
memcpy(test_buf, msg, mlen);
for (p = mlen; p < 64; p++) test_buf[p] = (uint8_t)('0' + (p - mlen) % 10);
DBG_INFO("Sending test data (64 bytes)");
ch395f_write_send_buf(sock, test_buf, 64);
}
/* Echo 循环 */
for (;;)
{
tick = HAL_GetTick();
while (HAL_GetTick() - tick < 10000)
{
uint16_t rlen = ch395f_get_recv_len(sock);
if (rlen > 0)
{
uint16_t read_len = (rlen > sizeof(rx_buf)) ? (uint16_t)sizeof(rx_buf) : rlen;
memset(rx_buf, 0, sizeof(rx_buf));
ch395f_read_recv_buf(sock, rx_buf, read_len);
DBG_INFO("recv: %d bytes [%.30s]", read_len, (char *)rx_buf);
ch395f_write_send_buf(sock, rx_buf, read_len);
break;
}
int_st = ch395f_get_glob_int_status_all();
if (int_st & (1U << (sock + 4)))
{
sock_int = ch395f_get_sock_int_status(sock);
if (sock_int & CH395F_SINT_STAT_DISCONNECT) { DBG_INFO("DISCONNECT"); goto phase2_iter_done; }
if (sock_int & CH395F_SINT_STAT_SOCK_TIMEOUT) { DBG_INFO("SOCK_TIMEOUT"); goto phase2_iter_done; }
}
HAL_Delay(50);
}
}
phase2_iter_done:
/* 关闭 socket */
ch395f_close_socket(sock);
{
uint8_t st[2];
uint32_t wait = HAL_GetTick();
while (HAL_GetTick() - wait < 5000)
{
ch395f_get_socket_status(sock, st);
if (st[0] == CH395F_SOCKET_CLOSED) break;
HAL_Delay(50);
}
DBG_INFO("closed: sock=0x%02X tcp=0x%02X", st[0], st[1]);
}
HAL_Delay(200);
}
phase2_end:
restore_data_sockets(8080);
DBG_INFO("=== Phase 2 Tests Complete ===");
}
/*
* ============================ Phase 3 ============================
*/
void ch395f_phase3_tests(void)
{
uint8_t sock;
uint8_t rx_buf[300];
uint32_t tick, echo_start;
int i;
DBG_INFO("=== CH395F Phase 3 Tests (UDP) ===");
sock = (uint8_t)(NET_MAX_SOCKETS - 1);
/* UDP Server 模式DesIP=0xFFFFFFFF → 接受任意来源 */
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_UDP);
ch395f_set_send_buf(sock, 28, 2);
ch395f_set_recv_buf(sock, 30, 2);
ch395f_set_sour_port(sock, PHASE3_LOCAL_PORT);
{
uint8_t zero_ip[4] = {0xFF, 0xFF, 0xFF, 0xFF};
ch395f_set_des_ip(sock, zero_ip);
}
ch395f_set_des_port(sock, 0);
if (ch395f_open_socket(sock) != CH395F_ERR_SUCCESS)
{
DBG_ERROR("UDP open failed");
goto phase3_end;
}
DBG_INFO("UDP socket %d opened (server, port=%d, accept any)", sock, PHASE3_LOCAL_PORT);
HAL_Delay(200);
/* Echo 循环:收到 → 解析来源 → 回显,最多 PHASE3_ECHO_COUNT 轮 */
echo_start = HAL_GetTick();
for (i = 0; i < PHASE3_ECHO_COUNT; i++)
{
tick = HAL_GetTick();
while (HAL_GetTick() - tick < PHASE3_TIMEOUT_MS)
{
uint16_t rlen = ch395f_get_recv_len(sock);
if (rlen > 8)
{
memset(rx_buf, 0, sizeof(rx_buf));
ch395f_read_recv_buf(sock, rx_buf, rlen);
/* 8 字节头: [0-1]reserved [2-3]src_port(LE) [4-7]src_ip [8+]payload */
uint16_t payload_len = rlen - 8;
uint16_t src_port = (uint16_t)rx_buf[2] | ((uint16_t)rx_buf[3] << 8);
uint8_t *src_ip = &rx_buf[4];
uint8_t *payload = &rx_buf[8];
DBG_INFO("UDP #%d: %dB from %d.%d.%d.%d:%d [%.32s]",
i + 1, payload_len,
src_ip[0], src_ip[1], src_ip[2], src_ip[3], src_port,
payload_len > 0 ? (char *)payload : "");
/* 回显到发送方 */
ch395f_set_des_ip(sock, src_ip);
ch395f_set_des_port(sock, src_port);
ch395f_write_send_buf(sock, payload, payload_len);
/* 等 SENDBUF_FREE 再收下一个CH395F 要求写前必须空闲) */
{
uint32_t swait = HAL_GetTick();
while (HAL_GetTick() - swait < 2000)
{
uint16_t gs = ch395f_get_glob_int_status_all();
if (gs & (1U << (sock + 4)))
{
if (ch395f_get_sock_int_status(sock) & CH395F_SINT_STAT_SENBUF_FREE)
break;
}
HAL_Delay(10);
}
}
break;
}
HAL_Delay(50);
}
if (HAL_GetTick() - tick >= PHASE3_TIMEOUT_MS)
{
DBG_INFO("UDP recv timeout (%ds)", PHASE3_TIMEOUT_MS / 1000);
break;
}
}
if (i >= PHASE3_ECHO_COUNT)
DBG_INFO("Max echoes reached (%d)", PHASE3_ECHO_COUNT);
else
DBG_INFO("Elapsed: %lums", HAL_GetTick() - echo_start);
ch395f_close_socket(sock);
DBG_INFO("UDP socket closed");
phase3_end:
restore_data_sockets(8080);
DBG_INFO("=== Phase 3 Tests Complete ===");
}
/*
* ============================ Phase 4 ============================
* NET 层 TCP Echo在主循环中调用用 net_recv + net_send 回显
*/
static char s_phase4_buf[2048];
void ch395f_phase4_tests(void)
{
int i;
#ifdef ENABLE_PHASE4_TESTS
for (i = NET_TCP_SERVER_DATA_SOCK_START; i < NET_MAX_SOCKETS; i++)
{
net_sock_t *sk = net_get_sock(i);
if (sk == NULL || !sk->in_use || sk->state != NET_SOCK_STATE_ESTABLISHED)
{
continue;
}
int n = net_recv(i, s_phase4_buf, sizeof(s_phase4_buf), NET_MSG_DONTWAIT);
if (n > 0)
{
net_send(i, s_phase4_buf, n, 0);
}
}
#else
(void)i;
(void)s_phase4_buf;
#endif
}
/*
* ============================ Phase 5 ============================
* NET 层 UDP Echo主循环中运行用 net_recvfrom + net_sendto 回显
*/
#ifndef PHASE5_LOCAL_PORT
#define PHASE5_LOCAL_PORT 60000
#endif
#ifdef ENABLE_PHASE5_TESTS
static int s_phase5_sock = -1;
static char s_phase5_buf[2048];
#endif
void ch395f_phase5_init(void)
{
#ifdef ENABLE_PHASE5_TESTS
struct net_sockaddr_in addr;
s_phase5_sock = net_socket(NET_AF_INET, NET_SOCK_DGRAM, 0);
if (s_phase5_sock < 0)
{
DBG_ERROR("Phase5: net_socket failed");
return;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = NET_AF_INET;
addr.sin_port = net_htons(PHASE5_LOCAL_PORT);
if (net_bind(s_phase5_sock, (struct net_sockaddr *)&addr, sizeof(addr)) < 0)
{
DBG_ERROR("Phase5: net_bind failed");
net_close(s_phase5_sock);
s_phase5_sock = -1;
return;
}
DBG_INFO("Phase5: UDP socket %d bound to port %d", s_phase5_sock, PHASE5_LOCAL_PORT);
#endif
}
void ch395f_phase5_tests(void)
{
#ifdef ENABLE_PHASE5_TESTS
if (s_phase5_sock < 0) return;
struct net_sockaddr_in src;
int addrlen = sizeof(src);
int n = net_recvfrom(s_phase5_sock, s_phase5_buf, sizeof(s_phase5_buf),
NET_MSG_DONTWAIT,
(struct net_sockaddr *)&src, &addrlen);
if (n > 0)
{
net_sendto(s_phase5_sock, s_phase5_buf, n, 0,
(struct net_sockaddr *)&src, sizeof(src));
}
#endif
}
/*
* ============================ Phase 6 ============================
* DHCP 自动获取 IP 测试
*/
void ch395f_phase6_tests(void)
{
#ifdef ENABLE_PHASE6_TESTS
uint8_t status;
uint8_t ip_info[20];
uint32_t tick;
DBG_INFO("=== CH395F Phase 6 Tests (DHCP) ===");
/* 1. 启用 DHCP */
ch395f_set_dhcp(1);
DBG_INFO("DHCP enabled, waiting for IP...");
/* 2. 轮询等待 DHCP 完成 */
HAL_Delay(500);
tick = HAL_GetTick();
while (HAL_GetTick() - tick < 30000)
{
uint16_t gint = ch395f_get_glob_int_status_all();
if (gint & CH395F_GINT_STAT_DHCP)
{
DBG_INFO("DHCP interrupt");
}
status = ch395f_get_dhcp_status();
if (status == 0x00)
{
DBG_INFO("DHCP: SUCCESS");
break;
}
HAL_Delay(500);
}
if (HAL_GetTick() - tick >= 30000)
{
status = ch395f_get_dhcp_status();
DBG_ERROR("DHCP timeout (status=0x%02X)", status);
goto phase6_end;
}
/* 3. 读取获取的 IP */
ch395f_get_ip_inf(ip_info);
DBG_INFO(" IP: %d.%d.%d.%d", ip_info[0], ip_info[1], ip_info[2], ip_info[3]);
DBG_INFO(" GW: %d.%d.%d.%d", ip_info[4], ip_info[5], ip_info[6], ip_info[7]);
DBG_INFO(" MASK: %d.%d.%d.%d", ip_info[8], ip_info[9], ip_info[10], ip_info[11]);
DBG_INFO(" DNS1: %d.%d.%d.%d", ip_info[12], ip_info[13], ip_info[14], ip_info[15]);
if (ip_info[0] == 0 && ip_info[1] == 0 && ip_info[2] == 0 && ip_info[3] == 0)
{
DBG_ERROR("DHCP: got 0.0.0.0 (failed)");
}
phase6_end:
DBG_INFO("=== Phase 6 Tests Complete ===");
#endif
}
/*
* ============================ Phase 7 ============================
* Select/Poll I/O 多路复用测试 — net_select() + net_recv/send echo
*/
#include "net_select.h"
#ifdef ENABLE_PHASE7_TESTS
static char s_phase7_buf[2048];
#endif
void ch395f_phase7_tests(void)
{
#ifdef ENABLE_PHASE7_TESTS
int i;
net_fd_set readfds;
net_timeval tv;
NET_FD_ZERO(&readfds);
/* 把 ESTABLISHED 状态的 TCP 数据 socket 加入监听集合 */
for (i = NET_TCP_SERVER_DATA_SOCK_START; i < NET_MAX_SOCKETS; i++)
{
net_sock_t *sk = net_get_sock(i);
if (sk && sk->in_use && sk->state == NET_SOCK_STATE_ESTABLISHED)
{
NET_FD_SET(i, &readfds);
}
}
/* 50ms 超时,避免阻塞主循环 */
tv.tv_sec = 0;
tv.tv_usec = 50000;
int n = net_select(NET_MAX_SOCKETS, &readfds, NULL, NULL, &tv);
if (n <= 0) return;
/* 处理就绪的 socket */
for (i = 0; i < NET_MAX_SOCKETS; i++)
{
if (!NET_FD_ISSET(i, &readfds)) continue;
int len = net_recv(i, s_phase7_buf, sizeof(s_phase7_buf), NET_MSG_DONTWAIT);
if (len > 0)
{
net_send(i, s_phase7_buf, len, 0);
}
}
#else
(void)0;
#endif
}