walker2048 发表于 2024-7-21 23:53

全能小网关|CH32V208--6、板载ETH功能初体验

#### WCHNET协议库
随着物联网的普及,越来越多的单片机系统需要用到网络通讯。
WCHNET 芯片自带以太网 MAC,部分芯片有内置 10M PHY,支持 10M 以太网(部分芯片还支持 100M/1000M 速率),全双工,半双工,自动协商,线路自动转换等功能,可以直接和网络终端如 PC,嵌入式设备进行数据交互。WCHNET 协议栈库提供了 TCP/IP 子程序库,集成了 TCP、UDP、ARP、RARP、ICMP、IGMP等以太网协议栈,可以同时支持 TCP、UDP 和 IPRAW 三种模式。

#### 库函数
WCHNET的库其实并没有多复杂,也只是提供一些简单的功能,实现一些TCP/IP的简单应用是不错的。例如socket,http服务,mqtt应用等等。
但是如果想实现更多的功能,例如sntp校时,websocket服务,grpc服务等等应用,还是需要考虑类似lwip这些使用更广泛的库。



#### 运行DHCP例程
这段代码演示了如何使用WCH提供的以太网库进行DHCP操作,获取IP地址,并创建TCP连接。
```c
/********************************** (C) COPYRIGHT *******************************
* File Name          : main.c
* Author             : WCH
* Version            : V1.0.0
* Date               : 2022/05/20
* Description      : Main program body.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
/*
*@Note
DHCP example, demonstrating that DHCP automatically obtains an IP address.
For details on the selection of engineering chips,
please refer to the "CH32V20x Evaluation Board Manual" under the CH32V20xEVT\EVT\PUB folder.
*/
#include "string.h"
#include "eth_driver.h"

u8 MACAddr;                                    //MAC address
u8 IPAddr   = {0, 0, 0, 0};                  //IP address
u8 GWIPAddr = {0, 0, 0, 0};                  //Gateway IP address
u8 IPMask   = {0, 0, 0, 0};                  //subnet mask
u8 DESIP    = {192, 168, 1, 100};            //destination IP address
u16 desport = 1000;                               //destination port
u16 srcport = 1000;                               //source port

u8 SocketId;
u8 SocketRecvBuf;//socket receive buffer
u8 MyBuf;

/*********************************************************************
* @fn      mStopIfError
*
* @brief check if error.
*
* @param   iError - error constants.
*
* @return none
*/
void mStopIfError(u8 iError)
{
    if (iError == WCHNET_ERR_SUCCESS)
      return;
    printf("Error: %02X\r\n", (u16) iError);
}

/*********************************************************************
* @fn      TIM2_Init
*
* @brief   Initializes TIM2.
*
* @returnnone
*/
void TIM2_Init(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / 1000000;
    TIM_TimeBaseStructure.TIM_Prescaler = WCHNETTIMERPERIOD * 1000 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    TIM_Cmd(TIM2, ENABLE);
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    NVIC_SetPriority(TIM2_IRQn, 0x80);
    NVIC_EnableIRQ(TIM2_IRQn);
}

/*********************************************************************
* @fn      WCHNET_CreateTcpSocket
*
* @brief   Create TCP Socket
*
* @returnnone
*/
void WCHNET_CreateTcpSocket(void)
{
    u8 i;
    SOCK_INF TmpSocketInf;

    memset((void *) &TmpSocketInf, 0, sizeof(SOCK_INF));
    memcpy((void *) TmpSocketInf.IPAddr, DESIP, 4);
    TmpSocketInf.DesPort = desport;
    TmpSocketInf.SourPort = srcport++;
    TmpSocketInf.ProtoType = PROTO_TYPE_TCP;
    TmpSocketInf.RecvBufLen = RECE_BUF_LEN;
    i = WCHNET_SocketCreat(&SocketId, &TmpSocketInf);
    printf("WCHNET_SocketCreat %d\r\n", SocketId);
    mStopIfError(i);
    i = WCHNET_SocketConnect(SocketId);                        //make a TCP connection
    mStopIfError(i);
}

/*********************************************************************
* @fn      WCHNET_DataLoopback
*
* @brief   Data loopback function.
*
* @param   id - socket id.
*
* @returnnone
*/
void WCHNET_DataLoopback(u8 id)
{
#if 1
    u8 i;
    u32 len;
    u32 endAddr = SocketInf.RecvStartPoint + SocketInf.RecvBufLen;       //Receive buffer end address

    if ((SocketInf.RecvReadPoint + SocketInf.RecvRemLen) > endAddr) {    //Calculate the length of the received data
      len = endAddr - SocketInf.RecvReadPoint;
    }
    else {
      len = SocketInf.RecvRemLen;
    }
    i = WCHNET_SocketSend(id, (u8 *) SocketInf.RecvReadPoint, &len);      //send data
    if (i == WCHNET_ERR_SUCCESS) {
      WCHNET_SocketRecv(id, NULL, &len);                                    //Clear sent data
    }
#else
    u32 len, totallen;
    u8 *p = MyBuf, TransCnt = 255;

    len = WCHNET_SocketRecvLen(id, NULL);                              //query length
    printf("Receive Len = %d\r\n", len);
    totallen = len;
    WCHNET_SocketRecv(id, MyBuf, &len);                                  //Read the data of the receive buffer into MyBuf
    while(1){
      len = totallen;
      WCHNET_SocketSend(id, p, &len);                                  //Send the data
      totallen -= len;                                                 //Subtract the sent length from the total length
      p += len;                                                      //offset buffer pointer
      if( !--TransCnt )break;                                        //Timeout exit
      if(totallen) continue;                                           //If the data is not sent, continue to send
      break;                                                         //After sending, exit
    }
#endif
}

/*********************************************************************
* @fn      WCHNET_HandleSockInt
*
* @brief   Socket Interrupt Handle
*
* @param   socketid - socket id.
*          intstat - interrupt status
*
* @returnnone
*/
void WCHNET_HandleSockInt(u8 socketid, u8 intstat)
{
    if (intstat & SINT_STAT_RECV)                               //receive data
    {
      WCHNET_DataLoopback(socketid);                        //Data loopback
    }
    if (intstat & SINT_STAT_CONNECT)                            //connect successfully
    {
      WCHNET_ModifyRecvBuf(socketid, (u32) SocketRecvBuf, RECE_BUF_LEN);
      printf("TCP Connect Success\r\n");
    }
    if (intstat & SINT_STAT_DISCONNECT)                         //disconnect
    {
      printf("TCP Disconnect\r\n");
    }
    if (intstat & SINT_STAT_TIM_OUT)                            //timeout disconnect
    {
      printf("TCP Timeout\r\n");
      WCHNET_CreateTcpSocket();
    }
}

/*********************************************************************
* @fn      WCHNET_HandleGlobalInt
*
* @brief   Global Interrupt Handle
*
* @returnnone
*/
void WCHNET_HandleGlobalInt(void)
{
    u8 intstat;
    u16 i;
    u8 socketint;

    intstat = WCHNET_GetGlobalInt();                              //get global interrupt flag
    if (intstat & GINT_STAT_UNREACH)                              //Unreachable interrupt
    {
      printf("GINT_STAT_UNREACH\r\n");
    }
    if (intstat & GINT_STAT_IP_CONFLI)                            //IP conflict
    {
      printf("GINT_STAT_IP_CONFLI\r\n");
    }
    if (intstat & GINT_STAT_PHY_CHANGE)                           //PHY status change
    {
      i = WCHNET_GetPHYStatus();
      if (i & PHY_Linked_Status)
            printf("PHY Link Success\r\n");
    }
    if (intstat & GINT_STAT_SOCKET) {                           //socket related interrupt
      for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) {
            socketint = WCHNET_GetSocketInt(i);
            if (socketint)
                WCHNET_HandleSockInt(i, socketint);
      }
    }
}

/*********************************************************************
* @fn      WCHNET_DHCPCallBack
*
* @brief   DHCPCallBack
*
* @param   status - status returned by DHCP
*                   0x00 - Success
*                   0x01 - Failure
*          arg - Data returned by DHCP
*
* @returnDHCP status
*/
u8 WCHNET_DHCPCallBack(u8 status, void *arg)
{
    u8 *p;
    u8 tmp = {0, 0, 0, 0};

    if(!status)
    {
      p = arg;
      printf("DHCP Success\r\n");
      /*If the obtained IP is the same as the last IP, exit this function.*/
      if(!memcmp(IPAddr, p ,sizeof(IPAddr)))
            return READY;
      /*Determine whether it is the first successful IP acquisition*/
      if(memcmp(IPAddr, tmp ,sizeof(IPAddr))){
            /*The obtained IP is different from the last value,
             * then disconnect the last connection.*/
            WCHNET_SocketClose(SocketId, TCP_CLOSE_NORMAL);
      }
      memcpy(IPAddr, p, 4);
      memcpy(GWIPAddr, &p, 4);
      memcpy(IPMask, &p, 4);
      printf("IPAddr = %d.%d.%d.%d \r\n", (u16)IPAddr, (u16)IPAddr,
               (u16)IPAddr, (u16)IPAddr);
      printf("GWIPAddr = %d.%d.%d.%d \r\n", (u16)GWIPAddr, (u16)GWIPAddr,
               (u16)GWIPAddr, (u16)GWIPAddr);
      printf("IPMask = %d.%d.%d.%d \r\n", (u16)IPMask, (u16)IPMask,
               (u16)IPMask, (u16)IPMask);
      printf("DNS1: %d.%d.%d.%d \r\n", p, p, p, p);
      printf("DNS2: %d.%d.%d.%d \r\n", p, p, p, p);
      WCHNET_CreateTcpSocket();                                                   //Create a TCP connection
      return READY;
    }
    else
    {
      printf("DHCP Fail %02x \r\n", status);
      /*Determine whether it is the first successful IP acquisition*/
      if(memcmp(IPAddr, tmp ,sizeof(IPAddr))){
            /*The obtained IP is different from the last value*/
            WCHNET_SocketClose(SocketId, TCP_CLOSE_NORMAL);
      }
      return NoREADY;
    }
}

/*********************************************************************
* @fn      main
*
* @brief   Main program
*
* @returnnone
*/
int main(void)
{
    u8 i;

    Delay_Init();
    USART_Printf_Init(115200);                                          //USART initialize
    printf("DHCP Test\r\n");       
    if((SystemCoreClock == 60000000) || (SystemCoreClock == 120000000))
      printf("SystemClk:%d\r\n", SystemCoreClock);
    else
      printf("Error: Please choose 60MHz and 120MHz clock when using Ethernet!\r\n");
    printf("net version:%x\n", WCHNET_GetVer());
    if( WCHNET_LIB_VER != WCHNET_GetVer()){
      printf("version error.\n");
    }
    WCHNET_GetMacAddr(MACAddr);                                           //get the chip MAC address
    printf("mac addr:");
    for(i = 0; i < 6; i++)
      printf("%x ", MACAddr);
    printf("\n");
    TIM2_Init();
    WCHNET_DHCPSetHostname("WCHNET");                                     //Configure DHCP host name
    i = ETH_LibInit(IPAddr,GWIPAddr,IPMask,MACAddr);                      //Ethernet library initialize
    mStopIfError(i);
    if(i == WCHNET_ERR_SUCCESS)
      printf("WCHNET_LibInit Success\r\n");
    WCHNET_DHCPStart(WCHNET_DHCPCallBack);                              //Start DHCP

    while(1)
    {
      /*Ethernet library main task function,
         * which needs to be called cyclically*/
      WCHNET_MainTask();
      /*Query the Ethernet global interrupt,
         * if there is an interrupt, call the global interrupt handler*/
      if(WCHNET_QueryGlobalInt())
      {
            WCHNET_HandleGlobalInt();
      }
    }
}

```
#### 运行程序
运行程序后,插入网线,并重启设备,等待10秒左右,可以看到设备已获取到IP地址,如下图

在电脑上使用ps shell来ping板子,可以看到可以顺利连上板子


WCH官方库调用起来是挺简单的,官方给的demo也很好,可以快速帮开发者熟悉网络库的使用,整体体验下来还是挺舒服的。

lugl4313820 发表于 2024-7-23 07:34

<p>WCH官方库调用起来是挺简单的,官方给的demo也很好,可以快速帮开发者熟悉网络库的使用,整体体验下来还是挺舒服的。</p>

<p>确实非常好的一家公司。</p>

御坂10032号 发表于 2024-8-6 03:18

<p>我也有这个单片机, 能不能每次写完代码分享一下工程文件. 也好互相学习下</p>

walker2048 发表于 2024-8-6 10:43

御坂10032号 发表于 2024-8-6 03:18
我也有这个单片机, 能不能每次写完代码分享一下工程文件. 也好互相学习下

<p>我这边运行的都是官方提供的SDK里面的demo呀,并不是自己写的代码</p>
页: [1]
查看完整版本: 全能小网关|CH32V208--6、板载ETH功能初体验