~
移植平台使用STM32F407为核心芯片的正点原子开发平台,网络芯片使用LAN8720。LWIP使用官方1.4.1版本。本文章只说移植,详细说明请参考正点原子《STM32F4 LWIP开发手册》。
基础工程为UCOSII操作系统,使用LWIP网络协议栈。见上一章。代码参考自正点原子。
1、初始化
(1)头文件和宏定义
#include "mysys.h"
#include "includes.h"
#include "lwip_comm.h"
#include "lwip/api.h"
#include "lwip/lwip_sys.h"
#include "string.h"
#define UDP_DEMO_BUFSIZE 2000 //定义udp最大接收发送数据长度
#define UDP_DEMO_PORT 8001 //定义udp连接的本地端口号
#define LWIP_SEND_DATA 0X80 //定义有数据发送
u8 udp_sendbuf[UDP_DEMO_BUFSIZE];
u8 udp_recvbuf[UDP_DEMO_BUFSIZE]; //UDP接收数据缓冲区
//UDP数据标志位
//使用第七位作为发送标记为
u8 udp_flag;
//创建netconn描述符
struct netconn *udpconn;
(2)对UDP进行初始化、创建UDP进程
//TCP客户端任务
#define UDP_RECV_PRIO 7
//任务堆栈大小
#define UDP_RECV_STK_SIZE 300
//任务堆栈
OS_STK UDP_RECV_TASK_STK[UDP_RECV_STK_SIZE];
//创建UDP线程
//返回值:0 UDP创建成功
//其他 UDP创建失败
INT8U udp_demo_init(void)
{
INT8U res;
OS_CPU_SR cpu_sr;
UDP_Config();
OS_ENTER_CRITICAL(); //关中断
res = OSTaskCreate(udp_recv_thread,(void*)0,(OS_STK*)&UDP_RECV_TASK_STK[UDP_RECV_STK_SIZE-1],UDP_RECV_PRIO); //创建UDP线程
OS_EXIT_CRITICAL(); //开中断
return res;
}
(3)UDP初始化函数
err_t UDP_Config(void)
{
err_t err;
struct ip_addr destipaddr;
udpconn = netconn_new(NETCONN_UDP); //创建一个UDP链接
udpconn->recv_timeout = 10; //recv接受线程阻塞时间
if(udpconn != NULL) //创建UDP连接成功
{
//绑定IP地址和端口号
err = netconn_bind(udpconn,IP_ADDR_ANY,UDP_DEMO_PORT);
//构造目的IP地址
IP4_ADDR(&destipaddr,lwipdev.remoteip[0],lwipdev.remoteip[1], lwipdev.remoteip[2],lwipdev.remoteip[3]);
//连接到远端主机
netconn_connect(udpconn,&destipaddr,UDP_DEMO_PORT);
if(err != ERR_OK)//绑定完成
{printf("UDP绑定失败\r\n");return 1;}
}
else
{printf("UDP连接创建失败\r\n");return 2;}
return 0;
}
2、UDP接受发送
(1)接受进程
//udp任务函数
static void udp_recv_thread(void *arg)
{
OS_CPU_SR cpu_sr;
static struct netbuf *recvbuf;
u32 data_len = 0;
struct pbuf *q;
err_t err;
LWIP_UNUSED_ARG(arg);
while(1)
{
netconn_recv(udpconn,&recvbuf); //接收数据
if(recvbuf != NULL) //接收到数据
{
OS_ENTER_CRITICAL(); //关中断
memset(udp_recvbuf,0,UDP_DEMO_BUFSIZE); //数据接收缓冲区清零
for(q=recvbuf->p;q!=NULL;q=q->next) //遍历完整个pbuf链表
{
//判断要拷贝到UDP_DEMO_RX_BUFSIZE中的数据是否大于UDP_DEMO_RX_BUFSIZE的剩余空间,如果大于
//的话就只拷贝UDP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
if(q->len > (UDP_DEMO_BUFSIZE-data_len))
memcpy(udp_recvbuf+data_len,q->payload,(UDP_DEMO_BUFSIZE-data_len));//拷贝数据
else
memcpy(udp_recvbuf+data_len,q->payload,q->len);
data_len += q->len;
if(data_len > UDP_DEMO_BUFSIZE) break; //超出TCP客户端接收数组,跳出
}
OS_EXIT_CRITICAL(); //开中断
data_len=0; //复制完成后data_len要清零。
UDP_Receive_Handle();//对接受的数据进行处理-----自写
netbuf_delete(recvbuf); //删除buf
}
OSTimeDlyHMSM(0,0,0,5); //延时5ms
}
}
(2)UDP发送函数
void UDP_Send(void)
{
err_t err;
static struct netbuf *sentbuf;
if((udp_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) //有数据要发送
{
sentbuf = netbuf_new();
netbuf_alloc(sentbuf,strlen((char *)udp_sendbuf));
memcpy(sentbuf->p->payload,(void*)udp_sendbuf,strlen((char*)udp_sendbuf));
err = netconn_send(udpconn,sentbuf); //将netbuf中的数据发送出去
if(err != ERR_OK)
printf("UDP发送失败\r\n");
udp_flag &= ~LWIP_SEND_DATA; //清除数据发送标志
netbuf_delete(sentbuf); //删除buf
}
}
3、主函数
主函数中仅创建了入口函数,并无其他内容,硬件初始化在入口函数中进行,不在展示。
1、start入口函数
void start_task(void *pdata)
{
OS_CPU_SR cpu_sr;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
SysLED_Init();
SysTick_init();
USART_Config();
FSMC_SRAM_Init(); //外部SRAM初始化
mymem_init(); //初始化存池
lwip_comm_init();
udp_demo_init();
printf("初始化完成\r\n");
OSStatInit(); //初始化统计任务
OS_ENTER_CRITICAL(); //关中断
OSTaskCreate(led_task,(void*)0,(OS_STK*)&LED_TASK_STK[LED_STK_SIZE-1],LED_TASK_PRIO);//创建LED任务
OSTaskSuspend(OS_PRIO_SELF); //挂起start_task任务
OS_EXIT_CRITICAL(); //开中断
}