uip1.0 学习笔记--NUC472

主机环境:Windows 7 SP1

开发环境:MDK5.18

目标板:NUVOTON NUC472

之前领了一块新唐的NUC472开发板一直没有动,吃了很久的灰,后来接触了一下新唐的M451芯片,休闲之余把NUC472开发板拿出来研究了一哈,该开发板只有两个接口以太网接口和一个USB接口,当然所有引脚是都引出来的,如下图所示:

uip1.0 学习笔记--NUC472_第1张图片

该开发板刚好可以用来研究一下网络的开发,之前一直在使用STM32的芯片搞开发,但苦于没有领到过带网络模块的STM32开发板,因此一直没动静。。。之前的STM32 USB开发板还是花了二十多块钱在网上淘的。

NUC472开发板例程中关于网络的开发示例有两种:uip和lwip,目前来看lwip是开发网络的最佳选择,资料很多,而uip几乎是小众选择了,但这两种协议栈的作者是同一个人--Adam Dunkels,感觉他是一个很牛叉的人,写过很多开源软件,uip目前好像停止单独更新了,uip项目合并到contiki项目中了,目前uip单独的最新版本是uip1.0,lwip目前更新记录是lwip2.0 RC2,之所以先选择uip来研究是因为它比较小源代码文件较少,方便入手,但后面肯定还是要学习lwip的,而且lwip2.0的稳定版还没放出来,等放出来后再研究吧。

uip-1.0源码目录很少,如下:

uip1.0 学习笔记--NUC472_第2张图片

uip-1.0比uip-0.9多了一些文件,相比而言uip-0.9更简洁一些。apps目录包含了一些uip应用的示例,方便用于编辑代码时可以参考,其中包含了一些常用的示例如:webserver、webclient、telnetd、smtp等,doc目录包含了参考手册方便用户学习该源码,lib目录是比uip-0.9多出的一个目录,其为uip-1.0的内存管理模块,uip目录则是协议栈的核心文件,最后的unix则是一个示例程序,方便用户移植uip-1.0协议栈到所需要的平台。我们在NUC472平台上使用该协议栈,所需要做的工作就是重写unix目录下的文件

uip1.0 学习笔记--NUC472_第3张图片

在uip-conf.h文件中我们选择引用apps目录下的哪个示例,默认使用的是webserver示例。tapdev文件则是跟硬件相关的以太网发送和接收数据的函数,uip协议栈只是处理以太网数据包,但具体的数据包来源以及处理完之后的数据包发送还是要交由具体硬件来实现的,也就是我们的以太网驱动的编写,clock-arch文件则是由具体的硬件提供一个时钟以便于uip检测时间,这些文件说明都可以在参考手册中找到,在初始学习uip协议栈时建议由简入难,因此,这里我实现的示例是helloworld示例,先看下tapdev模块的编写,tapdev模块只需要完成三个函数即可:tapdev_init()、tapdev_send()、tapdev_read(),在NUC472中实现这三个文件还是比较简单的,如下:

/*
 * Copyright (c) 2001, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Author: Adam Dunkels 
 *
 * $Id: tapdev.c,v 1.8 2006/06/07 08:39:58 adam Exp $
 */

#include "NUC472_442.h"
#include "uip.h"
#include "string.h"

extern struct uip_eth_addr ethaddr;
uint32_t volatile u32PktLen = 0;//接收到的数据包长度

/*---------------------------------------------------------------------------*/

/**
  * @brief  EMAC Tx interrupt handler.
  * @param  None
  * @return None
  */
void EMAC_TX_IRQHandler(void)
{
    // Clean up Tx resource occupied by previous sent packet(s)
    EMAC_SendPktDone();
}


/**
  * @brief  EMAC Rx interrupt handler.
  * @param  None
  * @return None
  */
void EMAC_RX_IRQHandler(void)
{
    while(1) 
	{
        // Get all recv data
        if(EMAC_RecvPkt(uip_buf, (uint32_t *)&u32PktLen) == 0)
            break;
        // Clean up Rx resource occupied by previous received packet
        EMAC_RecvPktDone();
    }
}

void
tapdev_init(void)
{
	// Select RMII interface by default
    EMAC_Open(ethaddr.addr);
    NVIC_EnableIRQ(EMAC_TX_IRQn);
    NVIC_EnableIRQ(EMAC_RX_IRQn);
    EMAC_ENABLE_RX();
    EMAC_ENABLE_TX();
}
/*---------------------------------------------------------------------------*/
unsigned int
tapdev_read(void)
{
	int len = 0;
	len = u32PktLen;
	u32PktLen = 0;
  	return len;
}
/*---------------------------------------------------------------------------*/
void
tapdev_send(void)
{
	EMAC_SendPkt(uip_buf, uip_len);
}
/*---------------------------------------------------------------------------*/
在NUC472中数据的发送和接收是由中断完成的,因此tapdev_read()函数只是返回u32PktLen即可,在接收中断中数据已经保存在uip_buf中了。数据发送只需要调用EMAC_SendPkt接口即可。clock-arch模块的编写也很简单只需要实现两个函数即可:clock_init()、clock_time(),这两个函数的声明是在uip的clock.h文件中,这样便于移植的接口一致性,保证接口不变,文件如下:

/*
 * Copyright (c) 2006, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This file is part of the uIP TCP/IP stack
 *
 * $Id: clock-arch.c,v 1.2 2006/06/12 08:00:31 adam Exp $
 */

/**
 * \file
 *         Implementation of architecture-specific clock functionality
 * \author
 *         Adam Dunkels 
 */

#include "clock-arch.h"
#include "uip-timer.h"
#include "NUC472_442.h"

volatile clock_time_t curTime = 0;
/*---------------------------------------------------------------------------*/
void TMR0_IRQHandler(void)
{
    curTime++;
    TIMER_ClearIntFlag(TIMER0);
}

void 
clock_init(void)
{
	/* Set the Timer by 2Hz & Start it */
    TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 2);/* 2Hz */
    TIMER_EnableInt(TIMER0);
    NVIC_EnableIRQ(TMR0_IRQn);
    TIMER_Start(TIMER0);
}

clock_time_t
clock_time(void)
{
  return curTime;
}
/*---------------------------------------------------------------------------*/
在该文件中我们需要实现的是提供一个时钟,这里使用定时器0来实现的,定时频率为2Hz,每次定时器溢出时curTime计数器增加,在clock_time()函数中返回该计数器,clock-arch.h文件实现如下:

#ifndef __CLOCK_ARCH_H__
#define __CLOCK_ARCH_H__

typedef int clock_time_t;
#define CLOCK_CONF_SECOND 2

#endif /* __CLOCK_ARCH_H__ */
由于我们定时器频率为2Hz,因此测量1S时间需要的时钟计数器变化量为2,即需要两次定时器溢出的时间才为1S,这里需要注意的是uip源码中提供了定时器的模块timer.c/h,跟NUC472库中的定时器模块文件名有冲突,因此这里把uip源码中的timer.c.h文件更改为了uip-timer.c/h,这也充分说明了新唐的库代码的名字起的太简单的,很容易跟第三方库代码的文件名冲突。uip-conf.h文件如下:

/**
 * \addtogroup uipopt
 * @{
 */

/**
 * \name Project-specific configuration options
 * @{
 *
 * uIP has a number of configuration options that can be overridden
 * for each project. These are kept in a project-specific uip-conf.h
 * file and all configuration names have the prefix UIP_CONF.
 */

/*
 * Copyright (c) 2006, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This file is part of the uIP TCP/IP stack
 *
 * $Id: uip-conf.h,v 1.6 2006/06/12 08:00:31 adam Exp $
 */

/**
 * \file
 *         An example uIP configuration file
 * \author
 *         Adam Dunkels 
 */

#ifndef __UIP_CONF_H__
#define __UIP_CONF_H__

#include 

/**
 * 8 bit datatype
 *
 * This typedef defines the 8-bit type used throughout uIP.
 *
 * \hideinitializer
 */
typedef uint8_t u8_t;

/**
 * 16 bit datatype
 *
 * This typedef defines the 16-bit type used throughout uIP.
 *
 * \hideinitializer
 */
typedef uint16_t u16_t;

/**
 * Statistics datatype
 *
 * This typedef defines the dataype used for keeping statistics in
 * uIP.
 *
 * \hideinitializer
 */
typedef unsigned short uip_stats_t;

/**
 * Maximum number of TCP connections.
 *
 * \hideinitializer
 */
#define UIP_CONF_MAX_CONNECTIONS 40

/**
 * Maximum number of listening TCP ports.
 *
 * \hideinitializer
 */
#define UIP_CONF_MAX_LISTENPORTS 40

/**
 * uIP buffer size.
 *
 * \hideinitializer
 */
#define UIP_CONF_BUFFER_SIZE     420

/**
 * CPU byte order.
 *
 * \hideinitializer
 */
#define UIP_CONF_BYTE_ORDER      LITTLE_ENDIAN

/**
 * Logging on or off
 *
 * \hideinitializer
 */
#define UIP_CONF_LOGGING         1

/**
 * UDP support on or off
 *
 * \hideinitializer
 */
#define UIP_CONF_UDP             0

/**
 * UDP checksums on or off
 *
 * \hideinitializer
 */
#define UIP_CONF_UDP_CHECKSUMS   1

/**
 * uIP statistics on or off
 *
 * \hideinitializer
 */
#define UIP_CONF_STATISTICS      1

/* Here we include the header file for the application(s) we use in
   our project. */
/*#include "smtp.h"*/
#include "hello-world.h"
/*#include "telnetd.h"*/
/*#include "webserver.h"*/
/*#include "dhcpc.h"*/
/*#include "resolv.h"*/
/*#include "webclient.h"*/

#endif /* __UIP_CONF_H__ */

/** @} */
/** @} */
跟unix中的uip-conf.h文件是一样的只是加载的应用头文件改为了hello-world模块,uip-conf.h文件是对uipopt.h文件的补充,之前在uip-0.9中这两个文件是没有分开的,是修改的uipopt.h,main.c文件编写同样比较简单,如下:

/******************************************************************************
 * @file     main.c
 * @version  V1.00
 * $Revision: 2 $
 * $Date: 14/11/25 10:00a $
 * @brief    A uIP httpd sample for NUC472 MCU.
 *
 * @note
 * Copyright (C) 2014 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include 
#include 
#include "NUC472_442.h"
#include "uip.h"
#include "uip_arp.h"
#include "tapdev.h"
#include "uip-timer.h"

#define PLL_CLOCK           84000000

#define BUF ((struct uip_eth_hdr *)&uip_buf[0])

// Our MAC address
struct uip_eth_addr ethaddr = {0x00, 0x00, 0x00, 0x59, 0x16, 0x88};

void SYS_Init(void)
{

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init System Clock                                                                                       */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Enable External XTAL (4~24 MHz) */
    CLK_EnableXtalRC(CLK_PWRCTL_HXTEN_Msk);

    /* Waiting for 12MHz clock ready */
    CLK_WaitClockReady( CLK_STATUS_HXTSTB_Msk);

    /* Switch HCLK clock source to HXT */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HXT,CLK_CLKDIV0_HCLK(1));

    /* Set PLL to power down mode and PLL_STB bit in CLKSTATUS register will be cleared by hardware.*/
    CLK->PLLCTL |= CLK_PLLCTL_PD_Msk;

    /* Set PLL frequency */
    CLK->PLLCTL = CLK_PLLCTL_84MHz_HXT;

    /* Waiting for clock ready */
    CLK_WaitClockReady(CLK_STATUS_PLLSTB_Msk);

    /* Switch HCLK clock source to PLL */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL,CLK_CLKDIV0_HCLK(1));

    /* Enable IP clock */
    CLK_EnableModuleClock(UART0_MODULE);
    CLK_EnableModuleClock(EMAC_MODULE);
    CLK_EnableModuleClock(TMR0_MODULE);

    /* Select IP clock source */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UARTSEL_HXT, CLK_CLKDIV0_UART(1));
    CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_HXT, 0);

    // Configure MDC clock rate to HCLK / (127 + 1) = 656 kHz if system is running at 84 MHz
    CLK_SetModuleClock(EMAC_MODULE, 0, CLK_CLKDIV3_EMAC(127));

    /* Update System Core Clock */
    /* User can use SystemCoreClockUpdate() to calculate SystemCoreClock. */
    SystemCoreClockUpdate();


    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Set GPG multi-function pins for UART0 RXD and TXD */
    SYS->GPG_MFPL = SYS_GPG_MFPL_PG1MFP_UART0_RXD | SYS_GPG_MFPL_PG2MFP_UART0_TXD ;
    // Configure RMII pins
    SYS->GPC_MFPL = SYS_GPC_MFPL_PC0MFP_EMAC_REFCLK |
                    SYS_GPC_MFPL_PC1MFP_EMAC_MII_RXERR |
                    SYS_GPC_MFPL_PC2MFP_EMAC_MII_RXDV |
                    SYS_GPC_MFPL_PC3MFP_EMAC_MII_RXD1 |
                    SYS_GPC_MFPL_PC4MFP_EMAC_MII_RXD0 |
                    SYS_GPC_MFPL_PC6MFP_EMAC_MII_TXD0 |
                    SYS_GPC_MFPL_PC7MFP_EMAC_MII_TXD1;


    SYS->GPC_MFPH = SYS_GPC_MFPH_PC8MFP_EMAC_MII_TXEN;
    // Enable high slew rate on all RMII pins
    PC->SLEWCTL |= 0x1DF;

    // Configure MDC, MDIO at PB14 & PB15
    SYS->GPB_MFPH = SYS_GPB_MFPH_PB14MFP_EMAC_MII_MDC | SYS_GPB_MFPH_PB15MFP_EMAC_MII_MDIO;

    /* Lock protected registers */
    SYS_LockReg();

}

// This sample application can response to ICMP ECHO packets (ping)
// IP address is configure with DHCP, but if a lease cannot be acquired, a static IP will be used.
int main(void)
{
	int i;
	uip_ipaddr_t ipaddr;
	struct timer periodic_timer, arp_timer;

	timer_set(&periodic_timer, CLOCK_SECOND / 2);
	timer_set(&arp_timer, CLOCK_SECOND * 10);

	SYS_Init();
	UART_Open(UART0, 115200);
	
	clock_init();
	tapdev_init();
	uip_init();

	uip_ipaddr(ipaddr, 192,168,2,105);
	uip_sethostaddr(ipaddr);
	uip_ipaddr(ipaddr, 192,168,2,1);
	uip_setdraddr(ipaddr);
	uip_ipaddr(ipaddr, 255,255,255,0);
	uip_setnetmask(ipaddr);
	uip_setethaddr(ethaddr);
	hello_world_init();
    printf("NUC472 uIP sample code start\n");

	while(1) 
	{
		uip_len = tapdev_read();
		if(uip_len > 0) 
		{
			if(BUF->type == htons(UIP_ETHTYPE_IP)) 
			{
				uip_arp_ipin();
				uip_input();
				/* If the above function invocation resulted in data that
				should be sent out on the network, the global variable
				uip_len is set to a value > 0. */
				if(uip_len > 0) 
				{
					uip_arp_out();
					tapdev_send();
				}
			} 
			else if(BUF->type == htons(UIP_ETHTYPE_ARP)) 
			{
				uip_arp_arpin();
				/* If the above function invocation resulted in data that
				should be sent out on the network, the global variable
				uip_len is set to a value > 0. */
				if(uip_len > 0) 
				{
					tapdev_send();
				}
			}
		} 
		else if(timer_expired(&periodic_timer)) 
		{
			timer_reset(&periodic_timer);
			for(i = 0; i < UIP_CONNS; i++) 
			{
				uip_periodic(i);
				/* If the above function invocation resulted in data that
				should be sent out on the network, the global variable
				uip_len is set to a value > 0. */
				if(uip_len > 0) 
				{
					uip_arp_out();
					tapdev_send();
				}
			}
		#if UIP_UDP
			for(i = 0; i < UIP_UDP_CONNS; i++) 
			{
				uip_udp_periodic(i);
				/* If the above function invocation resulted in data that
				should be sent out on the network, the global variable
				uip_len is set to a value > 0. */
				if(uip_len > 0) 
				{
					uip_arp_out();
					tapdev_send();
				}
			}
		#endif /* UIP_UDP */

			/* Call the ARP timer function every 10 seconds. */
			if(timer_expired(&arp_timer)) 
			{
				timer_reset(&arp_timer);
				uip_arp_timer();
			}
		}
	}
}

void
uip_log(char *m)
{
  printf("uIP log message: %s\n", m);
}

/*** (C) COPYRIGHT 2014 Nuvoton Technology Corp. ***/
main()函数的流程比较简单,可以参考uip库中doc目录下的example-mainloop-with-arp.c和example-mainloop-without-arp.c,这里使用的example-mainloop-with-arp.c文件,在uip参考文件中有提到应用程序的流程说明。uip是使用查询法来检测是否需要发送数据以及是否有数据进来,因此在mainloop中不停的执行tapdev_read(),进而检测uip_len变量值,不为0则表明收到了数据,之后便调用uip协议栈对数据进行处理,处理完成之后再次检测uip_len变量查看是否需要发送数据,如果需要发送数据则调用tapdev_send()函数。整个流程还是比较简单的,而uip协议栈对数据的处理则需要一个UIP_APPCALL宏来实现,对于不同的应用需要实现不同的UIP_APPCALL,本例中是加载的hello-wolrd模块,因此UIP_APPCALL定义在了hello-world.h文件中,如下:

/**
 * \addtogroup apps
 * @{
 */

/**
 * \defgroup helloworld Hello, world
 * @{
 *
 * A small example showing how to write applications with
 * \ref psock "protosockets".
 */

/**
 * \file
 *         Header file for an example of how to write uIP applications
 *         with protosockets.
 * \author
 *         Adam Dunkels 
 */

#ifndef __HELLO_WORLD_H__
#define __HELLO_WORLD_H__

/* Since this file will be included by uip.h, we cannot include uip.h
   here. But we might need to include uipopt.h if we need the u8_t and
   u16_t datatypes. */
#include "uipopt.h"

#include "psock.h"

/* Next, we define the uip_tcp_appstate_t datatype. This is the state
   of our application, and the memory required for this state is
   allocated together with each TCP connection. One application state
   for each TCP connection. */
typedef struct hello_world_state {
  struct psock p;
  char inputbuffer[10];
  char name[40];
} uip_tcp_appstate_t;

/* Finally we define the application function to be called by uIP. */
void hello_world_appcall(void);
#ifndef UIP_APPCALL
#define UIP_APPCALL hello_world_appcall
#endif /* UIP_APPCALL */

void hello_world_init(void);

#endif /* __HELLO_WORLD_H__ */
/** @} */
/** @} */
在uip协议栈中会调用UIP_APPCALL处理数据,hello-world.c文件比较简单,如下:

/**
 * \addtogroup helloworld
 * @{
 */

/**
 * \file
 *         An example of how to write uIP applications
 *         with protosockets.
 * \author
 *         Adam Dunkels 
 */

/*
 * This is a short example of how to write uIP applications using
 * protosockets.
 */

/*
 * We define the application state (struct hello_world_state) in the
 * hello-world.h file, so we need to include it here. We also include
 * uip.h (since this cannot be included in hello-world.h) and
 * , since we use the memcpy() function in the code.
 */
#include "hello-world.h"
#include "uip.h"
#include 

/*
 * Declaration of the protosocket function that handles the connection
 * (defined at the end of the code).
 */
static int handle_connection(struct hello_world_state *s);
/*---------------------------------------------------------------------------*/
/*
 * The initialization function. We must explicitly call this function
 * from the system initialization code, some time after uip_init() is
 * called.
 */
void
hello_world_init(void)
{
  /* We start to listen for connections on TCP port 1000. */
  uip_listen(HTONS(1000));
}
/*---------------------------------------------------------------------------*/
/*
 * In hello-world.h we have defined the UIP_APPCALL macro to
 * hello_world_appcall so that this funcion is uIP's application
 * function. This function is called whenever an uIP event occurs
 * (e.g. when a new connection is established, new data arrives, sent
 * data is acknowledged, data needs to be retransmitted, etc.).
 */
void
hello_world_appcall(void)
{
  /*
   * The uip_conn structure has a field called "appstate" that holds
   * the application state of the connection. We make a pointer to
   * this to access it easier.
   */
  struct hello_world_state *s = &(uip_conn->appstate);

  /*
   * If a new connection was just established, we should initialize
   * the protosocket in our applications' state structure.
   */
  if(uip_connected()) {
    PSOCK_INIT(&s->p, s->inputbuffer, sizeof(s->inputbuffer));
  }

  /*
   * Finally, we run the protosocket function that actually handles
   * the communication. We pass it a pointer to the application state
   * of the current connection.
   */
  handle_connection(s);
}
/*---------------------------------------------------------------------------*/
/*
 * This is the protosocket function that handles the communication. A
 * protosocket function must always return an int, but must never
 * explicitly return - all return statements are hidden in the PSOCK
 * macros.
 */
static int
handle_connection(struct hello_world_state *s)
{
  PSOCK_BEGIN(&s->p);

  PSOCK_SEND_STR(&s->p, "Hello. What is your name?\n");
  PSOCK_READTO(&s->p, '\n');
  strncpy(s->name, s->inputbuffer, sizeof(s->name));
  PSOCK_SEND_STR(&s->p, "Hello ");
  PSOCK_SEND_STR(&s->p, s->name);
  PSOCK_CLOSE(&s->p);
  
  PSOCK_END(&s->p);
}
/*---------------------------------------------------------------------------*/
功能比较简单就是检测是否有tcp连接到来,如果有连接到来则发送一条消息"Hello, What is your name?\n",之后便等待对方的回应,收到回应后输出"Hello " + "用户名"。编译代码并下载到目标板中运行,我们需要一个tcp调试工具来辅助我们查看运行结果,这里是使用的USR-TCP232-Test-V1.3软件,因为源代码中用户的回应必须要以‘\n’结束,而该工具刚好提供了一个自动发送附加位的选项,

uip1.0 学习笔记--NUC472_第4张图片
在该软件中设置完协议类型、服务器地址以及端口号之后,点击连接之后,便会收到服务器发来的提示信息,如下:

uip1.0 学习笔记--NUC472_第5张图片
在发送窗口发送数据之后便会再次收到服务器的响应信息,并断开连接,如下:

uip1.0 学习笔记--NUC472_第6张图片
此时需要注意的是用户的输入信息不能过长,因为在hello_world_state结构中inputbuffer只有10个字节,最后贴一张工程目录图,不是所有的uip源代码都参与了编译,有一些文件我们没有用到,如下:

uip1.0 学习笔记--NUC472_第7张图片
至此,NUC472上uip-1.0协议栈的移植就结束了,剩下的就是对该源码的接口的熟悉了,我们只需要对uip协议栈的接口熟悉就可以编写好代码了,至于其内部的实现有兴趣的话可以深入了解一下,而在uip-1.0源码中提供了一个protosock接口这个是值得一看的,protosock是立足于protothreads,这又是作者编写的另外一个有趣的开源项目,目前最新版本是pt-1.4。protothreads其实是使用了C语言switch语句的特性,

pt官网:http://dunkels.com/adam/pt/index.html

pt中用到的C语言switch语句特性可以在以下网址中查看:http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

这种用法很少有人这么用,我们在编写带操作系统的应用时,一个任务就是一个线程,而我们要求任务调度线程时有这么一个特性:线程切走后在恢复时必须从上次切走的地方继续运行,即记录上次的断点把运行状态入栈,下次进来时重新恢复现场。而pt很巧妙的利用switch语句以及Duff's Device实现了断点续运行,并且不需要压栈入栈,很适合在资源较少的平台上使用。推荐大家去官网上看下这两个项目,足可以大开眼界。当然在现在32位单片机的普及很少会遇到资源不足的情况了,但是了解这么一种编程观点还是很有帮助的。




你可能感兴趣的:(编程语言,单片机)