Files
STM32F4-Base/Drivers/BSP/CH395F/ch395f.c
2026-07-22 11:14:22 +08:00

1263 lines
36 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称CH395F 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>?15msTE0函数内部已包含延<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>?30usTE5<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
* 限定条件:指针非空,长度大于 0TCP 已连接或 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;
/* 数据部分填充 0xFFSPI 时钟驱动用) */
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();
}