SMARTARM2200 ADS工程在IAR EWARM 5.3上的移植(7)-LwIP 1.2的移植(RTL8019AS驱动)

实现LwIP与uCOSII的操作系统模拟层后,剩下重要的一部分就是网卡驱动了.SMARTARM2200用的网卡芯片是RTL8019AS.
RTL8019AS.有3种工作方式:
第一种为跳线方式,网卡的i/o和中断由跳线决定
第二种为即插即用方式,由软件进行自动配置plug and play
第三种为免跳线方式,网卡的i/o和中断由外接的93c46里的内容决定。

SMARTARM2200使用第一种方式即跳线方式.65脚JP为高电平时即为跳线模式.其基地址为300H,中断源为INT0(P0.9),操作地址为0x83400000~0x0x83400001F,该地址为CS3,A23,A22,A21通过ATF16LV8C译码而得.
更详细的信息可以参考我上传的工程http://download.csdn.net/source/1661278

由于很多涉及操作RTL8019AS寄存器,关于这方面更详细的内容请参考RTL8019AS的DataSheet.
RTL8019AS的驱动实现在2个文件里:
RTL8019.c:实现一些操作RTL8019AS的基本函数
ethernetif.c:实现LwIP与网卡接收发送的接口函数,主要是low_level_input和low_level_output以及中断处理函数
先讲RTL8019.c实现的一些函数:
1.定义读写寄存器的宏

1 //因为LPC2220的SAO~SA4与LPC2220的ADDRl~ADDR5相连,RTL8019AS工作在16位模式
2  #define RTL8019Addr (0x83400000>>1)
3  #define Write8019Reg(addr, dat) *((volatile unsigned char *)((RTL8019Addr+addr)<<1)) = dat
4  #define Read8019Reg(addr) *((volatile unsigned char *)((RTL8019Addr+addr)<<1))

2.页选择,RTL8019AS有4页寄存器,前3页与NE2000兼容,最后一页是自己的定义的,用来PNP,我们用不到,不用去设置

1 void page(unsigned char pagenumber)
2 {
3 unsigned char temp;
4
5 temp = Read8019Reg(0x00) & 0x3B;
6 //CR(00H)第6,7位PS1,PS0进行页选择
7 temp |= (pagenumber <<6);
8 Write8019Reg(0x00, temp);
9 }

3.GPIO初始化

1 void RTL8019AS_GPIOInit(void)
2 {
3 PINSEL0 |= 3<<18; //设置中断管脚连接,P0.9设置为EINT3
4   OSTimeDly(10);
5 IO0DIR |= NET_RST;//set reset pin direction to output
6  }

4.中断初始化

1 void RTL8019AS_InterruptInit(void)
2 {
3 //设置触发方式,上升沿触发
4 EXTMODE |= 0x08;
5 EXTPOLAR |= 0x08;
6 //EINT3清除中断状态
7 EXTINT = 1<<3;
8 //配置LPC2220中断向量,并使能
9 VICIntSelect &= ~(1 << VIC_EINT3); /* Configure the Ethernet VIC interrupt source for IRQ */
10 VICVectCntl14 = 0x20 | VIC_EINT3;
11 VICVectAddr14 = (CPU_INT32U)IRQ_Eint3; /* Set the vector address */
12 VICIntEnable = (1 << VIC_EINT3); /* Enable the VIC interrupt source, but no local sources */
13 }
14

5.复位网卡

1 static void RTL8019AS_Reset() //复位网卡
2  {
3 IO0SET |= NET_RST;
4 OSTimeDly(10);
5 IO0CLR |= NET_RST;
6 OSTimeDly(10);
7 }

6.写MAC地址

RTL8019AS_WriteMAC
1 static void RTL8019AS_WriteMAC(void)
2 {
3 page(1);
4 Write8019Reg(0x01, My_hwaddr[0]);
5 Write8019Reg(0x02, My_hwaddr[1]);
6 Write8019Reg(0x03, My_hwaddr[2]);
7 Write8019Reg(0x04, My_hwaddr[3]);
8 Write8019Reg(0x05, My_hwaddr[4]);
9 Write8019Reg(0x06, My_hwaddr[5]);
10 page(0);
11 }

7.准备好以上几个基本函数后,就可以进行RTL8019AS的初始化了

RTL8019AS_Init
1 void RTL8019AS_Init(void)
2 {
3 unsigned char i;
4
5 RTL8019AS_GPIOInit();
6 while(1)
7 {
8 //复位8019
9   RTL8019AS_Reset();
10 OSTimeDly(10);
11 //使芯片处于停止模式,这时进行寄存器设置 停止模式下,将不会发送和接收数据包
12   Write8019Reg(0x00, 0x21);
13 //延时10毫秒,确保芯片进入停止模式
14   OSTimeDly(10);
15 i = Read8019Reg(0x00);
16 //复位后,CR bit0(STP)应为1,芯片处于停止模式
17 if(i == 0x21)
18 break;
19 }
20
21 page(0);
22 Write8019Reg(0x0a, 0x00);//RBCR0
23 Write8019Reg(0x0b, 0x00);//RBCR1
24 Write8019Reg(0x0c, 0xe0);//RCR monitor mode (no packet receive)
25 Write8019Reg(0x0d, 0xe2);//loop back mode 使芯片处于mon和loopback模式,跟外部网络断开
26
27 Write8019Reg(0x01, 0x4c);//PSTART 设置起始页
28 Write8019Reg(0x02, 0x80);//PSTOP 设置停止页
29 Write8019Reg(0x03, 0x4c);//BNRY 设置读缓冲指针
30 Write8019Reg(0x04, 0x40);//TPSR 设置发送包起始页
31
32 Write8019Reg(0x07, 0xff);//ISR 清除所有中断标志位
33 Write8019Reg(0x0f, 0x11);//IMR 禁止OVW PRX中断
34
35 Write8019Reg(0x0e, 0xc8);//DCR 8位dma方式
36 page(1);
37 Write8019Reg(0x07, 0x4d);//CURR 设置接收包起始页
38 Write8019Reg(0x08, 0x00);//MAR0-7 多播地址(未使用设置为0)
39 Write8019Reg(0x09, 0x00);
40 Write8019Reg(0x0a, 0x00);
41 Write8019Reg(0x0b, 0x00);
42
43 Write8019Reg(0x0c, 0x00);
44 Write8019Reg(0x0d, 0x00);
45 Write8019Reg(0x0e, 0x00);
46 Write8019Reg(0x0f, 0x00);
47
48 Write8019Reg(0x00, 0x22);//CR 这时让芯片开始工作
49 RTL8019AS_WriteMAC();//将网卡MAC地址写入到mar寄存器
50 page(0);
51
52 Write8019Reg(0x0c, 0xcc);//RCR 设置接收模式
53 Write8019Reg(0x0d, 0xe0);//TCR 将网卡设置成正常模式
54 Write8019Reg(0x00, 0x22);//CR 这时让芯片开始工作
55 Write8019Reg(0x07, 0xff);//ISR 清除所有中断标志位
56 }

8.发送数据包send_frame,该函数在ethernetif.c中的low_level_output被调用

send_frame
1 void send_frame(unsigned char * outbuf, unsigned short len)
2 {
3 unsigned char i;
4 unsigned short j;
5 unsigned char temp;
6
7 page(0);
8 if(len<60)len=60;//长度最小为60字节
9 txd_buffer_select=!txd_buffer_select;//交换输出缓冲区
10 if(txd_buffer_select)
11 Write8019Reg(0x09, 0x40); //txdwrite highaddress将远程DMA地址分为两个,40-45,46-4b。前面用于发送包。
12 else
13 Write8019Reg(0x09, 0x46); //txdwrite highaddress后面用于构造端的包
14
15 Write8019Reg(0x08, 0x00);//RSAR 设置远程DMA起始地址 //read page address low
16 Write8019Reg(0x0b, (unsigned char)(len>>8)); //read count high
17 Write8019Reg(0x0a, (unsigned char)(len&0xff)); //read count low
18 Write8019Reg(0x00, 0x12); //write dma, page0从远端DMA写到8019RAM中数据
19 for(j=0; j<len; j++)
20 {
21 Write8019Reg(0x10, *(outbuf+j));
22 }
23
24
25 for(i=0;i<16;i++) //最多重发16次
26 {
27 for(j=0;j<1000;j++) //检查txp为是否为低
28 {
29 temp = Read8019Reg(0x00);
30 if((temp&0x04)==0) break;
31 }
32
33 temp = Read8019Reg(0x04);
34 if((temp&0x01)!=0) break; //表示发送成功,如果不成功,则执行reg00=3e,继续发送。
35
36 Write8019Reg(0x00, 0x3e);
37 }
38
39 Write8019Reg(0x07, 0xff); //清除所有中断标志位
40 if(txd_buffer_select)
41
42 Write8019Reg(0x04, 0x40); //txd packet start; //设置发送开始页的地址
43 else
44 Write8019Reg(0x04, 0x46); //txd packet start;
45 Write8019Reg(0x06, (unsigned char)(len>>8)); //high byte counter要发送的包的字节数(长度)
46 Write8019Reg(0x05, (unsigned char)(len&0xff)); //low byte counter
47
48 Write8019Reg(0x07, 0xff); //清除所有中断
49 Write8019Reg(0x00, 0x3e); //to sendpacket; 从本地DMA中发送包

50}

9.RTL8019AS_Query,查询是否有新数据包接收,这个函数在接收中断处理函数中被调用

RTL8019AS_Query
1 char RTL8019AS_Query(void)
2 {
3 unsigned char my_bnry=0;
4 unsigned char my_curr=0;
5
6 page(0);
7 my_bnry = Read8019Reg(0x03);//bnry page have read 读页指针
8 page(1);
9 my_curr = Read8019Reg(0x07);//curr writepoint 8019写页指针
10 page(0);
11 if ((my_curr==0)) return 0;
12 my_bnry++;
13 if (my_bnry>0x7f) my_bnry=0x4c;
14 if (my_bnry!=my_curr)//此时表示有新的数据包在缓冲区里
15 {
16 //complete dma page 0
17 //Remote DMA Write
18 Write8019Reg(0x0b, 0x00);
19 Write8019Reg(0x0a, 0x00);
20 Write8019Reg(0x00, 0x22);
21 return 1;
22 }
23 Write8019Reg(0x0b, 0x00);
24 Write8019Reg(0x0a, 0x00);
25 Write8019Reg(0x00, 0x22);
26 return 0;
27
28 }

上面的函数都实现在RTL8019.c,在ethernetif.c中实现了low_level_input和low_level_output,这两个函数是实现LwIP接收发送数据报的接口,这块驱动代码主要来源于网络,我做了部分修改,如output发送部分
10.low_level_input

low_level_input
1 static struct pbuf *
2 low_level_input(struct netif *netif)
3 {
4 struct pbuf *p, *q;
5 u16_t len,j;
6 unsigned char bnry,curr;
7 unsigned char temp,temp1;
8 unsigned char S_next_page;
9 unsigned long * ttmp;
10 /**********************读取数据长度*******************************/
11 page(0);
12 bnry = Read8019Reg(0x03);//bnry page have read 读页指针
13 page(1);
14 curr = Read8019Reg(0x07);//curr writepoint 8019写页指针
15 page(0);
16 //防止溢出
17 if((bnry<0x4c) || (bnry>0x7f)){
18 bnry = curr-1;
19 if(bnry<0x4c)
20 bnry = 0x7f;
21 }
22
23 if ((curr==0)) return 0; //读的过程出错
24 S_next_page=bnry;
25 bnry++;
26 if (bnry>0x7f) bnry=0x4c;
27 if (bnry!=curr) //此时表示有新的数据包在缓冲区里
28 {
29 //读取一包的前4个字节:4字节的8019头部,0:接收状态;1:下一包的指针;2:本包低位;3:本包高位;2,3组成1个字的帧长度
30 page(0);
31 //read page address high
32 Write8019Reg(0x09, bnry);
33 //read page address low
34 Write8019Reg(0x08, 0x00);
35 //read count high
36 Write8019Reg(0x0b, 0x00);
37 //read count low;
38 Write8019Reg(0x0a, 0x04);//读4字节
39 //read dma
40 Write8019Reg(0x00, 0x0a);
41
42 temp = Read8019Reg(0x10);//状态
43 temp1 = Read8019Reg(0x10);//下一帧的指针
44 //接收错误
45 if(temp1>0x7f||temp1<0x4c){
46 if(curr==0x4c){
47 curr = 0x7f;
48 }
49 else{
50 curr--;
51 }
52
53 page(0);
54 Write8019Reg(0x03, curr);
55 Write8019Reg(0x0b, 0x00); //complete dma page 0
56 Write8019Reg(0x0a, 0x00);
57 Write8019Reg(0x00, 0x22);
58 return 0;
59 }
60
61 S_next_page = temp1-1; //next page start-1 本次帧的末地址
62 //读取数据包长度
63 len = Read8019Reg(0x10);
64 temp = Read8019Reg(0x10);
65 len += temp<<8;
66 //大于1518或小于60放弃
67 if(len>1518||len<60){
68
69 if(curr==0x4c){
70 curr = 0x7f;
71 }
72 else{
73 curr--;
74 }
75
76 page(0);
77 Write8019Reg(0x03, curr);
78 Write8019Reg(0x0b, 0x00); //complete dma page 0
79 Write8019Reg(0x0a, 0x00);
80 Write8019Reg(0x00, 0x22);
81 return 0;
82 }
83
84 }
85 //没有数据接收
86 else{
87 Write8019Reg(0x0b, 0x00); //complete dma page 0
88 Write8019Reg(0x0a, 0x00);
89 Write8019Reg(0x00, 0x22);
90 return 0;
91 } //
92 /*************************读取数据长度****************************/
93 /* Obtain the size of the packet and put it into the "len"
94 variable. */
95
96 #if ETH_PAD_SIZE
97 len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
98 #endif
99
100 /* We allocate a pbuf chain of pbufs from the pool. */
101 p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
102
103 if (p != NULL) {
104
105 #if ETH_PAD_SIZE
106 pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
107 #endif
108
109 /* We iterate over the pbuf chain until we have read the entire
110 * packet into the pbuf. */
111 for(q = p; q != NULL; q = q->next) {
112 /* Read enough bytes to fill this pbuf in the chain. The
113 * available data in the pbuf is given by the q->len
114 * variable. */
115 //read data into(q->payload, q->len);
116 /*************************读取数据********************************************************************/
117 if (q->payload == NULL)
118 {
119 //out of RAM
120 //Tell 8019 to skip the frame
121 page(1); //page1
122 curr = Read8019Reg(0x07);
123 page(0); //切换回page0
124 bnry = curr -1;
125 if(bnry < 0x4c) bnry =0x7f; //write to bnry
126 Write8019Reg(0x03, bnry);
127 Write8019Reg(0x07, 0xff); //清除中断状态可以不用
128 return 0;
129 }
130 //This flag keeps track of allocated rcve memory
131 //rcve_buf_allocated = TRUE;
132 //Call the assembler function to get the incoming frame
133
134 Write8019Reg(0x09, bnry); //read page address high
135 Write8019Reg(0x08, 0x04); //read page address low
136 Write8019Reg(0x0b, (unsigned char)(q->len>>8)); //read count high
137 Write8019Reg(0x0a, (unsigned char)(q->len&0xff)); //read count low
138 Write8019Reg(0x00, 0x0a); //read dma
139
140 ttmp = q->payload;
141 for(j=0;j<q->len;j++)
142 {
143 *((unsigned char *)((unsigned long)ttmp+j)) = Read8019Reg(0x10);
144 }
145
146 //dma complete page0
147 Write8019Reg(0x0b, 0x00);
148 Write8019Reg(0x0a, 0x00);
149 Write8019Reg(0x00, 0x22);
150 //Return pointer to start of buffer
151 bnry=S_next_page;
152 if (bnry<0x4c) bnry=0x7f; //write to bnry 写入新的bnry
153 Write8019Reg(0x03, bnry);
154 Write8019Reg(0x07, 0xff);
155 /**********************读取数据*************************************************************************/
156 }
157 //acknowledge that packet has been read();
158
159 #if ETH_PAD_SIZE
160 pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
161 #endif
162
163 #if LINK_STATS
164 lwip_stats.link.recv++;
165 #endif /* LINK_STATS */
166 }
167 else {
168 /**********drop packet();*********************************************************/
169 Write8019Reg(0x0b, 0x00);
170 Write8019Reg(0x0a, 0x00);
171 Write8019Reg(0x00, 0x22);
172 bnry=S_next_page;
173 if (bnry<0x4c) bnry=0x7f; //write to bnry
174 Write8019Reg(0x03, bnry);
175 Write8019Reg(0x07, 0xff);
176 /**********drop packet();*********************************************************/
177
178 #if LINK_STATS
179 lwip_stats.link.memerr++;
180 lwip_stats.link.drop++;
181 #endif /* LINK_STATS */
182 }
183
184 return p;
185 }

11.low_level_output

low_level_output
1 static err_t
2 low_level_output(struct netif *netif, struct pbuf *p)
3 {
4 struct pbuf *q;
5 u8_t* p1=RTL8019AS_ETH_FRAME;
6 u16_t length=0;
7
8 #if ETH_PAD_SIZE
9 pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
10 #endif
11
12 for(q = p; q != NULL; q = q->next) {
13 /* Send the data from the pbuf to the interface, one pbuf at a
14 time. The size of the data in each pbuf is kept in the ->len
15 variable. */
16 //merge packets, or UDP frame is sent seperately
17 //原来在这里来直接调用send_frame(q->payload, q->len)
18 //结果发现进行UDP包发送,一个包会被两次或多次(q的个数次)发送
19 //从而上位机程序无法正确接收,因此采用先合并成一个数据包再发送的方法
20 if(length<RTL8019AS_TX_BUF_SIZE)
21 memcpy(p1,q->payload,q->len);
22 p1+=q->len;
23 length+=q->len;
24
25 }
26 send_frame(RTL8019AS_ETH_FRAME, length);
27 //signal that packet should be sent();
28
29 #if ETH_PAD_SIZE
30 pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
31 #endif
32
33 #if LINK_STATS
34 lwip_stats.link.xmit++;
35 #endif /* LINK_STATS */
36
37 return ERR_OK;
38 }

12.low_level_init,添加RTL8019AS初始化

low_level_init
1 static void
2 low_level_init(struct netif *netif)
3 {
4 //struct ethernetif *ethernetif = netif->state;
5 /* set MAC hardware address length */
6 netif->hwaddr_len = 6;
7
8 /* set MAC hardware address */
9 netif->hwaddr[0] = My_hwaddr[0];
10 netif->hwaddr[1] = My_hwaddr[1];
11 netif->hwaddr[2] = My_hwaddr[2];
12 netif->hwaddr[3] = My_hwaddr[3];
13 netif->hwaddr[4] = My_hwaddr[4];
14 netif->hwaddr[5] = My_hwaddr[5];
15
16 /* maximum transfer unit */
17 netif->mtu = 1500;
18
19 /* broadcast capability */
20 netif->flags = NETIF_FLAG_BROADCAST;
21 netif->flags |= NETIF_FLAG_UP;
22 /* Do whatever else is needed to initialize interface. */
23 RTL8019AS_InterruptInit();
24 RTL8019AS_Init();
25 }

13.接收中断处理函数

IRQ_Eint3
1 /*接收中断处理函数,它主要处理当一个接收完成后,进行包的解析和通知上层协议进行包的处理*/
2 void IRQ_Eint3(void)
3 {
4 unsigned char RegOfISR;
5 unsigned char temp;
6 unsigned char curr;
7
8 page(0);
9 Write8019Reg(0x0f, 0x00);
10 /*停止接收*/
11 temp = Read8019Reg(0x00);
12 Write8019Reg(0x00, (temp&0xFC)|RTL_REV_STP|RTL_DMA_OVR);
13 //读中断状态
14 RegOfISR = Read8019Reg(0x07);
15 //OVW 接收缓冲溢出
16 if((RegOfISR) & 0x10){
17 Write8019Reg(0x07, 0x10);//清除中断状态
18 }
19 //RXE 接收错误
20 if((RegOfISR) & 0x04){
21 Write8019Reg(0x07, 0x04);
22 //写指针返回之前正确数据位置
23 page(1);
24 curr = Read8019Reg(0x07);
25
26 if(curr==0x4c){
27 curr = 0x7f;
28 }
29 else{
30 curr--;
31 }
32
33 page(0);
34 Write8019Reg(0x03, curr);
35 }
36 //PRX 接收正确
37 if((RegOfISR) & 0x01){
38 Write8019Reg(0x07, 0x01);
39 //接收数据
40 while(RTL8019AS_Query())
41 {
42 ethernetif_input(MyNetif);
43 }
44 }
45 //发送数据
46 if((RegOfISR) & 0x02){
47 Write8019Reg(0x07, 0x02);
48 }
49 //复位
50 if((RegOfISR) & 0x80){
51 Write8019Reg(0x07, 0xff);
52 }
53
54 page(0);
55 Write8019Reg(0x07, 0xff);//清除中断状态
56 Write8019Reg(0x0f, 0x11);//使能中断
57
58 EXTINT = 1<<3;
59 VICVectAddr = 0x00;
60 }

移植完成,ping测试在2ms左右

 

 发表于 @ 2009年09月09日

posted on 2010-03-23 10:26  shevsten  阅读(905)  评论(0编辑  收藏  举报