STM32移植uIP

文件说明:



  • 这是uIP1.0源码文件,apps是作者写好的应用程序demo,doc是一些文档,lib里面只有一个文件是内存申请与释放函数的接口,uip是tcp/ip的协议栈了,unix是与外部的接口,我们移植也主要去改这些文件。

  • 说一下一眼看不出作用的文件。lc.h、lc-addrlabels.h、lc-switch.h、psock.c、psock.h和pt.h这些是一个模拟线程的轻量级库protothreads。uip_arp.c地址解析协议的接口。uip-fw.c是多重网络转发数据包的借口(没用到)。uiplib.c里只有一个函数uiplib_ipaddrconv:把ip地址转换成字符格式(没用到)。uip-neighbor.c可能是存储网络上一下ip与mac(网络邻居?没用到)。uip-split.c拆包发送的借口,tcp/ip协议规定包的长度不能超过1500个字节,否则需要拆包发送(没用到)。

  • clock-arch.c里面只有一个函数clock_time。main函数自己重写吧。tapdev.c是与硬件网络模块的接口。uip-conf.h是一些配置。
  • 为什么我觉得uip比RL_TCPnet移植更麻烦?就是因为uip的文件关系有点乱,而且一些数据类型需要重定义,甚至还需要改动库文件里的,可能RL_TCPnet是因为Keil公司专门写了用于M3内核的库才没有这么些问题吧。下面是简单的头文件包含关系,在移植的时候需要注意。



移植:

  • 因为只做udp、tcp(客户端与服务器)和http,所以添加这些文件就够了。工程结构如下图。uip_user.c是自己写的应用函数。

  • 我用的网络模块是enc28j60。网络上有很多该模块的驱动,只要改一下spi管脚配置就行。
  • tapdev.c就是uip与硬件的接口函数了。具体见下。
void
tapdev_init(unsigned char *my_mac)
{
     int i;
     /*初始化 enc28j60*/
     enc28j60Init(my_mac);
     for (i = 0; i < 6; i++)
     {
          uip_ethaddr.addr[i] = my_mac[i];
     }                          

}
/*---------------------------------------------------------------------------*/
/****************************************************************************
* 名    称:uint16_t tapdev_read(void)
* 功    能:                                                                                         
* 入口参数:读取一包数据
* 出口参数: 如果一个数据包收到返回数据包长度,以字节为单位,否则为零。
* 说    明:
* 调用方法:
****************************************************************************/
unsigned int tapdev_read(void)
{    
  return  enc28j60PacketReceive(1500,uip_buf);
}
/****************************************************************************
* 名    称:void tapdev_send(void)
* 功    能:                                                                                         
* 入口参数:发送一包数据
* 出口参数:
* 说    明:
* 调用方法:
****************************************************************************/
void tapdev_send(void)
{
  enc28j60PacketSend(uip_len,uip_buf);
}


  • uip-conf.h是配置文件,关闭日志输出:#define UIP_CONF_LOGGING         0。打开udp连接:#define UIP_CONF_UDP             1。只留下#include webserver.h(里面有个http回调函数,不过最好在之前自己写回调函数,因为还要做tcp的其他连接)。ps.实际的配置文件是uipopth.h,不过那里面的一般不用改。
/* 定义应用程序回调函数 */
#ifndef UIP_APPCALL
#define UIP_APPCALL tcp_appcall
#endif
#define UIP_UDP_APPCALL udp_appcall
//uip-tcp的总回调函数
void tcp_appcall(void)
{
    if(uip_conn->lport == HTONS(80))
    {
        httpd_appcall();//指向了http.c的httpd_appcall()回调函数。
    }
    if(uip_conn->lport == HTONS(LocalPort))
    {
        tcp_server_appcall();
    }

    if(uip_conn->rport == HTONS(RemotePort))
    {
        tcp_client_appcall();
    }
}


  • clock-arch.h中只有一个函数clock_time()。uip这个协议栈需要时间轮询函数,在clock-arch.h中定义#define CLOCK_CONF_SECOND 100,也就是100毫秒。由于我是用的ucos-ii,所以就写在系统时间钩子函数(在os_cpu_c.c里),如果没有用的话,也只需要另开一个定时器就可以了。
volatile INT32U uIP_time = 0;
#if (OS_CPU_HOOKS_EN > 0) && (OS_TIME_TICK_HOOK_EN > 0)
void  OSTimeTickHook (void)
{
#if OS_APP_HOOKS_EN > 0
    App_TimeTickHook();
#endif
     static INT8U s_count = 0;
     if (++s_count >= 10)
     {
          s_count = 0;
          uIP_time++;     /* 全局运行时间每10ms增1 */    
          if (uIP_time == 0x80000000)
          {
               uIP_time = 0;
          }
     }
#if OS_TMR_EN > 0
    OSTmrCtr++;
    if (OSTmrCtr >= (OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC)) {
        OSTmrCtr = 0;
        OSTmrSignal();
    }
#endif
}
#endif


  • uip_user.c是自己写的一些应用函数
extern u16 LocalPort;               //本地端口
extern u16 RemotePort;               //远端端口
extern u8 RemoteIP[4];               //远端ip
extern u8 enc_send_buf[256];     //发送数据最大字节256
extern u8 enc_send_size;       //需要发送的字节数
void Net_Init(void);             //网络初始化
void Udp_Init(void);             //udp初始化
void Tcp_Init(void);             //tcp初始化
void tcp_appcall(void);             //tcp总回调函数
void udp_appcall(void);             //udp总回调函数
void Uip_Proc(void);             //uip的处理函数(需要循环调用)
void tcp_server_appcall(void); //tcp作为服务器时的回调函数
void tcp_client_appcall(void); //tcp作为客户端时的回调函数
void tcp_newdata(void);             //tcp接收函数
void tcp_senddata(void);        //tcp发送函数
void udp_newdata(void);             //udp接收函数
void udp_senddata(void);        //udp发送函数


  • 到此,uip的移植基本上差不多了。只需要再修改一些小错误:比如,数据类型的重定义,删除一些打印函数,tcp_udp_appstate_t(用户自定义类型)的重定义在http.c里面有了,如果不需要用到作者的一些应用函数可以直接定义成int,同样uip_udp_appstate_t(用户自定义类型)也可以直接定义为int,当然也可以定义成数组,把数据的指针和长度放里面。
  • pt.h中115行中把PT_YIELD_FLAG定义为volatile可变的,可以减少好的警告信息。
  • psock.c中164行,添加一行uip_flags &= ~UIP_ACKDATA。还有一个udp连接bug在uip.c里,具体可见奋斗板。
static char
data_acked(register struct psock *s)
{
  if(s->state == STATE_DATA_SENT && uip_acked()) {
    uip_flags &= ~UIP_ACKDATA;
    if(s->sendlen > uip_mss()) {
      s->sendlen -= uip_mss();
      s->sendptr += uip_mss();
    } else {
      s->sendptr += s->sendlen;
      s->sendlen = 0;
    }
    s->state = STATE_ACKED;
    return 1;
  }
  return 0;
}

应用:


  • uip初始化要设置网络基本信息和轮询时间。
void Net_Init(void)
{
    uip_ipaddr_t ipaddr;
    unsigned char mymac[6] = {0x04, 0x02, 0x35, 0x00, 0x00, 0x01}; //MAC地址
    tapdev_init(mymac);                               //ENC28J60初始化
    uip_init();                                        //UIP协议栈初始化
    uip_ipaddr(ipaddr, 172, 72, 100, 212);          //设置IP地址
    uip_sethostaddr(ipaddr);
    uip_ipaddr(ipaddr, 172, 72, 101, 1);          //设置默认路由器IP地址
    uip_setdraddr(ipaddr);
    uip_ipaddr(ipaddr, 255, 255, 252, 0);          //设置网络掩码
    uip_setnetmask(ipaddr);
    timer_set(&periodic_timer, CLOCK_SECOND / 2);
    timer_set(&arp_timer, CLOCK_SECOND * 10);     //设置轮询时间

}
  • udp的初始化连接注意一下绑定本地ip。tcp服务器端只需要监听某个端口(80是http服务端口)。tcp客户端需要建立连接。
//建立UDP连接初始化*****************************
void Udp_Init(void)
{

    uip_ipaddr_t ipaddr;//定义IP类型变量
    uip_ipaddr(ipaddr, RemoteIP[0], RemoteIP[1], RemoteIP[2], RemoteIP[3]); //远端IP
    if(my_udp_conn != NULL)
    {
        uip_udp_remove(my_udp_conn);//如果连接已经建立,则删除之
    }

    my_udp_conn = uip_udp_new(&ipaddr, HTONS(RemotePort));//建立到远程端口
    if(my_udp_conn != NULL)
    {
        uip_udp_bind(my_udp_conn, HTONS(LocalPort));//绑定本地端口
    }

}
//作为tcp客户端初始化
void Tcp_Init(void)
{
    uip_ipaddr_t ipaddr;//定义IP类型变量
    uip_ipaddr(ipaddr, RemoteIP[0], RemoteIP[1], RemoteIP[2], RemoteIP[3]); //远程IP
    my_tcp_conn = uip_connect(&ipaddr, HTONS(RemotePort));//建立到远程端口
}
  • 还有一个问题是:作为tcp客户端的时候,pc机使用网络调试助手,如果在连接后再点断开的话就重连不上。 PC机断开连接后,那个端口先是timeout然后应该就删除了,断开后会进入2次uip_closed(),所以判断一下重新连接(会分配新的端口)。如果在程序运行开始但是PC机并没有打开连接的话,会一直进入uip_aborted(),在里面设置重新连接,这样会直到PC机打开。
    if(uip_closed())
    {
        tcp_server_flag++;
        if(tcp_server_flag == 2)
        {
            uip_ipaddr_t ipaddr;//定义IP类型变量
            uip_ipaddr(ipaddr, RemoteIP[0], RemoteIP[1], RemoteIP[2], RemoteIP[3]); //远程IP
            my_tcp_conn = uip_connect(&ipaddr, HTONS(RemotePort));//建立到远程端口
            tcp_server_flag = 0;
        }
    }
    if( uip_aborted())
    {
        uip_ipaddr_t ipaddr;//定义IP类型变量
        uip_ipaddr(ipaddr, RemoteIP[0], RemoteIP[1], RemoteIP[2], RemoteIP[3]); //远程IP
        my_tcp_conn = uip_connect(&ipaddr, HTONS(RemotePort));     //建立到远程端口
     }
  • 有关http的应用请见下一篇。ps.uip1.0的smtp好像不完整,请参照0.9版。

下载:


  • uip1.0英文使用指南下载,请点击
  • stm32移植uip1.0工程源码下载,请点击。如果使用HG 只要clone https://zouw96@bitbucket.org/zouw96/uip_http


  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值