实现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.定义读写寄存器的宏
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,我们用不到,不用去设置
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初始化
2 {
3 PINSEL0 |= 3 << 18 ; // 设置中断管脚连接,P0.9设置为EINT3
4 OSTimeDly( 10 );
5 IO0DIR |= NET_RST; // set reset pin direction to output
6 }
4.中断初始化
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.复位网卡
2 {
3 IO0SET |= NET_RST;
4 OSTimeDly( 10 );
5 IO0CLR |= NET_RST;
6 OSTimeDly( 10 );
7 }
6.写MAC地址
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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的初始化了
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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被调用
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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,查询是否有新数据包接收,这个函数在接收中断处理函数中被调用
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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初始化
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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.接收中断处理函数
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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日