首先,为什么要做无OS的LWIP呢?原因很简单,因为stm32f103c8t6只有20K的RAM,很难支持OS+LWIP。
无OS的LWIP的一个优点是,可以提高单片机的以太网的响应速度。
LWIP的介绍就不说了,网上查一大把。
我的无OS移植LWIP基于ST官网的一个示例:
STSW-STM32026 | LwIP TCP/IP stack demonstration for STM32F107xx (AN3102) |
大家可以到官网下载。
这个示例是运行在stm32f107上面的,如果要运行到stm32f103上,还需要进行一些修改。下面是移植的主要步骤。
1. 从ST官网项目中的lwip-1.3.1目录整个复制到你的项目中
2. 修改文件lwip-1.3.1\port\ethernetif.c,我的网卡是enc28j60,主要修改如下
2.1 添加
uint8_t mac[6] = {0x00, 0xe0, 0x3d, 0xf4, 0xdd, 0xf7}; //网卡的mac地址
uint8_t Tx_Data_Buf[512]; //网卡发送缓冲区
uint8_t Rx_Data_Buf[512]; //网卡接收缓冲区
2.2 修改low_level_init
static void
low_level_init(struct netif *netif)
{
/* set MAC hardware address length */
netif->hwaddr_len = ETHARP_HWADDR_LEN;
/* set MAC hardware address */
netif->hwaddr[0] = mac[0];
netif->hwaddr[1] = mac[1];
netif->hwaddr[2] = mac[2];
netif->hwaddr[3] = mac[3];
netif->hwaddr[4] = mac[4];
netif->hwaddr[5] = mac[5];
/* maximum transfer unit */
netif->mtu = 1500;
/* device capabilities */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
enc28j60_init(netif->hwaddr);
}
2.3 修改low_level_output
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *q;
unsigned int i = 0;
#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. */
memcpy(&Tx_Data_Buf[i], (u8_t*)q->payload, q->len);
i = i + q->len;
}
enc28j60_packet_send(Tx_Data_Buf,i);
#if ETH_PAD_SIZE
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
LINK_STATS_INC(link.xmit);
return ERR_OK;
}
2.4 修改low_level_input
static struct pbuf *
low_level_input(struct netif *netif)
{
struct pbuf *p, *q;
u16_t len;
unsigned int i =0;
/* Obtain the size of the packet and put it into the "len"
variable. */
len = enc28j60_packet_receive( Rx_Data_Buf,512);
if(len == 0) return 0;
#if ETH_PAD_SIZE
len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif
/* We allocate a pbuf chain of pbufs from the pool. */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL) {
#if ETH_PAD_SIZE
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
/* We iterate over the pbuf chain until we have read the entire
* packet into the pbuf. */
for(q = p; q != NULL; q = q->next) {
/* Read enough bytes to fill this pbuf in the chain. The
* available data in the pbuf is given by the q->len
* variable.
* This does not necessarily have to be a memcpy, you can also preallocate
* pbufs for a DMA-enabled MAC and after receiving truncate it to the
* actually received size. In this case, ensure the tot_len member of the
* pbuf is the sum of the chained pbuf len members.
*/
memcpy((u8_t*)q->payload, (u8_t*)&Rx_Data_Buf[i], q->len);
i = i + q->len;
}
#if ETH_PAD_SIZE
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;
}
3. 修改stm32f10x_it.c
添加:
__IO uint32_t LocalTime = 0;
修改SysTick_Handler:
void SysTick_Handler(void)
{
LocalTime += 1;
}
4. 制作main函数
struct netif netif;
__IO uint32_t TCPTimer = 0;
__IO uint32_t ARPTimer = 0;
void LwIP_Init(void)
{
struct ip_addr ipaddr;
struct ip_addr netmask;
struct ip_addr gw;
/* Initializes the dynamic memory heap defined by MEM_SIZE.*/
mem_init();
/* Initializes the memory pools defined by MEMP_NUM_x.*/
memp_init();
IP4_ADDR(&ipaddr, 192, 168, 2, 8);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 2, 1);
/* - netif_add(struct netif *netif, struct ip_addr *ipaddr,
struct ip_addr *netmask, struct ip_addr *gw,
void *state, err_t (* init)(struct netif *netif),
err_t (* input)(struct pbuf *p, struct netif *netif))
Adds your network interface to the netif_list. Allocate a struct
netif and pass a pointer to this structure as the first argument.
Give pointers to cleared ip_addr structures when using DHCP,
or fill them with sane numbers otherwise. The state pointer may be NULL.
The init function pointer must point to a initialization function for
your ethernet netif interface. The following code illustrates it's use.*/
netif_add(&netif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
/* Registers the default network interface.*/
netif_set_default(&netif);
/* When the netif is fully configured this function must be called.*/
netif_set_up(&netif);
}
void LwIP_Periodic_Handle(__IO uint32_t localtime)
{
/* TCP periodic process every 250 ms */
if (localtime - TCPTimer >= TCP_TMR_INTERVAL)
{
TCPTimer = localtime;
tcp_tmr();
}
/* ARP periodic process every 5s */
if (localtime - ARPTimer >= ARP_TMR_INTERVAL)
{
ARPTimer = localtime;
etharp_tmr();
}
}
int main()
{
RCC_Config();
LwIP_Init();
SysTick_Config(SystemCoreClock / 1000);
HelloWorld_init();
while (1)
{
/* Periodic tasks */
ethernetif_input(&netif);
LwIP_Periodic_Handle(LocalTime);
}
}
5. 修改lwipopts.h文件,因为只有20K的RAM,把一些数改小点,不然编译不通过
#define MEM_SIZE (10*1024)
#define PBUF_POOL_SIZE 8
#define PBUF_POOL_BUFSIZE 300
#define TCP_MSS (300 - 40)
//#define CHECKSUM_BY_HARDWARE
6. 编译,下载,在cmd命令行里ping和telnet 单片机,运行正常
7. 总结,lwip的无OS移植还是比较简单的,对比UIP的无OS移植,编写应用程序要简单得多。
学习lwip,推荐学习这个https://www.kancloud.cn/jiejietop/tcpip