【全能小网关|CH32V208】--7.DHCP
[复制链接]
本篇讲述用沁恒CH32V208开发板实现DHCP.
一.了解与准备
DHCP(Dynamic Host Configuration Protocol),动态主机配置协议,是一个应用层协议。当我们将客户主机ip地址设置为动态获取方式时,DHCP服务器就会根据DHCP协议给客户端分配IP,使得客户机能够利用这个IP上网。可以用Wireshark抓取DHCP包。
CH32V208芯片自带以太网MAC,内置10MPHY,支持10M以太网,我们可以看到芯片引脚直接与网口座连接,这款芯片具备蓝牙与以太网功能,比较适合做小型IOT网关。
沁恒自有一套WCHNET协议栈库,该协议栈库提供了TCP/IP 子程序库,集成了 TCP、UDP、ARP、RARP、ICMP、IGMP等以太网协议栈,可以同时支持 TCP、UDP 和 IPRAW 三种模式。在SDK里有一个专门文档介绍,可以根据需要参考详细说明使用。
二.代码准备
1.获取芯片MAC地址
#define ROM_CFG_USERADR_ID 0x1FFFF7E8
/*********************************************************************
* @fn WCHNET_GetMacAddr
*
* [url=home.php?mod=space&uid=159083]@brief[/url] Get MAC address
*
* [url=home.php?mod=space&uid=784970]@return[/url] none.
*/
void WCHNET_GetMacAddr( uint8_t *p )
{
uint8_t i;
uint8_t *macaddr=(uint8_t *)(ROM_CFG_USERADR_ID+5);
for(i=0;i<6;i++)
{
*p = *macaddr;
p++;
macaddr--;
}
}
2.配置DHCP主机名,这个由库函数完成
WCHNET_DHCPSetHostname("WCHNET"); //Configure DHCP host name
3.以太网库初始化
/*********************************************************************
* @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);
}
4.启动DHCP,使用的库函数,回调函数里获取ip分配
/*********************************************************************
* @fn WCHNET_DHCPCallBack
*
* @brief DHCPCallBack
*
* @param status - status returned by DHCP
* 0x00 - Success
* 0x01 - Failure
* arg - Data returned by DHCP
*
* @return DHCP status
*/
u8 WCHNET_DHCPCallBack(u8 status, void *arg)
{
u8 *p;
u8 tmp[4] = {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], 4);
memcpy(IPMask, &p[8], 4);
printf("IPAddr = %d.%d.%d.%d \r\n", (u16)IPAddr[0], (u16)IPAddr[1],
(u16)IPAddr[2], (u16)IPAddr[3]);
printf("GWIPAddr = %d.%d.%d.%d \r\n", (u16)GWIPAddr[0], (u16)GWIPAddr[1],
(u16)GWIPAddr[2], (u16)GWIPAddr[3]);
printf("IPMask = %d.%d.%d.%d \r\n", (u16)IPMask[0], (u16)IPMask[1],
(u16)IPMask[2], (u16)IPMask[3]);
printf("DNS1: %d.%d.%d.%d \r\n", p[12], p[13], p[14], p[15]);
printf("DNS2: %d.%d.%d.%d \r\n", p[16], p[17], p[18], p[19]);
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;
}
}
5.主函数循环里调用wchnet主任务函数,其内由库组成
/*********************************************************************
* @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( );
}
6.检测中断事件,并做处理
/*检测以太网全局中断*/
WCHNET_QueryGlobalInt()
/*获取以太网状态及处理*/
/*********************************************************************
* @fn WCHNET_HandleGlobalInt
*
* @brief Global Interrupt Handle
*
* @return none
*/
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);
}
}
}
7.main函数如下
/*********************************************************************
* @fn main
*
* @brief Main program
*
* @return none
*/
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();
}
}
}
三.测验
编译烧录后,插上网线,上电,可看到日志如上,DHCP获取IP信息成功。
|