网络通,测试flash
This commit is contained in:
918
Drivers/BSP/CH395F/ch395f.c
Normal file
918
Drivers/BSP/CH395F/ch395f.c
Normal file
@@ -0,0 +1,918 @@
|
||||
/*
|
||||
* 模块名称:CH395F SPI Ethernet Protocol Stack Driver
|
||||
* 模块功能:CH395F 以太网协议栈芯片 SPI 驱动实现,提供芯片检测、初始化、
|
||||
* 网络参数配置、PHY 管理、DHCP、Socket 管理及数据收发功能
|
||||
* 适用平台:STM32F4 系列(SPI2 接口)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-03-14
|
||||
* 修改记录:
|
||||
* 2026-03-14 王建锋 创建初始版本,按代码规范重构
|
||||
*/
|
||||
|
||||
/* 头文件包含区 - 先包含本模块头文件,再包含依赖头文件 */
|
||||
#include "ch395f.h"
|
||||
#include "spi.h"
|
||||
#include <string.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) */
|
||||
|
||||
/*
|
||||
* 私有函数声明区
|
||||
*/
|
||||
static void ch395f_spi_begin(void);
|
||||
static void ch395f_spi_end(void);
|
||||
|
||||
/*
|
||||
* 私有函数实现区
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:开始 SPI 事务(CS 拉高 -> 拉低)
|
||||
* 函数说明:手册要求 SPI 模式下字节间无需间隔
|
||||
*/
|
||||
static void ch395f_spi_begin(void)
|
||||
{
|
||||
/* CS 先拉高,确保空闲状态 */
|
||||
HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
|
||||
HAL_Delay(CH395F_SPI_BEGIN_DELAY_MS);
|
||||
/* CS 拉低,启动事务 */
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 公共函数实现区
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:向 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:软件复位 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:初始化 CH395(包含 MAC、PHY、TCP/IP 协议栈)
|
||||
* 返回值:CH395F_STATUS_OK - 成功,CH395F_STATUS_TIMEOUT - 超时失败
|
||||
* 限定条件:芯片已通过 ch395f_reset() 复位
|
||||
* 函数说明:阻塞等待初始化完成或超时(典型 5ms,TE1)
|
||||
*/
|
||||
ch395f_status_t ch395f_init(void)
|
||||
{
|
||||
uint8_t status;
|
||||
uint32_t retry_count = 0U;
|
||||
|
||||
/* 开始 SPI 事务 */
|
||||
ch395f_spi_begin();
|
||||
|
||||
/* 发送初始化命令 */
|
||||
ch395f_write_cmd(CH395F_CMD_INIT_CH395);
|
||||
|
||||
/* 结束 SPI 事务 */
|
||||
ch395f_spi_end();
|
||||
|
||||
/* 轮询等待初始化完成 */
|
||||
while (1)
|
||||
{
|
||||
/* 每次轮询延时 1ms */
|
||||
HAL_Delay(1U);
|
||||
|
||||
/* 查询命令执行状态 */
|
||||
status = ch395f_get_cmd_status();
|
||||
|
||||
/* 状态非 BUSY 表示执行完毕 */
|
||||
if (status != CH395F_ERR_BUSY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* 超时判断 */
|
||||
if (++retry_count > CH395F_INIT_TIMEOUT_MS)
|
||||
{
|
||||
return CH395F_STATUS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/* 返回最终执行结果 */
|
||||
if (status == CH395F_ERR_SUCCESS)
|
||||
{
|
||||
return CH395F_STATUS_OK;
|
||||
}
|
||||
|
||||
return 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();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:启用/禁用 DHCP
|
||||
* 入口参数:enable - 1 启用,0 禁用 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 - 3
|
||||
* 限定条件:协议类型、目标 IP、端口已设置
|
||||
*/
|
||||
void ch395f_open_socket(uint8_t sock)
|
||||
{
|
||||
/* 开始 SPI 事务 */
|
||||
ch395f_spi_begin();
|
||||
|
||||
/* 发送打开 Socket 命令 */
|
||||
ch395f_write_cmd(CH395F_CMD_OPEN_SOCKET_SN);
|
||||
|
||||
/* 发送 Socket 索引 */
|
||||
ch395f_write_data(sock);
|
||||
|
||||
/* 结束 SPI 事务 */
|
||||
ch395f_spi_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:关闭 Socket
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:Socket 已打开
|
||||
*/
|
||||
void ch395f_close_socket(uint8_t sock)
|
||||
{
|
||||
/* 开始 SPI 事务 */
|
||||
ch395f_spi_begin();
|
||||
|
||||
/* 发送关闭 Socket 命令 */
|
||||
ch395f_write_cmd(CH395F_CMD_CLOSE_SOCKET_SN);
|
||||
|
||||
/* 发送 Socket 索引 */
|
||||
ch395f_write_data(sock);
|
||||
|
||||
/* 结束 SPI 事务 */
|
||||
ch395f_spi_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:启动 TCP 监听模式
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:Socket 已打开且协议类型为 TCP
|
||||
*/
|
||||
void ch395f_tcp_listen(uint8_t sock)
|
||||
{
|
||||
/* 开始 SPI 事务 */
|
||||
ch395f_spi_begin();
|
||||
|
||||
/* 发送 TCP 监听命令 */
|
||||
ch395f_write_cmd(CH395F_CMD_TCP_LISTEN_SN);
|
||||
|
||||
/* 发送 Socket 索引 */
|
||||
ch395f_write_data(sock);
|
||||
|
||||
/* 结束 SPI 事务 */
|
||||
ch395f_spi_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:启动 TCP 连接
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:Socket 已打开且协议类型为 TCP
|
||||
*/
|
||||
void ch395f_tcp_connect(uint8_t sock)
|
||||
{
|
||||
/* 开始 SPI 事务 */
|
||||
ch395f_spi_begin();
|
||||
|
||||
/* 发送 TCP 连接命令 */
|
||||
ch395f_write_cmd(CH395F_CMD_TCP_CONNECT_SN);
|
||||
|
||||
/* 发送 Socket 索引 */
|
||||
ch395f_write_data(sock);
|
||||
|
||||
/* 结束 SPI 事务 */
|
||||
ch395f_spi_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:断开 TCP 连接
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:TCP 已建立连接
|
||||
*/
|
||||
void ch395f_tcp_disconnect(uint8_t sock)
|
||||
{
|
||||
/* 开始 SPI 事务 */
|
||||
ch395f_spi_begin();
|
||||
|
||||
/* 发送 TCP 断开命令 */
|
||||
ch395f_write_cmd(CH395F_CMD_TCP_DISCONNECT_SN);
|
||||
|
||||
/* 发送 Socket 索引 */
|
||||
ch395f_write_data(sock);
|
||||
|
||||
/* 结束 SPI 事务 */
|
||||
ch395f_spi_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:向 Socket 发送缓冲区写入数据
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* p_data - 数据指针 uint8_t*
|
||||
* len - 数据长度 uint16_t
|
||||
* 限定条件:指针非空,长度大于 0,TCP 已连接或 UDP 已打开
|
||||
*/
|
||||
void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
/* 检查输入参数合法性 */
|
||||
if (p_data == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 开始 SPI 事务 */
|
||||
ch395f_spi_begin();
|
||||
|
||||
/* 发送写入发送缓冲区命令 */
|
||||
ch395f_write_cmd(CH395F_CMD_WRITE_SEND_BUF_SN);
|
||||
|
||||
/* 发送 Socket 索引 */
|
||||
ch395f_write_data(sock);
|
||||
|
||||
/* 发送数据长度低字节 */
|
||||
ch395f_write_data(len & 0xFFU);
|
||||
|
||||
/* 发送数据长度高字节 */
|
||||
ch395f_write_data((len >> 8) & 0xFFU);
|
||||
|
||||
/* 逐字节发送数据 */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
ch395f_write_data(p_data[i]);
|
||||
}
|
||||
|
||||
/* 结束 SPI 事务 */
|
||||
ch395f_spi_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:从 Socket 接收缓冲区读取数据
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* p_data - 输出缓冲区指针 uint8_t*
|
||||
* len - 待读取数据长度 uint16_t
|
||||
* 限定条件:指针非空,长度大于 0,接收缓冲区有数据
|
||||
*/
|
||||
void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
/* 检查输入参数合法性 */
|
||||
if (p_data == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 开始 SPI 事务 */
|
||||
ch395f_spi_begin();
|
||||
|
||||
/* 发送读取接收缓冲区命令 */
|
||||
ch395f_write_cmd(CH395F_CMD_READ_RECV_BUF_SN);
|
||||
|
||||
/* 发送 Socket 索引 */
|
||||
ch395f_write_data(sock);
|
||||
|
||||
/* 发送读取长度低字节 */
|
||||
ch395f_write_data(len & 0xFFU);
|
||||
|
||||
/* 发送读取长度高字节 */
|
||||
ch395f_write_data((len >> 8) & 0xFFU);
|
||||
|
||||
/* 逐字节读取数据 */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
p_data[i] = ch395f_read_data();
|
||||
}
|
||||
|
||||
/* 结束 SPI 事务 */
|
||||
ch395f_spi_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:获取 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:获取 Socket 中断状态
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 返回值: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;
|
||||
}
|
||||
495
Drivers/BSP/CH395F/ch395f.h
Normal file
495
Drivers/BSP/CH395F/ch395f.h
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* 模块名称:CH395F SPI Ethernet Protocol Stack Driver
|
||||
* 模块功能:CH395F 以太网协议栈芯片 SPI 驱动,提供网络初始化、Socket 管理、
|
||||
* 数据收发等功能。命令码参考 CH395 手册 v2.2 Table 5-1
|
||||
* 适用平台:STM32F4 系列(SPI2 接口)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-06-06
|
||||
* 修改记录:
|
||||
* 2026-06-06 王建锋 创建初始版本,按代码规范重构
|
||||
*/
|
||||
|
||||
#ifndef __CH395F_H
|
||||
#define __CH395F_H
|
||||
|
||||
/*
|
||||
* 头文件包含区 - 仅包含本模块必需的头文件
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/*
|
||||
* 宏定义区 - SPI 命令码(来自 CH395 手册 v2.2 Table 5-1)
|
||||
*/
|
||||
|
||||
/* 无输入数据,无输出数据 */
|
||||
#define CH395F_CMD_RESET_ALL 0x05U
|
||||
#define CH395F_CMD_ENTER_SLEEP 0x03U
|
||||
|
||||
/* 无输入数据,1 字节输出 */
|
||||
#define CH395F_CMD_GET_IC_VER 0x01U
|
||||
#define CH395F_CMD_GET_CMD_STATUS 0x2CU
|
||||
#define CH395F_CMD_GET_GLOB_INT_STATUS 0x29U
|
||||
#define CH395F_CMD_GET_GLOB_INT_STATUS_ALL 0x19U
|
||||
#define CH395F_CMD_GET_PHY_STATUS 0x26U
|
||||
#define CH395F_CMD_GET_DHCP_STATUS 0x42U
|
||||
|
||||
/* 无输入数据,6 字节输出 */
|
||||
#define CH395F_CMD_GET_MAC_ADDR 0x40U
|
||||
|
||||
/* 无输入数据,20 字节输出 */
|
||||
#define CH395F_CMD_GET_IP_INF 0x43U
|
||||
|
||||
/* 1 字节输入,无输出数据 */
|
||||
#define CH395F_CMD_SET_PHY 0x20U
|
||||
#define CH395F_CMD_PING_ENABLE 0x3FU
|
||||
#define CH395F_CMD_DHCP_ENABLE 0x41U
|
||||
#define CH395F_CMD_SET_RETRAN_COUNT 0x2AU
|
||||
|
||||
/* 1 字节输入,1 字节输出 */
|
||||
#define CH395F_CMD_CHECK_EXIST 0x06U
|
||||
#define CH395F_CMD_SET_FUN_PARA 0x55U
|
||||
|
||||
/* 2 字节输入,无输出数据 */
|
||||
#define CH395F_CMD_SET_TTL 0x51U
|
||||
#define CH395F_CMD_SET_ARP 0x44U
|
||||
|
||||
/* 3 字节输入,无输出数据 */
|
||||
#define CH395F_CMD_SET_BAUDRATE 0x02U
|
||||
#define CH395F_CMD_SET_RETRAN_PERIOD 0x2BU
|
||||
|
||||
/* 4 字节输入,无输出数据 */
|
||||
#define CH395F_CMD_SET_IP_ADDR 0x22U
|
||||
#define CH395F_CMD_SET_GWIP_ADDR 0x23U
|
||||
#define CH395F_CMD_SET_MASK_ADDR 0x24U
|
||||
|
||||
/* 6 字节输入,无输出数据 */
|
||||
#define CH395F_CMD_SET_MAC_ADDR 0x21U
|
||||
|
||||
/* 初始化命令(仅写入,执行时间较长) */
|
||||
#define CH395F_CMD_INIT_CH395 0x27U
|
||||
|
||||
/* Socket 命令 */
|
||||
/* 1 字节输入(socket 索引),无输出数据 */
|
||||
#define CH395F_CMD_CLOSE_SOCKET_SN 0x3DU
|
||||
#define CH395F_CMD_OPEN_SOCKET_SN 0x35U
|
||||
#define CH395F_CMD_TCP_LISTEN_SN 0x36U
|
||||
#define CH395F_CMD_TCP_CONNECT_SN 0x37U
|
||||
#define CH395F_CMD_TCP_DISCONNECT_SN 0x38U
|
||||
#define CH395F_CMD_CLEAR_RECV_BUF_SN 0x2EU
|
||||
|
||||
/* 1 字节输入,1 字节输出(socket 索引) */
|
||||
#define CH395F_CMD_GET_INT_STATUS_SN 0x30U
|
||||
#define CH395F_CMD_GET_SOCKET_STATUS_SN 0x2FU
|
||||
|
||||
/* 1 字节输入,2 字节输出 */
|
||||
#define CH395F_CMD_GET_RECV_LEN_SN 0x3BU
|
||||
|
||||
/* 2 字节输入,无输出数据 */
|
||||
#define CH395F_CMD_SET_PROTO_TYPE_SN 0x34U
|
||||
#define CH395F_CMD_SET_IPRAW_PRO_SN 0x3EU
|
||||
|
||||
/* 3 字节输入,无输出数据 */
|
||||
#define CH395F_CMD_SET_DES_PORT_SN 0x32U
|
||||
#define CH395F_CMD_SET_SOUR_PORT_SN 0x33U
|
||||
|
||||
/* 5 字节输入,无输出数据(socket + 4 字节 IP) */
|
||||
#define CH395F_CMD_SET_IP_ADDR_SN 0x31U
|
||||
|
||||
/* 1 字节输入,6 字节输出(socket 索引 -> 远程 IP + 端口) */
|
||||
#define CH395F_CMD_GET_REMOT_IPP_SN 0x2DU
|
||||
|
||||
/* 3 字节输入 + 数据输出(socket + 长度) */
|
||||
#define CH395F_CMD_READ_RECV_BUF_SN 0x3CU
|
||||
|
||||
/* 3 字节输入 + 数据输入(socket + 长度 + 数据) */
|
||||
#define CH395F_CMD_WRITE_SEND_BUF_SN 0x39U
|
||||
|
||||
/* 其他命令 */
|
||||
#define CH395F_CMD_SET_TCP_MSS 0x50U
|
||||
#define CH395F_CMD_SET_RECV_BUF 0x52U
|
||||
#define CH395F_CMD_SET_SEND_BUF 0x53U
|
||||
#define CH395F_CMD_SET_KEEPALIVE_IDLE 0x56U
|
||||
#define CH395F_CMD_SET_KEEPALIVE_INTVL 0x57U
|
||||
#define CH395F_CMD_SET_KEEPALIVE_CNT 0x58U
|
||||
#define CH395F_CMD_SET_KEEPALIVE_SN 0x59U
|
||||
#define CH395F_CMD_EEPROM_ERASE 0xE9U
|
||||
#define CH395F_CMD_EEPROM_WRITE 0xEAU
|
||||
#define CH395F_CMD_EEPROM_READ 0xEBU
|
||||
#define CH395F_CMD_READ_GPIO_REG 0xECH
|
||||
#define CH395F_CMD_WRITE_GPIO_REG 0xEDU
|
||||
|
||||
/*
|
||||
* CH395 错误码(来自手册 CH395INC.H 引用)
|
||||
*/
|
||||
#define CH395F_ERR_SUCCESS 0x00U
|
||||
#define CH395F_ERR_BUSY 0x01U
|
||||
#define CH395F_ERR_CMD 0x02U
|
||||
#define CH395F_ERR_MAC 0x03U
|
||||
#define CH395F_ERR_PHY 0x04U
|
||||
#define CH395F_ERR_IP_CONFLI 0x05U
|
||||
#define CH395F_ERR_SOCK 0x06U
|
||||
#define CH395F_ERR_SOCK_BUSY 0x07U
|
||||
#define CH395F_ERR_SOCK_CLOSED 0x08U
|
||||
#define CH395F_ERR_SOCK_ERR 0x09U
|
||||
#define CH395F_ERR_UNKNOW 0xFFU
|
||||
|
||||
/*
|
||||
* PHY 状态码
|
||||
*/
|
||||
#define CH395F_PHY_DISCONN 0x01U /* PHY 断开连接 */
|
||||
#define CH395F_PHY_10M_FULL 0x02U /* 10M 全双工 */
|
||||
#define CH395F_PHY_10M_HALF 0x04U /* 10M 半双工 */
|
||||
#define CH395F_PHY_100M_FULL 0x08U /* 100M 全双工 */
|
||||
#define CH395F_PHY_100M_HALF 0x10U /* 100M 半双工 */
|
||||
#define CH395F_PHY_AUTO_NEGOTIATE 0x20U /* 自动协商 */
|
||||
|
||||
/*
|
||||
* 协议类型
|
||||
*/
|
||||
#define CH395F_PROTO_TYPE_IP_RAW 0x00U
|
||||
#define CH395F_PROTO_TYPE_MAC_RAW 0x01U
|
||||
#define CH395F_PROTO_TYPE_UDP 0x02U
|
||||
#define CH395F_PROTO_TYPE_TCP 0x03U
|
||||
|
||||
/*
|
||||
* Socket 状态
|
||||
*/
|
||||
#define CH395F_SOCKET_CLOSED 0x00U
|
||||
#define CH395F_SOCKET_OPEN 0x05U
|
||||
|
||||
/*
|
||||
* TCP 状态
|
||||
*/
|
||||
#define CH395F_TCP_CLOSED 0x00U
|
||||
#define CH395F_TCP_LISTEN 0x01U
|
||||
#define CH395F_TCP_SYN_SENT 0x02U
|
||||
#define CH395F_TCP_SYN_RCVD 0x03U
|
||||
#define CH395F_TCP_ESTABLISHED 0x04U
|
||||
#define CH395F_TCP_FIN_WAIT_1 0x05U
|
||||
#define CH395F_TCP_FIN_WAIT_2 0x06U
|
||||
#define CH395F_TCP_CLOSE_WAIT 0x07U
|
||||
#define CH395F_TCP_CLOSING 0x08U
|
||||
#define CH395F_TCP_LAST_ACK 0x09U
|
||||
#define CH395F_TCP_TIME_WAIT 0x0AU
|
||||
|
||||
/*
|
||||
* 全局中断状态位
|
||||
*/
|
||||
#define CH395F_GINT_STAT_UNREACH 0x01U
|
||||
#define CH395F_GINT_STAT_IP_CONFLI 0x02U
|
||||
#define CH395F_GINT_STAT_PHY_CHANGE 0x04U
|
||||
#define CH395F_GINT_STAT_DHCP 0x08U
|
||||
#define CH395F_GINT_STAT_SOCK0 0x10U
|
||||
#define CH395F_GINT_STAT_SOCK1 0x20U
|
||||
#define CH395F_GINT_STAT_SOCK2 0x40U
|
||||
#define CH395F_GINT_STAT_SOCK3 0x80U
|
||||
|
||||
/*
|
||||
* Socket 中断状态位
|
||||
*/
|
||||
#define CH395F_SINT_STAT_CONNECT 0x01U
|
||||
#define CH395F_SINT_STAT_DISCONNECT 0x02U
|
||||
#define CH395F_SINT_STAT_SEND_OK 0x04U
|
||||
#define CH395F_SINT_STAT_SENBUF_FREE 0x08U
|
||||
#define CH395F_SINT_STAT_RECV_OK 0x10U
|
||||
#define CH395F_SINT_STAT_DISCARD 0x20U
|
||||
#define CH395F_SINT_STAT_SOCK_TIMEOUT 0x40U
|
||||
|
||||
/*
|
||||
* 类型定义区 - 驱动返回码
|
||||
*/
|
||||
typedef enum {
|
||||
CH395F_STATUS_OK = 0,
|
||||
CH395F_STATUS_ERROR = -1,
|
||||
CH395F_STATUS_TIMEOUT = -2,
|
||||
CH395F_STATUS_NOT_DETECTED = -3
|
||||
} ch395f_status_t;
|
||||
|
||||
/*
|
||||
* 函数声明区 - SPI 基础事务层
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:向 CH395 写入命令字节
|
||||
* 入口参数:cmd - 命令码 uint8_t 0x00 - 0xFF
|
||||
* 返回值:命令码回显值 uint8_t
|
||||
* 限定条件:需在 ch395f_spi_begin() 和 ch395f_spi_end() 之间调用
|
||||
* 函数说明:SPI 模式下命令与数据之间无需间隔
|
||||
*/
|
||||
uint8_t ch395f_write_cmd(uint8_t cmd);
|
||||
|
||||
/*
|
||||
* 函数功能:向 CH395 写入数据字节
|
||||
* 入口参数:data - 数据字节 uint8_t 0x00 - 0xFF
|
||||
* 返回值:数据回显值 uint8_t
|
||||
* 限定条件:需在 ch395f_spi_begin() 和 ch395f_spi_end() 之间调用
|
||||
*/
|
||||
uint8_t ch395f_write_data(uint8_t data);
|
||||
|
||||
/*
|
||||
* 函数功能:从 CH395 读取数据字节
|
||||
* 返回值:接收到的数据字节 uint8_t
|
||||
* 限定条件:需在 ch395f_spi_begin() 和 ch395f_spi_end() 之间调用
|
||||
*/
|
||||
uint8_t ch395f_read_data(void);
|
||||
|
||||
/*
|
||||
* 函数声明区 - 芯片检测与版本
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:检测 CH395 芯片是否存在且通信正常
|
||||
* 返回值:CH395F_STATUS_OK - 检测到芯片,CH395F_STATUS_NOT_DETECTED - 未检测到
|
||||
* 限定条件:SPI2 已正确初始化
|
||||
* 函数说明:发送 CMD_CHECK_EXIST (0x06) 及测试字节 0x57,
|
||||
* 芯片应返回按位取反值 0xA8
|
||||
*/
|
||||
ch395f_status_t ch395f_check_exist(void);
|
||||
|
||||
/*
|
||||
* 函数功能:读取 CH395 芯片版本号
|
||||
* 返回值:版本字节(bit5:0 为版本号)uint8_t
|
||||
* 限定条件:芯片已通过 ch395f_check_exist() 检测
|
||||
*/
|
||||
uint8_t ch395f_get_version(void);
|
||||
|
||||
/*
|
||||
* 函数声明区 - 初始化
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:软件复位 CH395
|
||||
* 返回值:CH395F_STATUS_OK
|
||||
* 限定条件:SPI2 已正确初始化
|
||||
* 函数说明:复位耗时约 15ms(TE0),函数内部已包含延时
|
||||
*/
|
||||
ch395f_status_t ch395f_reset(void);
|
||||
|
||||
/*
|
||||
* 函数功能:查询命令执行状态
|
||||
* 返回值:状态字节(CH395F_ERR_SUCCESS / CH395F_ERR_BUSY / ...)uint8_t
|
||||
* 限定条件:仅在长执行命令(如初始化)后调用
|
||||
*/
|
||||
uint8_t ch395f_get_cmd_status(void);
|
||||
|
||||
/*
|
||||
* 函数功能:初始化 CH395(包含 MAC、PHY、TCP/IP 协议栈)
|
||||
* 返回值:CH395F_STATUS_OK - 成功,CH395F_STATUS_TIMEOUT - 超时失败
|
||||
* 限定条件:芯片已通过 ch395f_reset() 复位
|
||||
* 函数说明:阻塞等待初始化完成或超时(典型 5ms,TE1)
|
||||
*/
|
||||
ch395f_status_t ch395f_init(void);
|
||||
|
||||
/*
|
||||
* 函数声明区 - 网络参数配置
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:设置 CH395 IP 地址(低字节在前)
|
||||
* 入口参数:p_ip - 4 字节 IP 地址指针 uint8_t*
|
||||
* 限定条件:指针非空,芯片已初始化
|
||||
*/
|
||||
void ch395f_set_ip_addr(uint8_t *p_ip);
|
||||
|
||||
/*
|
||||
* 函数功能:设置网关 IP 地址(低字节在前)
|
||||
* 入口参数:p_gwip - 4 字节网关 IP 指针 uint8_t*
|
||||
* 限定条件:指针非空,芯片已初始化
|
||||
*/
|
||||
void ch395f_set_gwip_addr(uint8_t *p_gwip);
|
||||
|
||||
/*
|
||||
* 函数功能:设置子网掩码(低字节在前)
|
||||
* 入口参数:p_mask - 4 字节子网掩码指针 uint8_t*
|
||||
* 限定条件:指针非空,芯片已初始化
|
||||
*/
|
||||
void ch395f_set_mask_addr(uint8_t *p_mask);
|
||||
|
||||
/*
|
||||
* 函数功能:设置 MAC 地址(低字节在前,存储于 EEPROM)
|
||||
* 入口参数:p_mac - 6 字节 MAC 地址指针 uint8_t*
|
||||
* 限定条件:指针非空,芯片已初始化
|
||||
* 函数说明:执行耗时约 30us(TE5)
|
||||
*/
|
||||
void ch395f_set_mac_addr(uint8_t *p_mac);
|
||||
|
||||
/*
|
||||
* 函数功能:从 CH395 读取 MAC 地址
|
||||
* 出口参数:p_mac - 6 字节 MAC 地址缓冲区指针 uint8_t*
|
||||
* 限定条件:指针非空
|
||||
*/
|
||||
void ch395f_get_mac_addr(uint8_t *p_mac);
|
||||
|
||||
/*
|
||||
* 函数功能:读取 IP 信息(IP + 掩码 + DNS,共 20 字节)
|
||||
* 出口参数:p_buf - 20 字节缓冲区指针 uint8_t*
|
||||
* 限定条件:指针非空
|
||||
*/
|
||||
void ch395f_get_ip_inf(uint8_t *p_buf);
|
||||
|
||||
/*
|
||||
* 函数声明区 - PHY 管理
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:获取 PHY 连接状态
|
||||
* 返回值:PHY 状态码(CH395F_PHY_DISCONN、CH395F_PHY_10M_FULL 等)uint8_t
|
||||
* 限定条件:芯片已初始化
|
||||
*/
|
||||
uint8_t ch395f_get_phy_status(void);
|
||||
|
||||
/*
|
||||
* 函数功能:设置 PHY 连接模式
|
||||
* 入口参数:phystat - 连接模式码 uint8_t 0x20 = 自动协商
|
||||
* 限定条件:芯片已初始化
|
||||
*/
|
||||
void ch395f_set_phy(uint8_t phystat);
|
||||
|
||||
/*
|
||||
* 函数声明区 - DHCP
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:启用/禁用 DHCP
|
||||
* 入口参数:enable - 1 启用,0 禁用 uint8_t
|
||||
* 限定条件:芯片已初始化
|
||||
*/
|
||||
void ch395f_set_dhcp(uint8_t enable);
|
||||
|
||||
/*
|
||||
* 函数功能:获取 DHCP 状态
|
||||
* 返回值:0 = 成功,非零 = 错误/超时 uint8_t
|
||||
* 限定条件:芯片已初始化且 DHCP 已启用
|
||||
*/
|
||||
uint8_t ch395f_get_dhcp_status(void);
|
||||
|
||||
/*
|
||||
* 函数声明区 - Socket 管理
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:设置 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);
|
||||
|
||||
/*
|
||||
* 函数功能:设置 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);
|
||||
|
||||
/*
|
||||
* 函数功能:设置 Socket 目标端口(小端序)
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* port - 目标端口 uint16_t
|
||||
* 限定条件:无
|
||||
*/
|
||||
void ch395f_set_des_port(uint8_t sock, uint16_t port);
|
||||
|
||||
/*
|
||||
* 函数功能:设置 Socket 源端口(小端序)
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* port - 源端口 uint16_t
|
||||
* 限定条件:无
|
||||
*/
|
||||
void ch395f_set_sour_port(uint8_t sock, uint16_t port);
|
||||
|
||||
/*
|
||||
* 函数功能:打开 Socket
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:协议类型、目标 IP、端口已设置
|
||||
*/
|
||||
void ch395f_open_socket(uint8_t sock);
|
||||
|
||||
/*
|
||||
* 函数功能:关闭 Socket
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:Socket 已打开
|
||||
*/
|
||||
void ch395f_close_socket(uint8_t sock);
|
||||
|
||||
/*
|
||||
* 函数功能:启动 TCP 监听模式
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:Socket 已打开且协议类型为 TCP
|
||||
*/
|
||||
void ch395f_tcp_listen(uint8_t sock);
|
||||
|
||||
/*
|
||||
* 函数功能:启动 TCP 连接
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:Socket 已打开且协议类型为 TCP
|
||||
*/
|
||||
void ch395f_tcp_connect(uint8_t sock);
|
||||
|
||||
/*
|
||||
* 函数功能:断开 TCP 连接
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 限定条件:TCP 已建立连接
|
||||
*/
|
||||
void ch395f_tcp_disconnect(uint8_t sock);
|
||||
|
||||
/*
|
||||
* 函数声明区 - Socket 数据传输
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:向 Socket 发送缓冲区写入数据
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* p_data - 数据指针 uint8_t*
|
||||
* len - 数据长度 uint16_t
|
||||
* 限定条件:指针非空,长度大于 0,TCP 已连接或 UDP 已打开
|
||||
*/
|
||||
void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len);
|
||||
|
||||
/*
|
||||
* 函数功能:从 Socket 接收缓冲区读取数据
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* p_data - 输出缓冲区指针 uint8_t*
|
||||
* len - 待读取数据长度 uint16_t
|
||||
* 限定条件:指针非空,长度大于 0,接收缓冲区有数据
|
||||
*/
|
||||
void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len);
|
||||
|
||||
/*
|
||||
* 函数功能:获取 Socket 接收缓冲区数据长度
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 返回值:接收数据长度 uint16_t
|
||||
* 限定条件:Socket 已打开
|
||||
*/
|
||||
uint16_t ch395f_get_recv_len(uint8_t sock);
|
||||
|
||||
/*
|
||||
* 函数声明区 - 中断状态
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:获取全局中断状态
|
||||
* 返回值:中断状态字节 uint8_t
|
||||
* 限定条件:芯片已初始化
|
||||
*/
|
||||
uint8_t ch395f_get_glob_int_status(void);
|
||||
|
||||
/*
|
||||
* 函数功能:获取 Socket 中断状态
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
||||
* 返回值:Socket 中断状态字节 uint8_t
|
||||
* 限定条件:芯片已初始化
|
||||
*/
|
||||
uint8_t ch395f_get_sock_int_status(uint8_t sock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CH395F_H */
|
||||
35
Drivers/BSP/GD5F2GQ5UE/fal_cfg.h
Normal file
35
Drivers/BSP/GD5F2GQ5UE/fal_cfg.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file fal_cfg.h
|
||||
* @brief FAL 配置:Flash 设备表 + 分区表
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef _FAL_CFG_H_
|
||||
#define _FAL_CFG_H_
|
||||
|
||||
#define FAL_DEBUG 0
|
||||
|
||||
#define FAL_PART_HAS_TABLE_CFG
|
||||
|
||||
/* ===================== Flash 设备表 ===================== */
|
||||
extern const struct fal_flash_dev gd5f2gq5ue_flash;
|
||||
|
||||
#define FAL_FLASH_DEV_TABLE \
|
||||
{ \
|
||||
&gd5f2gq5ue_flash, \
|
||||
}
|
||||
|
||||
/* ===================== 分区表 ===================== */
|
||||
/* 分区名称 设备名称 偏移 大小 */
|
||||
/* KVDB: 64MB @ 0 */
|
||||
/* TSDB: 64MB @ 64MB */
|
||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
||||
#define FAL_PART_TABLE \
|
||||
{ \
|
||||
{FAL_PART_MAGIC_WORD, "fdb_kvdb1", "gd5f2gq5ue", 0, 64*1024*1024, 0}, \
|
||||
{FAL_PART_MAGIC_WORD, "fdb_tsdb1", "gd5f2gq5ue", 64*1024*1024, 64*1024*1024, 0}, \
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FAL_CFG_H_ */
|
||||
47
Drivers/BSP/GD5F2GQ5UE/fal_flash_gd5f2gq5ue.c
Normal file
47
Drivers/BSP/GD5F2GQ5UE/fal_flash_gd5f2gq5ue.c
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file fal_flash_gd5f2gq5ue.c
|
||||
* @brief FAL 端口:GD5F2GQ5UE SPI NAND Flash 适配层
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "fal_def.h"
|
||||
#include "gd5f2gq5ue.h"
|
||||
|
||||
/* ===================== FAL Flash 设备操作函数 ===================== */
|
||||
|
||||
static int gd5f_fal_init(void)
|
||||
{
|
||||
return gd5f2gq5ue_init();
|
||||
}
|
||||
|
||||
static int gd5f_fal_read(long offset, uint8_t *buf, size_t size)
|
||||
{
|
||||
return gd5f2gq5ue_read(offset, buf, size);
|
||||
}
|
||||
|
||||
static int gd5f_fal_write(long offset, const uint8_t *buf, size_t size)
|
||||
{
|
||||
return gd5f2gq5ue_write(offset, buf, size);
|
||||
}
|
||||
|
||||
static int gd5f_fal_erase(long offset, size_t size)
|
||||
{
|
||||
return gd5f2gq5ue_erase(offset, size);
|
||||
}
|
||||
|
||||
/* ===================== FAL Flash 设备定义 ===================== */
|
||||
|
||||
const struct fal_flash_dev gd5f2gq5ue_flash = {
|
||||
.name = "gd5f2gq5ue",
|
||||
.addr = 0,
|
||||
.len = GD5F_TOTAL_SIZE, /* 256MB */
|
||||
.blk_size = GD5F_BLOCK_SIZE, /* 128KB */
|
||||
.ops = {
|
||||
.init = gd5f_fal_init,
|
||||
.read = gd5f_fal_read,
|
||||
.write = gd5f_fal_write,
|
||||
.erase = gd5f_fal_erase,
|
||||
},
|
||||
.write_gran = 8,
|
||||
};
|
||||
32
Drivers/BSP/GD5F2GQ5UE/fdb_cfg.h
Normal file
32
Drivers/BSP/GD5F2GQ5UE/fdb_cfg.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file fdb_cfg.h
|
||||
* @brief FlashDB 配置文件
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef _FDB_CFG_H_
|
||||
#define _FDB_CFG_H_
|
||||
|
||||
/* 启用 KVDB(键值数据库) */
|
||||
#define FDB_USING_KVDB
|
||||
|
||||
#ifdef FDB_USING_KVDB
|
||||
/* 当 KVDB 版本号变化时自动更新到最新默认值 */
|
||||
/* #define FDB_KV_AUTO_UPDATE */
|
||||
#endif
|
||||
|
||||
/* 启用 TSDB(时序数据库) */
|
||||
#define FDB_USING_TSDB
|
||||
|
||||
/* 使用 FAL 存储模式(非文件模式) */
|
||||
#define FDB_USING_FAL_MODE
|
||||
|
||||
/* Flash 写入粒度(单位:bit)
|
||||
* STM32F4 = 8(字节可编程) */
|
||||
#define FDB_WRITE_GRAN 8
|
||||
|
||||
/* 调试输出 */
|
||||
#define FDB_DEBUG_ENABLE
|
||||
|
||||
#endif /* _FDB_CFG_H_ */
|
||||
367
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c
Normal file
367
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file gd5f2gq5ue.c
|
||||
* @brief GD5F2GQ5UE SPI NAND Flash 驱动实现
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "gd5f2gq5ue.h"
|
||||
#include <string.h>
|
||||
|
||||
extern SPI_HandleTypeDef hspi1;
|
||||
|
||||
/* ===================== 内部辅助函数 ===================== */
|
||||
|
||||
/**
|
||||
* @brief SPI 发送/接收单字节
|
||||
*/
|
||||
static uint8_t gd5f_spi_xfer(uint8_t tx)
|
||||
{
|
||||
uint8_t rx;
|
||||
HAL_SPI_TransmitReceive(&hspi1, &tx, &rx, 1, 100);
|
||||
return rx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI 发送缓冲区
|
||||
*/
|
||||
static void gd5f_spi_tx(const uint8_t *buf, size_t len)
|
||||
{
|
||||
HAL_SPI_Transmit(&hspi1, (uint8_t *)buf, len, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI 接收缓冲区
|
||||
*/
|
||||
static void gd5f_spi_rx(uint8_t *buf, size_t len)
|
||||
{
|
||||
HAL_SPI_Receive(&hspi1, buf, len, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 等待操作完成(OIP 位清零)
|
||||
* @param timeout_ms: 超时时间(毫秒)
|
||||
* @retval GD5F_OK 成功, GD5F_BUSY_TIMEOUT 超时
|
||||
*/
|
||||
static int gd5f_wait_busy(uint32_t timeout_ms)
|
||||
{
|
||||
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
||||
uint8_t addr = GD5F_REG_STATUS;
|
||||
uint8_t status;
|
||||
uint32_t tickstart = HAL_GetTick();
|
||||
|
||||
while (1) {
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(&cmd, 1);
|
||||
gd5f_spi_tx(&addr, 1);
|
||||
gd5f_spi_rx(&status, 1);
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
if ((status & GD5F_STATUS_OIP) == 0) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
if ((HAL_GetTick() - tickstart) >= timeout_ms) {
|
||||
return GD5F_BUSY_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写使能
|
||||
*/
|
||||
static int gd5f_write_enable(void)
|
||||
{
|
||||
uint8_t cmd = GD5F_CMD_WRITE_ENABLE;
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(&cmd, 1);
|
||||
GD5F_CS_HIGH();
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取状态寄存器
|
||||
*/
|
||||
static int gd5f_read_status(uint8_t *status)
|
||||
{
|
||||
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
||||
uint8_t addr = GD5F_REG_STATUS;
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(&cmd, 1);
|
||||
gd5f_spi_tx(&addr, 1);
|
||||
gd5f_spi_rx(status, 1);
|
||||
GD5F_CS_HIGH();
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 页读取到缓存(Page Read to Cache)
|
||||
* @param page_addr: 页地址(行地址,包含 block + page)
|
||||
*/
|
||||
static int gd5f_page_read(uint32_t page_addr)
|
||||
{
|
||||
uint8_t cmd[4];
|
||||
cmd[0] = GD5F_CMD_PAGE_READ;
|
||||
cmd[1] = (page_addr >> 16) & 0xFF;
|
||||
cmd[2] = (page_addr >> 8) & 0xFF;
|
||||
cmd[3] = page_addr & 0xFF;
|
||||
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(cmd, 4);
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
return gd5f_wait_busy(100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从缓存读取数据(Read from Cache)
|
||||
* @param column: 列地址(页内偏移)
|
||||
* @param buf: 数据缓冲区
|
||||
* @param size: 读取字节数
|
||||
*/
|
||||
static int gd5f_read_from_cache(uint16_t column, uint8_t *buf, size_t size)
|
||||
{
|
||||
uint8_t cmd[5];
|
||||
cmd[0] = GD5F_CMD_READ_FROM_CACHE;
|
||||
cmd[1] = (column >> 8) & 0xFF;
|
||||
cmd[2] = column & 0xFF;
|
||||
cmd[3] = 0x00; /* dummy byte */
|
||||
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(cmd, 4);
|
||||
gd5f_spi_rx(buf, size);
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 页编程(从缓存写入阵列)
|
||||
* @param page_addr: 页地址
|
||||
* @param column: 列地址
|
||||
* @param buf: 数据缓冲区
|
||||
* @param size: 写入字节数
|
||||
*/
|
||||
static int gd5f_page_program(uint32_t page_addr, uint16_t column, const uint8_t *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
uint8_t cmd[4];
|
||||
|
||||
/* 1. 写使能 */
|
||||
gd5f_write_enable();
|
||||
|
||||
/* 2. 数据加载到缓存 (Program Load) */
|
||||
cmd[0] = GD5F_CMD_PROGRAM_LOAD;
|
||||
cmd[1] = (column >> 8) & 0xFF;
|
||||
cmd[2] = column & 0xFF;
|
||||
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(cmd, 3);
|
||||
gd5f_spi_tx(buf, size);
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
/* 3. 编程执行 (Program Execute) */
|
||||
cmd[0] = GD5F_CMD_PROGRAM_EXEC;
|
||||
cmd[1] = (page_addr >> 16) & 0xFF;
|
||||
cmd[2] = (page_addr >> 8) & 0xFF;
|
||||
cmd[3] = page_addr & 0xFF;
|
||||
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(cmd, 4);
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
/* 4. 等待完成 */
|
||||
ret = gd5f_wait_busy(1000);
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
/* 5. 检查编程结果 */
|
||||
uint8_t status;
|
||||
gd5f_read_status(&status);
|
||||
if (status & GD5F_STATUS_P_FAIL) {
|
||||
/* 清除 P_FAIL:写使能 + 读状态 */
|
||||
gd5f_write_enable();
|
||||
return GD5F_PROGRAM_FAIL;
|
||||
}
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 块擦除
|
||||
* @param block_addr: 块地址 (0 ~ 2047)
|
||||
*/
|
||||
static int gd5f_block_erase(uint32_t block_addr)
|
||||
{
|
||||
int ret;
|
||||
uint8_t cmd[4];
|
||||
|
||||
/* 1. 写使能 */
|
||||
gd5f_write_enable();
|
||||
|
||||
/* 2. 块擦除命令 */
|
||||
cmd[0] = GD5F_CMD_BLOCK_ERASE;
|
||||
cmd[1] = (block_addr >> 8) & 0xFF;
|
||||
cmd[2] = block_addr & 0xFF;
|
||||
cmd[3] = 0x00;
|
||||
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(cmd, 4);
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
/* 3. 等待完成(擦除较慢) */
|
||||
ret = gd5f_wait_busy(5000);
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
/* 4. 检查擦除结果 */
|
||||
uint8_t status;
|
||||
gd5f_read_status(&status);
|
||||
if (status & GD5F_STATUS_E_FAIL) {
|
||||
gd5f_write_enable();
|
||||
return GD5F_ERASE_FAIL;
|
||||
}
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/* ===================== 公共接口实现 ===================== */
|
||||
|
||||
int gd5f2gq5ue_init(void)
|
||||
{
|
||||
int ret;
|
||||
uint8_t mid, did;
|
||||
|
||||
/* 确保控制引脚处于非激活状态 */
|
||||
GD5F_CS_HIGH();
|
||||
GD5F_WP_HIGH();
|
||||
GD5F_HOLD_HIGH();
|
||||
|
||||
/* 小延时等待上电稳定 */
|
||||
HAL_Delay(10);
|
||||
|
||||
/* 复位芯片 */
|
||||
ret = gd5f2gq5ue_reset();
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
HAL_Delay(5);
|
||||
|
||||
/* 读取 ID 验证通信 */
|
||||
ret = gd5f2gq5ue_read_id(&mid, &did);
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
if (mid != GD5F_MANUFACTURER_ID || did != GD5F_DEVICE_ID) {
|
||||
return GD5F_ID_MISMATCH;
|
||||
}
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did)
|
||||
{
|
||||
uint8_t cmd = GD5F_CMD_READ_ID;
|
||||
uint8_t id_buf[3];
|
||||
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(&cmd, 1);
|
||||
gd5f_spi_rx(id_buf, 3); /* dummy + MID + DID */
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
*mid = id_buf[1]; /* id_buf[0] = dummy */
|
||||
*did = id_buf[2];
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (size > 0) {
|
||||
/* 计算页地址和列偏移 */
|
||||
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
|
||||
uint16_t column = offset % GD5F_PAGE_SIZE;
|
||||
|
||||
/* 本次读取字节数(不超过当前页剩余空间) */
|
||||
size_t bytes = GD5F_PAGE_SIZE - column;
|
||||
if (bytes > size) bytes = size;
|
||||
|
||||
/* Page Read to Cache */
|
||||
ret = gd5f_page_read(page_addr);
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
/* Read from Cache */
|
||||
ret = gd5f_read_from_cache(column, buf, bytes);
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
offset += bytes;
|
||||
buf += bytes;
|
||||
size -= bytes;
|
||||
}
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (size > 0) {
|
||||
/* 计算页地址和列偏移 */
|
||||
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
|
||||
uint16_t column = offset % GD5F_PAGE_SIZE;
|
||||
|
||||
/* 本次写入字节数(不超过当前页剩余空间) */
|
||||
size_t bytes = GD5F_PAGE_SIZE - column;
|
||||
if (bytes > size) bytes = size;
|
||||
|
||||
/* 页编程 */
|
||||
ret = gd5f_page_program(page_addr, column, buf, bytes);
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
offset += bytes;
|
||||
buf += bytes;
|
||||
size -= bytes;
|
||||
}
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
int gd5f2gq5ue_erase(long offset, size_t size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* 擦除起始地址必须块对齐 */
|
||||
if (offset % GD5F_BLOCK_SIZE != 0) {
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
/* 擦除大小必须为块大小的整数倍 */
|
||||
if (size % GD5F_BLOCK_SIZE != 0) {
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
/* 逐块擦除 */
|
||||
while (size > 0) {
|
||||
uint32_t block_addr = offset / GD5F_BLOCK_SIZE;
|
||||
|
||||
ret = gd5f_block_erase(block_addr);
|
||||
if (ret != GD5F_OK) return ret;
|
||||
|
||||
offset += GD5F_BLOCK_SIZE;
|
||||
size -= GD5F_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
int gd5f2gq5ue_reset(void)
|
||||
{
|
||||
uint8_t cmd = GD5F_CMD_RESET;
|
||||
|
||||
GD5F_CS_LOW();
|
||||
gd5f_spi_tx(&cmd, 1);
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
HAL_Delay(5);
|
||||
|
||||
return GD5F_OK;
|
||||
}
|
||||
133
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h
Normal file
133
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file gd5f2gq5ue.h
|
||||
* @brief GD5F2GQ5UE SPI NAND Flash 驱动头文件
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __GD5F2GQ5UE_H
|
||||
#define __GD5F2GQ5UE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "main.h"
|
||||
#include "spi.h"
|
||||
|
||||
/* ===================== 硬件引脚定义 ===================== */
|
||||
#define GD5F_CS_LOW() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_RESET)
|
||||
#define GD5F_CS_HIGH() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_SET)
|
||||
#define GD5F_WP_HIGH() HAL_GPIO_WritePin(GD_WP_GPIO_Port, GD_WP_Pin, GPIO_PIN_SET)
|
||||
#define GD5F_HOLD_HIGH() HAL_GPIO_WritePin(GD_HOLD_GPIO_Port, GD_HOLD_Pin, GPIO_PIN_SET)
|
||||
|
||||
/* ===================== SPI 命令码 ===================== */
|
||||
#define GD5F_CMD_WRITE_ENABLE 0x06
|
||||
#define GD5F_CMD_WRITE_DISABLE 0x04
|
||||
#define GD5F_CMD_GET_FEATURE 0x0F
|
||||
#define GD5F_CMD_SET_FEATURE 0x1F
|
||||
#define GD5F_CMD_READ_ID 0x9F
|
||||
#define GD5F_CMD_PAGE_READ 0x13
|
||||
#define GD5F_CMD_READ_FROM_CACHE 0x0B /* Fast Read, 1 dummy byte */
|
||||
#define GD5F_CMD_PROGRAM_LOAD 0x02
|
||||
#define GD5F_CMD_PROGRAM_EXEC 0x10
|
||||
#define GD5F_CMD_BLOCK_ERASE 0xD8
|
||||
#define GD5F_CMD_RESET 0xFF
|
||||
|
||||
/* ===================== 寄存器地址 ===================== */
|
||||
#define GD5F_REG_STATUS 0xC0
|
||||
#define GD5F_REG_PROTECT 0xA0
|
||||
#define GD5F_REG_FEATURE 0xB0
|
||||
#define GD5F_REG_DRIVER 0xD0
|
||||
|
||||
/* ===================== 状态位定义 ===================== */
|
||||
#define GD5F_STATUS_OIP (1 << 0) /* 操作进行中 */
|
||||
#define GD5F_STATUS_WEL (1 << 1) /* 写使能锁存 */
|
||||
#define GD5F_STATUS_E_FAIL (1 << 2) /* 擦除失败 */
|
||||
#define GD5F_STATUS_P_FAIL (1 << 3) /* 编程失败 */
|
||||
#define GD5F_STATUS_BIT4 (1 << 4)
|
||||
#define GD5F_STATUS_ECCS0 (1 << 5)
|
||||
#define GD5F_STATUS_ECCS1 (1 << 6)
|
||||
#define GD5F_STATUS_ECCSE0 (1 << 4) /* ECC 单 bit 错误低位 */
|
||||
#define GD5F_STATUS_ECCSE1 (1 << 5) /* ECC 单 bit 错误高位 */
|
||||
#define GD5F_STATUS_ECCSE2 (1 << 6) /* ECC 单 bit 错误高位 */
|
||||
|
||||
/* ===================== Feature 位定义 ===================== */
|
||||
#define GD5F_FEATURE_ECC_EN (1 << 4)
|
||||
#define GD5F_FEATURE_QE (1 << 0) /* Quad Enable, B0[0] */
|
||||
|
||||
/* ===================== 芯片参数 ===================== */
|
||||
#define GD5F_PAGE_SIZE 2048 /* 主数据区字节数 */
|
||||
#define GD5F_SPARE_SIZE 64 /* ECC 启用时 spare 区 */
|
||||
#define GD5F_TOTAL_PAGE_SIZE 2112 /* 2048 + 64 */
|
||||
#define GD5F_PAGES_PER_BLOCK 64
|
||||
#define GD5F_BLOCK_SIZE (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE) /* 128KB */
|
||||
#define GD5F_TOTAL_BLOCKS 2048
|
||||
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE) /* 256MB */
|
||||
|
||||
/* 制造商 ID 和设备 ID */
|
||||
#define GD5F_MANUFACTURER_ID 0xC8
|
||||
#define GD5F_DEVICE_ID 0x52
|
||||
|
||||
/* ===================== 返回值定义 ===================== */
|
||||
#define GD5F_OK 0
|
||||
#define GD5F_ERROR -1
|
||||
#define GD5F_BUSY_TIMEOUT -2
|
||||
#define GD5F_ECC_ERROR -3
|
||||
#define GD5F_PROGRAM_FAIL -4
|
||||
#define GD5F_ERASE_FAIL -5
|
||||
#define GD5F_ID_MISMATCH -6
|
||||
|
||||
/* ===================== 公共接口函数 ===================== */
|
||||
|
||||
/**
|
||||
* @brief 初始化 GD5F2GQ5UE(SPI + GPIO + 读 ID 验证)
|
||||
* @retval GD5F_OK 成功, 其他为错误码
|
||||
*/
|
||||
int gd5f2gq5ue_init(void);
|
||||
|
||||
/**
|
||||
* @brief 读取芯片 ID(MID + DID)
|
||||
* @param mid: 制造商 ID 输出指针
|
||||
* @param did: 设备 ID 输出指针
|
||||
* @retval GD5F_OK 成功
|
||||
*/
|
||||
int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did);
|
||||
|
||||
/**
|
||||
* @brief 从 NAND 读取数据(支持跨页)
|
||||
* @param offset: 起始字节偏移(相对于 Flash 起始地址)
|
||||
* @param buf: 数据缓冲区
|
||||
* @param size: 读取字节数
|
||||
* @retval GD5F_OK 成功
|
||||
*/
|
||||
int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);
|
||||
|
||||
/**
|
||||
* @brief 向 NAND 写入数据(支持跨页)
|
||||
* @param offset: 起始字节偏移
|
||||
* @param buf: 数据缓冲区
|
||||
* @param size: 写入字节数
|
||||
* @retval GD5F_OK 成功
|
||||
*/
|
||||
int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);
|
||||
|
||||
/**
|
||||
* @brief 擦除块(按块擦除,最小单位 128KB)
|
||||
* @param offset: 起始字节偏移(必须块对齐)
|
||||
* @param size: 擦除字节数(必须为块大小的整数倍)
|
||||
* @retval GD5F_OK 成功
|
||||
*/
|
||||
int gd5f2gq5ue_erase(long offset, size_t size);
|
||||
|
||||
/**
|
||||
* @brief 复位芯片
|
||||
* @retval GD5F_OK 成功
|
||||
*/
|
||||
int gd5f2gq5ue_reset(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __GD5F2GQ5UE_H */
|
||||
Reference in New Issue
Block a user