【啰嗦2句】
写作不是我主业,所以写得少,但是尽可能给满是雷同知识海洋里面添加一点独特的元素。
本文是AT32F421C8T7芯片跟以太网芯片CH395Q的SPI对接与驱动。因部分属于商业内容,不便于公开,但也会把核心需要注意的内容讲出来。
【硬件简介】
CH395Q是南京沁恒公司出的一款”以太网协议栈“芯片,内置10/100M PHY模块,并且自带TCP/IP协议栈,接口SPI。要说类似的芯片很多,比如W5500(WIZnet),ENC28J60,但是经过相当多实践之后,我发现他们各自的优缺点。
ENC28J60:优点是便宜,到处有,他也集成了PHY,网上能找到大量有用的资料,我曾用STM32平台驱动过,很方便。缺点:没有协议栈,需要自行集成uIP或lwIP协议栈,导致固件复杂度提高,维护稍微麻烦些,抗干扰一般。
W5500:优点是价格还能接受,内置的协议栈非常方便,印象中已经很方便于跟服务器TCP通信,但是DHCP没有内置,要自行补充。缺点倒是不多,最大毛病是容易受电源干扰,抗干扰不行。
CH395Q:优点是集成了更多的协议栈,不用自己开发,普通的MCU就能驱动,难度也不大,厂家说明书也够用,内容不多,照着写个驱动没问题,抗干扰方面还勉强,厂家有技术支持,曾经跟他们联系过,工程师挺耐心的(反正比我有耐心),8个Socket可以灵活定制。缺点是特别贵,划不来,发热大,故障率较高,似乎对电源质量要求高,实际使用中遇到较多烧芯片现象。
原理图:
原理图有不明白的地方,请参考官方文档,点击这里查看http://wch.cn/download/CH395DS1_PDF.htmlCH395DS1.PDF 产品中心
【折腾什么呢】
一个MCU+一个以太网模块,能干吗呢?其实能做的不能说特别多,但是做一个智能家居网关却相当合适。可以给结合433模块控制开关,或者自身整合了开关,或者远程给电脑开关机等。本文介绍如何开放一个TCP端口和一个UDP端口跟服务器通信,实现收发指令。
【通信架构】
本文演示单片机跟上位机通信,采用UDP协议,定时2秒发消息(Hello文字)。同时上位机发送的内容,单片机接收后原文返回。上位机仅用普通UDP/TCP测试工具即可,类似工具很多可自行搜索。
单片机端采用DHCP获取到IP为192.168.1.231,端口7777,上位机固定IP为:192.168.1.51,端口8888。
【关键代码开发】
官方的案例代码可在沁恒官网找到。本文介绍重点驱动代码,并且TCP通信也能找到相应参考资料。
CH395Q驱动:
文件名:ch395.c
#include "includes.h" //整合了ch395q.h,mian.h等的头文件
u8 CH395Q_Ver=0; //CH395Q模块的硬件版本号
void CH395Q_SPI_NSS_H(void)
{
CH395Q_SPI_CSN_PORT->scr = CH395Q_SPI_CSN_PIN;
}
void CH395Q_SPI_NSS_L(void)
{
CH395Q_SPI_CSN_PORT->clr = CH395Q_SPI_CSN_PIN;
}
void CH395Q_RST_H(void)
{
CH395Q_RST_PORT->scr = CH395Q_RST_PIN;
}
void CH395Q_RST_L(void)
{
CH395Q_RST_PORT->clr = CH395Q_RST_PIN;
}
/*******************************************************************************
* 函数名 : CH395Q_GPIO_Configuration
* 描述 : CH395Q GPIO初始化配置
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void CH395Q_gpio_irq_init()
{
exint_init_type exint_init_struct;
crm_periph_clock_enable(CRM_SCFG_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
scfg_exint_line_config(SCFG_PORT_SOURCE_GPIOA, SCFG_PINS_SOURCE1);
exint_default_para_init(&exint_init_struct);
exint_init_struct.line_enable = TRUE;
exint_init_struct.line_mode = EXINT_LINE_INTERRUPUT;
exint_init_struct.line_select = EXINT_LINE_1;
exint_init_struct.line_polarity = EXINT_TRIGGER_RISING_EDGE;
exint_init(&exint_init_struct);
nvic_priority_group_config(NVIC_PRIORITY_GROUP_2);
nvic_irq_enable(EXINT1_0_IRQn, 1, 0);
}
/*******************************************************************************
* 函数名 : CH395Q_GPIO_Configuration
* 描述 : CH395Q GPIO初始化配置
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void CH395Q_GPIO_Configuration(void)
{
gpio_init_type gpio_initstructure;
spi_init_type spi_init_struct;
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE13, GPIO_MUX_0);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE14, GPIO_MUX_0);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE15, GPIO_MUX_0);
gpio_default_para_init(&gpio_initstructure);
/* sck */
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_DOWN;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_13;
gpio_init(GPIOB, &gpio_initstructure);
/* miso */
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_DOWN;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_14;
gpio_init(GPIOB, &gpio_initstructure);
/* mosi */
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_15;
gpio_init(GPIOB, &gpio_initstructure);
crm_periph_clock_enable(CRM_SPI2_PERIPH_CLOCK, TRUE);
spi_default_para_init(&spi_init_struct);
spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_16;
spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;
spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;
spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;
spi_init(SPI2, &spi_init_struct);
spi_enable(SPI2, TRUE);
//CS
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = CH395Q_SPI_CSN_PIN;
gpio_init(CH395Q_SPI_CSN_PORT, &gpio_initstructure);
//RST
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = CH395Q_RST_PIN;
gpio_init(CH395Q_RST_PORT, &gpio_initstructure);
//INT
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
gpio_initstructure.gpio_pins = CH395Q_INT_PIN;
gpio_initstructure.gpio_pull = GPIO_PULL_UP; //GPIO_PULL_DOWN;//
gpio_init(CH395Q_INT_PORT, &gpio_initstructure);
CH395Q_CS_HIGH;
CH395Q_gpio_irq_init();
}
/*******************************************************************************
* 函数名 : CH395Q_ReadWriteByte
* 描述 : SPI2发送1个字节数据
* 输入 : dat:待发送的数据
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
uint8_t CH395Q_ReadWriteByte(u8 TxData)
{
uint8_t i, temp;
temp = 0;
while (spi_i2s_flag_get(SPI2, SPI_I2S_TDBE_FLAG) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
{
i++;
if(i > 200)return 0;
}
spi_i2s_data_transmit(SPI2, TxData); //通过外设SPIx发送一个数据
i=0;
while (spi_i2s_flag_get(SPI2, SPI_I2S_RDBF_FLAG) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位
{
i++;
if(i > 200)return 0;
}
temp = spi_i2s_data_receive(SPI2);
return temp;
}
/*******************************************************************************
* 函数名 : CH395Q_Hardware_Reset
* 描述 : 硬件复位CH395Q
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : CH395Q的复位引脚保持低电平至少500us以上,才能重围CH395Q
*******************************************************************************/
void CH395Q_Hardware_Reset(void)
{
CH395Q_RST_LOW;//复位引脚拉低
delay_ms(100);
CH395Q_RST_HIGH;//复位引脚拉高
delay_ms(200);
//while((Read_CH395Q_1Byte(PHYCFGR)&LINK)==0);//等待以太网连接完成
}
/*******************************************************************************
* 函数名 : CH395Q_CheckExist
* 描述 : 检查CH395Q是否存在,如果存在会返回按位取反的字节
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 :
*******************************************************************************/
u8 CH395Q_CheckExist(void)
{
unsigned char dat;
CH395Q_CS_LOW;//置CH395Q的SCS为低电平
CH395Q_ReadWriteByte(CMD11_CHECK_EXIST);//检查状态命令
CH395Q_ReadWriteByte(0xA5);
dat=CH395Q_ReadWriteByte(0xFF);
CH395Q_CS_HIGH;//置CH395Q的SCS为高电平
//Uart1Send(dat);
if(dat==0x5A)
{
return 1;
}
else
{
return 0;
}
}
/*******************************************************************************
* 函数名 : CH395Q_getVersion
* 描述 : 获取芯片版本号
* 输入 : 无
* 输出 : 无
* 返回值 :
* 说明 : 芯片版本号大于0x44才支持获取全局中断数量,否则只支持0-3SOCKET
*******************************************************************************/
u8 CH395Q_getVersion(void)
{
unsigned char dat;
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD01_GET_IC_VER);//命令
dat=CH395Q_ReadWriteByte(0xFF);
CH395Q_CS_HIGH;
return dat;
}
/*******************************************************************************
* 函数名 : CH395Q_getPHYStatus
* 描述 : 获取物理链路连接状态,可以用于判断是否拔线
* 输入 : 无
* 输出 : 无
* 返回值 : 1--断开,2、4、8、10分别是10M全双工、10M半双工、100M全双工、100M半双工
* 说明 :
*******************************************************************************/
u8 CH395Q_getPHYStatus(void)
{
unsigned char dat;
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD01_GET_PHY_STATUS);//命令
dat=CH395Q_ReadWriteByte(0xFF);
CH395Q_CS_HIGH;
return dat;
}
/*******************************************************************************
* 函数名 : CH395Q_getMAC
* 描述 : 获取MAC
* 输入 : 无
* 输出 : 无
* 返回值 :
* 说明 : 存储在配置SP_MAC中
*******************************************************************************/
void CH395Q_getMAC(void)
{
u8 SP_MAC[6];
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD06_GET_MAC_ADDR);//命令
SP_MAC[0]=CH395Q_ReadWriteByte(0xFF);
SP_MAC[1]=CH395Q_ReadWriteByte(0xFF);
SP_MAC[2]=CH395Q_ReadWriteByte(0xFF);
SP_MAC[3]=CH395Q_ReadWriteByte(0xFF);
SP_MAC[4]=CH395Q_ReadWriteByte(0xFF);
SP_MAC[5]=CH395Q_ReadWriteByte(0xFF);
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_setDHCP
* 描述 : 设置DHCP开或关
* 输入 : 无
* 输出 : 无
* 返回值 :
* 说明 : 启用DHCP必须在已经初始化了网卡协议栈(不是硬件初始化)才可以使用
*******************************************************************************/
void CH395Q_setDHCP(u8 onoff)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD10_DHCP_ENABLE);//命令
CH395Q_ReadWriteByte(onoff);
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_InitNet
* 描述 : 启动网络协议栈
* 输入 : 无
* 输出 : 无
* 返回值 :
* 说明 : 初始化了网卡协议栈
*******************************************************************************/
u8 CH395Q_InitNet(void)
{
u8 status=0;
u8 i=0;
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD0W_INIT_CH395);//命令
CH395Q_CS_HIGH;
while(1)
{
delay_ms(10);
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD01_GET_CMD_STATUS);//命令
status = CH395Q_ReadWriteByte(0xFF); /* 不能过于频繁查询*/
CH395Q_CS_HIGH; /* 延时查询,建议2MS以上*/
if(status !=CH395_ERR_BUSY)break; /* 如果CH395芯片返回忙状态*/
if(i++ > 200)return CH395_ERR_UNKNOW; /* 超时退出,本函数需要500MS以上执行完毕 */
}
return CMD_ERR_SUCCESS;
}
/*******************************************************************************
* 函数名 : CH395Q_getDHCPStatus
* 描述 : 获取DHCP状态,一般在DHCP成功中断后读取
* 输入 : 无
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
u8 CH395Q_getDHCPStatus(void)
{
unsigned char dat;
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD01_GET_DHCP_STATUS);//命令
dat=CH395Q_ReadWriteByte(0xFF);
CH395Q_CS_HIGH;
return dat;
}
/*******************************************************************************
* 函数名 : CH395Q_getIPInfo
* 描述 : DHCP时,获取IP信息
* 输入 : 无
* 输出 : 无
* 返回值 :
* 说明 : 4字节IP地址、4字节网关IP、4字节子网掩码、4字节 DNS1(主 DNS)、4字节DNS2(次DNS)
*******************************************************************************/
u8 CH395Q_getIPInfo(void)
{
u8 ipinfo[20];
u8 i=0;
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD014_GET_IP_INF);//命令
for(i=0;i<20;i++)
{
ipinfo[i]=CH395Q_ReadWriteByte(0xFF);
}
CH395Q_CS_HIGH;
return 0;
}
/*******************************************************************************
* 函数名 : CH395Q_getGlobIntStatusAll
* 描述 : 获取全局中断状态,收到此命令CH395自动取消中断,0x44及以上版本使用
* 输入 : 无
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
u16 CH395Q_getGlobIntStatusAll(void)
{
unsigned int dat;
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD02_GET_GLOB_INT_STATUS_ALL);//命令
delay_us(2);
dat=CH395Q_ReadWriteByte(0xFF);
dat=((u16)CH395Q_ReadWriteByte(0xFF)<<8) | dat;
CH395Q_CS_HIGH;
return dat;
}
/*******************************************************************************
* 函数名 : CH395Q_setSocketDesIP
* 描述 : 设置Socket目的端口IP
* 输入 : socket--socket号,ipaddr--ip地址
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void CH395Q_setSocketDesIP(u8 socket,u8 *ipaddr)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD50_SET_IP_ADDR_SN);//命令
CH395Q_ReadWriteByte(socket);
CH395Q_ReadWriteByte(*ipaddr++);
CH395Q_ReadWriteByte(*ipaddr++);
CH395Q_ReadWriteByte(*ipaddr++);
CH395Q_ReadWriteByte(*ipaddr++);
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_setSocketProtType
* 描述 : Socket协议类型
* 输入 : socket--socket号,type--类型(TCP,UDP,MAC,IP等)
* 输出 : 无
* 返回值 :
* 说明 : 此命令必须在CMD_OPEN_SOCKET_SN之前执行
*******************************************************************************/
void CH395Q_setSocketProtType(u8 socket,u8 type)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD20_SET_PROTO_TYPE_SN);//命令
CH395Q_ReadWriteByte(socket);
CH395Q_ReadWriteByte(type);
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_setSocketDesPort
* 描述 : 设置socket n的目的端口号
* 输入 : socket--socket号,port--端口
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void CH395Q_setSocketDesPort(u8 socket,u16 port)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD30_SET_DES_PORT_SN);//命令
CH395Q_ReadWriteByte(socket);
CH395Q_ReadWriteByte(port);
CH395Q_ReadWriteByte(port>>8);
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_setSocketSrcPort
* 描述 : 设置socket n的源端口号
* 输入 : socket--socket号,port--端口
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void CH395Q_setSocketSrcPort(u8 socket,u16 port)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD30_SET_SOUR_PORT_SN);//命令
CH395Q_ReadWriteByte(socket);
CH395Q_ReadWriteByte(port);
CH395Q_ReadWriteByte(port>>8);
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_SendData
* 描述 : 向发送缓冲区写数据
* 输入 : socket--端口,buf--数据,length--长度
* 输出 : 无
* 返回值 :
* 说明 : 在SINT_STAT_SENBUF_FREE之前,不得向该 Socket发送缓冲区再次写入数据
*******************************************************************************/
void CH395Q_SendData(u8 socket,u8 *buf,u16 length)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD30_WRITE_SEND_BUF_SN);//命令
CH395Q_ReadWriteByte(socket);
CH395Q_ReadWriteByte(length);
CH395Q_ReadWriteByte(length>>8);
while(length--)
{
CH395Q_ReadWriteByte(*buf++);
}
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_getDataLength
* 描述 : 获取缓冲区数据长度
* 输入 : socket--端口,
* 输出 : 无
* 返回值 : 长度
* 说明 :
*******************************************************************************/
u16 CH395Q_getDataLength(u8 socket)
{
unsigned int dat;
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD12_GET_RECV_LEN_SN);//命令
CH395Q_ReadWriteByte(socket);
dat=CH395Q_ReadWriteByte(0xFF);
dat=((u16)CH395Q_ReadWriteByte(0xFF)<<8) | dat;
CH395Q_CS_HIGH;
return dat;
}
/*******************************************************************************
* 函数名 : CH395Q_ClearRecvBuf
* 描述 : 清除接收缓冲区
* 输入 : enable--1开,0关
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void CH395Q_ClearRecvBuf(u8 socket)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD10_CLEAR_RECV_BUF_SN);//命令
CH395Q_ReadWriteByte(socket);
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_RevData
* 描述 : 获取接收缓冲区长度
* 输入 : socket--端口,buf--数据,length--长度
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void CH395Q_RevData(u8 socket,u8 *buf,u16 length)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD30_READ_RECV_BUF_SN);//命令
CH395Q_ReadWriteByte(socket);
CH395Q_ReadWriteByte(length);
CH395Q_ReadWriteByte(length>>8);
while(length--)
{
*buf=CH395Q_ReadWriteByte(0xFF);
buf++;
}
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_getSocketStatus
* 描述 : 获取Socket状态,开或者关
* 输入 : socket--端口,info
* 输出 : 无
* 返回值 : 第一字节0x05开,0x00关,第二字节仅在TCP模式,并且打开时有意义
* 说明 :
*******************************************************************************/
void CH395Q_getSocketStatus(u8 socket,u8 *info)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD12_GET_SOCKET_STATUS_SN);//命令
CH395Q_ReadWriteByte(socket);
*info++=CH395Q_ReadWriteByte(0xFF);
*info++=CH395Q_ReadWriteByte(0xFF);
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_OpenSocket
* 描述 : 打开socket,此命令需要等待执行成功
* 输入 : socket--端口,
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void CH395Q_OpenSocket(u8 socket)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD1W_OPEN_SOCKET_SN);//命令
CH395Q_ReadWriteByte(socket);
//做个标记,用于判断是否打开成功
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_CloseSocket
* 描述 : 关闭socket,
* 输入 : socket--端口,
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void CH395Q_CloseSocket(u8 socket)
{
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD1W_CLOSE_SOCKET_SN);//命令
CH395Q_ReadWriteByte(socket);
//做个标记,用于判断是否成功
CH395Q_CS_HIGH;
}
/*******************************************************************************
* 函数名 : CH395Q_getSocketInt
* 描述 : 获取socket n的中断状态
* 输入 : socket--端口,
* 输出 : 无
* 返回值 : 长度
* 说明 :
*******************************************************************************/
u8 CH395Q_getSocketInt(u8 socket)
{
unsigned char dat;
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD11_GET_INT_STATUS_SN);//命令
CH395Q_ReadWriteByte(socket);
dat=CH395Q_ReadWriteByte(0xFF);
CH395Q_CS_HIGH;
return dat;
}
/*******************************************************************************
* 函数名 : CH395Q_UDPSendTo
* 描述 : UDP向指定的IP和端口发送数据
* 输入 : socket--端口,buf--数据,length--长度
* 输出 : 无
* 返回值 :
* 说明 : 在SINT_STAT_SENBUF_FREE之前,不得向该 Socket发送缓冲区再次写入数据
*******************************************************************************/
void CH395Q_UDPSendTo(u8 socket,u8 *buf,u16 length,u8 *ip,u16 port)
{
CH395Q_setSocketDesIP(socket,ip);
CH395Q_setSocketDesPort(socket,port);
CH395Q_SendData(socket,buf,length);
}
/*******************************************************************************
* 函数名 : CH395Q_Init
* 描述 : 初始化CH395Q寄存器函数
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 在使用CH395Q之前,先对CH395Q初始化
*******************************************************************************/
void CH395Q_Init(void)
{
//u8 value=0;
if(CH395Q_CheckExist()==1)
{
//版本号
CH395Q_Ver=CH395Q_getVersion();
//MAC
//CH395Q_getMAC();
//注意:如果是DHCP,则初始化协议栈之后打开,否则在初始化之前执行IP赋值
//初始化网卡协议栈
NetLinkFlag=0;
if(CH395Q_InitNet()==CMD_ERR_SUCCESS) //初始化协议栈成功
{
//DHCP
if(CH395Q_getPHYStatus()!=PHY_DISCONN)
{
CH395Q_setDHCP(1); //启动DHCP,每隔16秒会探测一次,直到获得IP
}
}
}
}
/*******************************************************************************
* 函数名 : Socket_Init
* 描述 : 初始化S0端口,用于内网配置服务
* 输入 : socket:待初始化的端口
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Socket0_Init(void)
{
u8 n=0;
u8 status=0;
CH395Q_CloseSocket(0);
//这里初始化0端口为UDP服务端,用于功能控制和配置服务,芯片默认发送缓存2K,接收缓存4K
CH395Q_setSocketProtType(0,PROTO_TYPE_UDP); //设置socket 0协议类型
CH395Q_setSocketDesIP(0,LastS0RemoteIP); // 设置socket 0目标IP地址,//注意:作为服务器UDP,必须设0xFFFFFFFF的目标IP
CH395Q_setSocketDesPort(0,LastS0RemotePort); // 设置socket 0目的端口
CH395Q_setSocketSrcPort(0,7777); // 设置socket 0源端口
CH395Q_OpenSocket(0); //打开socket 0 ,UDP没有连接,直接可以判断成功
while(n<10)
{
delay_ms(10);
CH395Q_CS_LOW;
CH395Q_ReadWriteByte(CMD01_GET_CMD_STATUS);//命令
status = CH395Q_ReadWriteByte(0xFF); // 不能过于频繁查询*/
CH395Q_CS_HIGH; // 延时查询,建议2MS以上*/
if(status !=CH395_ERR_BUSY) // 如果CH395芯片返回忙状态*/
{
Socket0_Status=SOCKET_OPEN;
break;
}
if(n++ >=10)
{
Socket0_Status=SOCKET_CLOSED;
}
}
status=0;
}
/*******************************************************************************
* 函数名 : CH395Q_Interrupt_Process
* 描述 : CH395Q中断处理程序框架
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void CH395Q_Interrupt_Process(void)
{
u16 IRQValue=0;
u8 value=0;
//u8 temp[31];
IRQValue=CH395Q_getGlobIntStatusAll();
if(IRQValue&GINT_STAT_UNREACH) //不可达中断
{
}
if(IRQValue&GINT_STAT_IP_CONFLI) //IP冲突中断
{
if(NetLinkFlag==1)
{
}
}
if(IRQValue&GINT_STAT_PHY_CHANGE) //物理连接状态改变
{
value=CH395Q_getPHYStatus();
if(value!=PHY_DISCONN) //如果网卡已经连接
{
NetLinkFlag=1;
CH395Q_setDHCP(1);
}
else
{
NetLinkFlag=0;
CH395Q_setDHCP(0);//关闭DHCP,不然反应很慢
}
}
if(IRQValue&GINT_STAT_DHCP) //DHCP和PPPOE中断
{
if(CH395Q_getDHCPStatus()==0) //DHCP成功
{
CH395Q_getIPInfo(); //获取IP信息
Socket0_Status=SOCKET_CLOSED;
}
}
if(IRQValue&GINT_STAT_SOCK0) //Socket0中断
{
Socket0_Handler();
}
if(IRQValue&GINT_STAT_SOCK1) //Socket1中断
{
}
}
文件名:ch395.h
#ifndef _CH395Q_H_
#define _CH395Q_H_
//CH395Q有1个通用寄存器,8个单独Socket寄存器,以及TX/RX内存
/* ********************************************************************************************************************* */
/* 命令代码 */
/* 一个命令操作顺序包含:
一个命令码(对于串口方式,命令码之前还需要两个同步码),
若干个输入数据(可以是0个),
若干个输出数据(可以是0个)
命令码起名规则: CMDxy_NAME
其中的x和y都是数字, x说明最少输入数据个数(字节数), y说明最少输出数据个数(字节数), y如果是W表示需要等待命令执行成功
有些命令能够实现0到多个字节的数据块读写, 数据块本身的字节数未包含在上述x或y之内 */
/* ********************************************************************************************************************* */
#define CMD01_GET_IC_VER 0x01 /* 获取芯片以及固件版本号 */
/* 输出: 版本号( 位7为0, 位6为1, 位5~位0为版本号 ) */
/* CH3395返回版本号的值为041H即版本号为01H */
#define CMD31_SET_BAUDRATE 0x02 /* 串口方式: 设置串口通讯波特率(上电或者复位后的默认波特率为9600bps */
/* 输入: 3字节波特率,第字节在前 */
/* 输出: 操作状态( CMD_RET_SUCCESS, 其它值说明操作未完成 ) */
#define CMD00_ENTER_SLEEP 0x03 /* 进入睡眠状态 */
#define CMD00_RESET_ALL 0x05 /* 执行硬件复位 */
#define CMD11_CHECK_EXIST 0x06 /* 测试通讯接口以及工作状态 */
/* 输入: 任意数据 */
/* 输出: 输入数据的按位取反 */
#define CMD02_GET_GLOB_INT_STATUS_ALL 0x19 /* 获取全局中断状态,V44版本以后的程序由于增加了socket数量需要用此命令获取全部的中断 */
/*输出:全局中断状态,参考全局中断状态定义 */
#define CMD10_SET_PHY 0x20 /* 设置PHY,默认为Auto,自动协商 */
/* 输入:PHY参数,参考PHY参数定义 */
#define CMD60_SET_MAC_ADDR 0x21 /* 设置MAC地址 必须在CMD00H_INIT_CH395之前设置完毕 */
/* 输入:6字节的MAC地址 */
#define CMD40_SET_IP_ADDR 0x22 /* 设置IP地址 必须在CMD00H_INIT_CH395之前设置完毕 */
/* 输入:4字节的IP地址 */
#define CMD40_SET_GWIP_ADDR 0x23 /* 设置网关IP地址 必须在CMD00H_INIT_CH395之前设置完毕 */
/* 输入:4字节的网关IP地址 */
#define CMD40_SET_MASK_ADDR 0x24 /* 设置子网掩码, 必须在CMD00H_INIT_CH395之前设置完毕 */
/* 输入:4字节的子网掩码 */
#define CMD90_SET_MAC_FILT 0x25 /* 设置MAC过滤 可以进行广播,多播等过滤 */
/* 输入:9字节参数,第1字节为过滤类型,参考过滤类型定义,*/
/* 第2至第5字节为HASH0,第6至第9字节为HASH1 */
#define CMD01_GET_PHY_STATUS 0x26 /* 获取PHY当前状态,如断开连接,10/100M FULL/HALF */
/* 输出:当前PHY状态,状态定义请参考PHY参数定义 */
#define CMD0W_INIT_CH395 0x27 /* 初始化CH395 */
/* 此命令执行时间大约200MS,需要等待此命令执行成功,才可以发下一条命令 */
#define CMD08_GET_UNREACH_IPPORT 0x28 /* 获取不可达信息 */
/* 输出:8字节,第1字节为不可达类型,参考不可达类型定义 */
/* 第2字节协议不可达协议码 */
/* 第3,4字节不可达端口 */
/* 第5-8字不可达IP */
#define CMD01_GET_GLOB_INT_STATUS 0x29 /* 获取全局中断状态,最大值为1S,不可以设置为0 */
/* 输出:全局中断状态,参考全局中断状态定义 */
#define CMD10_SET_RETRAN_COUNT 0x2A /* 重试次数,仅在TCP模式下有效 */
/* 输入:重试次数 */
#define CMD20_SET_RETRAN_PERIOD 0x2B /* 重试周期,最大值为20,仅在TCP模式下有效,不可以设置为0 */
/* 输入:重试周期 */
#define CMD01_GET_CMD_STATUS 0x2C /* 获取命令执行状态 */
/* 输出:命令执行状态,参考命令执行状态定义 */
#define CMD06_GET_REMOT_IPP_SN 0x2D /* 获取远端的端口以及IP地址,该命令在TCP服务器模式下使用 */
/* 输出:6字节,第1-4字节为远端的IP地址,第5-6字节为远端的端口号 */
#define CMD10_CLEAR_RECV_BUF_SN 0x2E /* 清除接收缓冲区 */
/* 输入:第1字节为socket的索引值 */
#define CMD12_GET_SOCKET_STATUS_SN 0x2F /* 获取socket n状态 */
/* 输入:socket的索引值,*/
/* 输出:第1字节:socket n 打开或者关闭
第2字节:TCP状态,仅在TCP模式且第1字节为打开状态下有意义 */
#define CMD11_GET_INT_STATUS_SN 0x30 /* 获取socket n的中断状态 */
/* 输入: socket的索引值*/
/* 输出:全局中断状态,参考全局中断状态定义 */
#define CMD50_SET_IP_ADDR_SN 0x31 /* 设置socket n的目的IP地址 */
/* 输入:第1字节为socket的索引值,第2至5字节为IP地址 */
#define CMD30_SET_DES_PORT_SN 0x32 /* 设置socket n的目的端口 */
/* 输入:第1字节为socket的索引值,第2至3字节为目的端口 */
#define CMD30_SET_SOUR_PORT_SN 0x33 /* 设置socket n的源端口 */
/* 输入:第1字节为socket的索引值,第2至3字节为源端口 */
#define CMD20_SET_PROTO_TYPE_SN 0x34 /* 设置socket n的协议类型 */
/* 输入:第1字节为socket的索引值,第2协议类型,参考协议类型定义 */
#define CMD1W_OPEN_SOCKET_SN 0x35 /* 打开socket n */
/* 输入:第1字节为socket的索引值,此命令需要等待命令执行成功 */
#define CMD1W_TCP_LISTEN_SN 0x36 /* socket n监听,收到此命令,socket n进入服务器模式,仅对TCP模式有效 */
/* 输入:第1字节为socket的索引值,此命令需要等待命令执行成功 */
#define CMD1W_TCP_CONNECT_SN 0x37 /* socket n连接,收到此命令,socket n进入客户端模式,仅对TCP模式有效 */
/* 输入:第1字节为socket的索引值,此命令需要等待命令执行成功 */
#define CMD1W_TCP_DISNCONNECT_SN 0x38 /* socket n断开连接,收到此命令,socket n断开已有连接,仅对TCP模式有效 */
/* 输入:第1字节为socket的索引值,此命令需要等待命令执行成功 */
#define CMD30_WRITE_SEND_BUF_SN 0x39 /* 向socket n缓冲区写入数据 */
/* 输入:第1字节为socket的索引值,第2至3字节为长度 */
#define CMD12_GET_RECV_LEN_SN 0x3B /* 获取socket n接收数据的长度 */
/* 输入:socket的索引值 */
/* 输出:2字节的接收长度 */
#define CMD30_READ_RECV_BUF_SN 0x3C /* 读取socket n接收缓冲区数据 */
/* 输入:第1字节为socket的索引值,第2至3字节为读取的长度n,低位在前 */
/* 输出:n个数据 */
#define CMD1W_CLOSE_SOCKET_SN 0x3D /* 关闭socket n */
/* 输入:socket的索引值 */
#define CMD20_SET_IPRAW_PRO_SN 0x3E /* 在IP RAW下,设置socket n的IP包协议类型 */
/* 输入:第1字节为socket的索引值,第2字节为IP RAW协议类型 */
#define CMD01_PING_ENABLE 0x3F /* 开启/关闭PING */
/* 输入:1字节,0为关闭PING,1为开启PING,默认开启 */
#define CMD06_GET_MAC_ADDR 0x40 /* 获取MAC地址 */
/* 输出:6字节的MAC地址 */
#define CMD10_DHCP_ENABLE 0x41 /* DHCP使能 */
/* 输入:1字节,1启动DHCP,0关闭DHCP */
#define CMD01_GET_DHCP_STATUS 0x42 /* 获取DHCP状态 */
/* 输出: 1字节状态码,0表示成功,其他值失败 */
#define CMD014_GET_IP_INF 0x43 /* IP,子网掩码,网关 */
/* 输出:20字节,分别为4字节IP,4字节网关,4字节掩码,4字节的DNS1,4字节的DNS2 */
#define CMD00_PPPOE_SET_USER_NAME 0x44 /* 设置PPPOE用户名 */
/* 输入:N个字节,0为结束符 */
#define CMD00_PPPOE_SET_PASSWORD 0x45 /* 设置密码 */
/* 输入:N个字节,0为结束符 */
#define CMD10_PPPOE_ENABLE 0x46 /* PPPOE使能 */
/* 输入:1字节,1启动PPPOE,0关闭PPPOE */
#define CMD01_GET_PPPOE_STATUS 0x47 /* 获取pppoe状态 */
/* 输出: 1字节状态码,0表示成功,其他值失败 */
#define CMD20_SET_TCP_MSS 0x50 /* 设置TCP MSS */
/* 输入:TCP MSS,低位在前 */
#define CMD20_SET_TTL 0x51 /* 设置TTL,TTL最大值为128 */
/* 输入:第1字节为socket的索引值,第2字节为TTL值,最大为128 */
#define CMD30_SET_RECV_BUF 0x52 /* 设置SOCKET接收缓冲区 */
/* 输入:第1字节为socket的索引值,第2字节为起始块索引,第3字节为块数 */
#define CMD30_SET_SEND_BUF 0x53 /* 设置SOCKET发送缓冲区 */
/* 输入:第1字节为socket的索引值,第2字节为起始块索引,第3字节为块数 */
#define CMD10_SET_MAC_RECV_BUF 0x54 /* 设置MAC接收缓冲区 */
/* 输入:输入1字节的MAC接收缓冲区的大小,16字节为单位 */
#define CMD40_SET_FUN_PARA 0x55 /* 设置功能参数 */
/* 输入:4字节的启动参数 */
#define CMD40_SET_KEEP_LIVE_IDLE 0x56 /* 设置KEEPLIVE空闲 */
/*输入:4字节的保活定时器空闲时间参数,低位在前 */
#define CMD40_SET_KEEP_LIVE_INTVL 0x57 /* 设置间隔时间 */
/*输入:4字节的保活定时器超时间隔,低位在前 */
#define CMD10_SET_KEEP_LIVE_CNT 0x58 /* 重试次数 */
/*输入:1字节重试次数 */
#define CMD20_SET_KEEP_LIVE_SN 0X59 /* 设置socket nkeeplive功能*/
/*输入:1个字节Socket索引,1个字节设置 */
#define CMD00_EEPROM_ERASE 0xE9 /* 擦除EEPROM*/
#define CMD30_EEPROM_WRITE 0xEA /* 写EEPROM */
/* 输入:2字节地址,1字节长度,长度必须小于64字节 */
#define CMD30_EEPROM_READ 0xEB /* 读EEPROM */
/* 输入:2字节地址,1字节长度,长度必须小于64字节 */
#define CMD10_READ_GPIO_REG 0xEC /* 读GPIO寄存器 */
/* 输入:第1个字节为REG地址,关于地址请参考相关宏定义 */
#define CMD20_WRITE_GPIO_REG 0xED /* 写GPIO寄存器 */
/* 输入:第1个字节为REG地址,关于地址请参考相关宏定义 */
/* 第2个字节为数据 */
/* 协议类型 */
#define PROTO_TYPE_IP_RAW 0 /* IP层原始数据 */
#define PROTO_TYPE_MAC_RAW 1 /* MAC层原始数据 */
#define PROTO_TYPE_UDP 2 /* UDP协议类型 */
#define PROTO_TYPE_TCP 3 /* TCP协议类型 */
/* PHY 命令参数/状态 */
#define PHY_DISCONN (1<<0) /* PHY断开 */
#define PHY_10M_FLL (1<<1) /* 10M全双工 */
#define PHY_10M_HALF (1<<2) /* 10M半双工 */
#define PHY_100M_FLL (1<<3) /* 100M全双工 */
#define PHY_100M_HALF (1<<4) /* 100M半双工 */
#define PHY_AUTO (1<<5) /* PHY自动模式,CMD10H_SET_PHY */
/*CH395 MAC过滤*/
#define MAC_FILT_RECV_BORADPKT (1<<0) /* 使能接收广播包 */
#define MAC_FILT_RECV_ALL (1<<1) /* 使能接收所有数据包 */
#define MAC_FILT_RECV_MULTIPKT (1<<2) /* 使能接收多播包 */
#define MAC_FILT_RECV_ENABLE (1<<3) /* 使能接收 */
#define MAC_FILT_SEND_ENABLE (1<<4) /* 使能发送 */
/* 中断状态 */
/* 以下为GLOB_INT会产生的状态 */
#define GINT_STAT_UNREACH (1<<0) /* 不可达中断 */
#define GINT_STAT_IP_CONFLI (1<<1) /* IP冲突 */
#define GINT_STAT_PHY_CHANGE (1<<2) /* PHY状态改变 */
#define GINT_STAT_DHCP (1<<3) /* PHY状态改变 */
#define GINT_STAT_SOCK0 (1<<4) /* socket0 产生中断 */
#define GINT_STAT_SOCK1 (1<<5) /* socket1 产生中断 */
#define GINT_STAT_SOCK2 (1<<6) /* socket2 产生中断 */
#define GINT_STAT_SOCK3 (1<<7) /* socket3 产生中断 */
#define GINT_STAT_SOCK4 (1<<8) /* scoket4 产生中断 */
#define GINT_STAT_SOCK5 (1<<9) /* scoket5 产生中断 */
#define GINT_STAT_SOCK6 (1<<10) /* scoket6 产生中断 */
#define GINT_STAT_SOCK7 (1<<11) /* scoket7 产生中断 */
/*以下为Sn_INT会产生的状态*/
#define SINT_STAT_SENBUF_FREE (1<<0) /* 发送缓冲区空闲 */
#define SINT_STAT_SEND_OK (1<<1) /* 发送成功 */
#define SINT_STAT_RECV (1<<2) /* socket端口接收到数据或者接收缓冲区不为空 */
#define SINT_STAT_CONNECT (1<<3) /* 连接成功,TCP模式下产生此中断 */
#define SINT_STAT_DISCONNECT (1<<4) /* 连接断开,TCP模式下产生此中断 */
#define SINT_STAT_TIM_OUT (1<<6) /* ARP和TCP模式下会发生此中断 */
/* 获取命令状态 */
#define CMD_ERR_SUCCESS 0x00 /* 命令操作成功 */
#define CMD_RET_ABORT 0x5F /* 命令操作失败 */
#define CH395_ERR_BUSY 0x10 /* 忙状态,表示当前正在执行命令 */
#define CH395_ERR_MEM 0x11 /* 内存错误 */
#define CH395_ERR_BUF 0x12 /* 缓冲区错误 */
#define CH395_ERR_TIMEOUT 0x13 /* 超时 */
#define CH395_ERR_RTE 0x14 /* 路由错误*/
#define CH395_ERR_ABRT 0x15 /* 连接停止*/
#define CH395_ERR_RST 0x16 /* 连接复位 */
#define CH395_ERR_CLSD 0x17 /* 连接关闭/socket 在关闭状态 */
#define CH395_ERR_CONN 0x18 /* 无连接 */
#define CH395_ERR_VAL 0x19 /* 错误的值 */
#define CH395_ERR_ARG 0x1a /* 错误的参数 */
#define CH395_ERR_USE 0x1b /* 已经被使用 */
#define CH395_ERR_IF 0x1c /* MAC错误 */
#define CH395_ERR_ISCONN 0x1d /* 已连接 */
#define CH395_ERR_OPEN 0X20 /* 已经打开 */
#define CH395_ERR_UNKNOW 0xFA /* 未知错误 */
/* PPP状态 */
#define CH395_PPP_SUCCESS 0 /* 成功 */
#define CH395_PPPERR_PARM 1 /* 无效参数 */
#define CH395_PPPERR_OPEN 2 /* 无法打开PPP会话 */
#define CH395_PPPERR_DEVICE 3 /* 无效的PPP设备 */
#define CH395_PPPERR_ALLOC 4 /* 资源分配失败 */
#define CH395_PPPERR_USER 5 /* 用户中断 */
#define CH395_PPPERR_CONNECT 6 /* 连接断开 */
#define CH395_PPPERR_AUTHFAIL 7 /* 挑战鉴别失败 */
#define CH395_PPPERR_PROTOCOL 8 /* 握手协议失败 */
#define CH395_PPPERR_TIME_OUT 9 /* 超时失败 */
#define CH395_PPPERR_CLOSE 10 /* 关闭失败 */
/* 不可达代码 */
#define UNREACH_CODE_HOST 0 /* 主机不可达 */
#define UNREACH_CODE_NET 1 /* 网络不可达 */
#define UNREACH_CODE_PROTOCOL 2 /* 协议不可达 */
#define UNREACH_CODE_PROT 3 /* 端口不可达 */
/*其他值请参考RFC792文档*/
/* 命令包头 */
#define SER_SYNC_CODE1 0x57 /* 串口命令同步码1 */
#define SER_SYNC_CODE2 0xAB /* 串口命令同步码2 */
/* TCP状态 */
#define TCP_CLOSED 0
#define TCP_LISTEN 1
#define TCP_SYN_SENT 2
#define TCP_SYN_RCVD 3
#define TCP_ESTABLISHED 4
#define TCP_FIN_WAIT_1 5
#define TCP_FIN_WAIT_2 6
#define TCP_CLOSE_WAIT 7
#define TCP_CLOSING 8
#define TCP_LAST_ACK 9
#define TCP_TIME_WAIT 10
/* GPIO寄存器地址 */
#define GPIO_DIR_REG 0x80 /* 寄存器方向寄存器,1:输出;0:输入 */
#define GPIO_IN_REG 0x81 /* GPIO输入寄存器 */
#define GPIO_OUT_REG 0x82 /* GPIO输出寄存器 */
#define GPIO_CLR_REG 0x83 /* GPIO输出清除: 0=keep, 1=clear */
#define GPIO_PU_REG 0x84 /* GPIO上拉使能寄存器 */
#define GPIO_PD_REG 0x85 /* GPIO下拉使能寄存器 */
/* 功能参数 */
#define FUN_PARA_FLAG_TCP_SERVER (1<<1) /* tcp server 多连接模式标志,0X44版本及以后支持 */
#define FUN_PARA_FLAG_LOW_PWR (1<<2) /* 低耗能模式 */
#define SOCK_CTRL_FLAG_SOCKET_CLOSE (1<<3) /* CH395不主动关闭Socket */
#define SOCK_DISABLE_SEND_OK_INT (1<<4) /* send ok中断控制位,为1表示关闭send ok中断 */
#define SOCKET_CLOSED 0x00 //Socket状态关闭
#define SOCKET_OPEN 0x05 //Socket状态打开
/***************----- CH395Q GPIO定义 -----***************/
#define CH395Q_SPI_CSN_PIN GPIO_PINS_12
#define CH395Q_SPI_CSN_PORT GPIOB
#define CH395Q_SPI_CSN CRM_GPIOB_PERIPH_CLOCK
#define CH395Q_SPI_CLK_PIN GPIO_PINS_13
#define CH395Q_SPI_CLK_PORT GPIOB
#define CH395Q_SPI_CLK CRM_GPIOB_PERIPH_CLOCK
#define CH395Q_SPI_MISO_PIN GPIO_PINS_14
#define CH395Q_SPI_MISO_PORT GPIOB
#define CH395Q_SPI_MISO CRM_GPIOB_PERIPH_CLOCK
#define CH395Q_SPI_MOSI_PIN GPIO_PINS_15
#define CH395Q_SPI_MOSI_PORT GPIOB
#define CH395Q_SPI_MOSI CRM_GPIOB_PERIPH_CLOCK
//RST
#define CH395Q_RST_PIN GPIO_PINS_11
#define CH395Q_RST_PORT GPIOA
#define CH395Q_RST CRM_GPIOA_PERIPH_CLOCK
//INT
#define CH395Q_INT_PIN GPIO_PINS_12
#define CH395Q_INT_PORT GPIOA
#define CH395Q_INT CRM_GPIOA_PERIPH_CLOCK
#define CH395Q_CS_HIGH CH395Q_SPI_CSN_PORT->scr = CH395Q_SPI_CSN_PIN
#define CH395Q_CS_LOW CH395Q_SPI_CSN_PORT->clr = CH395Q_SPI_CSN_PIN
#define CH395Q_RST_HIGH CH395Q_RST_PORT->scr = CH395Q_RST_PIN
#define CH395Q_RST_LOW CH395Q_RST_PORT->clr = CH395Q_RST_PIN
extern u8 CH395Q_Ver;
void CH395Q_GPIO_Configuration(void);//CH395Q GPIO初始化配置
u8 CH395Q_ReadWriteByte(u8 TxData);
void CH395Q_Hardware_Reset(void);//硬件复位CH395Q
unsigned char CH395Q_CheckExist(void);
u8 CH395Q_getVersion(void);
u8 CH395Q_getPHYStatus(void);
void CH395Q_getMAC(void);
u8 CH395Q_getDHCPStatus(void);
u8 CH395Q_getIPInfo(void);
void CH395Q_getRemoteIPP(u8 socket,u8 *info);
void CH395Q_getUnreachIPPT(u8 *info);
u16 CH395Q_getDataLength(u8 socket);
void CH395Q_ClearRecvBuf(u8 socket);
void CH395Q_SendData(u8 socket,u8 *buf,u16 length);
void CH395Q_RevData(u8 socket,u8 *buf,u16 length);
u8 CH395Q_getSocketInt(u8 socket);
void CH395Q_UDPSendTo(u8 socket,u8 *buf,u16 length,u8 *ip,u16 port);
void CH395Q_TCPDisconnect(u8 socket);
void CH395Q_CloseSocket(u8 socket);
void CH395Q_TCPConnect(u8 socket);
void Socket_Connect(u8 s);
u8 CH395Q_getCMDStauts(void);
void CH395Q_getSocketStatus(u8 socket,u8 *info);
void Socket0_Open(void);
void Socket0_Init(void);
extern void CH395Q_Init(void);//初始化CH395Q寄存器函数
void CH395Q_Interrupt_Process(void);
#endif
文件名:main.c
/******************** (C) COPYRIGHT 2024 移动中的鸭子 ********************
* File Name : main.c
* Author : CZY Application Team
* Version : V1.0.0
* Date : 2024-08-03
* Description : 演示入口及网络处理
*************************************************************************/
#include "includes.h"
u8 NetS0_RevBuffer[S0REV_MAX]; //UDP的缓存
u8 NetS0_SendBuffer[S0SEND_MAX]; //UDP的缓存
u16 Net0SendLength=0;
u8 NetLinkFlag=0; //网卡连接网络的标记,只用于判断是否网络工作,不是和服务器通信
u8 Socket0_Status=0; //端口0的状态
u8 LastS0RemoteIP[4]={192,168,1,51};
u16 LastS0RemotePort=8888;
/*******************************************************************************
* 函数名 : Socket0_ReplyOK
* 描述 : Socket0应答成功
* 输入 :
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void Socket0_ReplyOK(void)
{
memcpy(NetS0_SendBuffer,"Hi CZYDemo!Are you OK!",22);
CH395Q_UDPSendTo(0,NetS0_SendBuffer,22,LastS0RemoteIP,LastS0RemotePort);
}
/*******************************************************************************
* 函数名 : Socket0_Handler
* 描述 : 处理Socket0的中断,这里是作为UDP服务,主要用于控制和配置
* 输入 :
* 输出 : 无
* 返回值 :
* 说明 :
*******************************************************************************/
void Socket0_Handler(void)
{
u8 socketIRQ=0;
socketIRQ=CH395Q_getSocketInt(0);
if(socketIRQ & SINT_STAT_SENBUF_FREE) /* 发送缓冲区空闲,可以继续写入要发送的数据 */
{
}
if(socketIRQ & SINT_STAT_SEND_OK) /* 发送完成中断 */
{
}
if(socketIRQ & SINT_STAT_RECV) /* 接收中断 */
{
Net0SendLength= CH395Q_getDataLength(0);//注意:UDP服务器模式只能全部接收。服务器模式下CH395会在数据的头部添加8个字
//节的信息表,单片机可以根据信息表来获得数据包的来源信息,单片机必须一次性将数据全部读出
//从395的缓存读出到预设内存
CH395Q_RevData(0,&NetS0_RevBuffer[0],Net0SendLength);
//回发给上位机
CH395Q_UDPSendTo(0,NetS0_RevBuffer,Net0SendLength,LastS0RemoteIP,LastS0RemotePort);
//尽快读完,不过UDP暂时不需要很快速处理
memset(NetS0_RevBuffer,0,S0REV_MAX);
CH395Q_ClearRecvBuf(0); //后面暂时不要了
}
if(socketIRQ & SINT_STAT_TIM_OUT) //超时中断,UDP好像无效
{
}
}
/*******************************************************************************
* 函数名 : main
* 描述 : 入口函数,你懂的
* 说明 :
*******************************************************************************/
int main(void)
{
Set_System();
CH395Q_GPIO_Configuration(); //配置网卡
CH395Q_Hardware_Reset();
//检测并配置网卡
CH395Q_Init();
Socket0_Init();
while(1)
{
//网络管理
if(NetLinkFlag==1)
{
}
CH395Q_Interrupt_Process();//本例是查询法(未用中断法)
delay_ms(2000);//这里只是简单延时,演示效果而已,商业应用不建议这么做
Socket0_ReplyOK();
}
}
文件名:main.h
#ifndef _MAIN_H_
#define _MAIN_H_
#define S0REV_MAX 512
#define S0SEND_MAX 512
extern u8 NetS0_RevBuffer[S0REV_MAX]; //UDP的缓存
extern u8 NetS0_SendBuffer[S0SEND_MAX]; //UDP的缓存
extern u16 Net0SendLength;
extern u8 NetLinkFlag; //网卡连接网络的标记,只用于判断是否网络工作,不是和服务器通信
extern u8 Socket0_Status; //端口0的状态
extern u8 LastS0RemoteIP[4];
extern u16 LastS0RemotePort;
void Socket0_Handler(void);
#endif
【实测效果】
以上讲解完成,该程序是为了本文而专门编写,经过测试运行成功。
特别注意:为了演示方便,本例用查询法事实解析接收结果,mian函数while主体用delay_ms(2000)进行延时,会导致接收可能丢包,因为UDP是不可靠,并且不保证到达。实际商业使用时,必须尽可能快地将CH395Q中的缓存读出,否则会丢包。要么用中断读,要么尽可能地快查询。