一、简介:
本文会基于沁恒微电子以太网协议栈芯片CH394进行DHCP和DNS示例(主控芯片为CH32V307,SPI接口),简单演示通过UDP模式下进行DHCP报文发送和解析,同时利用DNS进行域名访问。
CH394芯片自带10/100M以太网介质传输层MAC和物理层收发器PHY,完全兼容IEEE802.3协议, 内置了IP、ARP、ICMP、IGMP、UDP、TCP等以太网协议栈固件。单片机系统可以方便的通过CH394芯 片进行网络通讯。CH394支持网络唤醒模式(WOL)和掉电模式。
二、应用:
DHCP过程是通过UDP作为传输协议,设备端通过UDP将消息传输到具有DHCP服务器设备的67号端口,DHCP服务器返回消息给客户端的68号端口。大致流程为设备端发起DHCP DISCOVER报文请求HDCP------DHCP服务器提供OFFER------设备端发送Request报文确认------服务器发起ACK,DHCP成功。具体流程和协议内容可以看此贴DHCP介绍与实现方法 - 小小小学僧 - 博客园。
程序步骤:
1、初始化SPI接口--->复位CH394Q--->检测PHY状态--->初始化CH394--->收发缓存
CH394Q_GPIO_Init(); CH394Q_SPIPort_Init(); Delay_Ms(100); CH394Q_ResetHW(); // CH394Q硬件复位 printf("\r\n CH394Q EVT Test Demo \r\n"); i = CH394Q_GetCHIPV(); printf(" CH394Q CHIPV : %2x\r\n", i); CH394Q_PHY_Check(); // PHY连接检测 CH394Q_InfParam(CH394Q_IPAddr, CH394Q_IPMask , CH394Q_GWIPAddr, CH394Q_MACAddr); // 设置CH394Q网络参数 CH394Q_SocketBuf_Init(TX_BUFF, RX_BUFF); // 初始化8个Socket的发送接收缓存大小
2、DHCP运行:封装DHCP DISCOVER报文------创建UDP SOCKET(SOCKET3)发送该报文------接收DHCP服务器offer数据并解析DHCP服务器提供的IP等参数------通过UDP发送DHCP request报文-------接收DHCP服务器的ACK报文,此时表明DHCP成功。其他情况下通过定时器进行计数,当DHCP流程进入某一阶段无法或者相应状态时进超时计数,超时后并重新进行DHCP DIscover报文发送,直到DHCP流程完成。(报文封装和校验附件代码查看)
u8 DHCP_run(void)//0 { uint8_t type; uint8_t ret;
if(Conflict_flag == 1) { init_dhcp_client(); /*初始化DHCP客户端*/ TIM2_Init(); /*初始化定时器*/ Conflict_flag = 0; } if(dhcp_state == STATE_DHCP_STOP) { Conflict_flag = 1; return DHCP_STOPPED; } if(CH394Q_GetSn_STA(SOCK_DHCP) != SOCK_UDP) CH394Socket_Init(3, Sn_MODE_UDP, DHCP_CLIENT_PORT, 0x00); ret = DHCP_RUNNING; type = parseDHCPMSG(); switch ( dhcp_state ) { case STATE_DHCP_READY : DHCP_allocated_ip[0] = 0; DHCP_allocated_ip[1] = 0; DHCP_allocated_ip[2] = 0; DHCP_allocated_ip[3] = 0; send_DHCP_DISCOVER(); dhcp_time = 0; dhcp_state = STATE_DHCP_DISCOVER; //Delay_Us(1); break; case STATE_DHCP_DISCOVER : if (type == DHCP_OFFER) { DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0]; DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1]; DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2]; DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3]; send_DHCP_REQUEST(); dhcp_time = 0; dhcp_state = STATE_DHCP_REQUEST; } else ret = check_DHCP_timeout(); break; case STATE_DHCP_REQUEST : if (type == DHCP_ACK) { if (check_DHCP_leasedIP()) { // Network info assignment from DHCP printf(" DHCP 已获取:\r\n"); printf(" I P 地址:%d.%d.%d.%d\r\n",DHCP_allocated_ip[0],DHCP_allocated_ip[1],DHCP_allocated_ip[2],DHCP_allocated_ip[3]); printf(" 子网掩码:%d.%d.%d.%d\r\n",DHCP_allocated_sn[0],DHCP_allocated_sn[1],DHCP_allocated_sn[2],DHCP_allocated_sn[3]); printf(" 网关地址:%d.%d.%d.%d\r\n",DHCP_allocated_gw[0],DHCP_allocated_gw[1],DHCP_allocated_gw[2],DHCP_allocated_gw[3]); dhcp_ip_assign(); reset_DHCP_timeout(); dhcp_state = STATE_DHCP_LEASED; DHCP_Flag=1; } else { reset_DHCP_timeout(); dhcp_ip_conflict(); dhcp_state = STATE_DHCP_READY; } } else if (type == DHCP_NAK) { printf("> Receive DHCP_NACK\r\n"); reset_DHCP_timeout(); dhcp_state = STATE_DHCP_DISCOVER; } else ret = check_DHCP_timeout(); break; case STATE_DHCP_LEASED : ret = DHCP_IP_LEASED; if ((dhcp_lease_time != DEFAULT_LEASETIME) && ((dhcp_lease_time/2) < dhcp_time)) { #ifdef _DHCP_DEBUG_ printf("> Maintains the IP address \r\n"); #endif type = 0; OLD_allocated_ip[0] = DHCP_allocated_ip[0]; OLD_allocated_ip[1] = DHCP_allocated_ip[1]; OLD_allocated_ip[2] = DHCP_allocated_ip[2]; OLD_allocated_ip[3] = DHCP_allocated_ip[3]; DHCP_XID++; send_DHCP_REQUEST(); reset_DHCP_timeout(); dhcp_state = STATE_DHCP_REREQUEST; } break; case STATE_DHCP_REREQUEST : ret = DHCP_IP_LEASED; if (type == DHCP_ACK) { dhcp_retry_count = 0; if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] || OLD_allocated_ip[1] != DHCP_allocated_ip[1] || OLD_allocated_ip[2] != DHCP_allocated_ip[2] || OLD_allocated_ip[3] != DHCP_allocated_ip[3]) { ret = DHCP_IP_CHANGED; dhcp_ip_update(); #ifdef _DHCP_DEBUG_ printf(">IP changed.\r\n"); #endif } #ifdef _DHCP_DEBUG_ else printf(">IP is continued.\r\n"); #endif reset_DHCP_timeout(); dhcp_state = STATE_DHCP_LEASED; } else if (type == DHCP_NAK) { #ifdef _DHCP_DEBUG_ printf("> Receive DHCP_NACK, Failed to maintain ip\r\n"); #endif reset_DHCP_timeout(); dhcp_state = STATE_DHCP_DISCOVER; } else ret = check_DHCP_timeout(); break; default : break; } return ret; }
3、DNS域名解析:创建UDP Socket------封装DNS查询报文------通过UDP Socket发送DNS查询报文(注意DNS的目的端口为固定端口:53,MDNS端口为5353)。------DNS服务器回复DNS应答报文------接收报文并解析(具体报文内容参考该贴:DNS介绍与实现方法 - 小小小学僧 - 博客园),
uint8_t DnsQuery(uint8_t s, uint8_t * name, uint8_t * pSip) { struct dhdr dhp; uint16_t len,cnt; if(status>1) { count++; printf("count = %2d\n",(uint16_t)count); Delay_Ms(1); if(count>2000) { printf("DNS Fail!!!!\n"); count =0; status =0; } } if(status == 1) { CH394Q_UDPSocketInit(s, Sn_MODE_UDP, 2000); status = 2; printf("status = 2!\n"); } if(status ==2) { len = MakeDnsQueryMsg(0,(char *)name, dns_buf, MAX_DNS_BUF_SIZE); cnt = CH394Q_SocketSendTo(s,dns_buf,len,DNS_SERVER_IP,IPPORT_DOMAIN); if (cnt == 0) return(0); else { status = 3; printf("status = 3!\n"); } } if(status ==4) { return(parseMSG(&dhp, dns_buf, pSip)); /*解析响应报文并返回结果*/ } return 0; }
3、主函数运行:
while(1) { DHCP_run(); //DHCP运行 if(DHCP_Flag==1) { if(Socket_FLag==0) { CH394Q_SetGINTE(0XFF); // 使能中断 CH394Q_SetSINTE(0XFF); // 使能socket中断 printf(" TCP SERVER......\r\n"); CH394Q_SetSn_MODE(0, Sn_MODE_NA); //创建TCP SERVER socket printf("scoket1= %x\r\n", CH394Q_GetSn_MODE(0)); CH394Q_TCPServerSocketInit( 0, Sn_MODE_TCP |Sn_MODE_NA , CH394Q_Port); printf("scoket= %x\r\n", CH394Q_GetSn_MODE(0)); Socket_FLag=1; } if(QueryCH394QInterrupt() == 0) { CH394Q_GlobalInterrupt(); } i=DnsQuery(SOCK_DNS,url_dn1,ip); //DNS域名解析 if(i) { printf("Domain name: %s \n",url_dn1); printf(" HTTPs_IP= %d.%d.%d.%d\n\n",ip[0],ip[1],ip[2],ip[3]); status = 1; break; } } }
二、应用:
将CH394设备接入路由中,运行日志如下,可以看到默认IP是192.168.1.200,DHCP成功后的IP为192.168.1.139,再进行DNS解析百度域名,解析完成后IP地址为180.101.50.242.
例程链接: https://files.cnblogs.com/files/blogs/805237/CH394Q_DHCP%26DNS.rar?t=1733989245&download=true
标签:ip,CH394,以太网,dhcp,printf,DHCP,CH394Q,allocated From: https://www.cnblogs.com/sw111/p/18600039