2、将源码包FreeRTOS\\Source下的include文件复制到KEIL的工程目录下,再此之前要将portmacro.hFreeRTOSConfig.h复制到该文件(include)下。 portmacro.h在FreeRTOS\\Source\\portable\\RVDS\\ARM_CM3下
FreeRTOSConfig.h在FreeRTOSV7.2.0\\FreeRTOS\\Demo\\CORTEX_STM32F103_Keil下 FreeRTOSConfig.h是一个STM32对应的DEMO已经配置好了,如需修改请参照《配置FreeRTOS》,《配置FreeRTOS》详细的说明了FreeRTOSConfig.h里每个宏定义的意义,根据自己需要进行配置。
找到Project->options for target->C/C++->Include Paths
将头文件文件include包含到工程里面去。
3、修改启动文件startup_stm32f10x_hd.s(32内核中)
在050行在__heap_limit 下面添加: PRESERVE8 THUMB
IMPORT xPortPendSVHandler IMPORT xPortSysTickHandler IMPORT vPortSVCHandler
跳到 076 行 DCD SVC_Handler 改成 DCD vPortSVCHandler 跳到079行 DCD PendSV_Handler成 DCD xPortPendSVHandler 跳到080行
DCD SysTick_Handler 成 DCD xPortSysTickHandler
4、在main.c的头部里#include一下头文件:FreeRTOS.h task.h queue.h 这样就移植完毕了。移植过程参考《在STM32中移植FreeRTOS》文档。
7 LWIP的移植
LWIP是TCP/IP协议栈的一个开放源代码实现,它由瑞士计算机科学院的Adam Dunkels等开发,目的是减少内存使用率和代码空间大小,因此LWIP适用于运行在资源受限的嵌入式系统环境中。LWIP可以在几百字节或几十KB的RAM空间运行。LWIP既可以移植到操作系统上运行,也可以在无操作系统下独立运行。 LWIP具有如下特性:
LWIP在STM32上的底层驱动的移植
LWIP在STM32上的底层驱动的移植主要包括两方面:一、修改文件ethernetif.c,该文件
是连接LWIP协议栈和STM32网络驱动程序的桥梁,LWIP协议栈为开发者提供了ethernetif.c的程序模板;二、编写STM32的网络驱动程序,实现网络底层的初始化、收发报文功能。
1、 ethernetif.c文件的移植
ethernetif.c是LWIP协议栈和STM32网络驱动程序之间的接口,它主要包含ethernetif_init、ethernetif_input、low_level_init、low_level_input、low_level_output等函数。
1.1 ethernetif_init函数
该函数是LWIP底层网络接口的初始化函数,指定了网络接口netif对应的主机名及网卡的描述,并指定了网卡的mac地址。同时,该函数还指定了netif的发送数据报文函数,并调用了网络底层驱动初始化函数low_level_init对网络底层进行初始化。(要根据自己选用的网卡调用网卡驱动)
1.2 low_level_init函数
这个函数是网卡的初始化函数。虽然该函数代码量不大,但是它完成的工作量却不少。代码如下:
low_level_init(struct netif *netif) {
// struct ethernetif *ethernetif = netif->state; /* set MAC hardware address length */ netif->hwaddr_len = ETHARP_HWADDR_LEN; /* set MAC hardware address */ netif->hwaddr[0] = mymac[0]; netif->hwaddr[1] = mymac[1]; netif->hwaddr[2] = mymac[2]; netif->hwaddr[3] = mymac[3]; netif->hwaddr[4] = mymac[4]; netif->hwaddr[5] = mymac[5]; /* maximum transfer unit */ netif->mtu = MAX_FRAMELEN;
if(ENC28J60_Init((u8*)mymac)) //初始化ENC28J60 {
return ERR_IF; //底层网络接口错误 }
//指示灯状态:0x476 is PHLCON LEDA(绿)=links status, LEDB(红)=receive/transmit //PHLCON:PHY 模块LED 控制寄存器 ENC28J60_PHY_Write(PHLCON,0x0476); /* 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; return ERR_OK;
/* Do whatever else is needed to initialize interface. */ }
Low_level_init函数设定了网卡的物理地址和每帧最大传输数据字节数。
1.3 ethernetif_input函数
该函数用于从底层物理网卡读取报文,并将该报文向上传递给LWIP协议栈函数ethernet_input进行处理。源代码如下:
void ethernetif_input(struct netif *netif) {
// struct ethernetif *ethernetif; struct eth_hdr *ethhdr; struct pbuf *p;
// ethernetif = netif->state;
/* move received packet into a new pbuf */ p = low_level_input(netif);
/* no packet could be read, silently ignore this */ if (p == NULL) return;
/* points to packet payload, which starts with an Ethernet header */ ethhdr = p->payload;
switch (htons(ethhdr->type)) { /* IP or ARP packet? */ case ETHTYPE_IP: case ETHTYPE_ARP: #if PPPOE_SUPPORT /* PPPoE packet? */ case ETHTYPE_PPPOEDISC: case ETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
/* full packet send to tcpip_thread to process */ if (netif->input(p, netif)!=ERR_OK)
{ LWIP_DEBUGF(NETIF_DEBUG, (\ pbuf_free(p); p = NULL; } break; default:
pbuf_free(p); p = NULL; break; }
}
1.4 low_level_input函数
该函数用于从内存中申请一个新的pbuf,并把接收到的数据报文内容拷贝至该pbuf中。源代码如下:
static struct pbuf *
low_level_input(struct netif *netif) {
// struct ethernetif *ethernetif = netif->state; struct pbuf *p, *q; u16_t len; int rev_len=0;
/* Obtain the size of the packet and put it into the \variable. */
len = ENC28J60_Packet_Receive(MAX_FRAMELEN,lwip_buf); #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. */
//read data into(q->payload, q->len); memcpy((u8_t*)q->payload, (u8_t*)&lwip_buf[rev_len],q->len); rev_len +=q->len; }
// acknowledge that packet has been read();
#if ETH_PAD_SIZE