CH32V208系列TCP_Server例程解读
首先使用电脑,按以下步骤进行配置IP。
接着单片机下载好程序,用网线连接电脑和单片机即可,使用TCP调试工具创建客户端设置好目标IP(单片机使用的IP地址),端口号一定要选择单片机程序中所定义的端口。
测试发送是否能够成功接收。
看完现象,我们再来看下程序中是如何实现的,
(1) WCHNET_NUM_IPRAW
用于配置 IPRAW(IP 原始套接字)连接的个数,最小值为 1。用于 IPRAW 通讯。
(2) WCHNET_NUM_UDP
用于配置 UDP 连接的个数,最小值为 1。用于 UDP 通讯。
(3) WCHNET_NUM_TCP
用于配置 TCP 连接的个数,最小值为 1。用于 TCP 通讯。
(4) WCHNET_NUM_TCP_LISTEN
用于配置 TCP 监听的个数,最小值为 1。TCP 监听的 socket 仅仅用于监听,一旦监听到TCP 连接,会立即分配一个 TCP 连接,占用 WCHNET_NUM_TCP 的个数。
(5) WCHNET_MAX_SOCKET_NUM
用于配置 socket 个数,等于 WCHNET_NUM_IPRAW、WCHNET_NUM_UDP、WCHNET_NUM_TCP、WCHNET_NUM_TCP_LISTEN 之和。
/********************************************************************* * @fn ETH_LibInit(以太网配置初始化) * * @brief Ethernet library initialization program * * @return command status */ uint8_t ETH_LibInit( uint8_t *ip, uint8_t *gwip, uint8_t *mask, uint8_t *macaddr ) { uint8_t s; struct _WCH_CFG cfg; memset(&cfg,0,sizeof(cfg)); cfg.TxBufSize = ETH_TX_BUF_SZE; /* 以太网发送缓存区大小 */ cfg.TCPMss = WCHNET_TCP_MSS; /* TCP最大报文段的长度,此值最大为1460,最小为60。综合传输和资源考虑,建议此值不要小于536字节 */
cfg.HeapSize = WCHNET_MEM_HEAP_SIZE; /* 堆分配内存大小,主要用于一些不定长度的内存分配,例如发送数据。如果 TCP 有大批量数据收发,则此值应该设置大些 */
cfg.ARPTableNum = WCHNET_NUM_ARP_TABLE; /*ARP缓存,存放IP和MAC,此值最小可以设置为1,最大为0x7F。如果WCHNET需要和4台PC进行网络通讯,其中两台会大批量收发数据,
则建议设置为4。如果小于2,则会严重影响通讯效率 */
cfg.MiscConfig0 = WCHNET_MISC_CONFIG0;
cfg.MiscConfig1 = WCHNET_MISC_CONFIG1;
cfg.led_link = ETH_LedLinkSet; /* 以太网连接指示灯 */
cfg.led_data = ETH_LedDataSet; /* 以太网数据传输指示灯 */
cfg.net_send = ETH_TxPktChainMode; /* 数据发送包发送结果 */
cfg.CheckValid = WCHNET_CFG_VALID; /* 配置值有效标志 */
s = WCHNET_ConfigLIB(&cfg);
if(s)
{
return (s);
}
s = WCHNET_Init(ip,gwip,mask,macaddr); /* 对IP地址,Gwip网关地址,Mask子网掩码,MAC地址进行初始化配置 */
ETH_Init(macaddr);
return (s);
}
/*********************************************************************
* @fn WCHNET_MainTask
*
* @brief library main task function
*
* @return none.
*/
void WCHNET_MainTask(void)
{
WCHNET_NetInput( ); /* Ethernet data input */
WCHNET_PeriodicHandle( ); /* Protocol stack time-related task processing */
WCHNET_HandlePhyNegotiation( ); /* 主要用来处理连接过程中出现的异常情况,每隔一定时间重连一次 */
}
/*Query the Ethernet global interrupt,
* if there is an interrupt, call the global interrupt handler*/
全局中断
if(WCHNET_QueryGlobalInt())
{
WCHNET_HandleGlobalInt();
}
/*********************************************************************
* @fn WCHNET_HandleGlobalInt
*
* @brief Global Interrupt Handle
*
* @return none
*/
void WCHNET_HandleGlobalInt(void)
{
u8 intstat;
u16 i;
u8 socketint;
intstat = WCHNET_GetGlobalInt(); /* 获取全局中断标志位 */
/* 识别到中断请求 */
if (intstat & GINT_STAT_UNREACH) /* Unreachable interrupt */
{ printf("GINT_STAT_UNREACH\r\n"); }
/* GINT_STAT_UNREACH,不可达中断。 当库收到 ICMP 不可达中断报文后,将不可达 IP数据包的 IP 地址,端口,协议类型保存到不可达信息表中,然后产生此中断,应用程序
可以通过查询_NET_SYS 结构中的 UnreachCode,UnreachProto 和 UnreachPort 来获取不可达的相关信息 */
if (intstat & GINT_STAT_IP_CONFLI) /* IP conflict,当相互连接的两个设备IP相同是会出现IP冲突提示 */
{
printf("GINT_STAT_IP_CONFLI\r\n");
}
if (intstat & GINT_STAT_PHY_CHANGE) /* PHY status change */ { i = WCHNET_GetPHYStatus(); /* 当 WCHNET 的 PHY 连接有变化时产生此中断,例如 PHY 状态由连接状态变化为断开状态或者由断开状态变化为连接状态。
应用程序可以通过 WCHNET_GetPHYStatus 来获取当前的 PHY 状态 */
if (i & PHY_Linked_Status) printf("PHY Link Success\r\n"); } if (intstat & GINT_STAT_SOCKET) /* socket related interrupt,当 socket 有中断事件时库会产生此中断,
应用程序可以通过 WCHNET_GetSocketInt 来获取 socket 的中断状态 */
{
for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) /* 此处作为以太网接收到了数据后,进入中断,i从0取到最大Socket数量时,逐个端口判别数据来源以达到数据准确发送的目的 */
{
socketint = WCHNET_GetSocketInt(i);
if (socketint)
WCHNET_HandleSockInt(i, socketint);
}
}
}
接下来看下WCHNET_HandleSockInt函数,
/*********************************************************************
* @fn WCHNET_HandleSockInt
*
* @brief Socket Interrupt Handle
*
* @param socketid - socket id.
* intstat - interrupt status
*
* @return none
*/
void WCHNET_HandleSockInt(u8 socketid, u8 intstat)
{
u8 i;
if (intstat & SINT_STAT_RECV) /* receive data,接收数据事件 */
{
WCHNET_DataLoopback(socketid); /* Data loopback,通过此函数进行数据的收发过程 */
}
if (intstat & SINT_STAT_CONNECT) /* connect successfully,判断是否连接成功事件 */
{
#if KEEPLIVE_ENABLE
WCHNET_SocketSetKeepLive(socketid, ENABLE);
#endif
WCHNET_ModifyRecvBuf(socketid, (u32) SocketRecvBuf[socketid],
RECE_BUF_LEN);
for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) {
if (socket[i] == 0xff) { /* save connected socket id */
socket[i] = socketid;
break;
}
}
printf("TCP Connect Success\r\n");
printf("socket id: %d\r\n",socket[i]);
}
if (intstat & SINT_STAT_DISCONNECT) /* disconnect,失联事件 */
{
for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { /* delete disconnected socket id */
if (socket[i] == socketid) {
socket[i] = 0xff;
break;
}
}
printf("TCP Disconnect\r\n");
}
if (intstat & SINT_STAT_TIM_OUT) /* timeout disconnect,以太网连接超时 */
{
for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { /* delete disconnected socket id */
if (socket[i] == socketid) {
socket[i] = 0xff;
break;
}
}
printf("TCP Timeout\r\n");
}
}
(1)SINT_STAT_RECV,接收缓冲区非空中断,当 socket 收到数据后,会产生此中断,应用层收到此中断后,应该使用 WCHNET_SocketRecvLen 来获取接收数据长度,根据长度使用WCHNET_SocketRecv 来读取接收缓冲区的数据。
(2) SINT_STAT_CONNECT,TCP 连接中断,仅在 TCP 模式下有效。TCP 连接成功后,会产生此中断。应用层必须在产生此中断后,才可以进行数据传输。
(3) SINT_STAT_DISCONNECT,TCP 连接断开中断,仅在 TCP 模式下有效。TCP 连接断开后,会产生此中断。连接断开后,应用层不得再进行数据传输。
(4) SINT_STAT_TIM_OUT,TCP 模式下, TCP 连接、断开、发送数据等过程中出现超时,会产生此中断。如果发生某些异常情况,库内部会关闭此连接时,也会产生该中断。TCP 模式下一旦产生此中断,socket 将会被关闭,且 socket 的相关配置被清除,所以应用层如果需要再次使用此 socket 必须重新初始化并连接或者监听。