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

1334 lines
37 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以太网协议栈驱动
* 模块功能CH395F以太网协议栈芯片SPI驱动实现提供芯片检测、初始化、
* 网络参数配置、PHY管理、DHCP、Socket管理及数据收发功能
* 适用平台STM32F4系列SPI2接口
* 作者:王建锋
* 创建日期2026-03-14
* 修改记录:
* 2026-03-14 王建锋 创建初始版本,按代码规范重构
* 2026-07-18 王建锋 改进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 /* 芯片检测测试数据 */
#define CH395F_RESET_DELAY_MS 20U /* 复位后等待延时ms */
#define CH395F_INIT_TIMEOUT_MS 500U /* 初始化超时时间ms */
#define CH395F_SPI_BEGIN_DELAY_MS 1U /* SPI事务开始延时ms */
/* DMA缓冲大小使用ch395f.h中公开的CH395F_SPI_DMA_BUF_SIZE */
#define CH395F_DMA_BUF_SIZE CH395F_SPI_DMA_BUF_SIZE
/*
* 静态变量定义区 - 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接收缓冲区 */
/*
* 私有函数声明区
*/
static void ch395f_spi_begin(void);
static void ch395f_spi_end(void);
static void ch395f_spi_dma_transfer(uint16_t len);
/*
* 私有函数定义区
*/
/*
* 函数功能启动一次SPI事务CS高→低
* 限定条件SPI2外设已初始化
* 函数说明1. 先将CS置高再置低满足片选信号最小间隔要求
* 2. SPI模式下字节间无需额外延时
*/
static void ch395f_spi_begin(void) {
/* CS置高确保空闲状态 */
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
/* CS置低启动事务无需额外延时SPI时序由SCK频率决定 */
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_RESET);
}
/*
* 函数功能结束一次SPI事务CS置高
* 限定条件SPI2外设已初始化
*/
static void ch395f_spi_end(void) {
/* CS置高结束事务 */
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
}
/*
* 函数功能DMA全双工传输并等待完成
* 入口参数len - 传输长度(字节) uint16_t 1 - CH395F_DMA_BUF_SIZE
* 限定条件1. s_spi2_dma_tx_buf中已填充待发送数据
* 2. 需确保DMA传输缓冲区已对齐到4字节边界
* 函数说明1. 发送s_spi2_dma_tx_buf内容同时接收数据存入s_spi2_dma_rx_buf
* 2. 阻塞等待DMA完成回调置标志位
* 3. 超时1秒时强制中止SPI DMA并打印错误信息
* 4. 传输完成后检查SPI错误码并清除溢出标志
*/
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传输完成回调函数
* 入口参数hspi - SPI句柄指针 SPI_HandleTypeDef*
* 函数说明由HAL SPI驱动在DMA TX+RX完成后调用
*/
/* 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错误回调函数
* 入口参数hspi - SPI句柄指针 SPI_HandleTypeDef*
* 函数说明由HAL SPI驱动在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;
}
}
/*
* 公共函数定义区
*/
/*
* 函数功能向CH395写入一个命令字节
* 入口参数cmd - 命令字节 uint8_t 0x00 - 0xFF
* 返回值:命令回显字节 uint8_t
* 限定条件必须在ch395f_spi_begin()和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
* 返回值:数据回显字节 uint8_t
* 限定条件必须在ch395f_spi_begin()和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
* 限定条件必须在ch395f_spi_begin()和ch395f_spi_end()之间调用
* 函数说明发送0xFF作为SPI时钟源同时读取芯片返回的数据
*/
uint8_t ch395f_read_data(void) {
uint8_t tx_dummy = 0xFFU;
uint8_t rx_data;
/* 发送0xFF作为时钟源读取芯片返回数据 */
HAL_SPI_TransmitReceive(&hspi2, &tx_dummy, &rx_data, 1, HAL_MAX_DELAY);
return rx_data;
}
/*
* 函数功能检测CH395芯片是否存在且通信正常
* 返回值CH395F_STATUS_OK - 芯片检测成功 ch395f_status_t
* CH395F_STATUS_NOT_DETECTED - 芯片未检测到 ch395f_status_t
* 限定条件SPI2外设已正确初始化
* 函数说明1. 发送CMD_CHECK_EXIST0x06后跟测试数据0x57
* 2. 芯片应返回测试数据的按位取反结果0xA8
* 3. 回显匹配则返回成功,否则返回未检测到
*/
ch395f_status_t ch395f_check_exist(void) {
uint8_t test_data = CH395F_CHECK_EXIST_TEST_DATA;
uint8_t reply_data;
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送检测命令 */
ch395f_write_cmd(CH395F_CMD_CHECK_EXIST);
/* 发送测试数据 */
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;
}
/*
* 函数功能读取CH395芯片版本号
* 返回值版本字节bits 5:0 = 版本号) uint8_t
* 限定条件芯片已通过ch395f_check_exist()检测
*/
uint8_t ch395f_get_version(void) {
uint8_t version;
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送版本读取命令 */
ch395f_write_cmd(CH395F_CMD_GET_IC_VER);
/* 读取版本号 */
version = ch395f_read_data();
/* 结束SPI事务 */
ch395f_spi_end();
return version;
}
/*
* 函数功能获取远程IP地址和端口号
* 入口参数sock - Socket索引 uint8_t 0 - 7
* p_buf - 6字节输出缓冲区指针4字节IP + 2字节端口 uint8_t*
* 限定条件1. p_buf不可为NULL
* 2. 需在TCP Server模式下连接建立后调用
* 函数说明IP和端口均为低字节在前
*/
void ch395f_get_remot_ipp(uint8_t sock, uint8_t *p_buf) {
uint8_t i;
/* 检查输入参数合法性 */
if (p_buf == NULL) {
return;
}
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送获取远程IP和端口命令 */
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字节端口号 */
for (i = 4; i < 6; i++) {
p_buf[i] = ch395f_read_data();
}
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能设置CH395功能参数
* 入口参数para - 4字节功能参数 uint32_t
* 限定条件必须在ch395f_init()之前调用
*/
void ch395f_set_fun_para(uint32_t para) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置功能参数命令 */
ch395f_write_cmd(CH395F_CMD_SET_FUN_PARA);
/* 发送4字节参数低字节在前 */
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();
}
/*
* 函数功能:设置 TCP 最大分段大小MSS
* 入口参数mss - MSS值 uint16_t 60 - 1460
* 限定条件:必须在 ch395f_init() 之前调用
*/
void ch395f_set_tcp_mss(uint16_t mss) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置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();
}
/*
* 函数功能设置Socket接收缓冲区
* 入口参数sock - Socket索引 uint8_t 0 - 7
* start_block - 起始块地址 uint8_t
* block_count - 块数量 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();
}
/*
* 函数功能设置Socket发送缓冲区
* 入口参数sock - Socket索引 uint8_t 0 - 7
* start_block - 起始块地址 uint8_t
* block_count - 块数量 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();
}
/*
* 函数功能清除Socket接收缓冲区
* 入口参数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();
}
/*
* 函数功能设置TCP KeepAlive空闲时间
* 入口参数time - 空闲时间(秒) uint32_t
* 限定条件必须在ch395f_init()之前调用
*/
void ch395f_set_keepalive_idle(uint32_t time) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置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();
}
/*
* 函数功能设置TCP KeepAlive探测间隔
* 入口参数time - 探测间隔(秒) uint32_t
* 限定条件必须在ch395f_init()之前调用
*/
void ch395f_set_keepalive_intvl(uint32_t time) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置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();
}
/*
* 函数功能设置TCP KeepAlive探测次数
* 入口参数cnt - 探测次数 uint8_t
* 限定条件必须在ch395f_init()之前调用
*/
void ch395f_set_keepalive_cnt(uint8_t cnt) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置KeepAlive探测次数命令 */
ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_CNT);
/* 发送探测次数 */
ch395f_write_data(cnt);
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能:使能/禁能指定Socket的KeepAlive功能
* 入口参数sock - Socket索引 uint8_t 0 - 7
* enable - 使能标志 uint8_t 1 = 使能0 = 禁能
* 限定条件Socket必须已打开
*/
void ch395f_set_keepalive_enable(uint8_t sock, uint8_t enable) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置KeepAlive使能命令 */
ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_SN);
/* 发送Socket索引 */
ch395f_write_data(sock);
/* 发送使能标志 */
ch395f_write_data(enable);
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能设置TCP重传次数
* 入口参数cnt - 重传次数 uint8_t 0 = 使用默认值
* 限定条件必须在ch395f_init()之前调用
*/
void ch395f_set_retrans_count(uint8_t cnt) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置重传次数命令 */
ch395f_write_cmd(CH395F_CMD_SET_RETRAN_COUNT);
/* 发送重传次数 */
ch395f_write_data(cnt);
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能设置TCP重传周期
* 入口参数period - 重传周期(毫秒) uint16_t
* 限定条件必须在ch395f_init()之前调用
*/
void ch395f_set_retrans_period(uint16_t period) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置重传周期命令 */
ch395f_write_cmd(CH395F_CMD_SET_RETRAN_PERIOD);
/* 发送2字节周期值低字节在前 */
ch395f_write_data(period & 0xFFU);
ch395f_write_data((period >> 8) & 0xFFU);
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能软件复位CH395芯片
* 返回值CH395F_STATUS_OK - 复位成功 ch395f_status_t
* 限定条件SPI2外设已正确初始化
* 函数说明复位耗时约15msTE0函数内部包含延时等待
*/
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;
}
/*
* 函数功能:查询命令执行状态
* 返回值状态字节CH395F_ERR_SUCCESS / CH395F_ERR_BUSY / ... uint8_t
* 限定条件:仅用于耗时较长的命令(如初始化)之后查询
*/
uint8_t ch395f_get_cmd_status(void) {
uint8_t status;
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送状态查询命令 */
ch395f_write_cmd(CH395F_CMD_GET_CMD_STATUS);
/* 读取状态字节 */
status = ch395f_read_data();
/* 结束SPI事务 */
ch395f_spi_end();
return status;
}
/*
* 函数功能:轮询等待命令执行完成
* 入口参数timeout_ms - 超时时间(毫秒) uint32_t
* 返回值命令执行状态CH395F_ERR_SUCCESS = 成功CH395F_ERR_BUSY = 超时) uint8_t
* 限定条件:需在发送耗时较长的命令后调用
* 函数说明每20ms查询一次直到命令完成或超时
*/
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包括MAC、PHY、TCP/IP协议栈
* 返回值CH395F_STATUS_OK - 初始化成功 ch395f_status_t
* CH395F_STATUS_TIMEOUT - 初始化超时 ch395f_status_t
* CH395F_STATUS_ERROR - 初始化失败 ch395f_status_t
* 限定条件芯片已通过ch395f_reset()复位
* 函数说明芯片初始化需约200ms函数内等待最多4秒
*/
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;
}
/*
* 函数功能设置CH395的IP地址低字节在前
* 入口参数p_ip - 4字节IP地址指针 uint8_t* 192.168.x.x格式
* 限定条件1. p_ip不可为NULL
* 2. 芯片已初始化
*/
void ch395f_set_ip_addr(uint8_t *p_ip) {
uint8_t i;
/* 检查输入参数合法性 */
if (p_ip == NULL) {
return;
}
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置IP命令 */
ch395f_write_cmd(CH395F_CMD_SET_IP_ADDR);
/* 逐字节发送IP地址 */
for (i = 0; i < 4; i++) {
ch395f_write_data(p_ip[i]);
}
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能设置CH395的网关IP地址低字节在前
* 入口参数p_gwip - 4字节网关IP指针 uint8_t*
* 限定条件1. p_gwip不可为NULL
* 2. 芯片已初始化
*/
void ch395f_set_gwip_addr(uint8_t *p_gwip) {
uint8_t i;
/* 检查输入参数合法性 */
if (p_gwip == NULL) {
return;
}
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置网关命令 */
ch395f_write_cmd(CH395F_CMD_SET_GWIP_ADDR);
/* 逐字节发送网关IP地址 */
for (i = 0; i < 4; i++) {
ch395f_write_data(p_gwip[i]);
}
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能设置CH395的子网掩码低字节在前
* 入口参数p_mask - 4字节子网掩码指针 uint8_t*
* 限定条件1. p_mask不可为NULL
* 2. 芯片已初始化
*/
void ch395f_set_mask_addr(uint8_t *p_mask) {
uint8_t i;
/* 检查输入参数合法性 */
if (p_mask == NULL) {
return;
}
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置掩码命令 */
ch395f_write_cmd(CH395F_CMD_SET_MASK_ADDR);
/* 逐字节发送子网掩码 */
for (i = 0; i < 4; i++) {
ch395f_write_data(p_mask[i]);
}
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能设置CH395的MAC地址低字节在前存入EEPROM
* 入口参数p_mac - 6字节MAC地址指针 uint8_t*
* 限定条件1. p_mac不可为NULL
* 2. 芯片已初始化
* 函数说明执行耗时约30usTE5
*/
void ch395f_set_mac_addr(uint8_t *p_mac) {
uint8_t i;
/* 检查输入参数合法性 */
if (p_mac == NULL) {
return;
}
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置MAC命令 */
ch395f_write_cmd(CH395F_CMD_SET_MAC_ADDR);
/* 逐字节发送MAC地址 */
for (i = 0; i < 6; i++) {
ch395f_write_data(p_mac[i]);
}
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能读取CH395的MAC地址
* 出口参数p_mac - 6字节MAC地址缓冲区指针 uint8_t*
* 限定条件p_mac不可为NULL
*/
void ch395f_get_mac_addr(uint8_t *p_mac) {
uint8_t i;
/* 检查输入参数合法性 */
if (p_mac == NULL) {
return;
}
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送读取MAC命令 */
ch395f_write_cmd(CH395F_CMD_GET_MAC_ADDR);
/* 逐字节读取MAC地址 */
for (i = 0; i < 6; i++) {
p_mac[i] = ch395f_read_data();
}
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能读取IP信息IP地址 + 子网掩码 + DNS共20字节
* 出口参数p_buf - 20字节缓冲区指针 uint8_t*
* 限定条件p_buf不可为NULL
*/
void ch395f_get_ip_inf(uint8_t *p_buf) {
uint8_t i;
/* 检查输入参数合法性 */
if (p_buf == NULL) {
return;
}
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送读取IP信息命令 */
ch395f_write_cmd(CH395F_CMD_GET_IP_INF);
/* 逐字节读取IP信息共20字节 */
for (i = 0; i < 20; i++) {
p_buf[i] = ch395f_read_data();
}
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能获取PHY连接状态
* 返回值PHY状态码CH395F_PHY_DISCONN / CH395F_PHY_10M_FULL / ... uint8_t
* 限定条件:芯片已初始化
*/
uint8_t ch395f_get_phy_status(void) {
uint8_t status;
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送读取PHY状态命令 */
ch395f_write_cmd(CH395F_CMD_GET_PHY_STATUS);
/* 读取PHY状态 */
status = ch395f_read_data();
/* 结束SPI事务 */
ch395f_spi_end();
return status;
}
/*
* 函数功能设置PHY连接模式
* 入口参数phystat - 连接模式 uint8_t 0x20 = 自动协商
* 限定条件:芯片已初始化
*/
void ch395f_set_phy(uint8_t phystat) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置PHY命令 */
ch395f_write_cmd(CH395F_CMD_SET_PHY);
/* 发送连接模式参数 */
ch395f_write_data(phystat);
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能:使能/禁能CH395的PING响应
* 入口参数enable - 使能标志 uint8_t 1 = 使能PING响应0 = 禁能
* 限定条件:芯片已初始化
* 函数说明参考手册5.37节CMD_PING_ENABLE默认禁能
*/
void ch395f_ping_enable(uint8_t enable) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送PING使能命令 */
ch395f_write_cmd(CH395F_CMD_PING_ENABLE);
/* 发送使能标志 */
ch395f_write_data(enable);
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能:使能/禁能DHCP
* 入口参数enable - 使能标志 uint8_t 1 = 使能0 = 禁能
* 限定条件:芯片已初始化
*/
void ch395f_set_dhcp(uint8_t enable) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送DHCP使能命令 */
ch395f_write_cmd(CH395F_CMD_DHCP_ENABLE);
/* 发送使能/禁能参数 */
ch395f_write_data(enable);
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能获取DHCP状态
* 返回值0 = 成功,非零 = 错误/超时 uint8_t
* 限定条件芯片已初始化且DHCP已使能
*/
uint8_t ch395f_get_dhcp_status(void) {
uint8_t status;
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送读取DHCP状态命令 */
ch395f_write_cmd(CH395F_CMD_GET_DHCP_STATUS);
/* 读取DHCP状态 */
status = ch395f_read_data();
/* 结束SPI事务 */
ch395f_spi_end();
return status;
}
/*
* 函数功能设置Socket协议类型
* 入口参数sock - Socket索引 uint8_t 0 - 7
* proto - 协议类型 uint8_t CH395F_PROTO_TYPE_xxx
* 限定条件Socket必须处于关闭状态
*/
void ch395f_set_proto_type(uint8_t sock, uint8_t proto) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置协议类型命令 */
ch395f_write_cmd(CH395F_CMD_SET_PROTO_TYPE_SN);
/* 发送Socket索引 */
ch395f_write_data(sock);
/* 发送协议类型 */
ch395f_write_data(proto);
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能设置Socket目标IP地址
* 入口参数sock - Socket索引 uint8_t 0 - 7
* p_ip - 4字节目标IP指针 uint8_t*
* 限定条件p_ip不可为NULL
*/
void ch395f_set_des_ip(uint8_t sock, uint8_t *p_ip) {
uint8_t i;
/* 检查输入参数合法性 */
if (p_ip == NULL) {
return;
}
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置目标IP命令 */
ch395f_write_cmd(CH395F_CMD_SET_IP_ADDR_SN);
/* 发送Socket索引 */
ch395f_write_data(sock);
/* 逐字节发送目标IP地址 */
for (i = 0; i < 4; i++) {
ch395f_write_data(p_ip[i]);
}
/* 结束SPI事务 */
ch395f_spi_end();
}
/*
* 函数功能设置Socket目标端口小端格式
* 入口参数sock - Socket索引 uint8_t 0 - 7
* port - 目标端口号 uint16_t
*/
void ch395f_set_des_port(uint8_t sock, uint16_t port) {
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送设置目标端口命令 */
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();
}
/*
* 函数功能设置Socket源端口小端格式
* 入口参数sock - Socket索引 uint8_t 0 - 7
* port - 源端口号 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 = 成功) uint8_t
* 限定条件协议类型、目标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);
}
/*
* 函数功能关闭Socket
* 入口参数sock - Socket索引 uint8_t 0 - 7
* 返回值命令执行状态CH395F_ERR_SUCCESS = 成功) 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);
}
/*
* 函数功能启动TCP监听模式
* 入口参数sock - Socket索引 uint8_t 0 - 7
* 返回值命令执行状态CH395F_ERR_SUCCESS = 成功) 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);
}
/*
* 函数功能发起TCP连接
* 入口参数sock - Socket索引 uint8_t 0 - 7
* 返回值命令执行状态CH395F_ERR_SUCCESS = 成功) 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 = 成功) uint8_t
* 限定条件TCP连接必须已建立
*/
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批量传输
* 入口参数sock - Socket索引 uint8_t 0 - 7
* p_data - 数据指针 uint8_t*
* len - 数据长度 uint16_t
* 限定条件1. p_data不可为NULL
* 2. 数据长度必须大于0
* 3. TCP必须已连接或UDP已打开
* 函数说明1. 将命令头和数据打包到DMA发送缓冲区
* 2. 一次性SPI DMA传输完成
* 3. 数据同步屏障确保memcpy写入对DMA可见
*/
void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
uint16_t total;
/* 检查输入参数合法性 */
if (p_data == NULL) {
return;
}
/* 计算总长度1字节命令 + 1字节Socket + 2字节长度 + 数据 */
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;
/* 拷贝用户数据到DMA发送缓冲区 */
memcpy(&s_spi2_dma_tx_buf[4], p_data, len);
/* 数据同步屏障确保memcpy写入对DMA可见 */
__DSB();
/* CS低 → DMA批量传输 → CS高 */
ch395f_spi_begin();
ch395f_spi_dma_transfer(total);
ch395f_spi_end();
}
/*
* 函数功能从Socket接收缓冲区读取数据DMA批量传输
* 入口参数sock - Socket索引 uint8_t 0 - 7
* p_data - 输出缓冲区指针 uint8_t*
* len - 待读取数据长度 uint16_t
* 限定条件1. p_data不可为NULL
* 2. 数据长度必须大于0
* 3. 接收缓冲区中必须有数据
* 函数说明1. 命令头打包到发送缓冲区数据部分填充0xFF作为时钟源
* 2. 一次性SPI DMA全双工传输
* 3. 有效数据从接收缓冲区偏移4处拷贝到用户缓冲区
*/
void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
uint16_t total;
/* 检查输入参数合法性 */
if (p_data == NULL) {
return;
}
/* 计算总长度1字节命令 + 1字节Socket + 2字节长度 + 数据 */
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();
/* 从DMA接收缓冲区偏移4处拷贝有效数据到用户缓冲区 */
memcpy(p_data, &s_spi2_dma_rx_buf[4], len);
}
/*
* 函数功能获取Socket接收缓冲区中数据长度
* 入口参数sock - Socket索引 uint8_t 0 - 7
* 返回值:接收数据长度 uint16_t实际上 ch395F 接收缓冲区最大也就 8192
* 限定条件Socket必须已打开
*/
uint16_t ch395f_get_recv_len(uint8_t sock) {
uint16_t recv_len;
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送获取接收长度命令 */
ch395f_write_cmd(CH395F_CMD_GET_RECV_LEN_SN);
/* 发送Socket索引 */
ch395f_write_data(sock);
/* 读取长度低字节 */
recv_len = ch395f_read_data();
/* 读取长度高字节并组合 */
recv_len |= (uint16_t)ch395f_read_data() << 8;
/* 结束SPI事务 */
ch395f_spi_end();
return recv_len;
}
/*
* 函数功能获取并清除全局中断状态2字节版支持Socket 0~7
* 返回值:中断状态 uint16_t低字节 = Socket 0~3 + PHY/DHCP
* 高字节 = Socket 4~7 + DHCPv6
* 限定条件:芯片已初始化
* 注意事项:读取后就自动清除全局中断状态了
*/
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;
}
/*
* 函数功能:获取并清除 Socket 中断状态
* 入口参数sock - Socket索引 uint8_t 0 - 7
* 返回值Socket中断状态字节 uint8_t
* 注意事项:读取后就自动清除 Socket 中断
* 限定条件:芯片已初始化
*/
uint8_t ch395f_get_sock_int_status(uint8_t sock) {
uint8_t status;
/* 启动SPI事务 */
ch395f_spi_begin();
/* 发送读取Socket中断状态命令 */
ch395f_write_cmd(CH395F_CMD_GET_INT_STATUS_SN);
/* 发送Socket索引 */
ch395f_write_data(sock);
/* 读取中断状态 */
status = ch395f_read_data();
/* 结束SPI事务 */
ch395f_spi_end();
return status;
}
/*
* 函数功能获取Socket状态2字节输出
* 入口参数sock - Socket索引 uint8_t 0 - 7
* p_status - 2字节输出缓冲区指针 uint8_t*
* [0] = Socket状态, [1] = TCP状态
* 限定条件:芯片已初始化
* 函数说明Socket状态: 00H=CLOSED, 05H=OPEN
* TCP状态: 00H=CLOSED, 01H=LISTEN, 02H=SYN_SENT,
* 03H=SYN_RCVD, 04H=ESTABLISHED
*/
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();
}
/*
* 函数功能设置ARP重传参数
* 入口参数period_100ms - ARP重传周期单位100ms默认10 = 1s uint8_t 0 - 255
* cnt - ARP重传次数默认3 uint8_t 0 - 255
* 限定条件应在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();
}
/*
* 函数功能设置Socket的TTL值
* 入口参数sock - Socket索引 uint8_t 0 - 7
* ttl - TTL值 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字节输出缓冲区指针类型+协议+端口+IP uint8_t*
* 限定条件收到GINT_STAT_UNREACH中断后调用
*/
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();
}