1263 lines
36 KiB
C
1263 lines
36 KiB
C
/*
|
||
* 模块名称:CH395F SPI Ethernet Protocol Stack Driver
|
||
* 模块功能:CH395F 以太网协议栈芯片 SPI 驱动实现,提供芯片检测、初始化<E5A78B>? * 网络参数配置、PHY 管理、DHCP、Socket 管理及数据收发功<E58F91>? * 适用平台:STM32F4 系列(SPI2 接口<E68EA5>? * 作者:王建<E78E8B>? * 创建日期<E697A5>?026-03-14
|
||
* 修改记录<E8AEB0>? * 2026-03-14 王建<E78E8B>? 创建初始版本,按代码规范重构
|
||
* 2026-07-18 王建<E78E8B>? 改<>?write_send_buf / read_recv_buf 使用 SPI2 DMA 批量传输
|
||
*/
|
||
|
||
/* 头文件包含区 - 先包含系统头文件,再包含本模块头文件 */
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include "ch395f.h"
|
||
#include "spi.h"
|
||
#include "usart.h"
|
||
|
||
/*
|
||
* 调试输出配置
|
||
*/
|
||
#define DBG_TAG "[CH395]"
|
||
#include "dbg_log.h"
|
||
|
||
/*
|
||
* 私有宏定义区
|
||
*/
|
||
#define CH395F_CHECK_EXIST_TEST_DATA 0x57U /* 芯片检测测试字<E8AF95>?*/
|
||
#define CH395F_RESET_DELAY_MS 20U /* 复位后等待延时(ms<6D>?*/
|
||
#define CH395F_INIT_TIMEOUT_MS 500U /* 初始化超时时间(ms<6D>?*/
|
||
#define CH395F_SPI_BEGIN_DELAY_MS 1U /* SPI 事务开始延时(ms<6D>?*/
|
||
/* DMA 缓冲大小使用 ch395f.h 中公开的 CH395F_SPI_DMA_BUF_SIZE */
|
||
#define CH395F_DMA_BUF_SIZE CH395F_SPI_DMA_BUF_SIZE
|
||
|
||
/*
|
||
* 私有变量<E58F98>?- DMA 传输相关
|
||
*/
|
||
static volatile uint8_t s_spi2_dma_done = 0U; /* DMA 传输完成标志 */
|
||
|
||
__attribute__((aligned(4)))
|
||
static uint8_t s_spi2_dma_tx_buf[CH395F_DMA_BUF_SIZE]; /* DMA 发送缓冲区 */
|
||
__attribute__((aligned(4)))
|
||
static uint8_t s_spi2_dma_rx_buf[CH395F_DMA_BUF_SIZE]; /* DMA 接收缓冲<E7BC93>?*/
|
||
|
||
/*
|
||
* 私有函数声明<E5A3B0>? */
|
||
static void ch395f_spi_begin(void);
|
||
static void ch395f_spi_end(void);
|
||
static void ch395f_spi_dma_transfer(uint16_t len);
|
||
|
||
/*
|
||
* 私有函数实现<E5AE9E>? */
|
||
|
||
/*
|
||
* 函数功能:开<EFBC9A>?SPI 事务(CS 拉高 -> 拉低<E68B89>? * 函数说明:手册要<E5868C>?SPI 模式下字节间无需间隔<E997B4>? * CS 拉高再拉低满足最小片选间隔要<E99A94>? */
|
||
static void ch395f_spi_begin(void) {
|
||
/* CS 先拉高,确保空闲状<E997B2>?*/
|
||
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
|
||
/* CS 拉低,启动事务(无需额外延时,SPI 时序<E697B6>?SCK 频率决定<E586B3>?*/
|
||
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_RESET);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:结<EFBC9A>?SPI 事务(CS 拉高<E68B89>? */
|
||
static void ch395f_spi_end(void) {
|
||
/* CS 拉高,结束事<E69D9F>?*/
|
||
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:DMA 全双工传输并等待完成
|
||
* 入口参数:len - 传输字节<E5AD97>? uint16_t
|
||
* 函数说明:发<EFBC9A>?s_spi2_dma_tx_buf,接收到 s_spi2_dma_rx_buf<75>? * 阻塞等待 DMA 完成回调置位标志
|
||
*/
|
||
static void ch395f_spi_dma_transfer(uint16_t len) {
|
||
HAL_StatusTypeDef status;
|
||
uint32_t tickstart;
|
||
volatile uint32_t loop_cnt = 0U;
|
||
|
||
s_spi2_dma_done = 0U;
|
||
status = HAL_SPI_TransmitReceive_DMA(&hspi2, s_spi2_dma_tx_buf, s_spi2_dma_rx_buf, len);
|
||
if (status != HAL_OK) {
|
||
DBG_ERROR("HAL_SPI_TransmitReceive_DMA FAIL: %d, SPI_State=%d, SPI_Error=0x%lX, DMA_State=%d",
|
||
(int)status,
|
||
(int)hspi2.State,
|
||
hspi2.ErrorCode,
|
||
(int)hspi2.hdmarx->State);
|
||
return;
|
||
}
|
||
|
||
tickstart = HAL_GetTick();
|
||
while (s_spi2_dma_done == 0U) {
|
||
loop_cnt++;
|
||
if ((HAL_GetTick() - tickstart) > 1000U) {
|
||
DBG_ERROR("TIMEOUT after 1s, len=%d, loop=%lu, SPI_State=%d, SPI_Error=0x%lX",
|
||
(int)len, loop_cnt,
|
||
(int)hspi2.State,
|
||
hspi2.ErrorCode);
|
||
DBG_ERROR(" DMA_RX: State=%d, Dir=%lu, CNT=%lu",
|
||
(int)hspi2.hdmarx->State,
|
||
hspi2.hdmarx->Init.Direction,
|
||
__HAL_DMA_GET_COUNTER(hspi2.hdmarx));
|
||
DBG_ERROR(" DMA_TX: State=%d, Dir=%lu, CNT=%lu",
|
||
(int)hspi2.hdmatx->State,
|
||
hspi2.hdmatx->Init.Direction,
|
||
__HAL_DMA_GET_COUNTER(hspi2.hdmatx));
|
||
DBG_ERROR(" SPI2_CR1=0x%lX, CR2=0x%lX, SR=0x%lX",
|
||
SPI2->CR1, SPI2->CR2, SPI2->SR);
|
||
/* 强制复位 SPI2 DMA 状态,避免死锁 */
|
||
HAL_SPI_Abort(&hspi2);
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* 检查传输是否成功:SPI 错误码必须为 0 */
|
||
if (hspi2.ErrorCode != 0U) {
|
||
DBG_INFO("SPI DMA done with errors: ErrorCode=0x%lX, SPI_State=%d",
|
||
hspi2.ErrorCode, (int)hspi2.State);
|
||
__HAL_SPI_CLEAR_OVRFLAG(&hspi2);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:SPI 收发完成回调
|
||
* 函数说明:由 HAL SPI 驱动<E9A9B1>?DMA TX+RX 完成后调用(HAL_SPI_TransmitReceive_DMA 触发此回调)
|
||
*/
|
||
/* SPI1 DMA 完成标志(定义在 gd5f2gq5ue.c 中) */
|
||
extern volatile uint8_t g_spi1_dma_done;
|
||
|
||
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||
if (hspi->Instance == SPI2) {
|
||
s_spi2_dma_done = 1U;
|
||
} else if (hspi->Instance == SPI1) {
|
||
g_spi1_dma_done = 1U;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:SPI 错误回调
|
||
* 函数说明:由 HAL SPI 驱动<E9A9B1>?DMA 传输出错时调用,释放等待
|
||
*/
|
||
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
|
||
if (hspi->Instance == SPI2) {
|
||
s_spi2_dma_done = 1U;
|
||
} else if (hspi->Instance == SPI1) {
|
||
g_spi1_dma_done = 1U;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 公共函数实现<E5AE9E>? */
|
||
|
||
/*
|
||
* 函数功能:向 CH395 写入命令字节
|
||
* 入口参数:cmd - 命令<E591BD>? uint8_t 0x00 - 0xFF
|
||
* 返回值:命令码回显<E59B9E>? uint8_t
|
||
* 限定条件:需<EFBC9A>?ch395f_spi_begin() <20>?ch395f_spi_end() 之间调用
|
||
* 函数说明:SPI 模式下命令与数据之间无需间隔
|
||
*/
|
||
uint8_t ch395f_write_cmd(uint8_t cmd) {
|
||
uint8_t rx_data;
|
||
|
||
/* 通过 SPI 发送命令并接收回显 */
|
||
HAL_SPI_TransmitReceive(&hspi2, &cmd, &rx_data, 1, HAL_MAX_DELAY);
|
||
return rx_data;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:向 CH395 写入数据字节
|
||
* 入口参数:data - 数据字节 uint8_t 0x00 - 0xFF
|
||
* 返回值:数据回显<E59B9E>? uint8_t
|
||
* 限定条件:需<EFBC9A>?ch395f_spi_begin() <20>?ch395f_spi_end() 之间调用
|
||
*/
|
||
uint8_t ch395f_write_data(uint8_t data) {
|
||
uint8_t rx_data;
|
||
|
||
/* 通过 SPI 发送数据并接收回显 */
|
||
HAL_SPI_TransmitReceive(&hspi2, &data, &rx_data, 1, HAL_MAX_DELAY);
|
||
return rx_data;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:从 CH395 读取数据字节
|
||
* 返回值:接收到的数据字节 uint8_t
|
||
* 限定条件:需<EFBC9A>?ch395f_spi_begin() <20>?ch395f_spi_end() 之间调用
|
||
*/
|
||
uint8_t ch395f_read_data(void) {
|
||
uint8_t tx_dummy = 0xFFU;
|
||
uint8_t rx_data;
|
||
|
||
/* 发<>?0xFF 作为时钟源,读取芯片返回的数<E79A84>?*/
|
||
HAL_SPI_TransmitReceive(&hspi2, &tx_dummy, &rx_data, 1, HAL_MAX_DELAY);
|
||
return rx_data;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:检<EFBC9A>?CH395 芯片是否存在且通信正常
|
||
* 返回值:CH395F_STATUS_OK - 检测到芯片,CH395F_STATUS_NOT_DETECTED - 未检测到
|
||
* 限定条件:SPI2 已正确初始化
|
||
* 函数说明:发<EFBC9A>?CMD_CHECK_EXIST (0x06) 及测试字<E8AF95>?0x57<35>? * 芯片应返回按位取反<E58F96>?0xA8
|
||
*/
|
||
ch395f_status_t ch395f_check_exist(void) {
|
||
uint8_t test_data = CH395F_CHECK_EXIST_TEST_DATA;
|
||
uint8_t reply_data;
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送检测命<E6B58B>?*/
|
||
ch395f_write_cmd(CH395F_CMD_CHECK_EXIST);
|
||
|
||
/* 发送测试数<E8AF95>?*/
|
||
ch395f_write_data(test_data);
|
||
|
||
/* 读取芯片回复 */
|
||
reply_data = ch395f_read_data();
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
|
||
/* 验证回复是否为测试数据的按位取反 */
|
||
if (reply_data == (uint8_t)(~test_data)) {
|
||
return CH395F_STATUS_OK;
|
||
}
|
||
|
||
return CH395F_STATUS_NOT_DETECTED;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读<EFBC9A>?CH395 芯片版本<E78988>? * 返回值:版本字节(bit5:0 为版本号)uint8_t
|
||
* 限定条件:芯片已通过 ch395f_check_exist() 检<>? */
|
||
uint8_t ch395f_get_version(void) {
|
||
uint8_t version;
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送版本读取命<E58F96>?*/
|
||
ch395f_write_cmd(CH395F_CMD_GET_IC_VER);
|
||
|
||
/* 读取版本<E78988>?*/
|
||
version = ch395f_read_data();
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
|
||
return version;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取远<E58F96>?IP 和端<E5928C>? * 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* p_buf - 6 字节输出缓冲区(4字节IP + 2字节端口<E7ABAF>? * 限定条件:TCP Server 模式下连接建立后调用
|
||
* 函数说明:IP 低字节在前,端口低字节在<E88A82>? */
|
||
void ch395f_get_remot_ipp(uint8_t sock, uint8_t *p_buf) {
|
||
uint8_t i;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_buf == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送获取远<E58F96>?IP 和端口命<E58FA3>?*/
|
||
ch395f_write_cmd(CH395F_CMD_GET_REMOT_IPP_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 读取 4 字节 IP 地址 */
|
||
for (i = 0; i < 4; i++) {
|
||
p_buf[i] = ch395f_read_data();
|
||
}
|
||
|
||
/* 读取 2 字节端口<E7ABAF>?*/
|
||
for (i = 4; i < 6; i++) {
|
||
p_buf[i] = ch395f_read_data();
|
||
}
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?CH395 功能参数
|
||
* 入口参数:para - 4 字节功能参数 uint32_t
|
||
* 限定条件:必须在 ch395f_init() 之前调用
|
||
*/
|
||
void ch395f_set_fun_para(uint32_t para) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置功能参数命<E695B0>?*/
|
||
ch395f_write_cmd(CH395F_CMD_SET_FUN_PARA);
|
||
|
||
/* 发<>?4 字节参数(低字节在前<E59CA8>?*/
|
||
ch395f_write_data((uint8_t)(para));
|
||
ch395f_write_data((uint8_t)(para >> 8));
|
||
ch395f_write_data((uint8_t)(para >> 16));
|
||
ch395f_write_data((uint8_t)(para >> 24));
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?TCP MSS
|
||
* 入口参数:mss - MSS <20>? uint16_t 60~1460
|
||
* 限定条件:必须在 ch395f_init() 之前调用
|
||
*/
|
||
void ch395f_set_tcp_mss(uint16_t mss) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设<E98081>?TCP MSS 命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_TCP_MSS);
|
||
|
||
/* 发<>?2 字节 MSS 值(低字节在前) */
|
||
ch395f_write_data(mss & 0xFFU);
|
||
ch395f_write_data((mss >> 8) & 0xFFU);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?Socket 接收缓冲<E7BC93>? * 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* start_block - 起始块地址 uint8_t
|
||
* block_count - 块个<E59D97>? uint8_t
|
||
* 限定条件:必须在 OPEN_SOCKET_SN 之前调用
|
||
*/
|
||
void ch395f_set_recv_buf(uint8_t sock, uint8_t start_block, uint8_t block_count) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置接收缓冲区命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_RECV_BUF);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 发送起始块地址 */
|
||
ch395f_write_data(start_block);
|
||
|
||
/* 发送块个数 */
|
||
ch395f_write_data(block_count);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?Socket 发送缓冲区
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* start_block - 起始块地址 uint8_t
|
||
* block_count - 块个<E59D97>? uint8_t
|
||
* 限定条件:必须在 OPEN_SOCKET_SN 之前调用
|
||
*/
|
||
void ch395f_set_send_buf(uint8_t sock, uint8_t start_block, uint8_t block_count) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置发送缓冲区命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_SEND_BUF);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 发送起始块地址 */
|
||
ch395f_write_data(start_block);
|
||
|
||
/* 发送块个数 */
|
||
ch395f_write_data(block_count);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:清<EFBC9A>?Socket 接收缓冲<E7BC93>? * 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* 限定条件:Socket 已打开
|
||
*/
|
||
void ch395f_clear_recv_buf(uint8_t sock) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送清空接收缓冲区命令 */
|
||
ch395f_write_cmd(CH395F_CMD_CLEAR_RECV_BUF_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?TCP KeepAlive 空闲时间
|
||
* 入口参数:time - 空闲时间(秒<EFBC88>? uint32_t
|
||
* 限定条件:必须在 ch395f_init() 之前调用
|
||
*/
|
||
void ch395f_set_keepalive_idle(uint32_t time) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设<E98081>?KeepAlive 空闲时间命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_IDLE);
|
||
|
||
/* 发<>?4 字节时间值(低字节在前) */
|
||
ch395f_write_data((uint8_t)(time));
|
||
ch395f_write_data((uint8_t)(time >> 8));
|
||
ch395f_write_data((uint8_t)(time >> 16));
|
||
ch395f_write_data((uint8_t)(time >> 24));
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?TCP KeepAlive 探测间隔
|
||
* 入口参数:time - 间隔时间(秒<EFBC88>? uint32_t
|
||
* 限定条件:必须在 ch395f_init() 之前调用
|
||
*/
|
||
void ch395f_set_keepalive_intvl(uint32_t time) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设<E98081>?KeepAlive 探测间隔命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_INTVL);
|
||
|
||
/* 发<>?4 字节时间值(低字节在前) */
|
||
ch395f_write_data((uint8_t)(time));
|
||
ch395f_write_data((uint8_t)(time >> 8));
|
||
ch395f_write_data((uint8_t)(time >> 16));
|
||
ch395f_write_data((uint8_t)(time >> 24));
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?TCP KeepAlive 探测次数
|
||
* 入口参数:cnt - 探测次数 uint8_t
|
||
* 限定条件:必须在 ch395f_init() 之前调用
|
||
*/
|
||
void ch395f_set_keepalive_cnt(uint8_t cnt) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设<E98081>?KeepAlive 探测次数命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_CNT);
|
||
|
||
/* 发送探测次<E6B58B>?*/
|
||
ch395f_write_data(cnt);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:启<EFBC9A>?禁用指定 Socket <20>?KeepAlive
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* enable - 1 启用<E590AF>? 禁用 uint8_t
|
||
* 限定条件:Socket 已打开
|
||
*/
|
||
void ch395f_set_keepalive_enable(uint8_t sock, uint8_t enable) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设<E98081>?KeepAlive 使能命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 发送使能标<E883BD>?*/
|
||
ch395f_write_data(enable);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?TCP 重传次数
|
||
* 入口参数:cnt - 重传次数 uint8_t<5F>?=使用默认<E9BB98>?<3F>? * 限定条件:必须在 ch395f_init() 之前调用
|
||
*/
|
||
void ch395f_set_retrans_count(uint8_t cnt) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置重传次数命<E695B0>?*/
|
||
ch395f_write_cmd(CH395F_CMD_SET_RETRAN_COUNT);
|
||
|
||
/* 发送重传次<E4BCA0>?*/
|
||
ch395f_write_data(cnt);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?TCP 重传周期
|
||
* 入口参数:period - 重传周期(毫秒) uint16_t
|
||
* 限定条件:必须在 ch395f_init() 之前调用
|
||
*/
|
||
void ch395f_set_retrans_period(uint16_t period) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置重传周期命<E69C9F>?*/
|
||
ch395f_write_cmd(CH395F_CMD_SET_RETRAN_PERIOD);
|
||
|
||
/* 发<>?2 字节周期值(低字节在前) */
|
||
ch395f_write_data(period & 0xFFU);
|
||
ch395f_write_data((period >> 8) & 0xFFU);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:软件复<E4BBB6>?CH395
|
||
* 返回值:CH395F_STATUS_OK
|
||
* 限定条件:SPI2 已正确初始化
|
||
* 函数说明:复位耗时<E88097>?15ms(TE0),函数内部已包含延<E590AB>? */
|
||
ch395f_status_t ch395f_reset(void) {
|
||
DBG_INFO("ch395f_reset: spi_begin");
|
||
ch395f_spi_begin();
|
||
|
||
DBG_INFO("ch395f_reset: write_cmd RESET_ALL");
|
||
ch395f_write_cmd(CH395F_CMD_RESET_ALL);
|
||
|
||
DBG_INFO("ch395f_reset: spi_end");
|
||
ch395f_spi_end();
|
||
|
||
DBG_INFO("ch395f_reset: HAL_Delay(%d)", CH395F_RESET_DELAY_MS);
|
||
DBG_INFO("ch395f_reset: tick=%lu basepri=0x%02X", HAL_GetTick(), __get_BASEPRI());
|
||
HAL_Delay(CH395F_RESET_DELAY_MS);
|
||
|
||
DBG_INFO("ch395f_reset: done");
|
||
return CH395F_STATUS_OK;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:查询命令执行状<E8A18C>? * 返回值:状态字节(CH395F_ERR_SUCCESS / CH395F_ERR_BUSY / ...)uint8_t
|
||
* 限定条件:仅在长执行命令(如初始化)后调<E5908E>? */
|
||
uint8_t ch395f_get_cmd_status(void) {
|
||
uint8_t status;
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送状态查询命<E8AFA2>?*/
|
||
ch395f_write_cmd(CH395F_CMD_GET_CMD_STATUS);
|
||
|
||
/* 读取状态字<E68081>?*/
|
||
status = ch395f_read_data();
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:轮询等待命令执行完<E8A18C>? * 入口参数:timeout_ms - 超时时间(毫秒) uint32_t
|
||
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS <20>?CH395F_CMD_RET_ABORT<52>? * 限定条件:在发送长执行命令后调<E5908E>? * 函数说明:每 20ms 查询一次,直到命令完成或超<E68896>? */
|
||
uint8_t ch395f_poll_cmd_status(uint32_t timeout_ms) {
|
||
uint8_t status;
|
||
uint32_t retry_count = 0U;
|
||
uint32_t max_retries = (timeout_ms + 20U - 1U) / 20U;
|
||
|
||
while (1) {
|
||
HAL_Delay(20U);
|
||
status = ch395f_get_cmd_status();
|
||
|
||
if (status != CH395F_ERR_BUSY) {
|
||
return status;
|
||
}
|
||
|
||
if (++retry_count >= max_retries) {
|
||
return CH395F_ERR_BUSY; /* 超时 */
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:初始化 CH395(包<EFBC88>?MAC、PHY、TCP/IP 协议栈)
|
||
* 返回值:CH395F_STATUS_OK - 成功,CH395F_STATUS_TIMEOUT - 超时失败
|
||
* 限定条件:芯片已通过 ch395f_reset() 复位
|
||
* 函数说明:阻塞等待初始化完成或超时(芯片需要约 200ms,这里等最<E7AD89>?4s<34>? */
|
||
ch395f_status_t ch395f_init(void) {
|
||
uint8_t status;
|
||
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_INIT_CH395);
|
||
ch395f_spi_end();
|
||
|
||
status = ch395f_poll_cmd_status(4000U);
|
||
|
||
if (status == CH395F_ERR_SUCCESS) {
|
||
return CH395F_STATUS_OK;
|
||
}
|
||
|
||
return (status == CH395F_ERR_BUSY) ? CH395F_STATUS_TIMEOUT : CH395F_STATUS_ERROR;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?CH395 IP 地址(低字节在前<E59CA8>? * 入口参数:p_ip - 4 字节 IP 地址指针 uint8_t*
|
||
* 限定条件:指针非空,芯片已初始化
|
||
*/
|
||
void ch395f_set_ip_addr(uint8_t *p_ip) {
|
||
uint8_t i;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_ip == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设<E98081>?IP 命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_IP_ADDR);
|
||
|
||
/* 逐字节发<E88A82>?IP 地址 */
|
||
for (i = 0; i < 4; i++) {
|
||
ch395f_write_data(p_ip[i]);
|
||
}
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设置网<E7BDAE>?IP 地址(低字节在前<E59CA8>? * 入口参数:p_gwip - 4 字节网关 IP 指针 uint8_t*
|
||
* 限定条件:指针非空,芯片已初始化
|
||
*/
|
||
void ch395f_set_gwip_addr(uint8_t *p_gwip) {
|
||
uint8_t i;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_gwip == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置网关命<E585B3>?*/
|
||
ch395f_write_cmd(CH395F_CMD_SET_GWIP_ADDR);
|
||
|
||
/* 逐字节发送网<E98081>?IP */
|
||
for (i = 0; i < 4; i++) {
|
||
ch395f_write_data(p_gwip[i]);
|
||
}
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设置子网掩码(低字节在前)
|
||
* 入口参数:p_mask - 4 字节子网掩码指针 uint8_t*
|
||
* 限定条件:指针非空,芯片已初始化
|
||
*/
|
||
void ch395f_set_mask_addr(uint8_t *p_mask) {
|
||
uint8_t i;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_mask == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置掩码命<E7A081>?*/
|
||
ch395f_write_cmd(CH395F_CMD_SET_MASK_ADDR);
|
||
|
||
/* 逐字节发送子网掩<E7BD91>?*/
|
||
for (i = 0; i < 4; i++) {
|
||
ch395f_write_data(p_mask[i]);
|
||
}
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?MAC 地址(低字节在前,存储于 EEPROM<4F>? * 入口参数:p_mac - 6 字节 MAC 地址指针 uint8_t*
|
||
* 限定条件:指针非空,芯片已初始化
|
||
* 函数说明:执行耗时<E88097>?30us(TE5<45>? */
|
||
void ch395f_set_mac_addr(uint8_t *p_mac) {
|
||
uint8_t i;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_mac == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设<E98081>?MAC 命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_MAC_ADDR);
|
||
|
||
/* 逐字节发<E88A82>?MAC 地址 */
|
||
for (i = 0; i < 6; i++) {
|
||
ch395f_write_data(p_mac[i]);
|
||
}
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:从 CH395 读取 MAC 地址
|
||
* 出口参数:p_mac - 6 字节 MAC 地址缓冲区指<E58CBA>? uint8_t*
|
||
* 限定条件:指针非<E99288>? */
|
||
void ch395f_get_mac_addr(uint8_t *p_mac) {
|
||
uint8_t i;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_mac == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送读<E98081>?MAC 命令 */
|
||
ch395f_write_cmd(CH395F_CMD_GET_MAC_ADDR);
|
||
|
||
/* 逐字节读<E88A82>?MAC 地址 */
|
||
for (i = 0; i < 6; i++) {
|
||
p_mac[i] = ch395f_read_data();
|
||
}
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读<EFBC9A>?IP 信息(IP + 掩码 + DNS,共 20 字节<E5AD97>? * 出口参数:p_buf - 20 字节缓冲区指<E58CBA>? uint8_t*
|
||
* 限定条件:指针非<E99288>? */
|
||
void ch395f_get_ip_inf(uint8_t *p_buf) {
|
||
uint8_t i;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_buf == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送读<E98081>?IP 信息命令 */
|
||
ch395f_write_cmd(CH395F_CMD_GET_IP_INF);
|
||
|
||
/* 逐字节读<E88A82>?IP 信息(共 20 字节<E5AD97>?*/
|
||
for (i = 0; i < 20; i++) {
|
||
p_buf[i] = ch395f_read_data();
|
||
}
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获<EFBC9A>?PHY 连接状<E68EA5>? * 返回值:PHY 状态码(CH395F_PHY_DISCONN、CH395F_PHY_10M_FULL 等)uint8_t
|
||
* 限定条件:芯片已初始<E5889D>? */
|
||
uint8_t ch395f_get_phy_status(void) {
|
||
uint8_t status;
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送读<E98081>?PHY 状态命<E68081>?*/
|
||
ch395f_write_cmd(CH395F_CMD_GET_PHY_STATUS);
|
||
|
||
/* 读取 PHY 状<>?*/
|
||
status = ch395f_read_data();
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?PHY 连接模式
|
||
* 入口参数:phystat - 连接模式<E6A8A1>? uint8_t 0x20 = 自动协商
|
||
* 限定条件:芯片已初始<E5889D>? */
|
||
void ch395f_set_phy(uint8_t phystat) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设<E98081>?PHY 命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_PHY);
|
||
|
||
/* 发送连接模式参<E5BC8F>?*/
|
||
ch395f_write_data(phystat);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:使<EFBC9A>?关闭 CH395 响应 PING
|
||
* 入口参数:enable - 1 使能 PING 响应<E5938D>? 关闭
|
||
* 限定条件:芯片已初始<E5889D>? * 函数说明:手<EFBC9A>?5.37 CMD_PING_ENABLE,默认关<E8AEA4>? */
|
||
void ch395f_ping_enable(uint8_t enable) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发<>?PING 使能命令 */
|
||
ch395f_write_cmd(CH395F_CMD_PING_ENABLE);
|
||
|
||
/* 发送使能标<E883BD>?*/
|
||
ch395f_write_data(enable);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:启<EFBC9A>?禁用 DHCP
|
||
* 入口参数:enable - 1 启用<E590AF>? 禁用 uint8_t
|
||
* 限定条件:芯片已初始<E5889D>? */
|
||
void ch395f_set_dhcp(uint8_t enable) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发<>?DHCP 使能命令 */
|
||
ch395f_write_cmd(CH395F_CMD_DHCP_ENABLE);
|
||
|
||
/* 发送启<E98081>?禁用参数 */
|
||
ch395f_write_data(enable);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获<EFBC9A>?DHCP 状<>? * 返回值:0 = 成功,非<EFBC8C>?= 错误/超时 uint8_t
|
||
* 限定条件:芯片已初始化且 DHCP 已启<E5B7B2>? */
|
||
uint8_t ch395f_get_dhcp_status(void) {
|
||
uint8_t status;
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送读<E98081>?DHCP 状态命<E68081>?*/
|
||
ch395f_write_cmd(CH395F_CMD_GET_DHCP_STATUS);
|
||
|
||
/* 读取 DHCP 状<>?*/
|
||
status = ch395f_read_data();
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?Socket 协议类型
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||
* proto - 协议类型 uint8_t CH395F_PROTO_TYPE_xxx
|
||
* 限定条件:Socket 未打开
|
||
*/
|
||
void ch395f_set_proto_type(uint8_t sock, uint8_t proto) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置协议类型命<E59E8B>?*/
|
||
ch395f_write_cmd(CH395F_CMD_SET_PROTO_TYPE_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 发送协议类<E8AEAE>?*/
|
||
ch395f_write_data(proto);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?Socket 目标 IP 地址
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||
* p_ip - 4 字节目标 IP 指针 uint8_t*
|
||
* 限定条件:指针非<E99288>? */
|
||
void ch395f_set_des_ip(uint8_t sock, uint8_t *p_ip) {
|
||
uint8_t i;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_ip == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置目<E7BDAE>?IP 命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_IP_ADDR_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 逐字节发送目<E98081>?IP */
|
||
for (i = 0; i < 4; i++) {
|
||
ch395f_write_data(p_ip[i]);
|
||
}
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?Socket 目标端口(小端序<E7ABAF>? * 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||
* port - 目标端口 uint16_t
|
||
* 限定条件:无
|
||
*/
|
||
void ch395f_set_des_port(uint8_t sock, uint16_t port) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置目标端口命<E58FA3>?*/
|
||
ch395f_write_cmd(CH395F_CMD_SET_DES_PORT_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 发送端口低字节 */
|
||
ch395f_write_data(port & 0xFFU);
|
||
|
||
/* 发送端口高字节 */
|
||
ch395f_write_data((port >> 8) & 0xFFU);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?Socket 源端口(小端序)
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||
* port - 源端<E6BA90>? uint16_t
|
||
* 限定条件:无
|
||
*/
|
||
void ch395f_set_sour_port(uint8_t sock, uint16_t port) {
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送设置源端口命令 */
|
||
ch395f_write_cmd(CH395F_CMD_SET_SOUR_PORT_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 发送端口低字节 */
|
||
ch395f_write_data(port & 0xFFU);
|
||
|
||
/* 发送端口高字节 */
|
||
ch395f_write_data((port >> 8) & 0xFFU);
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:打开 Socket
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功<E68890>? uint8_t
|
||
* 限定条件:协议类型、目<E38081>?IP、端口已设置
|
||
*/
|
||
uint8_t ch395f_open_socket(uint8_t sock) {
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_OPEN_SOCKET_SN);
|
||
ch395f_write_data(sock);
|
||
ch395f_spi_end();
|
||
|
||
return ch395f_poll_cmd_status(4000U);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:关<EFBC9A>?Socket
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功<E68890>? uint8_t
|
||
* 限定条件:Socket 已打开
|
||
*/
|
||
uint8_t ch395f_close_socket(uint8_t sock) {
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_CLOSE_SOCKET_SN);
|
||
ch395f_write_data(sock);
|
||
ch395f_spi_end();
|
||
|
||
return ch395f_poll_cmd_status(4000U);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:启<EFBC9A>?TCP 监听模式
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功<E68890>? uint8_t
|
||
* 限定条件:Socket 已打开且协议类型为 TCP
|
||
*/
|
||
uint8_t ch395f_tcp_listen(uint8_t sock) {
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_TCP_LISTEN_SN);
|
||
ch395f_write_data(sock);
|
||
ch395f_spi_end();
|
||
|
||
return ch395f_poll_cmd_status(4000U);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:启<EFBC9A>?TCP 连接
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功<E68890>? uint8_t
|
||
* 限定条件:Socket 已打开且协议类型为 TCP
|
||
*/
|
||
uint8_t ch395f_tcp_connect(uint8_t sock) {
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_TCP_CONNECT_SN);
|
||
ch395f_write_data(sock);
|
||
ch395f_spi_end();
|
||
|
||
return ch395f_poll_cmd_status(4000U);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:断开 TCP 连接
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功<E68890>? uint8_t
|
||
* 限定条件:TCP 已建立连<E7AB8B>? */
|
||
uint8_t ch395f_tcp_disconnect(uint8_t sock) {
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_TCP_DISCONNECT_SN);
|
||
ch395f_write_data(sock);
|
||
ch395f_spi_end();
|
||
|
||
return ch395f_poll_cmd_status(4000U);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:向 Socket 发送缓冲区写入数据(DMA 批量传输<E4BCA0>? * 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||
* p_data - 数据指针 uint8_t*
|
||
* len - 数据长度 uint16_t
|
||
* 限定条件:指针非空,长度大于 0,TCP 已连接或 UDP 已打开
|
||
* 函数说明:将命令头和数据打包<E68993>?DMA 缓冲区,一<EFBC8C>?SPI DMA 传输完成
|
||
*/
|
||
void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
|
||
uint16_t total;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_data == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 计算总长度:1(cmd) + 1(sock) + 2(len) + data */
|
||
total = 4U + len;
|
||
if (total > CH395F_DMA_BUF_SIZE) {
|
||
return;
|
||
}
|
||
|
||
/* 打包命令头到 DMA 发送缓冲区 */
|
||
s_spi2_dma_tx_buf[0] = CH395F_CMD_WRITE_SEND_BUF_SN;
|
||
s_spi2_dma_tx_buf[1] = sock;
|
||
s_spi2_dma_tx_buf[2] = len & 0xFFU;
|
||
s_spi2_dma_tx_buf[3] = (len >> 8) & 0xFFU;
|
||
|
||
/* 拷贝用户数据<E695B0>?DMA 发送缓冲区 */
|
||
memcpy(&s_spi2_dma_tx_buf[4], p_data, len);
|
||
|
||
/* 数据同步屏障:确<EFBC9A>?memcpy 写入<E58699>?DMA 读取前对总线可见 */
|
||
__DSB();
|
||
|
||
/* CS 拉低 -> DMA 批量传输 -> CS 拉高 */
|
||
ch395f_spi_begin();
|
||
ch395f_spi_dma_transfer(total);
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:从 Socket 接收缓冲区读取数据(DMA 批量传输<E4BCA0>? * 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||
* p_data - 输出缓冲区指<E58CBA>? uint8_t*
|
||
* len - 待读取数据长<E68DAE>? uint16_t
|
||
* 限定条件:指针非空,长度大于 0,接收缓冲区有数<E69C89>? * 函数说明:命令头打包到发送缓冲区(数据部分填 0xFF 作时钟)<E9929F>? * 一<>?SPI DMA 全双工传输,有效数据从接收缓冲区偏移 4 字节处拷<E5A484>? */
|
||
void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
|
||
uint16_t total;
|
||
|
||
/* 检查输入参数合法<E59088>?*/
|
||
if (p_data == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* 计算总长度:1(cmd) + 1(sock) + 2(len) + data */
|
||
total = 4U + len;
|
||
if (total > CH395F_DMA_BUF_SIZE) {
|
||
return;
|
||
}
|
||
|
||
/* 命令头打包到 DMA 发送缓冲区 */
|
||
s_spi2_dma_tx_buf[0] = CH395F_CMD_READ_RECV_BUF_SN;
|
||
s_spi2_dma_tx_buf[1] = sock;
|
||
s_spi2_dma_tx_buf[2] = len & 0xFFU;
|
||
s_spi2_dma_tx_buf[3] = (len >> 8) & 0xFFU;
|
||
|
||
/* 数据部分填充 0xFF(SPI 时钟驱动用) */
|
||
memset(&s_spi2_dma_tx_buf[4], 0xFFU, len);
|
||
|
||
/* CS 拉低 -> DMA 全双工传输(发命令头 + 收数据) -> CS 拉高 */
|
||
ch395f_spi_begin();
|
||
ch395f_spi_dma_transfer(total);
|
||
ch395f_spi_end();
|
||
|
||
/* <20>?DMA 接收缓冲区偏<E58CBA>?4 字节处拷贝有效数据到用户缓冲<E7BC93>?*/
|
||
memcpy(p_data, &s_spi2_dma_rx_buf[4], len);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获<EFBC9A>?Socket 接收缓冲区数据长<E68DAE>? * 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||
* 返回值:接收数据长度 uint16_t
|
||
* 限定条件:Socket 已打开
|
||
*/
|
||
uint16_t ch395f_get_recv_len(uint8_t sock) {
|
||
uint16_t recv_len;
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送获取接收长度命<E5BAA6>?*/
|
||
ch395f_write_cmd(CH395F_CMD_GET_RECV_LEN_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 读取长度低字<E4BD8E>?*/
|
||
recv_len = ch395f_read_data();
|
||
|
||
/* 读取长度高字节并组合 */
|
||
recv_len |= (uint16_t)ch395f_read_data() << 8;
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
|
||
return recv_len;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取全局中断状<E696AD>? * 返回值:中断状态字<E68081>? uint8_t
|
||
* 限定条件:芯片已初始<E5889D>? */
|
||
uint8_t ch395f_get_glob_int_status(void) {
|
||
uint8_t status;
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送读取全局中断状态命<E68081>?*/
|
||
ch395f_write_cmd(CH395F_CMD_GET_GLOB_INT_STATUS);
|
||
|
||
/* 读取中断状<E696AD>?*/
|
||
status = ch395f_read_data();
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取全局中断状态(2 字节版,支持 Socket 0~7<>? * 返回值:中断状<E696AD>?uint16_t(低字节=Socket 0~3 + PHY/DHCP,高字节=Socket 4~7 + DHCPv6<76>? * 限定条件:芯片已初始<E5889D>? */
|
||
uint16_t ch395f_get_glob_int_status_all(void) {
|
||
uint16_t status;
|
||
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_GET_GLOB_INT_STATUS_ALL);
|
||
status = (uint16_t)ch395f_read_data();
|
||
status |= ((uint16_t)ch395f_read_data() << 8U);
|
||
ch395f_spi_end();
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获<EFBC9A>?Socket 中断状<E696AD>? * 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* 返回值:Socket 中断状态字<E68081>? uint8_t
|
||
* 限定条件:芯片已初始<E5889D>? */
|
||
uint8_t ch395f_get_sock_int_status(uint8_t sock) {
|
||
uint8_t status;
|
||
|
||
/* 开<>?SPI 事务 */
|
||
ch395f_spi_begin();
|
||
|
||
/* 发送读<E98081>?Socket 中断状态命<E68081>?*/
|
||
ch395f_write_cmd(CH395F_CMD_GET_INT_STATUS_SN);
|
||
|
||
/* 发<>?Socket 索引 */
|
||
ch395f_write_data(sock);
|
||
|
||
/* 读取中断状<E696AD>?*/
|
||
status = ch395f_read_data();
|
||
|
||
/* 结束 SPI 事务 */
|
||
ch395f_spi_end();
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获<EFBC9A>?Socket 状态(2 字节输出<E8BE93>? * 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* status - 2 字节输出缓冲<E7BC93>?[0]=Socket状<74>? [1]=TCP状<50>? * 限定条件:芯片已初始<E5889D>? */
|
||
void ch395f_get_socket_status(uint8_t sock, uint8_t *p_status) {
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_GET_SOCKET_STATUS_SN);
|
||
ch395f_write_data(sock);
|
||
p_status[0] = ch395f_read_data();
|
||
p_status[1] = ch395f_read_data();
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?ARP 重传参数
|
||
* 入口参数:period_100ms - ARP 重传周期(单<EFBC88>?100ms<6D>?=默认<E9BB98>?10<31>?秒) uint8_t
|
||
* cnt - ARP 重传次数<E6ACA1>?=默认<E9BB98>?3<>? uint8_t
|
||
* 限定条件:应<EFBC9A>?ch395f_init() 之后调用
|
||
* 函数说明:周期最大值约 25.5 秒,255 表示无限重传
|
||
*/
|
||
void ch395f_set_arp(uint8_t period_100ms, uint8_t cnt) {
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_SET_ARP);
|
||
ch395f_write_data(period_100ms);
|
||
ch395f_write_data(cnt);
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:设<EFBC9A>?Socket TTL
|
||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||
* ttl - TTL <20>? uint8_t 最<>?128
|
||
* 限定条件:Socket 已打开,TCP Server 模式下不生效
|
||
*/
|
||
void ch395f_set_ttl(uint8_t sock, uint8_t ttl) {
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_SET_TTL);
|
||
ch395f_write_data(sock);
|
||
ch395f_write_data(ttl);
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取不可达信息(用于调试连接失败原因)
|
||
* 出口参数:p_buf - 8 字节输出缓冲区(类型+协议<E58D8F>?端口+IP<49>? * 限定条件:收<EFBC9A>?GINT_STAT_UNREACH 中断后调<E5908E>? */
|
||
void ch395f_get_unreach_info(uint8_t *p_buf) {
|
||
uint8_t i = 0;
|
||
|
||
ch395f_spi_begin();
|
||
ch395f_write_cmd(CH395F_CMD_GET_UNREACH_IPPT);
|
||
for (i = 0; i < 8; i++) {
|
||
p_buf[i] = ch395f_read_data();
|
||
}
|
||
ch395f_spi_end();
|
||
}
|
||
|
||
|