移植过程比较容易,网上一堆教程,本文主要将low_level_output和low_level_input针对Enc28j60做一些优化,减少数据拷贝。
网上比较多的应该是如下代码:
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *q = NULL;
uint16_t packet_len = 0;
//遍历pbuf链表
for(q = p; q != NULL; q = q->next)
{
memcpy(&lwip_txbuf[packet_len],q->payload,q->len);
packet_len += q->len;
if(packet_len > MAX_FRAMELEN || packet_len > p->tot_len)
{
LWIP_PLATFORM_DIAG(("PacketTx error! packet_len=%"U32_F",tot_len=%"U32_F"\n\t", packet_len, p->tot_len));
return ERR_BUF;
}
}
if(packet_len == p->tot_len)
{
Enc28j60_PacketTransfer(packet_len,lwip_txbuf);
return ERR_OK;
}
LWIP_PLATFORM_DIAG(("PacketTx error! packet_len=%"U32_F",tot_len=%"U32_F"\n\t", packet_len, p->tot_len));
return ERR_BUF;
}
static struct pbuf *
low_level_input(struct netif *netif)
{
struct pbuf *p = NULL;
struct pbuf *q = NULL;
uint16_t packet_len = 0;
uint16_t temp_len = 0;
packet_len = Enc28j60_PacketReceive(MAX_FRAMELEN,lwip_rxbuf);
if(packet_len == 0)
{
return NULL;
}
/* We allocate a pbuf chain of pbufs from the pool. */
p = pbuf_alloc(PBUF_RAW, packet_len, PBUF_POOL);
if(p != NULL)
{
for(q = p; q != NULL; q = q->next)
{
memcpy(q->payload, &lwip_rxbuf[temp_len],q->len);
temp_len += q->len;
if(temp_len >= packet_len) break;
}
}
else
{
LWIP_PLATFORM_DIAG(("PacketRx: pbuf_alloc fail ,len=%"U32_F"\n\t", packet_len));
return NULL;
}
return p;
}
从中可以看到有使用memcpy拷贝数据,大量数据通信时,会花费大量时间在拷贝上,为此做如下修改:
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
return pkt_send(p);
}
static struct pbuf *
low_level_input(struct netif *netif)
{
return pkt_recv();
}
err_t pkt_send(struct pbuf *pkt_buf)
{
while((Enc28j60_Read(ECON1) & ECON1_TXRTS)!=0);
Enc28j60_Write(EWRPTL, TXSTART_INIT&0xFF);
Enc28j60_Write(EWRPTH, TXSTART_INIT>>8);
Enc28j60_Write(ETXNDL, (TXSTART_INIT + pkt_buf->tot_len)&0xFF);
Enc28j60_Write(ETXNDH, (TXSTART_INIT + pkt_buf->tot_len)>>8);
Enc28j60_Write_Op(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
Packet_In(pkt_buf);
Enc28j60_Write_Op(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
if((Enc28j60_Read(EIR) & EIR_TXERIF))
{
Enc28j60_Set_Bank(ECON1);
Enc28j60_Write_Op(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
}
return ERR_OK;
}
static void Packet_In(struct pbuf *pkt_buf)
{
struct pbuf *q = NULL;
uint8_t *buf;
uint16_t len;
ENC28J60_CSL();
Enc28j60_ReadWrite(ENC28J60_WRITE_BUF_MEM);
for(q = pkt_buf; q != NULL; q = q->next)
{
len = q->len;
buf = q->payload;
while(len--)
{
Enc28j60_ReadWrite(*buf++);
}
}
ENC28J60_CSH();
}
struct pbuf *pkt_recv(void)
{
struct pbuf *pkt_buf = NULL;
uint16_t packet_len = 0;
if(Enc28j60_Read(EPKTCNT) ==0)
{
return NULL;
}
packet_len = get_pkt_length();
if(packet_len == 0)
{
return NULL;
}
/* We allocate a pbuf chain of pbufs from the pool. */
pkt_buf = pbuf_alloc(PBUF_RAW, packet_len, PBUF_POOL);
if(pkt_buf != NULL)
{
Packet_Out(pkt_buf);
}
Enc28j60_Write(ERXRDPTL, (NextPacketPtr));
Enc28j60_Write(ERXRDPTH, (NextPacketPtr)>>8);
Enc28j60_Write_Op(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
return pkt_buf;
}
static uint16_t get_pkt_length(void)
{
uint16_t pkt_len = 0;
uint16_t rx_status;
Enc28j60_Write(ERDPTL, (NextPacketPtr));
Enc28j60_Write(ERDPTH, (NextPacketPtr)>>8);
NextPacketPtr = Enc28j60_Read_Op(ENC28J60_READ_BUF_MEM, 0);
NextPacketPtr |= Enc28j60_Read_Op(ENC28J60_READ_BUF_MEM, 0)<<8;
pkt_len = Enc28j60_Read_Op(ENC28J60_READ_BUF_MEM, 0);
pkt_len |= Enc28j60_Read_Op(ENC28J60_READ_BUF_MEM, 0)<<8;
pkt_len -= 4;
rx_status = Enc28j60_Read_Op(ENC28J60_READ_BUF_MEM, 0);
rx_status |= Enc28j60_Read_Op(ENC28J60_READ_BUF_MEM, 0)<<8;
if(pkt_len > MAX_FRAMELEN-1)
{
pkt_len = MAX_FRAMELEN-1;
}
if((rx_status & 0x80)==0)
{
pkt_len = 0;
}
return pkt_len;
}
static void Packet_Out(struct pbuf *pkt_buf)
{
struct pbuf *q = NULL;
uint8_t *buf;
uint16_t len;
ENC28J60_CSL();
Enc28j60_ReadWrite(ENC28J60_READ_BUF_MEM);
for(q = pkt_buf; q != NULL; q = q->next)
{
len = q->len;
buf = q->payload;
while(len--)
{
*buf++ = Enc28j60_ReadWrite(0xFF);
}
}
ENC28J60_CSH();
}
以上操作可以避免memcpy的使用,除了做如上优化,还可以将SPI读写BUFF改为DMA读写。