小盒子的小盒

【原创】LWIP技术--->答网友问

0
阅读(3030)

最近这段时间我一直在忙着画一块PCB,由于要求的时间比较紧张,所以我一直没有更新博客。首先我要感谢 “冰火枫林” 这位网友的耐心等待,这次我将他的问题尽可能一一详细回答!

1.在ethernetif.c中缺少low_level_init函数,应该是board_eth_init函数在low_level_init()中被调用,还有就是ethernetif_input函数也是在low_level_init()被调用的吧。

low_level_init(struct netif *netif)
 {
  struct ethernetif *ethernetif = netif->state;
 
  /* set MAC hardware address length */
  netif->hwaddr_len = 6;
  /* set MAC hardware address */
  netif->hwaddr[0] = ;
  ...
  netif->hwaddr[5] = ;
  /* maximum transfer unit */
  netif->mtu = 1500;
 
  /* broadcast capability */
  netif->flags = NETIF_FLAG_BROADCAST;
 
  /* Do whatever else is needed to initialize interface. */
}
上面是函数在LWIP中的结构模式.board_eth_init应该放的位置是/* Do whatever else is needed to initialize interface. */  吧,作用是实际的网卡初始的完成.还有个问题就是在board_eth_init()中调用了SetMacID(),在static U8 SrcMacID[ETH_ALEN] = {0xBC,0x20,0x06,0x09,0x30,0x11};中的是MAC地址吧,是与low_level_init()中的 hwaddr[0]-hwaddr[5]相对应的吧.我的理解是除了在low_level_init中要
//* 设置MAC地址
 pstNetif->hwaddr[0] = 0xBC;
 pstNetif->hwaddr[1] = 0x20;
 pstNetif->hwaddr[2] = 0x06;
 pstNetif->hwaddr[3] = 0x09;
 pstNetif->hwaddr[4] = 0x30;
 pstNetif->hwaddr[5] = 0x11;
还要在low_level_init调用的board_eth_init中写MAC地址,以便通过
static void SetMacID()
{
 SetRegPage(1);
 outportb(SrcMacID[0], PAR0);
 outportb(SrcMacID[1], PAR1);
 outportb(SrcMacID[2], PAR2);
 outportb(SrcMacID[3], PAR3);
 outportb(SrcMacID[4], PAR4);
 outportb(SrcMacID[5], PAR5);
}
来设置8019物理MAC地址。不知道是不是这样理解呢?

答:board_eth_init()函数是应该在low_level_init()函数里面被调用的;

例如写法如下:

// MAC地址
#define  MAC_ADDR_0   0x00
#define  MAC_ADDR_1   0x50
#define  MAC_ADDR_2   0xba
#define  MAC_ADDR_3   0x33
#define  MAC_ADDR_4   0xbe
#define  MAC_ADDR_5   0x44

 

static void low_level_init(struct netif *netif)
{
  struct ethernetif *ethernetif;
 
  ethernetif = netif->state;
  netif->hwaddr_len = NETIF_MAX_HWADDR_LEN;
  netif->hwaddr[0] = MAC_ADDR_0;
  netif->hwaddr[1] = MAC_ADDR_1;
  netif->hwaddr[2] = MAC_ADDR_2;
  netif->hwaddr[3] = MAC_ADDR_3;
  netif->hwaddr[4] = MAC_ADDR_4;
  netif->hwaddr[5] = MAC_ADDR_5;
  netif->mtu = MTU;
  netif->flags = NETIF_FLAG_BROADCAST;
  board_eth_init();

}

第二个问题:对你你提到的ethernetif_input()函数,我希望你能作为一个任务 建立起来。这样最起码能达到实时的接收到数据。作为这样的想法前提是你的以太网采用查询的方式接收数据而不是中断的方式,若采用中断的方式,可以不用我这样的方法;

第三个问题:low_level_init()函数里面的MAC地址与static U8 SrcMacID[ETH_ALEN] = {0x00,0x50,0xba,0x33,0xbe,0x44}; 中的MAC地址是一样的。这样就设置了你系统的MAC地址了。

 

2.RTL8019_OP_16它代表什么呢?我的理解是根据下面来的
#ifdef RTL8019_OP_16
#define RPSTOP 0x80
#else
#define RPSTOP 0x60
#endif
也就是说ram的一部分用来存放接收的数据包,一部分用来存储待发送的数据包。还有一部分可以给用户使用。
在上面的意思就是定义RTL8019_OP_16后就是预留0x60-0x80给用户使用,是这样的吗?

答:RTL8019_OP_16 这个宏定义表示以太网芯片RTL8019 DMA内存的大小

例如程序里面这样写到

#ifdef RTL8019_OP_16
 outportb(0xc9, DCR);  // 16bit DMA
#else
 outportb(0xc8, DCR);  // 8bit DMA
#endif

说到这个DMA让我想起一个曾经面试我的人,那个人是搞软件的,他让我说DMA的工作方法,我说完以后,他在那直摇脑袋,一副感觉我说错的样子,我都给他讲到最底层了,设置寄存器了。他一个做软件的能想到我们硬件底层最直接的工作方式吗?我真是怀疑那个人的能力问题,希望以后能看到这篇文章的人,你要是没有那个知识的储备,希望你面试别人的时候,不要乱问,免得让面试的人很瞧不起你(这里有点个人的感情色彩,我就生气那个面试我的人,还是一个大的软件公司呢,咳!!!无言了)……好了言归正传。下一个问题。。。。。。

3.在RTL8019.C中有两个宏没定义.一个是ETH_ALEN,我觉得是少了#define ETH_ALEN 6,应该是大哥在其他头文件中定义了的吧。还有一个是ETH_FRAME_LEN,我还没看到那里,不知道应该如何定义。

答:你看的很仔细,这么一点点细节你都注意了;我在头文件里面定义的,定义值:

#define ETH_ALEN              6
#define ETH_FRAME_LEN  1514

4.还有个最关键的问题,low_level_init()函数我还可以自己搞定,但是我对 RTL8019IOInit()就搞不定了.RTL8019IOInit是初始哪些ARM与RTL8019连接的I/O口呢?怎么初始呢?求大哥给我看看你的初始呢.我刚学ARM,还有点苯,我的是RTL8019连接到S3C44B0X上的,该怎么弄呢?我在44blib.c中找到了
/************************* PORTS ****************************/
void Port_Init(void)
{    //CAUTION:Follow the configuration order for setting the ports.
    // 1) setting value
    // 2) setting control register
    // 3) configure pull-up resistor.
    //PORT A GROUP
    //GPA9 ADDR23 ADDR22 ADDR21 ADDR20 ADDR19 ADDR18 ADDR17 ADDR16 ADDR0       
    //  0,    1,     1,    1,     1,     1,     1,      1,     1,    1
    rPCONA=0x1ff;
    rPDATA=0x0;
    //PORT B GROUP
    //GPB10  GPB9 nGCS3 nGCS2 nGCS1 GPB5 GPB4 nSRAS nSCAS SCLK SCKE
    //  0,    0,    1, 1,    1,    0,    0, 1,    1,   1,   1
    rPCONB=0x1Cf;
   
    //PORT C GROUP
    //IISLRCK  IISD0 IISDI IISCLK VD7 VD6 VD5 VD4 nXDACK1 nXDREQ1 GPC10 GPC11 TXD1 RXD1 GPC14 GPC15
    //All input
    //  11      11    11    11    11  11   11  11   11      11     01 01     11   11   01    01
    //rPDATC=0x8400;
    //rPCONC=0x5F5FFFFF;
    //rPUPC=0x33ff; //should be enabled
    rPDATC=0xffff; //All I/O Is High
    rPCONC=0x0f05ff55;
    rPUPC=0x30f0; //PULL UP RESISTOR should be enabled to I/O
   
 
    //PORT D GROUP
    //VFRAME VM VLINE VCLK VD3 VD2 VD1 VD0
    //    10,10,   10, 10, 10, 10, 10, 10
    rPCOND=0xaaaa;
    rPUPD=0xff;
 //PORT E GROUP
 //PE0:FOUT, PE1:TxD0, PE2:RxD0, GPE3, GPE4, GPE5,GPE6,GPE7, CODECLK  
 //    10       10,      10,    01  , 01,   01,  01,  01,   10
 rPCONE=0x42B;//0x2552A;
 rPUPE=0x00; //0xff;
 rPDATE=0xdf;//0xff
 
 //PORT F GROUP
 //IICSCL IICSDA  nWAIT nXBACK0 nXDREQ0 GPF5 GPF6 GPF7 GPF8
 //  10     10      10    10      10     0    0    0    0
 rPCONF=0x9256A;//0x2A;
 rPUPF=0xff;
    //PORT G GROUP
    //EINT0 EINT1 EINT2 EINT3 GPG4 GPG5 GPG6 GPG7
    //   0x0
    //  11      11   11     11   01   01   01   01
    rPDATG=0x0;
    rPCONG=0x55FF;
    rPUPG=0xf;
   
    rSPUCR=0x7;  //pull-up disable
    rEXTINT=0x22222022;  //All EINT[7:0] will be falling edge triggered.
}
 
上面这个是初始ARM的端口,您所说的 RTL8019IOInit()是初始RTL8019的端口吧.还是说就是初始ARM的端口.用上面那个代替RTL8019IOInit()就可以了?

答:RTL8019IOInit() 是初始化ARM与RTL8019连接的端口,我的初始化函数给你,你也没有用,因为我们用的不是同一个处理器,这里需要设置ARM的端口 比如我设置的有,数据线的位宽,镜像地址,缓存大小,以及读写时序的时间控制。你完全根据你的硬件连接方式以及处理器的特性,来写你的初始化函数。

add1.在RTL8019.CZ中的low_level_input()函数中flag = board_eth_rcv(data,&len);其中len的定义是u16_t len;找到在RTL8019.h中的声明:int board_eth_rcv(unsigned char *data, unsigned int *len);len是unsigned int的。但是一般都是(我的就是)
typedef unsigned char  u8_t;
typedef char           s8_t;
typedef unsigned short u16_t;
typedef short          s16_t;
typedef unsigned int   u32_t;
typedef int            s32_t;
于是len的类型就不对应了,大哥的难道是
typedef unsigned int u16_t;如果是的话我就惨了,还请把其它的是如何定义的一同给我,我要改啊。或者说一下您的s16_t;u32_t;s32_t;诸如此类分别是代表什么类型啊。

答:对于你这个问题,当初的时候我遇到了。

后来为了以后的方便,我自己重新写了一个函数,将所有的与接收有关的 len 都定义了 unsigned int 型的了。其实按照理论上来讲的话,这个长度不会很大,一个以太网的包数据对多也就是1500,每次有数据的时候,无论是采用查询的方式或中断的方式,我想都超不过unsigned int范围的。你认为呢?

add2.在low_level_input中有p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
pbuf_alloc() 从pool中分配足够数量的pbufs用于保存到达的帧。。我查到pbufs pool是由固定大小的pbuf组成,每个pbuf的长度由协议栈提供的PBUF_POOL_BUFSIZE宏指定那我应该将opt.h中 PBUF_POOL_BUFSIZE设置为多少的呢?大哥您的是多少啊?还有个与此相关的问题:在RTL8019.CZ中的 low_level_input()有 unsigned char buffer[4096];为什么是4096呢?我认为的是EMAC数据手册规定接收缓冲区仅占128字节大小,128 * 32接收缓冲区共4096字节。那在opt.h中PBUF_POOL_BUFSIZE的设置是不是与那个没定义的宏ETH_FRAME_LEN有关啊。我会提这个问主要是对low_level_input函数的不理解。在函数中调用board_eth_rcv(data,&len);是为了什么作用啊。提如此概括的问题确实为难大哥了,而且您写这程序的时候都是06年了,希望您还能回忆起。大哥加油啊,真的很是辛苦您了。谢谢。

答:

在opt.h中

#ifndef PBUF_POOL_BUFSIZE
#define PBUF_POOL_BUFSIZE               128 //Modify by Small.Box at 07.03.09
#endif

这个需要根据你的实际内存大小来设定。

我的4096是完全为了给系统足够的缓冲区。免得数据溢出,系统瘫痪,这个数值也是和我们项目需求有点关系的。

low_level_input()这个函数是被ethernetif_input()函数调用的,而ethernetif_input()这个函数是作为一个任务建立起来的,这样每次在规定的时间里都去查询以太网是否有新数据。

例如:

   do{
    p = low_level_input(netif);
    if (p == NULL)
     OSTimeDlyHMSM(0,0,0,100);
   }while(p == NULL);

这里我会在100MS查询一次以太网,是否有新数据的到来。

 

对于以上我的回答,不知道你还有哪些不明白的地方,十分欢迎你提出来宝贵的问题,让我在重新接触一下LWIP,如果要是有什么不明白的地方,欢迎继续咨询!