1、DHCP概述
DHCP,全称为Dynamic Host Configuration Protocol,动态主机配置协议,该协议允许服务器向客户端动态分配 IP 地址和配置信息,实现了自动设置IP地址、统一管理IP地址分配,简单理解为实现即插即用。
2、例程介绍
main函数内容如下:
/********************************************************************* * @fn main * * @brief Main program * * @return none */ int main(void) { u8 i; SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); //USART initialize printf("DHCP Test\r\n"); printf("SystemClk:%d\r\n", SystemCoreClock); printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID()); 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[i]); 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(); } } }
下为具体介绍:
SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); //USART initialize printf("DHCP Test\r\n"); printf("SystemClk:%d\r\n", SystemCoreClock); printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID()); printf("net version:%x\n", WCHNET_GetVer()); if( WCHNET_LIB_VER != WCHNET_GetVer()) { printf("version error.\n"); }
首先是相关函数初始化以及例程相关信息的打印。SystemCoreClockUpdate函数、Delay_Init函数、USART_Printf_Init函数分别为系统时钟初始化、延时函数初始化、串口打印函数初始化,在此不再赘述。
打印函数中,WCHNET_GetVer函数为CH32V307以太网协议栈库函数,功能为获取库的版本号,直接调用即可。获取版本号之后会进行一个判断,若不一致,打印version error.
WCHNET_GetMacAddr(MACAddr); //get the chip MAC address printf("mac addr:"); for(i = 0; i < 6; i++) printf("%x ", MACAddr[i]); printf("\n");
TIM2_Init();
WCHNET_GetMacAddr函数主要用于获取CH32V307的MAC地址。MAC地址,即发送这个帧的设备希望接收这个帧的设备地址,由IEEE为生产商分配,全球唯一,6字节48 位。 CH32V307的MAC地址出厂时已烧录在芯片内部。地址的发送遵循低有效位在前的原则。
TIM2_Init函数主要用于检测以太网PHY连接的状态。定时器10ms进一次中断。在定时器中断函数中,主要对定时器周期进行加法计数。
WCHNET_DHCPSetHostname("WCHNET");
WCHNET_DHCPSetHostname为协议栈库函数,直接调用即可,用于配置DHCP主机名。关于配置的DHCP主机名,可在路由器网页中看到,在设备列表里有设备名称。
i = ETH_LibInit(IPAddr,GWIPAddr,IPMask,MACAddr); //Ethernet library initialize mStopIfError(i); if(i == WCHNET_ERR_SUCCESS) printf("WCHNET_LibInit Success\r\n");
ETH_LibInit为以太网库初始化,ETH_LibInit函数内容如下:
/********************************************************************* * @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; cfg.HeapSize = WCHNET_MEM_HEAP_SIZE; cfg.ARPTableNum = WCHNET_NUM_ARP_TABLE; 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); ETH_Init( macaddr ); return (s); }View Code
该函数中,首先对_WCH_CFG结构体参数进行配置,_WCH_CFG结构体内容如下:
struct _WCH_CFG { uint32_t TxBufSize; //MAC send buffer size, reserved for use uint32_t TCPMss; //TCP MSS size uint32_t HeapSize; //heap memory size uint32_t ARPTableNum; //Number of ARP lists uint32_t MiscConfig0; //Miscellaneous Configuration 0 /* Bit 0 TCP send buffer copy 1: copy, 0: not copy */ /* Bit 1 TCP receive replication optimization, used for internal debugging */ /* bit 2 delete oldest TCP connection 1: enable, 0: disable */ /* Bits 3-7 Number of PBUFs of IP segments */ /* Bit 8 TCP Delay ACK disable */ uint32_t MiscConfig1; //Miscellaneous Configuration 1 /* Bits 0-7 Number of Sockets*/ /* Bits 8-12 Reserved */ /* Bit 13 PING enable, 1: On 0: Off */ /* Bits 14-18 TCP retransmission times */ /* Bits 19-23 TCP retransmission period, in 50 milliseconds */ /* bit 25 send failed retry, 1: enable, 0: disable */ /* bit 26 Select whether to perform IPv4 checksum check on * the TCP/UDP/ICMP header of the received frame payload by hardware, * and calculate and insert the checksum of the IP header and payload of the sent frame by hardware.*/ /* Bits 27-31 period (in 250 milliseconds) of Fine DHCP periodic process */ led_callback led_link; //PHY Link Status Indicator led_callback led_data; //Ethernet communication indicator eth_tx_set net_send; //Ethernet send eth_rx_set net_recv; //Ethernet receive uint32_t CheckValid; //Configuration value valid flag, fixed value @WCHNET_CFG_VALID };View Code
TxBufSize主要配置MAC发送缓冲区大小
TCPMss主要配置TCP最大报文段长度
HeapSize主要配置堆大小
ARPTableNum,主要配置ARP列表数量
MiscConfig0为杂项配置,对应位的注释如下:
/*Bit 0 TCP发送缓冲区复制1:复制,0:不复制*/
/*Bit 1 TCP接收复制优化,用于内部调试*/
/*Bit 2删除最旧的TCP连接1:启用,0:禁用*/
/*Bits 3-7 IP段的PBUF数*/
/*Bit 8 TCP延迟ACK禁用*/
MiscConfig1为杂项配置,对应位的注释如下:
/*Bit 0-7Socket数量*/
/*Bit 8-12保留*/
/*Bit 13 PING使能,1:开0:关*/
/*Bit 14-18 TCP重传次数*/
/*Bit 19-23 TCP重传周期,以50毫秒为单位*/
/*Bit 25发送失败重试,1:启用,0:禁用*/
/*Bit 26选择是否对执行IPv4校验和检查
*由硬件接收的帧有效载荷的TCP/UDP/ICMP报头,
*并通过硬件计算和插入发送帧的IP报头和有效载荷的校验和*/
/*Bit 27-31精细DHCP周期过程的周期(以250毫秒为单位)*/
led_link主要配置PHY建立连接指示灯
led_data主要配置PHY数据传输指示灯
关于接口指示灯配置,例程默认配置的是PC0和PC1,可根据自己需求进行修改
net_send主要配置以太网发送以链模式发送数据帧。
CheckValid主要配置配置值有效标志,固定值为WCHNET_CFG_VALID
WCHNET_ConfigLIB为协议栈库函数,主要用于库参数配置,即配置上述介绍参数,返回值为0表示成功。
WCHNET_Init函数为协议栈库函数,主要用于配置库初始化,主要设置IP地址,网关地址、子网掩码以及MAC地址,返回值为0表示成功
ETH_Init函数为以太网初始化函数。
回到main函数,以太网库初始化完成后,调用WCHNET_DHCPStart函数启动DHCP。
当DHCP成功或者失败时,库会调用dhcp_callback函数,通知应用层DHCP的状态,WCHNET
向本函数传递两个参数,第一个参数为DHCP状态,0为成功,其他值失败,当DHCP成功时,用户可以通过第二个参数获取到一个指针,该指针指向的地址依次保存了 IP 地址,网关地址,子网掩码,主DNS和次DNS,一共20个字节。注意该指针为临时变量dhcp_callback
返回后,该指针失效。
如果当前网络内没有DHCP Server,会产生超时时间,超时时间约为10秒。超时后调用
dhcp_callback函数通知应用层,此时DHCP并不会停止,会一直查找DHCP Server。用户可
以调用 WCHNET_DHCPStop 来停止DHCP。
使用时注意以下两点:
(1)必须在WCHNET_Init成功之后启动DHCP(必须)。
(2)在DHCP成功之后,再创建socket(推荐)。
(3)DHCP功能会占用一个UDP socket
如果DHCP失败,则可以用WCHNET_Init 时使用的IP地址进行通讯。
while循环中,首先执行WCHNET_MainTask函数,WCHNET_MainTask函数内容如下:
/********************************************************************* * @fn WCHNET_MainTask * * @brief library main task function * * @param none. * * @return none. */ void WCHNET_MainTask(void) { WCHNET_NetInput( ); /* Ethernet data input */ WCHNET_PeriodicHandle( ); /* Protocol stack time-related task processing */ WCHNET_HandlePhyNegotiation(); WCHNET_RecProcess(); }View Code
WCHNET_NetInput函数为以太网数据输入函数,总是在主程序中调用,或者在检测到接收中断后调用。
WCHNET_PeriodicHandle函数处理协议栈中的时间相关任务
WCHNET_HandlePhyNegotiation函数处理PHY的协商
if(WCHNET_QueryGlobalInt()) { WCHNET_HandleGlobalInt(); }
查询全局中断状态,当查询到有相应的中断标志置1之后,执行中断处理函数,中断处理函数内容如下:
/********************************************************************* * @fn WCHNET_HandleGlobalInt * * @brief Global Interrupt Handle * * @return none */ void WCHNET_HandleGlobalInt(void) { u8 intstat; u16 i; u8 socketint; //WCHNET_GetGlobalInt,读全局中断并将全局中断清零,具体状态码请查阅 WCHNET.h intstat = WCHNET_GetGlobalInt(); //get global interrupt flag,获取全局中断并将全局中断清0 if (intstat & GINT_STAT_UNREACH) //Unreachable interrupt,无法访问的中断 { printf("GINT_STAT_UNREACH\r\n"); } if (intstat & GINT_STAT_IP_CONFLI) //IP conflict,IP冲突 { printf("GINT_STAT_IP_CONFLI\r\n"); } if (intstat & GINT_STAT_PHY_CHANGE) //PHY status change,PHY状态改变 { i = WCHNET_GetPHYStatus(); if (i & PHY_Linked_Status) printf("PHY Link Success\r\n"); } if (intstat & GINT_STAT_SOCKET) { //socket related interrupt,socket相关中断 for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { socketint = WCHNET_GetSocketInt(i); //获取套接字中断,并清除套接字中断。 if (socketint) WCHNET_HandleSockInt(i, socketint); //socket中断处理函数 } } }View Code
该函数具体可参考注释,此处主要注意socket中断处理函数,socket中断处理函数内容如下:
/********************************************************************* * @fn WCHNET_HandleSockInt * * @brief Socket Interrupt Handle * * @param socketid - socket id. * intstat - interrupt status * * @return none */ 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[socketid], 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(); } }View Code
该函数中主要关注WCHNET_DataLoopback函数,该函数内容如下:
/********************************************************************* * @fn WCHNET_DataLoopback * * @brief Data loopback function. * * @param id - socket id. * * @return none */ void WCHNET_DataLoopback(u8 id) { #if 1 u8 i; u32 len; u32 endAddr = SocketInf[id].RecvStartPoint + SocketInf[id].RecvBufLen; //Receive buffer end address if ((SocketInf[id].RecvReadPoint + SocketInf[id].RecvRemLen) > endAddr) { //Calculate the length of the received data len = endAddr - SocketInf[id].RecvReadPoint; } else { len = SocketInf[id].RecvRemLen; } i = WCHNET_SocketSend(id, (u8 *) SocketInf[id].RecvReadPoint, &len); //send data if (i == WCHNET_ERR_SUCCESS) { WCHNET_SocketRecv(id, NULL, &len); //Clear sent data } #else u32 len, totallen; u8 *p = MyBuf; len = WCHNET_SocketRecvLen(id, NULL); //query length 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(totallen)continue; //If the data is not sent, continue to send break; //After sending, exit } #endif }View Code
该函数主要是将接收的数据发送出去。
以上就是整个DHCP例程的工作流程。
标签:函数,例程,TCP,id,CH32V307,printf,DHCP,WCHNET From: https://www.cnblogs.com/liaigu/p/17928068.html