STM32F4XX LWIP+freeRTOS移植(二)

前面几个文件改好了之后,下面进入实战环节,从ucos_ii系统+lwip移植为freeRTOS+LWIP!

第一步:从原子例程中找一个现成的freeRTOS系统工程,这里我们选的FreeRTOS实验20-1 FreeRTOS内存管理实验,如下:
在这里插入图片描述
打开之后编译一下,确保工程没有问题!
在这里插入图片描述
1.1 然后将LWIP例程中的–>网络实验2 LWIP带UCOSII操作系统移植–>LWIP拷贝到freeRTOS工程下

1.2 然后将LWIP例程中的–>网络实验2 LWIP带UCOSII操作系统移植–>HARDWARE–>LAN8720拷贝到freeRTOS工程中的HARDWARE文件夹下

1.3 在工程上右键 manage project item,然后新增LWIP_APP、LWIP_CORE、LWIP_NETIF、LWIP_API、LWIP_ARCH,如下图所示:

1.4 将相应的文件添加进去,妈的,CSDN没法上传图片了?一直上传不成功

1.5 在main函数中添加头文件,否则系统默认没有定义LWIP_DHCP,系统是不会进行DHCP功能的,头文件如下:

#include "lan8720.h"
#include "string.h"
#include "malloc.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timer.h"
#include "lwip/netif.h"
#include "lwip_comm.h"
#include "lwipopts.h"
#include "tcp_server_demo.h"

1.6 main起始任务中添加如下:lwip_comm_init()一定要放在起始任务中,如果放在main函数中,则因为没有开启freeRTOS系统,会始终初始化不成功,在ucos_ii系统中因为有OSInit()初始化系统函数,所以放在main中没有问题,这一点要注意,否则启动之后始终会卡死在某一个位置!

int main(void)
{Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz   HAL_Init();                     //初始化HAL库delay_init(180);                //初始化延时函数uart_init(115200);              //初始化USART//usmart_dev.init(90); 		    //初始化USMART	LED_Init();                     //初始化LED KEY_Init();                     //初始化按键SDRAM_Init();                   //初始化SDRAMLCD_Init();                     //初始化LCDPCF8574_Init();                 //初始化PCF8574my_mem_init(SRAMIN);		    //初始化内部内存池my_mem_init(SRAMEX);		    //初始化外部内存池my_mem_init(SRAMCCM);		    //初始化CCM内存池POINT_COLOR = RED; 		        //红色字体LCD_ShowString(30,30,200,20,16,"Apollo STM32F4/F7");LCD_ShowString(30,50,200,20,16,"LWIP+UCOSII Test");LCD_ShowString(30,70,200,20,16,"ATOM@ALIENTEK");LCD_ShowString(30,90,200,20,16,"2016/1/14");	//创建开始任务xTaskCreate((TaskFunction_t )start_task,            //任务函数(const char*    )"start_task",          //任务名称(uint16_t       )START_STK_SIZE,        //任务堆栈大小(void*          )NULL,                  //传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       //任务优先级(TaskHandle_t*  )&StartTask_Handler);   //任务句柄                vTaskStartScheduler();          //开启任务调度
}//开始任务任务函数
void start_task(void *pvParameters)
{while(lwip_comm_init()) 	    //lwip初始化{LCD_ShowString(30,110,200,20,16,"Lwip Init failed!"); 	//lwip初始化失败delay_ms(500);LCD_Fill(30,110,230,150,WHITE);delay_ms(500);}LCD_ShowString(30,110,200,20,16,"Lwip Init Success!"); 		//lwip初始化成功while(tcp_server_init()) 									//初始化tcp_server(创建tcp_server程){LCD_ShowString(30,110,200,20,16,"TCP Server failed! "); delay_ms(500);LCD_Fill(30,110,230,170,WHITE);delay_ms(500);}taskENTER_CRITICAL();           //进入临界区#if LWIP_DHCPlwip_comm_dhcp_creat(); //创建DHCP任务#endif//创建TASK1任务xTaskCreate((TaskFunction_t )display_task,             (const char*    )"display_task",           (uint16_t       )DISPALY_STK_SIZE,        (void*          )NULL,                  (UBaseType_t    )DISPALY_TASK_PRIO,        (TaskHandle_t*  )&DisplayTask_Handler);   //创建TASK1任务xTaskCreate((TaskFunction_t )led_task,             (const char*    )"led_task",           (uint16_t       )LED_STK_SIZE,        (void*          )NULL,                  (UBaseType_t    )LED_TASK_PRIO,        (TaskHandle_t*  )&LedTask_Handler); vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL();            //退出临界区
}

第二步:修改lwip_common.c函数

2.1 定义LWIP TaskHandle_t的DHCP任务句柄,并删除ucos_ii的堆栈

2.2 修改lwip_comm_mem_malloc函数删除没用的内存申请

3.3 修改lwip_comm_dhcp_creat、lwip_comm_dhcp_delete函数为freeRTOS的API函数,整个文件如下:

__lwip_dev lwipdev;						//lwip控制结构体 
struct netif lwip_netif;				//定义一个全局的网络接口extern u32 memp_get_memorysize(void);	//在memp.c里面定义
extern u8_t *memp_memory;				//在memp.c里面定义.
extern u8_t *ram_heap;					//在mem.c里面定义.
TaskHandle_t LWIP_DHCP_TaskHandler;
/
//lwip两个任务定义(内核任务和DHCP任务)//lwip DHCP任务
//设置任务优先级
#define LWIP_DHCP_TASK_PRIO       		3
//设置任务堆栈大小
#define LWIP_DHCP_STK_SIZE  		    256
//任务函数
void lwip_dhcp_task(void *pdata); //用于以太网中断调用
void lwip_pkt_handle(void)
{ethernetif_input(&lwip_netif);
}//lwip中mem和memp的内存申请
//返回值:0,成功;
//    其他,失败
u8 lwip_comm_mem_malloc(void)
{u32 mempsize;u32 ramheapsize; INTX_DISABLE();//关中断mempsize=memp_get_memorysize();			//得到memp_memory数组大小memp_memory=mymalloc(SRAMIN,mempsize);	//为memp_memory申请内存ramheapsize=LWIP_MEM_ALIGN_SIZE(MEM_SIZE)+2*LWIP_MEM_ALIGN_SIZE(4*3)+MEM_ALIGNMENT;//得到ram heap大小ram_heap=mymalloc(SRAMIN,ramheapsize);	//为ram_heap申请内存     INTX_ENABLE();//开中断if(!memp_memory||!ram_heap)//有申请失败的{lwip_comm_mem_free();return 1;}return 0;	
}
//lwip中mem和memp内存释放
void lwip_comm_mem_free(void)
{ 	myfree(SRAMIN,memp_memory);myfree(SRAMIN,ram_heap);
}
//lwip 默认IP设置
//lwipx:lwip控制结构体指针
void lwip_comm_default_ip_set(__lwip_dev *lwipx)
{u32 sn0;sn0=*(vu32*)(0x1FFF7A10);//获取STM32的唯一ID的前24位作为MAC地址后三字节//默认远端IP为:192.168.1.100lwipx->remoteip[0]=192;	lwipx->remoteip[1]=168;lwipx->remoteip[2]=1;lwipx->remoteip[3]=104;//MAC地址设置(高三字节固定为:2.0.0,低三字节用STM32唯一ID)lwipx->mac[0]=2;//高三字节(IEEE称之为组织唯一ID,OUI)地址固定为:2.0.0lwipx->mac[1]=0;lwipx->mac[2]=0;lwipx->mac[3]=(sn0>>16)&0XFF;//低三字节用STM32的唯一IDlwipx->mac[4]=(sn0>>8)&0XFFF;lwipx->mac[5]=sn0&0XFF; //默认本地IP为:192.168.1.30lwipx->ip[0]=192;	lwipx->ip[1]=168;lwipx->ip[2]=1;lwipx->ip[3]=30;//默认子网掩码:255.255.255.0lwipx->netmask[0]=255;	lwipx->netmask[1]=255;lwipx->netmask[2]=255;lwipx->netmask[3]=0;//默认网关:192.168.1.1lwipx->gateway[0]=192;	lwipx->gateway[1]=168;lwipx->gateway[2]=1;lwipx->gateway[3]=1;	lwipx->dhcpstatus=0;//没有DHCP	
} //LWIP初始化(LWIP启动的时候使用)
//返回值:0,成功
//      1,内存错误
//      2,LAN8720初始化失败
//      3,网卡添加失败.
u8 lwip_comm_init(void)
{u8 retry=0;struct netif *Netif_Init_Flag;		//调用netif_add()函数时的返回值,用于判断网络初始化是否成功struct ip_addr ipaddr;  			//ip地址struct ip_addr netmask; 			//子网掩码struct ip_addr gw;      			//默认网关 if(ETH_Mem_Malloc())return 1;		//内存申请失败if(lwip_comm_mem_malloc())return 2;	//内存申请失败lwip_comm_default_ip_set(&lwipdev);	//设置默认IP等信息while(LAN8720_Init())		        //初始化LAN8720,如果失败的话就重试5次{retry++;if(retry>5) {retry=0;return 3;} //LAN8720初始化失败}tcpip_init(NULL,NULL);				//初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务#if LWIP_DHCP		//使用动态IPipaddr.addr = 0;netmask.addr = 0;gw.addr = 0;
#else				//使用静态IPIP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
#endifNetif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,&ethernetif_init,&tcpip_input);//向网卡列表中添加一个网口if(Netif_Init_Flag==NULL)return 4;//网卡添加失败 else//网口添加成功后,设置netif为默认值,并且打开netif网口{netif_set_default(&lwip_netif); //设置netif为默认网口netif_set_up(&lwip_netif);		//打开netif网口}return 0;//操作OK.
}   
//如果使能了DHCP
#if LWIP_DHCP
//创建DHCP任务
void lwip_comm_dhcp_creat(void)
{taskENTER_CRITICAL();  //进入临界区xTaskCreate((TaskFunction_t)lwip_dhcp_task,(const char*  )"DHCP_TASK",(uint16_t     )LWIP_DHCP_STK_SIZE,(void*        )NULL,(UBaseType_t  )LWIP_DHCP_TASK_PRIO,(TaskHandle_t*)&LWIP_DHCP_TaskHandler);//创建DHCP任务 						taskEXIT_CRITICAL();  //退出临界区
}
//删除DHCP任务
void lwip_comm_dhcp_delete(void)
{dhcp_stop(&lwip_netif); 		//关闭DHCPvTaskDelete(LWIP_DHCP_TaskHandler);	//删除DHCP任务
}
//DHCP处理任务
void lwip_dhcp_task(void *pdata)
{u32 ip=0,netmask=0,gw=0;dhcp_start(&lwip_netif);//开启DHCP lwipdev.dhcpstatus=0;	//正在DHCPprintf("正在查找DHCP服务器,请稍等...........\r\n");   while(1){ printf("正在获取地址...\r\n");ip=lwip_netif.ip_addr.addr;		//读取新IP地址netmask=lwip_netif.netmask.addr;//读取子网掩码gw=lwip_netif.gw.addr;			//读取默认网关 if(ip!=0)   					//当正确读取到IP地址的时候{lwipdev.dhcpstatus=2;	//DHCP成功printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);//解析出通过DHCP获取到的IP地址lwipdev.ip[3]=(uint8_t)(ip>>24); lwipdev.ip[2]=(uint8_t)(ip>>16);lwipdev.ip[1]=(uint8_t)(ip>>8);lwipdev.ip[0]=(uint8_t)(ip);printf("通过DHCP获取到IP地址..............%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);//解析通过DHCP获取到的子网掩码地址lwipdev.netmask[3]=(uint8_t)(netmask>>24);lwipdev.netmask[2]=(uint8_t)(netmask>>16);lwipdev.netmask[1]=(uint8_t)(netmask>>8);lwipdev.netmask[0]=(uint8_t)(netmask);printf("通过DHCP获取到子网掩码............%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);//解析出通过DHCP获取到的默认网关lwipdev.gateway[3]=(uint8_t)(gw>>24);lwipdev.gateway[2]=(uint8_t)(gw>>16);lwipdev.gateway[1]=(uint8_t)(gw>>8);lwipdev.gateway[0]=(uint8_t)(gw);printf("通过DHCP获取到的默认网关..........%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);break;}else if(lwip_netif.dhcp->tries>LWIP_MAX_DHCP_TRIES) //通过DHCP服务获取IP地址失败,且超过最大尝试次数{  lwipdev.dhcpstatus=0XFF;//DHCP失败.//使用静态IP地址IP4_ADDR(&(lwip_netif.ip_addr),lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);IP4_ADDR(&(lwip_netif.netmask),lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);IP4_ADDR(&(lwip_netif.gw),lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);printf("DHCP服务超时,使用静态IP地址!\r\n");printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);break;}  delay_ms(250); //延时250ms}lwip_comm_dhcp_delete();//删除DHCP任务 
}
#endif 

第三步:修改TCP/IP内核任务优先级最高
3.1 在lwipopts.h文件中修改TCP内核任务优先级为最高

#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 1) //内核任务优先级

3.2 编译,没有问题,下载开发板进行验证,这里没法上传图片就不上传了

第四步:Error:…\FreeRTOS\portable\RVDS\ARM_CM4F\port.c,768错误

大家运行之后发现可以获取到动态IP,也可以建立TCP/IP的链接,只是会一直报Error:…\FreeRTOS\portable\RVDS\ARM_CM4F\port.c,768 这个错误,这是什么原因呢?

CORTEX内核的优先级是数值越小优先级越高,即0是最高优先级。FreeRTOS为了满足某些应用对中断实时性要求高的需求,使得中断优先级高于某个值之后,就不能调用操作系统的内核函数来提高实时性。这个问题是将中断的优先级设置的高于这个值,却还在中断中调用操作系统提供的API引起的。当把优先级改小也就是数值改大之后,程序能够正常运行。

下面是程序卡住的地方,从上面的注释也可以看出是因为优先级的原因。

/* The following assertion will fail if a service routine (ISR) foran interrupt that has been assigned a priority aboveconfigMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS APIfunction.  ISR safe FreeRTOS API functions must *only* be calledfrom interrupts that have been assigned a priority at or belowconfigMAX_SYSCALL_INTERRUPT_PRIORITY.*/configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );

FREERTOS中的优先级数值设定的参数是configMAX_SYSCALL_INTERUPT_PRORITY。默认值是5,所以要想在中断中使用操作系统函数需要将中断的优先级设置的大于等于5.

#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	 ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

点开LAN8720.c文件,找到HAL_ETH_MspInit函数,在函数最下面2行可以看到ETH_IRQn的优先级是0,比5大,要改成6,

//ETH底层驱动,时钟使能,引脚配置
//此函数会被HAL_ETH_Init()调用
//heth:以太网句柄
void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
{GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_ETH_CLK_ENABLE();             //开启ETH时钟__HAL_RCC_GPIOA_CLK_ENABLE();			//开启GPIOA时钟__HAL_RCC_GPIOB_CLK_ENABLE();			//开启GPIOB时钟__HAL_RCC_GPIOC_CLK_ENABLE();			//开启GPIOC时钟__HAL_RCC_GPIOG_CLK_ENABLE();			//开启GPIOG时钟/*网络引脚设置 RMII接口 ETH_MDIO -------------------------> PA2ETH_MDC --------------------------> PC1ETH_RMII_REF_CLK------------------> PA1ETH_RMII_CRS_DV ------------------> PA7ETH_RMII_RXD0 --------------------> PC4ETH_RMII_RXD1 --------------------> PC5ETH_RMII_TX_EN -------------------> PB11ETH_RMII_TXD0 --------------------> PG13ETH_RMII_TXD1 --------------------> PG14ETH_RESET-------------------------> PCF8574扩展IO*///PA1,2,7GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7; GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用GPIO_Initure.Pull=GPIO_NOPULL;              //不带上下拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;         //高速GPIO_Initure.Alternate=GPIO_AF11_ETH;       //复用为ETH功能HAL_GPIO_Init(GPIOA,&GPIO_Initure);         //初始化//PB11GPIO_Initure.Pin=GPIO_PIN_11;               //PB11HAL_GPIO_Init(GPIOB,&GPIO_Initure);         //始化//PC1,4,5GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5; //PC1,4,5HAL_GPIO_Init(GPIOC,&GPIO_Initure);         //初始化//PG13,14GPIO_Initure.Pin=GPIO_PIN_13|GPIO_PIN_14;   //PG13,14HAL_GPIO_Init(GPIOG,&GPIO_Initure);         //初始化HAL_NVIC_SetPriority(ETH_IRQn,0,0); //此处优先级必须小于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITYHAL_NVIC_EnableIRQ(ETH_IRQn);
}

因为在ETH_IRQHandler的DMA中断接收处理函数中使用到了LWIP的协议栈,也就是使用到了freeRTOS的API,所以此处才会报错误,将HAL_NVIC_SetPriority(ETH_IRQn,0,0)改为HAL_NVIC_SetPriority(ETH_IRQn,6,0)就好了

//中断服务函数
void ETH_IRQHandler(void)
{while(ETH_GetRxPktSize(ETH_Handler.RxDesc))   {lwip_pkt_handle();//处理以太网数据,即将数据提交给LWIP}//清除中断标志位__HAL_ETH_DMA_CLEAR_IT(&ETH_Handler,ETH_DMA_IT_R); __HAL_ETH_DMA_CLEAR_IT(&ETH_Handler,ETH_DMA_IT_NIS);   
}

最后,改好之后编译,就不会有错了,使用ATKKPING测试10000次,与电脑直连时ping平均速度0.05ms,还算可以


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部