pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif
LINK_STATS_INC(link.recv); } else {
//drop packet();
LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); }
return p; }
1.5 low_level_output函数
该函数的功能是将pbuf中的数据帧通过底层发送函数ENC28J60_Packet_Send发送出去,由于要发送的数据可能被分割成多个pbuf,而这些pbuf通过pbuf->next指针连接起来,因此low_level_output函数需要使用foe循环将这些pbuf中的数据拷贝至当前的发送缓冲区中。源代码如下: static err_t
low_level_output(struct netif *netif, struct pbuf *p) {
// struct ethernetif *ethernetif = netif->state; struct pbuf *q; int send_len=0;
// initiate transfer(); #if ETH_PAD_SIZE
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ #endif
for(q = p; q != NULL; q = q->next) {
/* Send the data from the pbuf to the interface, one pbuf at a time. The size of the data in each pbuf is kept in the ->len variable. */
//send data from(q->payload, q->len); memcpy((u8_t*)&lwip_buf[send_len], (u8_t*)q->payload, q->len); send_len +=q->len; }
// signal that packet should be sent();
ENC28J60_Packet_Send(send_len,lwip_buf); #if ETH_PAD_SIZE
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif
LINK_STATS_INC(link.xmit); return ERR_OK; }
2、 网卡驱动
本次用的是ENC28J60网卡模块。ENC28J60 是带有行业标准串行外设接口
( Serial Peripheral Interface,SPI)的独立以太网控制器。它可作为任何配备有 SPI 的控制器的以太网接口。ENC28J60 符合IEEE 802.3的全部规范,采用了一系列包过滤机制以对传入数据包进行限制。它还提供了一个内部 DMA 模块,以实现快速数据吞吐和硬件支持的IP校验和计算。与主控制器的通信通过两个中断引脚和SPI 实现,数据传输速率高达10Mb/s。
ENC28J60由七个主要功能模块组成:
1、 SPI接口——充当主控制器和ENC28J60之间通信通道。 2、 控制寄存器|——用于控制和监视ENC28J60. 3、 双端口RAM缓冲器——用于接收和发送数据包。 4、 判优器——当DMA、发送和接收模块发出请求是对RAM缓冲器的访问进行控制。 5、 总线接口——对通过SPI接收的数据和命令进行解析。
6、 MAC(Medium Access control)模块——实现符合IEEE802.3标准的MAC逻
辑。
7、 PHY(物理层)模块——对双绞线上的模拟数据进行编码和译码。该期间还包
括其他支持模块,诸如振荡器、片内稳压器、电平变换器(提供可以接受5V电压的I/O引脚)和系统控制逻辑。
enc28j60.c:ENC28J60(以太网芯片)SPI的接口应用函数库。
ENC28J60_Reset(void)这个函数里SPI的硬件初始化、设置SPI的时钟SCK的频率和复位ENC28J60.
ENC28J60_Read_Op(u8 op,u8 addr)读取ENC28J60寄存器(带操作码)op:操作码addr:寄存器地址/参数返回值:读到的数据。
ENC28J60_Write_Op(u8 op,u8 addr,u8 data)读取ENC28J60寄存器(带操作码)op:操作码addr:寄存器地址data:参数 返回值 :无返回值(函数为Void类型的)。
ENC28J60_Read_Buf(u32 len,u8* data)读取ENC28J60接收缓存数据len:要读取的数据长度data:输出数据缓存区(末尾自动添加结束符) 返回值 :无返回值(函数为Void类型的)。
ENC28J60_Write_Buf(u32 len,u8* data)向ENC28J60写发送缓存数据len:要写入的数据长度data:数据缓存区 返回值 :无返回值(函数为Void类型的)。
ENC28J60_Set_Bank(u8 bank)设置ENC28J60寄存器Bank
bank:要设置的bank 返回值 :无返回值(函数为Void类型的)。
ENC28J60_Read(u8 addr)读取ENC28J60指定寄存器 addr:寄存器地址返回值:读到的数据
ENC28J60_Write(u8 addr,u8 data)向ENC28J60指定寄存器写数据 addr:寄存器地址data:要写入的数据 返回值 :无返回值(函数为Void类型的)。
ENC28J60_PHY_Write(u8 addr,u32 data)向ENC28J60的PHY寄存器写入数据addr:寄存器地址data:要写入的数据 返回值 :无返回值(函数为Void类型的)。
ENC28J60_Init(u8* macaddr)初始化ENC28J60
macaddr:MAC地址返回值:0,初始化成功;1,初始化失败;
ENC28J60_Get_EREVID(void)在EREVID 内也存储了版本信息。 EREVID 是一个只读控制寄存器,包含一个5 位标识符,用来标识器件特定硅片的版本号
ENC28J60_Packet_Send(u32 len,u8* packet)通过ENC28J60发送数据包到网络len:数据包大小packet:数据包 返回值 :无返回值(函数为Void类型的)。
ENC28J60_Packet_Receive(u32 maxlen,u8* packet)从网络获取一个数据包内容maxlen:数据包最大允许接收长度
packet:数据包缓存区 返回值:收到的数据包长度(字节)
基于FreeRTOS的LWIP协议栈移植
在FreeRTOS操作系统下的LWIP任务模型:
1 操作系统模拟层文件sys_arch.c的移植
在LWIP中,操作系统模拟层是LWIP协议栈的一部分,它存在的目的是方便将LWIP移植到各种不同的操作系统上,它为操作系统和LWIP协议栈之间提供一个接口桥梁,当用户移植LWIP到一个新的操作系统的时候,只需要修改操作系统模拟层内的各函数即可。Sys_arch.txt文件给出了详细说明。总的来说,操作系统模拟层主要完成了与信号量、消息邮箱机制、线程相关的功能。
看如下三个类型定义:
typedef xSemaphoreHandle sys_sem_t;//在LWIP中信号量使用这个类型定义 typedef xQueueHandle sys_mbox_t;//在LWIP中队列消息使用这个类型定义 typedef xTaskHandle sys_thread_t;//在LWIP中线程使用这个类型定义 1. sys_mbox_new函数
该函数的功能是使用FreeRTOS提供的消息队列机制创建一个空的消息队列。在FreeRTOS中,消息队列创建函数是xQueueCreate。创建的邮箱大小由sys_arch.h中的宏定义archMESG_QUEUE_LENGTH实现。具体代码如下:
err_t sys_mbox_new(sys_mbox_t *mbox, int size) {
(void ) size;
*mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) ); #if SYS_STATS
++lwip_stats.sys.mbox.used;
if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) {
lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; }
#endif /* SYS_STATS */ if (*mbox == NULL) return ERR_MEM; return ERR_OK; } 2. sys_mbox_free函数
该函数功能与sys_mbox_new相反,它用于删除一个队列。该队列中还有未被取出的消息时,该函数应当报错,并通知应用程序。代码如下:
void sys_mbox_free(sys_mbox_t *mbox)
{
if( uxQueueMessagesWaiting( *mbox ) ) { /* Line for breakpoint. Should never break here! */ portNOP(); #if SYS_STATS
lwip_stats.sys.mbox.err++; #endif /* SYS_STATS */ // TODO notify the user of failure. }
vQueueDelete( *mbox ); #if SYS_STATS
--lwip_stats.sys.mbox.used; #endif /* SYS_STATS */ }
3. sys_mbox_post函数
该函数用于将消息发送至消息队列中。该函数是一个阻塞函数。当消息被发送至队列后,该函数才退出阻塞状态。代码如下:
void sys_mbox_post(sys_mbox_t *mbox, void *data) {
while ( xQueueSendToBack(*mbox, &data, portMAX_DELAY ) != pdTRUE ){} }
4. sys_mbox_trypost函数
该函数用于尝试将某个消息发送至消息队列中,当消息被成功投递后,则返回成功,否则返回失败。代码如下:
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) {
err_t result;
if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS ) {
result = ERR_OK; } else {
// could not post, queue must be full result = ERR_MEM; #if SYS_STATS
lwip_stats.sys.mbox.err++; #endif /* SYS_STATS */ }
return result; }
5. sys_arch_mbox_fetch函数