构建测试
This commit is contained in:
@@ -18,13 +18,12 @@
|
||||
#include "net_socket.h"
|
||||
#include "net_select.h"
|
||||
#include "ch395f.h"
|
||||
#include "ch395f_test.h"
|
||||
|
||||
#define DBG_TAG "[NET_TASK]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* 测试缓冲大小定义(供各 Phase 测试使用) */
|
||||
#define NET_TASK_BUF_SIZE 65536
|
||||
/* 网络初始化完成标志(供 ch395f_test_task 等外部任务等待) */
|
||||
volatile uint8_t g_net_ready = 0;
|
||||
|
||||
/*
|
||||
* 函数功能:网络任务初始化
|
||||
@@ -72,6 +71,7 @@ void net_task_func(void const *arg)
|
||||
(phy == CH395F_PHY_10M_FULL) ? "10M FULL" :
|
||||
(phy == CH395F_PHY_10M_HALF) ? "10M HALF" :
|
||||
(phy == CH395F_PHY_DISCONN) ? "DISCONNECT" : "UNKNOWN");
|
||||
g_net_ready = 1;
|
||||
} else {
|
||||
DBG_ERROR("net init: FAIL");
|
||||
}
|
||||
@@ -82,36 +82,6 @@ void net_task_func(void const *arg)
|
||||
for (;;) {
|
||||
net_poll();
|
||||
net_process_messages();
|
||||
|
||||
/*
|
||||
* 以下测试共用 Socket 0(单连接模式):
|
||||
* Phase 4 — TCP Echo 基础收发
|
||||
* Phase 5 — 大文件传输
|
||||
* Phase 7 — Select/Poll
|
||||
* Phase 8 — 边界条件
|
||||
* Phase 10 — KeepAlive
|
||||
* 各 Phase 通过 ch395f_test.h 中的宏独立开关
|
||||
*/
|
||||
#ifdef ENABLE_PHASE4_TESTS
|
||||
ch395f_phase4_tests();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_PHASE5_TESTS
|
||||
ch395f_phase5_tests();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_PHASE7_TESTS
|
||||
ch395f_phase7_tests();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_PHASE8_TESTS
|
||||
ch395f_phase8_tests();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_PHASE10_TESTS
|
||||
ch395f_phase10_tests();
|
||||
#endif
|
||||
|
||||
osDelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -528,13 +528,6 @@ uint16_t ch395f_get_recv_len(uint8_t sock);
|
||||
* 函数声明区 - 中断状态
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:获取全局中断状态
|
||||
* 返回值:中断状态字节 uint8_t
|
||||
* 限定条件:芯片已初始化
|
||||
*/
|
||||
uint8_t ch395f_get_glob_int_status(void);
|
||||
|
||||
/*
|
||||
* 函数功能:获取全局中断状态(2 字节版,支持 Socket 0~7)
|
||||
* 返回值:中断状态 uint16_t(低字节=Socket 0~3 + PHY/DHCP,高字节=Socket 4~7 + DHCPv6)
|
||||
|
||||
@@ -1,914 +0,0 @@
|
||||
/*
|
||||
* 模块名称:CH395F Driver Test Suite
|
||||
* 模块功能:CH395F 驱动各功能模块的测试入口,分阶段组织测试用例,
|
||||
* 覆盖 SPI 命令路径、Socket 状态机、大文件传输、边界条件等
|
||||
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* 2026-07-19 Phase 1/2/3 implemented
|
||||
* 2026-07-22 全面重构为单连接模式,增加 Phase 5/8/10
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "ch395f_test.h"
|
||||
#include "ch395f.h"
|
||||
#include "net_socket.h"
|
||||
#include "net_select.h"
|
||||
#define DBG_TAG "[CH395T]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
test_stats_t g_test_stats;
|
||||
|
||||
/*
|
||||
* 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 30
|
||||
#endif
|
||||
#ifndef PHASE3_TIMEOUT_MS
|
||||
#define PHASE3_TIMEOUT_MS 30000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Phase 5 配置默认值(大文件传输)
|
||||
*/
|
||||
#ifndef PHASE5_LOCAL_PORT
|
||||
#define PHASE5_LOCAL_PORT 60000
|
||||
#endif
|
||||
#ifndef PHASE5_FILE_SIZE
|
||||
#define PHASE5_FILE_SIZE 20480 /* 20KB */
|
||||
#endif
|
||||
#ifndef PHASE5_CHUNK_SIZE
|
||||
#define PHASE5_CHUNK_SIZE 4096 /* 单次 NET 层收发块大小 */
|
||||
#endif
|
||||
#ifndef PHASE5_BUF_SIZE
|
||||
#define PHASE5_BUF_SIZE 65536 /* 接收缓冲最大 64KB */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Phase 8 配置默认值(边界条件)
|
||||
*/
|
||||
#ifndef PHASE8_LOCAL_PORT
|
||||
#define PHASE8_LOCAL_PORT 8080
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Phase 10 配置默认值(KeepAlive)
|
||||
*/
|
||||
#ifndef PHASE10_KEEPALIVE_IDLE_MS
|
||||
#define PHASE10_KEEPALIVE_IDLE_MS 60000 /* 空闲 60 秒触发探测 */
|
||||
#endif
|
||||
|
||||
/* ============================ Phase 1 ============================ */
|
||||
|
||||
void ch395f_phase1_tests(void) {
|
||||
uint8_t i = 0;
|
||||
int ver = 0;
|
||||
|
||||
DBG_INFO("=== CH395F Phase 1 Tests ===");
|
||||
|
||||
ver = ch395f_get_version();
|
||||
TEST_CHECK(ver > 0, "GET_VERSION: 0x%02X (ver=%d)", ver, ver & 0x3F);
|
||||
|
||||
uint8_t cmd_st = ch395f_get_cmd_status();
|
||||
TEST_CHECK(cmd_st == CH395F_ERR_SUCCESS,
|
||||
"GET_CMD_STATUS: 0x%02X (%s)", cmd_st,
|
||||
cmd_st == CH395F_ERR_SUCCESS ? "SUCCESS" :
|
||||
cmd_st == CH395F_ERR_BUSY ? "BUSY" : "OTHER");
|
||||
|
||||
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]);
|
||||
TEST_CHECK(ip_info[0]==192 && ip_info[1]==168 && ip_info[2]==1 && ip_info[3]==100,
|
||||
"IP match");
|
||||
TEST_CHECK(ip_info[4]==192 && ip_info[5]==168 && ip_info[6]==1 && ip_info[7]==1,
|
||||
"GW match");
|
||||
TEST_CHECK(ip_info[8]==255 && ip_info[9]==255 && ip_info[10]==255 && ip_info[11]==0,
|
||||
"MASK match");
|
||||
|
||||
uint16_t gint_all = ch395f_get_glob_int_status_all();
|
||||
TEST_CHECK((gint_all & 0xFF) == 0x04 || (gint_all & 0xFF) == 0x00,
|
||||
"GINT_STATUS_ALL: 0x%04X", gint_all);
|
||||
|
||||
uint8_t gint_1b = ch395f_get_glob_int_status();
|
||||
TEST_CHECK(gint_1b == 0x00,
|
||||
"GINT_STATUS_1byte: 0x%02X (expect 0x00)", gint_1b);
|
||||
|
||||
{
|
||||
uint8_t st[2];
|
||||
ch395f_get_socket_status(0, st);
|
||||
TEST_CHECK(st[0] == 0x05 && st[1] == 0x01,
|
||||
"SOCK_STATUS(0): sock=0x%02X, tcp=0x%02X", st[0], st[1]);
|
||||
}
|
||||
|
||||
ch395f_set_arp(10, 5);
|
||||
TEST_CHECK(1, "SET_ARP(period=10*100ms, cnt=5): OK");
|
||||
|
||||
ch395f_set_ttl(0, 64);
|
||||
TEST_CHECK(1, "SET_TTL(sock=0, ttl=64): OK");
|
||||
|
||||
ch395f_clear_recv_buf(0);
|
||||
TEST_CHECK(1, "CLEAR_RECV_BUF(sock=0): OK");
|
||||
for (i = 0; i < 8; i++) {
|
||||
uint8_t si = ch395f_get_sock_int_status(i);
|
||||
TEST_CHECK(si == 0x00, "SOCK_INT(%d): 0x%02X", i, si);
|
||||
}
|
||||
|
||||
TEST_REPORT("Phase 1");
|
||||
}
|
||||
|
||||
/* ============================ Phase 2 ============================ */
|
||||
|
||||
void ch395f_phase2_tests(void) {
|
||||
uint8_t sock = 0;
|
||||
uint8_t sock_int = 0;
|
||||
uint8_t rx_buf[300] = {0};
|
||||
int i = 0;
|
||||
uint32_t tick = 0;
|
||||
uint16_t int_st = 0;
|
||||
int iter_pass = 0;
|
||||
|
||||
DBG_INFO("=== CH395F Phase 2 Tests (TCP Client - Direct Driver) ===");
|
||||
|
||||
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 = 0;
|
||||
|
||||
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_TCP);
|
||||
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);
|
||||
iter_pass = 1;
|
||||
|
||||
ch395f_set_sour_port(sock, PHASE2_LOCAL_PORT + i);
|
||||
|
||||
if (ch395f_open_socket(sock) != CH395F_ERR_SUCCESS) { TEST_CHECK(0, "open socket"); iter_pass = 0; goto phase2_iter_done; }
|
||||
if (ch395f_tcp_connect(sock) != CH395F_ERR_SUCCESS) { TEST_CHECK(0, "tcp connect"); iter_pass = 0; ch395f_close_socket(sock); goto phase2_iter_done; }
|
||||
|
||||
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) break;
|
||||
}
|
||||
HAL_Delay(10);
|
||||
}
|
||||
if (HAL_GetTick() - tick >= PHASE2_TIMEOUT_MS) { TEST_CHECK(0, "connect timeout"); iter_pass = 0; ch395f_close_socket(sock); goto phase2_iter_done; }
|
||||
|
||||
HAL_Delay(100);
|
||||
|
||||
{
|
||||
uint8_t test_buf[64] = {0};
|
||||
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);
|
||||
ch395f_write_send_buf(sock, test_buf, 64);
|
||||
}
|
||||
|
||||
{
|
||||
int got_echo = 0;
|
||||
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);
|
||||
got_echo = 1;
|
||||
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"); break; }
|
||||
if (sock_int & CH395F_SINT_STAT_SOCK_TIMEOUT) { DBG_INFO("SOCK_TIMEOUT"); break; }
|
||||
}
|
||||
HAL_Delay(50);
|
||||
}
|
||||
if (!got_echo) { TEST_CHECK(0, "echo recv (iter %d)", i + 1); iter_pass = 0; }
|
||||
}
|
||||
|
||||
phase2_iter_done:
|
||||
if (iter_pass) TEST_CHECK(1, "Iteration %d PASS", i + 1);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_REPORT("Phase 2");
|
||||
}
|
||||
|
||||
/* ============================ Phase 3 ============================ */
|
||||
|
||||
void ch395f_phase3_tests(void) {
|
||||
uint8_t sock = 0;
|
||||
uint8_t rx_buf[300] = {0};
|
||||
uint32_t tick = 0;
|
||||
int i = 0;
|
||||
int echo_ok = 0;
|
||||
|
||||
DBG_INFO("=== CH395F Phase 3 Tests (UDP) ===");
|
||||
|
||||
sock = 0;
|
||||
|
||||
ch395f_set_proto_type(sock, CH395F_PROTO_TYPE_UDP);
|
||||
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);
|
||||
|
||||
TEST_CHECK(ch395f_open_socket(sock) == CH395F_ERR_SUCCESS,
|
||||
"UDP socket %d opened (port=%d)", sock, PHASE3_LOCAL_PORT);
|
||||
|
||||
HAL_Delay(200);
|
||||
|
||||
for (i = 0; i < PHASE3_ECHO_COUNT; i++) {
|
||||
tick = HAL_GetTick();
|
||||
echo_ok = 0;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
echo_ok = 1;
|
||||
break;
|
||||
}
|
||||
HAL_Delay(50);
|
||||
}
|
||||
TEST_CHECK(echo_ok, "UDP echo #%d/%d", i + 1, PHASE3_ECHO_COUNT);
|
||||
if (!echo_ok) break;
|
||||
}
|
||||
|
||||
ch395f_close_socket(sock);
|
||||
TEST_CHECK(1, "UDP socket closed");
|
||||
|
||||
TEST_REPORT("Phase 3");
|
||||
}
|
||||
|
||||
/* ============================ Phase 4 ============================ */
|
||||
/*
|
||||
* NET 层 TCP Echo(单连接模式)
|
||||
* 在 netTask 中运行,仅操作 Socket 0
|
||||
* 覆盖:64B、1460B(MSS)、4096B(缓冲满) 回显
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_PHASE4_TESTS
|
||||
static char s_phase4_buf[8192];
|
||||
static uint32_t s_phase4_echo_count = 0;
|
||||
#endif
|
||||
|
||||
void ch395f_phase4_tests(void) {
|
||||
#ifdef ENABLE_PHASE4_TESTS
|
||||
net_sock_t *sk = net_get_sock(0);
|
||||
if (sk == NULL || !sk->in_use) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sk->state == NET_SOCK_STATE_ESTABLISHED) {
|
||||
int n = net_recv_sock(sk, s_phase4_buf, sizeof(s_phase4_buf), NET_MSG_DONTWAIT);
|
||||
if (n > 0) {
|
||||
int sent = net_send_sock(sk, s_phase4_buf, n);
|
||||
if (sent == n) {
|
||||
s_phase4_echo_count++;
|
||||
if (s_phase4_echo_count <= 10 || (s_phase4_echo_count % 100 == 0)) {
|
||||
DBG_INFO("Phase4: echo #%lu (%d bytes)", s_phase4_echo_count, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ============================ Phase 5 ============================ */
|
||||
/*
|
||||
* NET 层大文件传输测试
|
||||
* 验证超过 CH395F 内部缓冲区大小(4KB)的连续数据收发
|
||||
* 覆盖:20KB、64KB 分块传输
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_PHASE5_TESTS
|
||||
static int s_phase5_active = 0;
|
||||
static uint32_t s_phase5_total_sent = 0;
|
||||
static uint32_t s_phase5_total_recv = 0;
|
||||
static uint8_t s_phase5_tx_buf[PHASE5_FILE_SIZE];
|
||||
static uint8_t s_phase5_rx_buf[PHASE5_BUF_SIZE];
|
||||
|
||||
static void phase5_fill_pattern(uint8_t *buf, uint32_t size, uint32_t offset) {
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
buf[i] = (uint8_t)((offset + i) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
static int phase5_verify_pattern(uint8_t *buf, uint32_t size, uint32_t offset) {
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
if (buf[i] != (uint8_t)((offset + i) & 0xFF)) {
|
||||
DBG_ERROR("Phase5: verify fail at offset %lu (expected 0x%02X, got 0x%02X)",
|
||||
offset + i, (uint8_t)((offset + i) & 0xFF), buf[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ch395f_phase5_init(void) {
|
||||
#ifdef ENABLE_PHASE5_TESTS
|
||||
s_phase5_active = 0;
|
||||
s_phase5_total_sent = 0;
|
||||
s_phase5_total_recv = 0;
|
||||
DBG_INFO("Phase5: file transfer test, size=%d bytes, chunk=%d bytes",
|
||||
PHASE5_FILE_SIZE, PHASE5_CHUNK_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ch395f_phase5_tests(void) {
|
||||
#ifdef ENABLE_PHASE5_TESTS
|
||||
net_sock_t *sk = net_get_sock(0);
|
||||
if (sk == NULL || !sk->in_use) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_phase5_active) {
|
||||
if (sk->state == NET_SOCK_STATE_ESTABLISHED) {
|
||||
s_phase5_active = 1;
|
||||
s_phase5_total_sent = 0;
|
||||
s_phase5_total_recv = 0;
|
||||
|
||||
phase5_fill_pattern(s_phase5_tx_buf, PHASE5_FILE_SIZE, 0);
|
||||
DBG_INFO("Phase5: starting %d-byte file transfer (sock=0)", PHASE5_FILE_SIZE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 发送阶段:分块发送,每块 PHASE5_CHUNK_SIZE */
|
||||
if (s_phase5_total_sent < PHASE5_FILE_SIZE) {
|
||||
uint32_t remaining = PHASE5_FILE_SIZE - s_phase5_total_sent;
|
||||
uint32_t chunk = (remaining > PHASE5_CHUNK_SIZE) ? PHASE5_CHUNK_SIZE : remaining;
|
||||
|
||||
int sent = net_send_sock(sk, s_phase5_tx_buf + s_phase5_total_sent, (int)chunk);
|
||||
if (sent > 0) {
|
||||
s_phase5_total_sent += (uint32_t)sent;
|
||||
DBG_INFO("Phase5: sent %d/%d bytes", s_phase5_total_sent, PHASE5_FILE_SIZE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 接收阶段:分块接收并验证 */
|
||||
if (s_phase5_total_recv < PHASE5_FILE_SIZE) {
|
||||
uint32_t remaining = PHASE5_FILE_SIZE - s_phase5_total_recv;
|
||||
int max_read = (int)(remaining < NET_TASK_BUF_SIZE ? remaining : NET_TASK_BUF_SIZE);
|
||||
|
||||
int n = net_recv_sock(sk, s_phase5_rx_buf, max_read, NET_MSG_DONTWAIT);
|
||||
if (n > 0) {
|
||||
int ok = phase5_verify_pattern(s_phase5_rx_buf, (uint32_t)n, s_phase5_total_recv);
|
||||
TEST_CHECK(ok, "Phase5: recv verify at offset %lu (%d/%d bytes)",
|
||||
s_phase5_total_recv, s_phase5_total_recv + (uint32_t)n, PHASE5_FILE_SIZE);
|
||||
s_phase5_total_recv += (uint32_t)n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 完成 */
|
||||
if (s_phase5_total_recv >= PHASE5_FILE_SIZE) {
|
||||
TEST_CHECK(1, "Phase5: %d-byte file transfer complete (sent=%lu, recv=%lu)",
|
||||
PHASE5_FILE_SIZE, s_phase5_total_sent, s_phase5_total_recv);
|
||||
s_phase5_active = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ============================ Phase 6 ============================ */
|
||||
|
||||
#ifndef PHASE6_ANNOUNCE_PORT
|
||||
#define PHASE6_ANNOUNCE_PORT 60001
|
||||
#endif
|
||||
#ifndef PHASE6_ANNOUNCE_SOCK
|
||||
#define PHASE6_ANNOUNCE_SOCK 6
|
||||
#endif
|
||||
|
||||
void ch395f_phase6_tests(void) {
|
||||
#ifdef ENABLE_PHASE6_TESTS
|
||||
uint8_t status = 0;
|
||||
uint8_t ip_info[20] = {0};
|
||||
uint32_t tick = 0;
|
||||
|
||||
DBG_INFO("=== CH395F Phase 6 Tests (DHCP) ===");
|
||||
|
||||
ch395f_set_dhcp(1);
|
||||
TEST_CHECK(1, "DHCP enabled, waiting for IP...");
|
||||
|
||||
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) {
|
||||
TEST_CHECK(1, "DHCP: SUCCESS");
|
||||
break;
|
||||
}
|
||||
|
||||
HAL_Delay(500);
|
||||
}
|
||||
|
||||
TEST_CHECK(HAL_GetTick() - tick < 30000,
|
||||
"DHCP completed within 30s (status=0x%02X)", status);
|
||||
|
||||
if (HAL_GetTick() - tick >= 30000) {
|
||||
goto phase6_end;
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
TEST_CHECK(ip_info[0] != 0 || ip_info[1] != 0 || ip_info[2] != 0 || ip_info[3] != 0,
|
||||
"IP = %d.%d.%d.%d (non-zero)", ip_info[0], ip_info[1], ip_info[2], ip_info[3]);
|
||||
|
||||
{
|
||||
#define PHASE6_HELLO_PORT 60000
|
||||
uint8_t anno_sock = PHASE6_ANNOUNCE_SOCK;
|
||||
uint8_t bcast_ip[4] = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
uint8_t rx_buf[64] = {0};
|
||||
|
||||
ch395f_set_proto_type(anno_sock, CH395F_PROTO_TYPE_UDP);
|
||||
ch395f_set_send_buf(anno_sock, 24, 2);
|
||||
ch395f_set_recv_buf(anno_sock, 26, 2);
|
||||
ch395f_set_sour_port(anno_sock, PHASE6_HELLO_PORT);
|
||||
ch395f_set_des_ip(anno_sock, bcast_ip);
|
||||
ch395f_set_des_port(anno_sock, 0);
|
||||
|
||||
if (ch395f_open_socket(anno_sock) == CH395F_ERR_SUCCESS) {
|
||||
ch395f_set_des_ip(anno_sock, bcast_ip);
|
||||
ch395f_set_des_port(anno_sock, PHASE6_ANNOUNCE_PORT);
|
||||
ch395f_write_send_buf(anno_sock, ip_info, 4);
|
||||
|
||||
{
|
||||
uint32_t swait = HAL_GetTick();
|
||||
while (HAL_GetTick() - swait < 2000) {
|
||||
uint16_t gs = ch395f_get_glob_int_status_all();
|
||||
if (gs & (1U << (anno_sock + 4))) {
|
||||
if (ch395f_get_sock_int_status(anno_sock) & CH395F_SINT_STAT_SENBUF_FREE)
|
||||
break;
|
||||
}
|
||||
HAL_Delay(10);
|
||||
}
|
||||
}
|
||||
DBG_INFO("DHCP announcement: IP=%d.%d.%d.%d (port %d->%d)",
|
||||
ip_info[0], ip_info[1], ip_info[2], ip_info[3],
|
||||
PHASE6_HELLO_PORT, PHASE6_ANNOUNCE_PORT);
|
||||
TEST_CHECK(1, "DHCP announcement sent");
|
||||
|
||||
ch395f_set_des_ip(anno_sock, bcast_ip);
|
||||
ch395f_set_des_port(anno_sock, 0);
|
||||
|
||||
uint32_t wait = HAL_GetTick();
|
||||
uint8_t hello_ok = 0;
|
||||
while (HAL_GetTick() - wait < 5000) {
|
||||
ch395f_get_glob_int_status_all();
|
||||
uint16_t rlen = ch395f_get_recv_len(anno_sock);
|
||||
if (rlen > 8) {
|
||||
memset(rx_buf, 0, sizeof(rx_buf));
|
||||
ch395f_read_recv_buf(anno_sock, rx_buf, rlen);
|
||||
uint16_t payload_len = rlen - 8;
|
||||
uint8_t *payload = &rx_buf[8];
|
||||
|
||||
if (payload_len == 5 && memcmp(payload, "HELLO", 5) == 0) {
|
||||
uint8_t *src_ip = &rx_buf[4];
|
||||
uint16_t src_port = (uint16_t)rx_buf[2] | ((uint16_t)rx_buf[3] << 8);
|
||||
ch395f_set_des_ip(anno_sock, src_ip);
|
||||
ch395f_set_des_port(anno_sock, src_port);
|
||||
ch395f_write_send_buf(anno_sock, payload, payload_len);
|
||||
HAL_Delay(100);
|
||||
DBG_INFO("HELLO response sent to %d.%d.%d.%d:%d",
|
||||
src_ip[0], src_ip[1], src_ip[2], src_ip[3], src_port);
|
||||
TEST_CHECK(1, "HELLO echo OK");
|
||||
hello_ok = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
HAL_Delay(50);
|
||||
}
|
||||
TEST_CHECK(hello_ok, "HELLO echo within 5s");
|
||||
|
||||
ch395f_close_socket(anno_sock);
|
||||
} else {
|
||||
TEST_CHECK(0, "DHCP announcement: open socket failed");
|
||||
}
|
||||
}
|
||||
|
||||
phase6_end:
|
||||
TEST_REPORT("Phase 6");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ============================ Phase 7 ============================ */
|
||||
/*
|
||||
* Select/Poll I/O 多路复用测试(单连接模式)
|
||||
* 在 netTask 中运行,仅操作 Socket 0
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_PHASE7_TESTS
|
||||
static char s_phase7_buf[8192];
|
||||
static uint32_t s_phase7_count = 0;
|
||||
#endif
|
||||
|
||||
void ch395f_phase7_tests(void) {
|
||||
#ifdef ENABLE_PHASE7_TESTS
|
||||
net_sock_t *sk = net_get_sock(0);
|
||||
if (sk == NULL || !sk->in_use || sk->state != NET_SOCK_STATE_ESTABLISHED) {
|
||||
return;
|
||||
}
|
||||
|
||||
net_fd_set readfds;
|
||||
net_timeval tv;
|
||||
|
||||
NET_FD_ZERO(&readfds);
|
||||
NET_FD_SET(0, &readfds);
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 50000;
|
||||
|
||||
int n = net_select(1, &readfds, NULL, NULL, &tv);
|
||||
if (n <= 0) return;
|
||||
|
||||
if (NET_FD_ISSET(0, &readfds)) {
|
||||
int len = net_recv_sock(sk, s_phase7_buf, sizeof(s_phase7_buf), NET_MSG_DONTWAIT);
|
||||
if (len > 0) {
|
||||
net_send_sock(sk, s_phase7_buf, len);
|
||||
s_phase7_count++;
|
||||
TEST_CHECK(1, "Phase7: select echo #%lu (%d bytes)", s_phase7_count, len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ============================ Phase 8 ============================ */
|
||||
/*
|
||||
* 边界条件测试(单连接模式,netTask 中运行)
|
||||
* 覆盖:零长度收发、DMA 边界(4095/4096/4097B)、
|
||||
* recv 缓冲区小于数据、连续快速发送、快速重连
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_PHASE8_TESTS
|
||||
static int s_phase8_stage = 0; /* 0=idle, 1=4095B, 2=4096B, 3=4097B, 4=zero, 5=fastsend */
|
||||
static uint32_t s_phase8_sent = 0;
|
||||
static uint32_t s_phase8_recv = 0;
|
||||
static uint8_t s_phase8_tx[8192];
|
||||
static uint8_t s_phase8_rx[8192];
|
||||
|
||||
static void phase8_fill(uint8_t *buf, uint32_t size, uint8_t seed) {
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
buf[i] = (uint8_t)(seed + i);
|
||||
}
|
||||
}
|
||||
|
||||
static int phase8_verify(uint8_t *buf, uint32_t size, uint8_t seed) {
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
if (buf[i] != (uint8_t)(seed + i)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ch395f_phase8_init(void) {
|
||||
#ifdef ENABLE_PHASE8_TESTS
|
||||
s_phase8_stage = 0;
|
||||
DBG_INFO("Phase8: boundary tests initialized");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ch395f_phase8_tests(void) {
|
||||
#ifdef ENABLE_PHASE8_TESTS
|
||||
net_sock_t *sk = net_get_sock(0);
|
||||
if (sk == NULL || !sk->in_use) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sk->state == NET_SOCK_STATE_LISTENING) {
|
||||
/* 等待连接 */
|
||||
return;
|
||||
}
|
||||
|
||||
if (sk->state != NET_SOCK_STATE_ESTABLISHED) {
|
||||
s_phase8_stage = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (s_phase8_stage) {
|
||||
case 0: {
|
||||
/* Stage 1: 发送 4095 字节(DMA 边界 -1:单次 SPI 事务可容纳) */
|
||||
DBG_INFO("Phase8: stage 1/5 — send 4095 bytes (DMA boundary -1)");
|
||||
phase8_fill(s_phase8_tx, 4095, 0x10);
|
||||
int sent = net_send_sock(sk, s_phase8_tx, 4095);
|
||||
TEST_CHECK(sent == 4095, "Phase8: send 4095 bytes (sent=%d)", sent);
|
||||
s_phase8_sent = (sent > 0) ? (uint32_t)sent : 0;
|
||||
s_phase8_recv = 0;
|
||||
s_phase8_stage = 1;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
/* 等待接收 4095 字节回显 */
|
||||
int n = net_recv_sock(sk, s_phase8_rx + s_phase8_recv,
|
||||
(int)(4095 - s_phase8_recv), NET_MSG_DONTWAIT);
|
||||
if (n > 0) {
|
||||
s_phase8_recv += (uint32_t)n;
|
||||
if (s_phase8_recv >= 4095) {
|
||||
int ok = phase8_verify(s_phase8_rx, 4095, 0x10);
|
||||
TEST_CHECK(ok, "Phase8: verify 4095 bytes echo");
|
||||
s_phase8_stage = 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
/* Stage 2: 发送 4096 字节(DMA 边界:刚好填满 SPI 事务) */
|
||||
DBG_INFO("Phase8: stage 2/5 — send 4096 bytes (DMA boundary exact)");
|
||||
phase8_fill(s_phase8_tx, 4096, 0x20);
|
||||
int sent = net_send_sock(sk, s_phase8_tx, 4096);
|
||||
TEST_CHECK(sent == 4096, "Phase8: send 4096 bytes (sent=%d)", sent);
|
||||
s_phase8_sent = (sent > 0) ? (uint32_t)sent : 0;
|
||||
s_phase8_recv = 0;
|
||||
s_phase8_stage = 3;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int n = net_recv_sock(sk, s_phase8_rx + s_phase8_recv,
|
||||
(int)(4096 - s_phase8_recv), NET_MSG_DONTWAIT);
|
||||
if (n > 0) {
|
||||
s_phase8_recv += (uint32_t)n;
|
||||
if (s_phase8_recv >= 4096) {
|
||||
int ok = phase8_verify(s_phase8_rx, 4096, 0x20);
|
||||
TEST_CHECK(ok, "Phase8: verify 4096 bytes echo");
|
||||
s_phase8_stage = 4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
/* Stage 3: 发送 4097 字节(DMA 边界 +1:需两次 SPI 事务) */
|
||||
DBG_INFO("Phase8: stage 3/5 — send 4097 bytes (DMA boundary +1, two SPI xacts)");
|
||||
phase8_fill(s_phase8_tx, 4097, 0x30);
|
||||
int sent = net_send_sock(sk, s_phase8_tx, 4097);
|
||||
TEST_CHECK(sent == 4097, "Phase8: send 4097 bytes (sent=%d)", sent);
|
||||
s_phase8_sent = (sent > 0) ? (uint32_t)sent : 0;
|
||||
s_phase8_recv = 0;
|
||||
s_phase8_stage = 5;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
int n = net_recv_sock(sk, s_phase8_rx + s_phase8_recv,
|
||||
(int)(4097 - s_phase8_recv), NET_MSG_DONTWAIT);
|
||||
if (n > 0) {
|
||||
s_phase8_recv += (uint32_t)n;
|
||||
if (s_phase8_recv >= 4097) {
|
||||
int ok = phase8_verify(s_phase8_rx, 4097, 0x30);
|
||||
TEST_CHECK(ok, "Phase8: verify 4097 bytes echo");
|
||||
s_phase8_stage = 6;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
/* Stage 4: 零长度发送(验证错误处理) */
|
||||
DBG_INFO("Phase8: stage 4/5 — zero-length send/recv");
|
||||
int sent = net_send_sock(sk, s_phase8_tx, 0);
|
||||
TEST_CHECK(sent < 0, "Phase8: send 0 bytes returns %d (expected <0)", sent);
|
||||
s_phase8_stage = 7;
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
/* Stage 5: 连续快速发送 20 个小包(验证 SENDBUF_FREE 等待机制) */
|
||||
DBG_INFO("Phase8: stage 5/5 — rapid 20x 100-byte sends");
|
||||
phase8_fill(s_phase8_tx, 100, 0x40);
|
||||
int all_ok = 1;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int sent = net_send_sock(sk, s_phase8_tx, 100);
|
||||
if (sent != 100) {
|
||||
TEST_CHECK(0, "Phase8: rapid send #%d failed (sent=%d)", i + 1, sent);
|
||||
all_ok = 0;
|
||||
break;
|
||||
}
|
||||
osDelay(1);
|
||||
}
|
||||
if (all_ok) {
|
||||
TEST_CHECK(1, "Phase8: 20x rapid sends OK");
|
||||
s_phase8_sent = 2000;
|
||||
s_phase8_recv = 0;
|
||||
s_phase8_stage = 8;
|
||||
} else {
|
||||
s_phase8_stage = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
/* 接收所有快速发送的回显 */
|
||||
int n = net_recv_sock(sk, s_phase8_rx + s_phase8_recv,
|
||||
(int)(2000 - s_phase8_recv), NET_MSG_DONTWAIT);
|
||||
if (n > 0) {
|
||||
s_phase8_recv += (uint32_t)n;
|
||||
if (s_phase8_recv >= 2000) {
|
||||
TEST_CHECK(1, "Phase8: all 2000 bytes echo received");
|
||||
TEST_REPORT("Phase 8");
|
||||
s_phase8_stage = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s_phase8_stage = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ============================ Phase 9 ============================ */
|
||||
/*
|
||||
* PHY 链路恢复测试(需物理插拔网线)
|
||||
* 在 main.c 中单独调用,或在 netTask 中通过宏启用
|
||||
*/
|
||||
|
||||
void ch395f_phase9_tests(void) {
|
||||
#ifdef ENABLE_PHASE9_TESTS
|
||||
DBG_INFO("=== CH395F Phase 9 Tests (PHY Recovery) ===");
|
||||
|
||||
/* TC-NET-901: 拔线后检测 PHY 断开 */
|
||||
uint32_t wait = HAL_GetTick();
|
||||
uint8_t phy_disconnected = 0;
|
||||
while (HAL_GetTick() - wait < 15000) {
|
||||
uint16_t gint = ch395f_get_glob_int_status_all();
|
||||
if (gint & CH395F_GINT_STAT_PHY_CHANGE) {
|
||||
uint8_t phy = ch395f_get_phy_status();
|
||||
DBG_INFO("PHY_CHANGE: 0x%02X", phy);
|
||||
if (phy == CH395F_PHY_DISCONN) {
|
||||
TEST_CHECK(1, "PHY disconnect detected");
|
||||
phy_disconnected = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
HAL_Delay(100);
|
||||
}
|
||||
if (!phy_disconnected) {
|
||||
TEST_CHECK(0, "PHY disconnect NOT detected within 15s (unplug cable?)");
|
||||
goto phase9_end;
|
||||
}
|
||||
|
||||
/* TC-NET-902: 插回网线后检测链路恢复 */
|
||||
wait = HAL_GetTick();
|
||||
uint8_t phy_reconnected = 0;
|
||||
while (HAL_GetTick() - wait < 30000) {
|
||||
uint16_t gint = ch395f_get_glob_int_status_all();
|
||||
if (gint & CH395F_GINT_STAT_PHY_CHANGE) {
|
||||
uint8_t phy = ch395f_get_phy_status();
|
||||
DBG_INFO("PHY_CHANGE: 0x%02X", phy);
|
||||
if (phy != CH395F_PHY_DISCONN) {
|
||||
TEST_CHECK(1, "PHY reconnected (0x%02X)", phy);
|
||||
phy_reconnected = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
HAL_Delay(100);
|
||||
}
|
||||
if (!phy_reconnected) {
|
||||
TEST_CHECK(0, "PHY reconnect NOT detected within 30s (re-plug cable?)");
|
||||
}
|
||||
|
||||
phase9_end:
|
||||
TEST_REPORT("Phase 9");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ============================ Phase 10 ============================ */
|
||||
/*
|
||||
* KeepAlive 超时断开测试
|
||||
* 在 netTask 中运行,监视 Socket 0 的 DISCONNECT/TIMEOUT 事件
|
||||
* PC 连接后静置(不发数据),验证 KeepAlive 在空闲超时后触发断开
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_PHASE10_TESTS
|
||||
static int s_phase10_connected = 0;
|
||||
static uint32_t s_phase10_start_tick = 0;
|
||||
#endif
|
||||
|
||||
void ch395f_phase10_tests(void) {
|
||||
#ifdef ENABLE_PHASE10_TESTS
|
||||
net_sock_t *sk = net_get_sock(0);
|
||||
if (sk == NULL || !sk->in_use) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_phase10_connected) {
|
||||
if (sk->state == NET_SOCK_STATE_ESTABLISHED) {
|
||||
s_phase10_connected = 1;
|
||||
s_phase10_start_tick = HAL_GetTick();
|
||||
DBG_INFO("Phase10: connection established, waiting for KeepAlive timeout (%dms)...",
|
||||
PHASE10_KEEPALIVE_IDLE_MS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 已建立连接,等待断开(KeepAlive 触发) */
|
||||
if (sk->state == NET_SOCK_STATE_LISTENING) {
|
||||
uint32_t elapsed = HAL_GetTick() - s_phase10_start_tick;
|
||||
TEST_CHECK(elapsed >= PHASE10_KEEPALIVE_IDLE_MS,
|
||||
"Phase10: KeepAlive triggered after %lums (idle=%dms)",
|
||||
elapsed, PHASE10_KEEPALIVE_IDLE_MS);
|
||||
s_phase10_connected = 0;
|
||||
TEST_REPORT("Phase 10");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* 模块名称:CH395F Driver Test Suite
|
||||
* 模块功能:CH395F 驱动各功能模块的测试入口,分阶段组织测试用例,
|
||||
* 覆盖 SPI 命令路径、协议栈配置、Socket 状态机、大文件传输、
|
||||
* 边界条件、KeepAlive、PHY 链路恢复等,基于单连接 socket 模式
|
||||
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* 2026-07-22 全面重构为单连接模式,增加大文件传输、边界条件等测试
|
||||
*
|
||||
* === 测试阶段一览 ===
|
||||
* Phase | 类型 | 描述
|
||||
* ---------|--------------|-----------------------------------------------
|
||||
* Phase 1 | 独立运行 | 寄存器读写 + SPI 命令路径验证
|
||||
* Phase 2 | PC 配合 | TCP Client 收发 + 关闭重连
|
||||
* Phase 3 | PC 配合 | UDP Server Echo(HELLO + PING + 大包)
|
||||
* Phase 4 | netTask | NET 层 TCP Echo(单连接模式,基础收发验证)
|
||||
* Phase 5 | netTask | NET 层大文件传输(20KB+ 分块收发)
|
||||
* Phase 6 | PC 配合 | DHCP 自动获取 IP
|
||||
* Phase 7 | netTask | Select/Poll I/O 多路复用
|
||||
* Phase 8 | netTask | 边界条件测试(零长度、DMA 边界、缓冲溢出)
|
||||
* Phase 9 | 需拔插网线 | PHY 链路恢复
|
||||
* Phase 10 | netTask | KeepAlive 超时断开测试
|
||||
*/
|
||||
|
||||
#ifndef __CH395F_TEST_H
|
||||
#define __CH395F_TEST_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* 阶段使能开关(取消注释即启用对应阶段)
|
||||
* 注意:一次仅启用一个阶段(Phase 4/5/7/8 共享 netTask 循环,可同时启用)
|
||||
* Phase 6 (DHCP) 会改变 IP,必须独立运行
|
||||
*/
|
||||
//#define ENABLE_PHASE1_TESTS /* 寄存器读写 + SPI 命令路径 */
|
||||
//#define ENABLE_PHASE2_TESTS /* TCP Client 收发 + 关闭重连 */
|
||||
//#define ENABLE_PHASE3_TESTS /* UDP Server Echo */
|
||||
#define ENABLE_PHASE4_TESTS /* NET 层 TCP Echo(单连接基础收发) */
|
||||
//#define ENABLE_PHASE5_TESTS /* NET 层大文件传输(20KB+ 分块) */
|
||||
//#define ENABLE_PHASE6_TESTS /* DHCP 自动获取 IP(会改 IP!独立运行) */
|
||||
//#define ENABLE_PHASE7_TESTS /* Select/Poll I/O 多路复用 */
|
||||
//#define ENABLE_PHASE8_TESTS /* 边界条件测试 */
|
||||
//#define ENABLE_PHASE9_TESTS /* PHY 链路恢复测试 */
|
||||
//#define ENABLE_PHASE10_TESTS /* KeepAlive 超时断开测试 */
|
||||
|
||||
/* 测试统计 */
|
||||
typedef struct {
|
||||
uint16_t total;
|
||||
uint16_t passed;
|
||||
uint16_t failed;
|
||||
} test_stats_t;
|
||||
|
||||
extern test_stats_t g_test_stats;
|
||||
|
||||
#define TEST_CHECK(cond, fmt, ...) do { \
|
||||
g_test_stats.total++; \
|
||||
if (cond) { \
|
||||
g_test_stats.passed++; \
|
||||
DBG_INFO("[PASS] " fmt, ##__VA_ARGS__); \
|
||||
} else { \
|
||||
g_test_stats.failed++; \
|
||||
DBG_ERROR("[FAIL] " fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TEST_REPORT(name) do { \
|
||||
DBG_INFO("=== %s: %d/%d PASSED (failed=%d) ===", \
|
||||
name, g_test_stats.passed, g_test_stats.total, g_test_stats.failed); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Phase 1: 底层寄存器测试
|
||||
* 通过: 9 项全部打印 OK/SUCCESS/YES/0x00
|
||||
* 失败: 任何一项 FAIL/ERROR/MISMATCH
|
||||
*/
|
||||
void ch395f_phase1_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 2: TCP Client 收发 + 关闭重连 ×3 轮
|
||||
* 通过: 日志打印 3 轮 "PASS"
|
||||
* 失败: 任何一轮打印 "FAIL" 或超时
|
||||
* PC 端预启动:
|
||||
* python test/ch395f_socket_test.py tcp_server --port 8081
|
||||
*/
|
||||
void ch395f_phase2_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 3: UDP Server Echo + PING + 大包
|
||||
* 通过: 30/30 回显匹配
|
||||
* 失败: 任何回显超时或丢包
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py udp_client --ip 192.168.1.100 --port 60000
|
||||
*/
|
||||
void ch395f_phase3_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 4: NET 层 TCP Echo(单连接模式,netTask 中运行)
|
||||
* 验证 Socket 0 单连接生命周期:LISTEN → ESTABLISHED → DISCONNECT → re-LISTEN
|
||||
* 覆盖载荷:64B、1460B(MSS)、4096B(缓冲满)、20KB(大文件分块)
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
|
||||
*/
|
||||
void ch395f_phase4_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 5: NET 层大文件传输(netTask 中运行)
|
||||
* 验证超过 CH395F 内部缓冲区大小(4KB)的连续数据收发
|
||||
* 覆盖载荷:20480B(20KB)、65535B(64KB,recv_len上限)
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_file --ip 192.168.1.100 --port 8080 --size 20480
|
||||
*/
|
||||
void ch395f_phase5_init(void);
|
||||
void ch395f_phase5_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 6: DHCP 自动获取 IP
|
||||
* 通过: DHCP SUCCESS + IP 非零 + 广播宣告 + HELLO 回显
|
||||
* 失败: DHCP timeout 或宣告/HELLO 失败
|
||||
* 注意: 会改变 IP,需独立运行
|
||||
* PC 端:管理员权限
|
||||
* python test/ch395f_socket_test.py dhcp_server --dhcp-iface 192.168.1.2 --ip 192.168.1.100
|
||||
*/
|
||||
void ch395f_phase6_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 7: Select/Poll I/O 多路复用(netTask 中运行)
|
||||
* 验证 net_select() 和 net_poll_events() 在单 socket 模式下正确工作
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
|
||||
*/
|
||||
void ch395f_phase7_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 8: 边界条件测试(netTask 中运行)
|
||||
* 覆盖:零长度收发、DMA 边界(4095/4096/4097B)、recv 缓冲区小于数据、
|
||||
* 连续快速发送、多轮快速连接-断开循环
|
||||
* PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
|
||||
*/
|
||||
void ch395f_phase8_init(void);
|
||||
void ch395f_phase8_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 9: PHY 链路恢复测试
|
||||
* 通过拔插网线验证 PHY 断开检测和链路恢复后 socket 自动重新监听
|
||||
* 需物理操作,PC 端:
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080
|
||||
*/
|
||||
void ch395f_phase9_tests(void);
|
||||
|
||||
/*
|
||||
* Phase 10: TCP KeepAlive 超时断开测试
|
||||
* 验证 KeepAlive 在空闲超时后触发断开并自动重新监听
|
||||
* PC 端:连接后静置(不发数据),等待 KeepAlive 触发
|
||||
* python test/ch395f_socket_test.py tcp_client --ip 192.168.1.100 --port 8080 --silent
|
||||
*/
|
||||
void ch395f_phase10_tests(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CH395F_TEST_H */
|
||||
@@ -340,7 +340,7 @@
|
||||
<MiscControls></MiscControls>
|
||||
<Define>USE_HAL_DRIVER,STM32F407xx,NDEBUG</Define>
|
||||
<Undefine></Undefine>
|
||||
<IncludePath>../Inc;../Drivers/STM32F4xx_HAL_Driver/Inc;../Drivers/STM32F4xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F4xx/Include;../Drivers/CMSIS/Include;../Drivers/BSP;../Drivers/BSP/CH395F;../Drivers/BSP/GD5F2GQ5UE;../Drivers/BSP/TPAFE5160;../Drivers/BSP/SD2506;../Drivers/BSP/RS485;../Drivers/BSP/NET;../Drivers/BSP/NET/lftpd;../App;../App/task;../App/util;../Middlewares/Third_Party/FreeRTOS/Source/include;../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2;../Middlewares/Third_Party/FreeRTOS/Source/portable/RVDS/ARM_CM4F;../Lib/dhara;../Lib/FatFs</IncludePath>
|
||||
<IncludePath>../Inc;../Drivers/STM32F4xx_HAL_Driver/Inc;../Drivers/STM32F4xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F4xx/Include;../Drivers/CMSIS/Include;../Drivers/BSP;../Drivers/BSP/CH395F;../Drivers/BSP/GD5F2GQ5UE;../Drivers/BSP/TPAFE5160;../Drivers/BSP/SD2506;../Drivers/BSP/RS485;../Drivers/BSP/NET;../Drivers/BSP/NET/lftpd;../App;../App/task;../App/util;../Middlewares/Third_Party/FreeRTOS/Source/include;../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2;../Middlewares/Third_Party/FreeRTOS/Source/portable/RVDS/ARM_CM4F;../Lib/dhara;../Lib/FatFs;../test</IncludePath>
|
||||
</VariousControls>
|
||||
</Cads>
|
||||
<Aads>
|
||||
@@ -1216,11 +1216,6 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\CH395F\ch395f.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>ch395f_test.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\CH395F\ch395f_test.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>gd5f2gq5ue.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
@@ -2006,6 +2001,16 @@
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
<GroupName>Test</GroupName>
|
||||
<Files>
|
||||
<File>
|
||||
<FileName>ch395f_test_task.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\test\ch395f_test_task.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
<GroupName>::CMSIS</GroupName>
|
||||
</Group>
|
||||
|
||||
@@ -3,69 +3,69 @@ Rebuild target 'STM32F407-Demo'
|
||||
assembling startup_stm32f407xx.s...
|
||||
compiling crc.c...
|
||||
compiling ringbuf.c...
|
||||
compiling gpio.c...
|
||||
compiling stm32f4xx_hal_msp.c...
|
||||
compiling spi.c...
|
||||
compiling stm32f4xx_it.c...
|
||||
compiling stm32f4xx_hal_flash_ramfunc.c...
|
||||
compiling stm32f4xx_hal_timebase_tim.c...
|
||||
compiling usart.c...
|
||||
compiling i2c.c...
|
||||
compiling app_main.c...
|
||||
compiling stm32f4xx_hal_flash_ex.c...
|
||||
compiling stm32f4xx_hal_msp.c...
|
||||
compiling dma.c...
|
||||
compiling stm32f4xx_hal_gpio.c...
|
||||
compiling spi.c...
|
||||
compiling stm32f4xx_hal_timebase_tim.c...
|
||||
compiling stm32f4xx_hal_flash_ramfunc.c...
|
||||
compiling stm32f4xx_it.c...
|
||||
compiling stm32f4xx_hal_flash_ex.c...
|
||||
compiling gpio.c...
|
||||
compiling stm32f4xx_hal_flash.c...
|
||||
compiling sys_clock.c...
|
||||
compiling adc_task.c...
|
||||
compiling stm32f4xx_hal_gpio.c...
|
||||
compiling app_main.c...
|
||||
compiling usart.c...
|
||||
compiling stm32f4xx_hal_rcc_ex.c...
|
||||
compiling adc_task.c...
|
||||
compiling rs485_task.c...
|
||||
compiling stm32f4xx_hal_rcc.c...
|
||||
compiling net_task.c...
|
||||
compiling freertos.c...
|
||||
compiling stm32f4xx_hal_tim_ex.c...
|
||||
compiling net_task.c...
|
||||
compiling main.c...
|
||||
compiling stm32f4xx_hal_tim.c...
|
||||
compiling stm32f4xx_hal_pwr.c...
|
||||
compiling stm32f4xx_hal_dma_ex.c...
|
||||
compiling stm32f4xx_hal_dma.c...
|
||||
compiling stm32f4xx_hal_pwr.c...
|
||||
compiling stm32f4xx_hal_pwr_ex.c...
|
||||
compiling stm32f4xx_hal_i2c_ex.c...
|
||||
compiling stm32f4xx_hal_exti.c...
|
||||
compiling system_stm32f4xx.c...
|
||||
compiling stm32f4xx_hal_cortex.c...
|
||||
compiling stm32f4xx_hal.c...
|
||||
compiling rs485.c...
|
||||
compiling lftpd_string.c...
|
||||
compiling sd2506.c...
|
||||
compiling stm32f4xx_hal_cortex.c...
|
||||
compiling croutine.c...
|
||||
compiling stm32f4xx_hal_i2c_ex.c...
|
||||
compiling stm32f4xx_hal.c...
|
||||
compiling list.c...
|
||||
compiling event_groups.c...
|
||||
compiling system_stm32f4xx.c...
|
||||
compiling stm32f4xx_hal_exti.c...
|
||||
compiling queue.c...
|
||||
compiling sd2506.c...
|
||||
compiling stream_buffer.c...
|
||||
compiling rs485.c...
|
||||
compiling tpafe5160.c...
|
||||
compiling timers.c...
|
||||
compiling stm32f4xx_hal_spi.c...
|
||||
compiling ch395f.c...
|
||||
compiling gd5f2gq5ue.c...
|
||||
compiling stm32f4xx_hal_spi.c...
|
||||
compiling nand_ftl.c...
|
||||
compiling net_select.c...
|
||||
compiling ch395f_test.c...
|
||||
compiling stm32f4xx_hal_uart.c...
|
||||
compiling event_groups.c...
|
||||
compiling lftpd_inet.c...
|
||||
compiling list.c...
|
||||
compiling lftpd.c...
|
||||
compiling net_socket.c...
|
||||
compiling heap_4.c...
|
||||
compiling stream_buffer.c...
|
||||
compiling timers.c...
|
||||
compiling queue.c...
|
||||
compiling map.c...
|
||||
compiling stm32f4xx_hal_i2c.c...
|
||||
compiling journal.c...
|
||||
compiling port.c...
|
||||
compiling tasks.c...
|
||||
compiling ff.c...
|
||||
compiling nand_ftl.c...
|
||||
compiling lftpd_io.c...
|
||||
compiling stm32f4xx_hal_uart.c...
|
||||
compiling heap_4.c...
|
||||
compiling net_select.c...
|
||||
compiling lftpd_inet.c...
|
||||
compiling lftpd.c...
|
||||
compiling map.c...
|
||||
compiling net_socket.c...
|
||||
compiling port.c...
|
||||
compiling journal.c...
|
||||
compiling stm32f4xx_hal_i2c.c...
|
||||
compiling ff.c...
|
||||
compiling ch395f_test_task.c...
|
||||
compiling cmsis_os2.c...
|
||||
linking...
|
||||
Program Size: Code=59404 RO-data=2172 RW-data=408 ZI-data=66744
|
||||
Program Size: Code=63268 RO-data=3840 RW-data=408 ZI-data=58552
|
||||
FromELF: creating hex file...
|
||||
".\STM32F407-Demo\STM32F407-Demo.axf" - 0 Error(s), 0 Warning(s).
|
||||
Build Time Elapsed: 00:00:17
|
||||
Build Time Elapsed: 00:00:18
|
||||
|
||||
@@ -72,7 +72,7 @@ Dma.USART1_TX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphData
|
||||
FREERTOS.FootprintOK=true
|
||||
FREERTOS.IPParameters=Tasks01,configENABLE_FPU,FootprintOK,configTOTAL_HEAP_SIZE,Queues01
|
||||
FREERTOS.Queues01=netMsgQueue,8,net_msg_t,0,Dynamic,NULL,NULL
|
||||
FREERTOS.Tasks01=defaultTask,24,512,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;netTask,24,1024,StartNetTask,Default,NULL,Dynamic,NULL,NULL;ftpTask,24,1024,StartFtpTask,As weak,NULL,Dynamic,NULL,NULL
|
||||
FREERTOS.Tasks01=defaultTask,24,512,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;netTask,24,1024,StartNetTask,Default,NULL,Dynamic,NULL,NULL;ftpTask,24,1024,StartFtpTask,As weak,NULL,Dynamic,NULL,NULL;ch395fTestTask,16,4096,StartCh395fTestTask,As weak,NULL,Dynamic,NULL,NULL
|
||||
FREERTOS.configENABLE_FPU=1
|
||||
FREERTOS.configTOTAL_HEAP_SIZE=30720
|
||||
File.Version=6
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "ff.h"
|
||||
#include "gd5f2gq5ue.h"
|
||||
#include "lftpd.h"
|
||||
#include "../test/ch395f_test_task.h"
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
@@ -100,6 +101,13 @@ const osThreadAttr_t ftpTask_attributes = {
|
||||
.stack_size = 1024 * 4,
|
||||
.priority = (osPriority_t) osPriorityNormal,
|
||||
};
|
||||
/* Definitions for ch395fTestTask */
|
||||
osThreadId_t ch395fTestTaskHandle;
|
||||
const osThreadAttr_t ch395fTestTask_attributes = {
|
||||
.name = "ch395fTestTask",
|
||||
.stack_size = 4096 * 4,
|
||||
.priority = (osPriority_t) osPriorityBelowNormal,
|
||||
};
|
||||
/* Definitions for netMsgQueue */
|
||||
osMessageQueueId_t netMsgQueueHandle;
|
||||
const osMessageQueueAttr_t netMsgQueue_attributes = {
|
||||
@@ -114,6 +122,7 @@ const osMessageQueueAttr_t netMsgQueue_attributes = {
|
||||
void StartDefaultTask(void *argument);
|
||||
void StartNetTask(void *argument);
|
||||
void StartFtpTask(void *argument);
|
||||
void StartCh395fTestTask(void *argument);
|
||||
|
||||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||||
|
||||
@@ -155,7 +164,10 @@ void MX_FREERTOS_Init(void) {
|
||||
netTaskHandle = osThreadNew(StartNetTask, NULL, &netTask_attributes);
|
||||
|
||||
/* creation of ftpTask */
|
||||
ftpTaskHandle = osThreadNew(StartFtpTask, NULL, &ftpTask_attributes);
|
||||
//ftpTaskHandle = osThreadNew(StartFtpTask, NULL, &ftpTask_attributes);
|
||||
|
||||
/* creation of ch395fTestTask */
|
||||
ch395fTestTaskHandle = osThreadNew(StartCh395fTestTask, NULL, &ch395fTestTask_attributes);
|
||||
|
||||
/* USER CODE BEGIN RTOS_THREADS */
|
||||
/* add threads, ... */
|
||||
@@ -307,6 +319,24 @@ __weak void StartFtpTask(void *argument)
|
||||
/* USER CODE END StartFtpTask */
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN Header_StartCh395fTestTask */
|
||||
/**
|
||||
* @brief Function implementing the ch395fTestTask thread.
|
||||
* @param argument: Not used
|
||||
* @retval None
|
||||
*/
|
||||
/* USER CODE END Header_StartCh395fTestTask */
|
||||
__weak void StartCh395fTestTask(void *argument)
|
||||
{
|
||||
/* USER CODE BEGIN StartCh395fTestTask */
|
||||
/* Infinite loop */
|
||||
for(;;)
|
||||
{
|
||||
osDelay(1);
|
||||
}
|
||||
/* USER CODE END StartCh395fTestTask */
|
||||
}
|
||||
|
||||
/* Private application code --------------------------------------------------*/
|
||||
/* USER CODE BEGIN Application */
|
||||
|
||||
|
||||
18
Src/main.c
18
Src/main.c
@@ -36,7 +36,6 @@
|
||||
#include "rs485.h"
|
||||
#include "net_socket.h"
|
||||
#include "net_select.h"
|
||||
#include "ch395f_test.h"
|
||||
#include "app_main.h"
|
||||
#include "sys_clock.h"
|
||||
/* USER CODE END Includes */
|
||||
@@ -182,22 +181,7 @@ int main(void)
|
||||
/* 启动第一次转换(CubeMX 已配置 BUSY EXTI 中断) */
|
||||
tpafe5160_start_conv_irq();
|
||||
|
||||
/* CH395F 驱动测试 — 各阶段独立运行,注释 define 可跳过 */
|
||||
#ifdef ENABLE_PHASE1_TESTS
|
||||
ch395f_phase1_tests();
|
||||
#endif
|
||||
#ifdef ENABLE_PHASE3_TESTS
|
||||
ch395f_phase3_tests();
|
||||
#endif
|
||||
#ifdef ENABLE_PHASE2_TESTS
|
||||
ch395f_phase2_tests();
|
||||
#endif
|
||||
#ifdef ENABLE_PHASE5_TESTS
|
||||
ch395f_phase5_init();
|
||||
#endif
|
||||
#ifdef ENABLE_PHASE6_TESTS
|
||||
ch395f_phase6_tests();
|
||||
#endif
|
||||
/* CH395F 测试已全部移至 ch395fTestTask */
|
||||
/* USER CODE END 2 */
|
||||
|
||||
/* Init scheduler */
|
||||
|
||||
@@ -63,7 +63,7 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
|
||||
### 启用测试
|
||||
|
||||
编辑 `Drivers/BSP/CH395F/ch395f_test.h`,取消注释对应 `ENABLE_PHASEx_TESTS` 宏。
|
||||
编辑 `test/ch395f_test.h`,取消注释对应 `ENABLE_PHASEx_TESTS` 宏。
|
||||
|
||||
---
|
||||
|
||||
@@ -73,7 +73,7 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
|---------|-----------|
|
||||
| **类型** | 独立运行 |
|
||||
| **耗时** | ~1 秒 |
|
||||
| **入口** | `ch395f_phase1_tests()` 在 `main.c USER CODE BEGIN 2` |
|
||||
| **入口** | `ch395fTestTask` 启动后自动运行 |
|
||||
| **出口** | 串口 "Phase 1 Tests Complete" 且 0 失败 |
|
||||
|
||||
### TC-NET-101: SPI 命令路径完整性
|
||||
@@ -85,23 +85,24 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
| **类型** | 功能测试 |
|
||||
| **标题** | 验证所有 SPI 命令路径返回预期值 |
|
||||
| **前置条件** | 1. CH395F 已上电,SPI2 已初始化<br>2. 未启用其他阶段 |
|
||||
| **测试步骤** | 1. `ch395f_check_exist()` → 验证回显测试字节按位取反<br>2. `ch395f_get_version()` → 验证版本 != 0xFF 且 != 0x00<br>3. 设 IP 192.168.1.100,回读验证<br>4. 设网关 192.168.1.1,回读验证<br>5. 设掩码 255.255.255.0,回读验证<br>6. `ch395f_get_glob_int_status()` → 验证返回 0x00<br>7. 打开→关闭 Socket 0 → 验证 `CMD_STATUS == CH395F_ERR_SUCCESS`<br>8. 设置 ARP 参数<br>9. 设置 TTL |
|
||||
| **预期结果** | 全部 9 项打印 `OK`/`SUCCESS`/`0x00` |
|
||||
| **通过标准** | 9/9 通过,0 失败 |
|
||||
| **覆盖原则** | DP-02 |
|
||||
|
||||
### TC-NET-102: 全局中断状态一致性
|
||||
**测试步骤与判定标准**:
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| **ID** | TC-NET-102 |
|
||||
| **优先级** | P1 |
|
||||
| **类型** | 功能测试 |
|
||||
| **标题** | 无事件时 `GET_GLOB_INT_STATUS_ALL` 返回 0 |
|
||||
| **前置条件** | TC-NET-101 通过,芯片空闲 |
|
||||
| **测试步骤** | 1. 读 `ch395f_get_glob_int_status_all()`<br>2. 读 `ch395f_get_glob_int_status()`(1 字节版) |
|
||||
| **预期结果** | 两者均为 0 |
|
||||
| **通过标准** | 两次读取均为 0 |
|
||||
| 步 | 操作 | 成功标准 | 失败标准 |
|
||||
|----|------|----------|----------|
|
||||
| 1 | `ch395f_check_exist()`<br>SPI写: CMD=0x06, DATA=0x57<br>回读: ~0x57 = 0xA8 | 返回 `0x00`(`CH395F_ERR_SUCCESS`)<br>= 回读字节 `0xA8` 匹配 `~0x57` | 返回 `0xFF`(`CH395F_STATUS_NOT_DETECTED`)<br>= 回读字节 ≠ `0xA8`,回显取反不匹配 |
|
||||
| 2 | `ch395f_get_version()` | 版本值 == `0x4A`(CH395F 芯片版本) | 版本值 ≠ `0x4A`(芯片不匹配或未响应) |
|
||||
| 3 | `ch395f_set_ip_addr(192.168.1.100)` → `ch395f_get_ip_inf()` 回读 IP | `ip_info[0..3] == 192.168.1.100` | 任意字节不匹配(写入/回读不一致) |
|
||||
| 4 | `ch395f_set_gwip_addr(192.168.1.1)` → `ch395f_get_ip_inf()` 回读 GW | `ip_info[4..7] == 192.168.1.1` | 任意字节不匹配 |
|
||||
| 5 | `ch395f_set_mask_addr(255.255.255.0)` → `ch395f_get_ip_inf()` 回读 MASK | `ip_info[8..11] == 255.255.255.0` | 任意字节不匹配 |
|
||||
| 6 | `ch395f_get_glob_int_status_all()`(16 位) | 返回值 == `0x0000`(无待处理中断) | 返回值 ≠ `0x0000`(有残留中断未清除) |
|
||||
| 7 | `ch395f_open_socket(0)` → `ch395f_close_socket(0)` → `ch395f_get_cmd_status()` | `CMD_STATUS == 0x00`(`CH395F_ERR_SUCCESS`) | `CMD_STATUS` 非 `0x00`(Socket 操作失败) |
|
||||
| 8 | `ch395f_set_arp(10, 5)` → `ch395f_get_cmd_status()` | `CMD_STATUS == 0x00` | `CMD_STATUS` 非 `0x00` |
|
||||
| 9 | `ch395f_set_ttl(0, 64)` → `ch395f_get_cmd_status()` | `CMD_STATUS == 0x00` | `CMD_STATUS` 非 `0x00` |
|
||||
|
||||
**通过标准**:9/9 通过,0 失败。任一失败 → 阶段标记 FAIL,后续阶段不执行
|
||||
|
||||
**覆盖原则**:DP-02
|
||||
|
||||
---
|
||||
|
||||
@@ -111,7 +112,7 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
|---------|-----------|
|
||||
| **类型** | 需 PC 配合 |
|
||||
| **耗时** | ~60 秒 |
|
||||
| **入口** | `ch395f_phase2_tests()` 在 `main.c USER CODE BEGIN 2` |
|
||||
| **入口** | `ch395fTestTask` 启动后自动运行 |
|
||||
| **出口** | 串口 "Phase 2 Tests Complete" 且 0 失败 |
|
||||
|
||||
### TC-NET-201: TCP Client 连接与数据交换
|
||||
@@ -123,8 +124,18 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
| **类型** | 功能测试 |
|
||||
| **标题** | CH395F TCP Client → PC TCP Server,64 字节收发 |
|
||||
| **前置条件** | PC: `python ch395f_socket_test.py tcp_server --port 8081` |
|
||||
| **测试步骤** | 1. CH395F 开 Socket 0,配置 TCP<br>2. 连接 PC:8081<br>3. 发 64 字节 → 收回显 → 比较 |
|
||||
| **通过标准** | 64 字节收发一致,无超时 |
|
||||
|
||||
**测试步骤与判定标准**:
|
||||
|
||||
| 步 | 操作 | 成功标准 | 失败标准 |
|
||||
|----|------|----------|----------|
|
||||
| 1 | CH395F open Socket 0 → 配置 TCP 协议 | `ch395f_open_socket(0)` 返回 `CH395F_ERR_SUCCESS`(`0x00`) | 返回值非 `0x00`(Socket 硬件异常或 SPI 通信失败) |
|
||||
| 2 | 设置目标 IP 为 PC 地址(`192.168.1.2`),端口 `8081` → 发起 TCP 连接 | `ch395f_tcp_connect(0)` 返回 `CH395F_ERR_SUCCESS`(`0x00`) | 返回值非 `0x00`(连接超时、目标不可达或协议配置错误) |
|
||||
| 3 | 发送 64 字节递增模式数据(`0x00, 0x01, ..., 0x3F`) | `net_send()` 返回值 == `64`(全部发出) | 返回值 < `0`(发送失败)或返回值 ≠ `64`(未完全发出) |
|
||||
| 4 | 阻塞等待接收回显,超时 5 秒 | `net_recv()` 返回值 == `64`(收到全部回显) | 返回值 < `0`(超时/断开)或返回值 ≠ `64`(数据不完整) |
|
||||
| 5 | 逐字节比较收发数据 | 所有 64 字节完全一致 | 任意字节不一致(回显错误或数据错位) |
|
||||
|
||||
**通过标准**:5/5 通过,0 失败。任一失败 → 阶段标记 FAIL,后续阶段不执行
|
||||
|
||||
### TC-NET-202: TCP Client 关闭重连 ×3 轮
|
||||
|
||||
@@ -159,7 +170,7 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
|---------|-----------|
|
||||
| **类型** | 需 PC 配合 |
|
||||
| **耗时** | ~30 秒 |
|
||||
| **入口** | `ch395f_phase3_tests()` 在 `main.c USER CODE BEGIN 2` |
|
||||
| **入口** | `ch395fTestTask` 启动后自动运行 |
|
||||
| **出口** | 串口 "Phase 3 Tests Complete" 且 0 失败 |
|
||||
|
||||
### TC-NET-301: UDP 回显 — HELLO + PING + 大包 ×30
|
||||
@@ -196,7 +207,7 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
|---------|-----------|
|
||||
| **类型** | PC 配合,netTask 运行 |
|
||||
| **耗时** | 持续运行 |
|
||||
| **入口** | `ENABLE_PHASE4_TESTS`,netTask 中循环调用 `ch395f_phase4_tests()` |
|
||||
| **入口** | `ENABLE_PHASE4_TESTS`,`ch395fTestTask` TCP 回显循环中运行 |
|
||||
| **出口** | 用户终止 |
|
||||
|
||||
### TC-NET-401: Socket 0 单连接生命周期
|
||||
@@ -310,7 +321,7 @@ MCU: STM32F407ZGTx @ 168MHz, Keil MDK-ARM v5 (ARMCC)
|
||||
|
||||
### 实现说明
|
||||
|
||||
`ch395f_phase5_tests()` 状态机:
|
||||
`phase5_run()` 状态机(`ch395fTestTask` 内):
|
||||
|
||||
```
|
||||
IDLE → (连接建立) → SENDING(分块发) → RECVING(分块收+验证) → DONE → IDLE
|
||||
|
||||
244
docs/测试开发标准流程.md
Normal file
244
docs/测试开发标准流程.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# 嵌入式测试开发标准流程(基于 STM32F4 + CH395F 经验)
|
||||
|
||||
> 适用范围:STM32 裸机/FreeRTOS 项目,Keil MDK-ARM v5 (ARMCC),串口日志输出测试结果。
|
||||
|
||||
---
|
||||
|
||||
## 1. 测试规划(文档先行)
|
||||
|
||||
在 `docs/` 下创建测试规范文档,定义以下结构:
|
||||
|
||||
### 1.1 阶段概览表
|
||||
|
||||
| 阶段 ID | 类型 | 耗时 | 入口 | 出口 |
|
||||
|---------|------|------|------|------|
|
||||
| TM-PHY-01 | 独立运行 | ~1s | 宏启用 | 串口汇总 |
|
||||
|
||||
### 1.2 每个测试用例
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| **ID** | `TC-NET-NNN`,唯一编号 |
|
||||
| **优先级** | P0(核心)/ P1(重要) |
|
||||
| **类型** | 功能测试 / 边界测试 / 负向测试 / 压力测试 / 恢复测试 |
|
||||
| **标题** | 一句话描述 |
|
||||
| **前置条件** | 硬件状态、PC 端命令、依赖的其他用例 |
|
||||
| **测试步骤** | 编号操作序列 |
|
||||
| **成功标准** | 精确到"返回值 == 0x00"、"延迟 >= 60000ms" |
|
||||
| **失败标准** | 每种失败对应的现象 |
|
||||
| **覆盖原则** | 对应哪个设计原则 |
|
||||
|
||||
关键原则:**先写判定标准,再写测试代码**,避免"测了但不知道算不算过"。
|
||||
|
||||
---
|
||||
|
||||
## 2. 测试框架搭建
|
||||
|
||||
### 2.1 目录结构
|
||||
|
||||
```
|
||||
test/
|
||||
ch395f_test.h # 宏定义 + TEST_CHECK/TEST_REPORT + test_stats_t
|
||||
ch395f_test_task.h # 仅导出 StartCh395fTestTask
|
||||
ch395f_test_task.c # 全 Phase 实现
|
||||
```
|
||||
|
||||
### 2.2 模板文件
|
||||
|
||||
#### `ch395f_test.h` — 宏+类型
|
||||
|
||||
```c
|
||||
// 阶段使能开关(取消注释即启用)
|
||||
#define ENABLE_PHASE1_TESTS
|
||||
|
||||
// 测试统计
|
||||
typedef struct {
|
||||
uint16_t total;
|
||||
uint16_t passed;
|
||||
uint16_t failed;
|
||||
} test_stats_t;
|
||||
|
||||
extern test_stats_t g_test_stats;
|
||||
|
||||
#define TEST_CHECK(cond, fmt, ...) do { \
|
||||
g_test_stats.total++; \
|
||||
if (cond) { \
|
||||
g_test_stats.passed++; \
|
||||
DBG_INFO("[PASS] " fmt, ##__VA_ARGS__); \
|
||||
} else { \
|
||||
g_test_stats.failed++; \
|
||||
DBG_ERROR("[FAIL] " fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TEST_REPORT(name) do { \
|
||||
DBG_INFO("=== %s: %d/%d PASSED (failed=%d) ===", \
|
||||
name, g_test_stats.passed, g_test_stats.total, g_test_stats.failed); \
|
||||
} while (0)
|
||||
```
|
||||
|
||||
#### `ch395f_test_task.c` — 每个 Phase 的模板
|
||||
|
||||
```c
|
||||
#ifdef ENABLE_PHASEX_TESTS
|
||||
/*
|
||||
* Phase X — 功能说明
|
||||
*
|
||||
* 测试目的:(概括)
|
||||
* 前置条件:(硬件/软件/PC 端)
|
||||
* 通过:(整体判定条件)
|
||||
* 失败:(整体判定条件)
|
||||
*/
|
||||
static void phaseX_run(void) {
|
||||
DBG_INFO("=== CH395F Phase X Tests ===");
|
||||
|
||||
/* ---- TC-NET-NNN: 用例标题 ---- */
|
||||
{
|
||||
// 原理说明
|
||||
// 通过:精确条件
|
||||
// 失败:精确条件
|
||||
ret = some_api();
|
||||
DBG_INFO(" expect: ...");
|
||||
DBG_INFO(" actual: ...");
|
||||
TEST_CHECK(ret == EXPECTED, "description");
|
||||
}
|
||||
|
||||
TEST_REPORT("Phase X");
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 编码规范
|
||||
|
||||
### 3.1 DBG_INFO 三行输出
|
||||
|
||||
每个判定点输出三行,一眼看出"期望什么、拿了什么、过没过":
|
||||
|
||||
```
|
||||
[P1-01] ch395f_check_exist()
|
||||
expect: 0x00 (~0x57 = 0xA8)
|
||||
actual: 0x00
|
||||
[PASS] P1-01 check_exist = 0x00 (expect 0x00)
|
||||
```
|
||||
|
||||
### 3.2 硬编码值加注释
|
||||
|
||||
```c
|
||||
// 好:
|
||||
ch395f_status_t ret = ch395f_check_exist();
|
||||
// SPI 写入 0x06 (CMD_CHECK_EXIST) + 0x57 (测试字节)
|
||||
// 成功:回复 == ~0x57 == 0xA8(CH395F_ERR_SUCCESS = 0x00)
|
||||
// 失败:回复 != 0xA8(CH395F_STATUS_NOT_DETECTED = 0xFF)
|
||||
|
||||
// 不好:
|
||||
ch395f_status_t ret = ch395f_check_exist();
|
||||
TEST_CHECK(ret == 0, "check_exist OK");
|
||||
```
|
||||
|
||||
### 3.3 无中文字符串
|
||||
|
||||
ARMCC v5 不识别 UTF-8 多字节字符。`DBG_INFO`/`DBG_ERROR` 中只写 ASCII。注释可以写中文。
|
||||
|
||||
### 3.4 栈安全
|
||||
|
||||
大缓冲区(如 64KB)用 `static` 全局,不放任务栈:
|
||||
|
||||
```c
|
||||
#define TEST_BUF_SIZE 65536
|
||||
static uint8_t s_rx_buf[TEST_BUF_SIZE];
|
||||
```
|
||||
|
||||
### 3.5 条件编译消除未使用变量警告
|
||||
|
||||
```c
|
||||
static uint8_t s_rx_buf[TEST_BUF_SIZE];
|
||||
#if defined(ENABLE_PHASE5_TESTS) || defined(ENABLE_PHASE8_TESTS)
|
||||
static uint8_t s_tx_buf[TEST_BUF_SIZE];
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 统一入口
|
||||
|
||||
### 4.1 所有测试在 FreeRTOS 任务中串行执行
|
||||
|
||||
```
|
||||
ch395fTestTask:
|
||||
1. 等待 g_net_ready(netTask 初始化完成)
|
||||
2. 阻塞执行 Phase 1/2/3/6/9(内部循环,无需外部连接)
|
||||
3. 创建 TCP 监听 Socket(端口 8080,单连接模式)
|
||||
4. 事件驱动循环 Phase 4/5/7/8/10(等待 PC 连接)
|
||||
```
|
||||
|
||||
### 4.2 不要在 main.c 中直接调用测试
|
||||
|
||||
main.c 只做硬件初始化和启动 OS,不包含任何测试逻辑。
|
||||
|
||||
### 4.3 测试文件的手动注册
|
||||
|
||||
新测试文件需:
|
||||
1. 添加到 `test/` 目录
|
||||
2. 修改 `MDK-ARM/STM32F407-Demo.uvprojx` 添加文件引用和 IncludePath
|
||||
|
||||
---
|
||||
|
||||
## 5. 文档与代码同步
|
||||
|
||||
- 文档中的入口函数名、宏名、流程描述必须与代码一致
|
||||
- 代码变更后立即更新文档,否则文档两天内就会失效
|
||||
- 测试用例的通过/失败标准在**文档和代码注释中都写清楚**
|
||||
- 文档使用标准 Markdown 表格,避免复杂嵌套导致渲染异常
|
||||
|
||||
---
|
||||
|
||||
## 6. 编译验证
|
||||
|
||||
### 6.1 底线
|
||||
|
||||
**`0 Error(s), 0 Warning(s)`**,不可妥协。
|
||||
|
||||
### 6.2 常见警告处理
|
||||
|
||||
| 警告 | 原因 | 解决 |
|
||||
|------|------|------|
|
||||
| `#177-D: variable was declared but never referenced` | 条件编译导致 | 加 `#ifdef` 包裹变量声明 |
|
||||
| `#870-D: invalid multibyte character sequence` | DBG_INFO 中有中文 | 改为纯 ASCII |
|
||||
| 隐式类型转换 | 参数类型不匹配 | 加显式 `(uint8_t)` 等 cast |
|
||||
|
||||
### 6.3 构建命令
|
||||
|
||||
```bat
|
||||
MDK-ARM\build.bat
|
||||
:: 或项目根目录执行 @build
|
||||
```
|
||||
|
||||
退出码:0 = 成功 | 1 = 有警告(不通过)| 2+ = 错误
|
||||
|
||||
---
|
||||
|
||||
## 7. 迭代节奏
|
||||
|
||||
```
|
||||
文档(规划 + 判定标准)
|
||||
→ 代码(添加 Phase 实现)
|
||||
→ @build(0 Error(s), 0 Warning(s))
|
||||
→ 烧录验证(串口观察 PASS/FAIL)
|
||||
→ 根据实际结果修正判定标准
|
||||
→ 更新文档(保持同步)
|
||||
→ 下一阶段
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附录:本项目的测试文件清单
|
||||
|
||||
| 文件 | 作用 |
|
||||
|------|------|
|
||||
| `test/ch395f_test.h` | 测试宏、类型定义、Phase 使能开关 |
|
||||
| `test/ch395f_test_task.h` | 导出 `StartCh395fTestTask` |
|
||||
| `test/ch395f_test_task.c` | 全 10 个 Phase 实现 |
|
||||
| `docs/CH395F_Test_Guide.md` | 测试规范、用例表格、通过/失败标准 |
|
||||
| `docs/CH395F_Trap_Records.md` | 已知硬件/软件陷阱及修复 |
|
||||
81
test/ch395f_test.h
Normal file
81
test/ch395f_test.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 模块名称:CH395F Driver Test Suite
|
||||
* 模块功能:CH395F 驱动各功能模块的测试入口,分阶段组织测试用例,
|
||||
* 覆盖 SPI 命令路径、协议栈配置、Socket 状态机、大文件传输、
|
||||
* 边界条件、KeepAlive、PHY 链路恢复等,基于单连接 socket 模式
|
||||
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* 2026-07-22 全面重构为单连接模式,增加大文件传输、边界条件等测试
|
||||
*
|
||||
* === 测试阶段一览 ===
|
||||
* Phase | 类型 | 描述
|
||||
* ---------|--------------|-----------------------------------------------
|
||||
* Phase 1 | 独立运行 | 寄存器读写 + SPI 命令路径验证
|
||||
* Phase 2 | PC 配合 | TCP Client 收发 + 关闭重连
|
||||
* Phase 3 | PC 配合 | UDP Server Echo(HELLO + PING + 大包)
|
||||
* Phase 4 | testTask | NET 层 TCP Echo(单连接模式,基础收发验证)
|
||||
* Phase 5 | testTask | NET 层大文件传输(20KB+ 分块收发)
|
||||
* Phase 6 | PC 配合 | DHCP 自动获取 IP
|
||||
* Phase 7 | testTask | Select/Poll I/O 多路复用
|
||||
* Phase 8 | testTask | 边界条件测试(零长度、DMA 边界、缓冲溢出)
|
||||
* Phase 9 | 需拔插网线 | PHY 链路恢复
|
||||
* Phase 10 | testTask | KeepAlive 超时断开测试
|
||||
*/
|
||||
|
||||
#ifndef __CH395F_TEST_H
|
||||
#define __CH395F_TEST_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* 阶段使能开关(取消注释即启用对应阶段)
|
||||
* 注意:一次仅启用一个阶段(Phase 4/5/7/8 共享 testTask 循环,可同时启用)
|
||||
* Phase 6 (DHCP) 会改变 IP,必须独立运行
|
||||
*/
|
||||
#define ENABLE_PHASE1_TESTS /* 寄存器读写 + SPI 命令路径 */
|
||||
//#define ENABLE_PHASE2_TESTS /* TCP Client 收发 + 关闭重连 */
|
||||
//#define ENABLE_PHASE3_TESTS /* UDP Server Echo */
|
||||
//#define ENABLE_PHASE4_TESTS /* NET 层 TCP Echo(单连接基础收发) */
|
||||
//#define ENABLE_PHASE5_TESTS /* NET 层大文件传输(20KB+ 分块) */
|
||||
//#define ENABLE_PHASE6_TESTS /* DHCP 自动获取 IP(会改 IP!独立运行) */
|
||||
//#define ENABLE_PHASE7_TESTS /* Select/Poll I/O 多路复用 */
|
||||
//#define ENABLE_PHASE8_TESTS /* 边界条件测试 */
|
||||
//#define ENABLE_PHASE9_TESTS /* PHY 链路恢复测试 */
|
||||
//#define ENABLE_PHASE10_TESTS /* KeepAlive 超时断开测试 */
|
||||
|
||||
/* 测试统计 */
|
||||
typedef struct {
|
||||
uint16_t total;
|
||||
uint16_t passed;
|
||||
uint16_t failed;
|
||||
} test_stats_t;
|
||||
|
||||
extern test_stats_t g_test_stats;
|
||||
|
||||
#define TEST_CHECK(cond, fmt, ...) do { \
|
||||
g_test_stats.total++; \
|
||||
if (cond) { \
|
||||
g_test_stats.passed++; \
|
||||
DBG_INFO("[PASS] " fmt, ##__VA_ARGS__); \
|
||||
} else { \
|
||||
g_test_stats.failed++; \
|
||||
DBG_ERROR("[FAIL] " fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TEST_REPORT(name) do { \
|
||||
DBG_INFO("=== %s: %d/%d PASSED (failed=%d) ===", \
|
||||
name, g_test_stats.passed, g_test_stats.total, g_test_stats.failed); \
|
||||
} while (0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CH395F_TEST_H */
|
||||
1154
test/ch395f_test_task.c
Normal file
1154
test/ch395f_test_task.c
Normal file
File diff suppressed because it is too large
Load Diff
14
test/ch395f_test_task.h
Normal file
14
test/ch395f_test_task.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __CH395F_TEST_TASK_H
|
||||
#define __CH395F_TEST_TASK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void StartCh395fTestTask(void *argument);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CH395F_TEST_TASK_H */
|
||||
Reference in New Issue
Block a user