Files
STM32F4-Base/Drivers/BSP/CH395F/ch395f.c
T
2026-07-19 15:27:49 +08:00

1253 lines
35 KiB
C

/*
* 模��称:CH395F SPI Ethernet Protocol Stack Driver
* 模�功能:CH395F 以太网�议栈芯片 SPI 驱动实现,�供芯片检测��始化� * 网络�数�置�PHY 管��DHCP�Socket 管��数�收�功� * 适用平�:STM32F4 系列(SPI2 接�� * 作者:王建� * 创建日期�026-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�*/
#define CH395F_DMA_BUF_SIZE 1500U /* DMA 缓冲区最大字节数 */
/*
* �有���- 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 拉高 -> 拉低� * 函数说明:手册��SPI 模�下字节间无需间隔� * CS 拉高�拉低满足最�片选间隔�� */
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 拉高� */
static void ch395f_spi_end(void) {
/* CS 拉高,结�事�*/
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
}
/*
* 函数功能:DMA 全�工传输并等待完�
* 入��数:len - 传输字节� uint16_t
* 函数说明:��s_spi2_dma_tx_buf,接收到 s_spi2_dma_rx_buf� * 阻塞等待 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 驱动�DMA TX+RX 完��调用(HAL_SPI_TransmitReceive_DMA 触�此回调)
*/
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
if (hspi->Instance == SPI2) {
s_spi2_dma_done = 1U;
}
}
/*
* 函数功能:SPI 错误回调
* 函数说明:由 HAL SPI 驱动�DMA 传输出错时调用,释放等待
*/
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
if (hspi->Instance == SPI2) {
s_spi2_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() 之间调用
*/
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_NOT_DETECTED - 未检测到
* �定�件:SPI2 已正确�始化
* 函数说明:��CMD_CHECK_EXIST (0x06) �测试字�0x57� * 芯片应返回按����?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();
/* ��检测命�*/
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 芯片版本� * 返回值:版本字节(bit5: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字节端�� * �定�件: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 - 1 �用� �用 uint8_t
* �定�件: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�=使用默认�� * �定�件:必须在 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
* �定�件:SPI2 已正确�始化
* 函数说明:��耗时�15ms(TE0),函数内部已包�延� */
ch395f_status_t ch395f_reset(void) {
/* 开�SPI 事务 */
ch395f_spi_begin();
/* ����命�*/
ch395f_write_cmd(CH395F_CMD_RESET_ALL);
/* 结� SPI 事务 */
ch395f_spi_end();
/* 等待��完�(TE0 = 15ms 典型值,等待 20ms�*/
HAL_Delay(CH395F_RESET_DELAY_MS);
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_CMD_RET_ABORT� * �定�件:在��长执行命令�调� * 函数说明:� 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_TIMEOUT - 超时失败
* �定�件:芯片已通过 ch395f_reset() ��
* 函数说明:阻塞等待�始化完�或超时(芯片需�约 200ms,这里等最�4s� */
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*
* �定�件:指针�空,芯片已�始化
*/
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();
}
/*
* 函数功能:设置网�IP 地�(低字节在�� * 入��数:p_gwip - 4 字节网关 IP 指针 uint8_t*
* �定�件:指针�空,芯片已�始化
*/
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();
}
/*
* 函数功能:设置�网掩�(低字节在�)
* 入��数:p_mask - 4 字节�网掩�指针 uint8_t*
* �定�件:指针�空,芯片已�始化
*/
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();
}
/*
* 函数功能:设�MAC 地�(低字节在�,存储于 EEPROM� * 入��数:p_mac - 6 字节 MAC 地�指针 uint8_t*
* �定�件:指针�空,芯片已�始化
* 函数说明:执行耗时�30us(TE5� */
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*
* �定�件:指针�� */
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*
* �定�件:指针�� */
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 - 1 使能 PING �应� 关闭
* �定�件:芯片已�始� * 函数说明:手�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 - 1 �用� �用 uint8_t
* �定�件:芯片已�始� */
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 - 3
* 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 - 3
* p_ip - 4 字节目标 IP 指针 uint8_t*
* �定�件:指针�� */
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 - 3
* 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 - 3
* 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 - 3
* p_data - 数�指针 uint8_t*
* len - 数�长度 uint16_t
* �定�件:指针�空,长度大于 0,TCP 已连接或 UDP 已打开
* 函数说明:将命令头和数�打包�DMA 缓冲区,一�SPI 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(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;
/* 拷�用户数��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 - 3
* p_data - 输出缓冲区指� uint8_t*
* len - 待读�数�长� uint16_t
* �定�件:指针�空,长度大于 0,接收缓冲区有数� * 函数说明:命令头打包到��缓冲区(数�部分填 0xFF 作时钟)� * 一�SPI DMA 全�工传输,有效数�从接收缓冲区�移 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(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();
/* �DMA 接收缓冲区��4 字节处拷�有效数�到用户缓冲�*/
memcpy(p_data, &s_spi2_dma_rx_buf[4], len);
}
/*
* 函数功能:获�Socket 接收缓冲区数�长� * 入��数: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();
/* ��获�接收长度命�*/
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;
}
/*
* 函数功能:获�全局中断状�? * 返回值:中断状�字� uint8_t
* �定�件:芯片已�始� */
uint8_t ch395f_get_glob_int_status(void) {
uint8_t status;
/* 开�SPI 事务 */
ch395f_spi_begin();
/* ��读�全局中断状�命�*/
ch395f_write_cmd(CH395F_CMD_GET_GLOB_INT_STATUS);
/* 读�中断状�?*/
status = ch395f_read_data();
/* 结� SPI 事务 */
ch395f_spi_end();
return status;
}
/*
* 函数功能:获�全局中断状�(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
* �定�件:芯片已�始� */
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
* status - 2 字节输出缓冲�[0]=Socket状�? [1]=TCP状�? * �定�件:芯片已�始� */
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�秒) uint8_t
* cnt - ARP �传次数�=默认�3� uint8_t
* �定�件:应�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� * �定�件:收�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();
}