当前位置:网站首页>STM32入门开发 LWIP网络协议栈移植(网卡采用DM9000)
STM32入门开发 LWIP网络协议栈移植(网卡采用DM9000)
2022-08-11 09:33:00 【InfoQ】
一、环境介绍
二、D9000网卡
2.1 DM9000简介
2.2 DM9000 中断引脚电平设置
2.3 DM9000 数据位宽设置
2.4 DM9000寄存器表
2.5 DM9000常用寄存器介绍
2.6 DM9000 直接内存访问控制(DMAC)
2.7 DM9000原理图介绍
2.8 DM9000时序图介绍
三、LWIP(TCP/IP)网络协议栈介绍
3.1 LWIP介绍
3.2 几种开源TCPIP协议概述
四、LWIP协议栈移植
4.1 LWIP源码下载
4.2 将LWIP源码加入到工程目录
4.3 配置lwipopts.h文件
4.4 修改ethernetif.c文件
4.5 修改sys_arch.c文件
4.6 新建lwip_config.c文件
/*
函数功能: LWIP协议栈初始化
*/
void lwip_config_init(void)
{
ip_addr_t ipaddr; //IP地址
ip_addr_t netmask; //子网掩码
ip_addr_t gw; //网关
//全部初始化为0 -因为使用了动态IP地址分配
ipaddr.addr=0;
netmask.addr=0;
gw.addr=0;
/*1. 初始化LWIP内核*/
lwip_init();
/*2. 向网卡列表中添加一个网络设备*/
netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,ðernet_input);
/*3. 开启DHCP服务 */
dhcp_start(&lwip_netif);
/*4. 设置netif为默认网口*/
netif_set_default(&lwip_netif);
/*5. 打开netif网口*/
netif_set_up(&lwip_netif);
}
u32 TCPTimer=0; //TCP查询计时器
u32 ARPTimer=0; //ARP查询计时器
u32 DHCPfineTimer=0; //DHCP精细处理计时器
u32 DHCPcoarseTimer=0; //DHCP粗糙处理计时器
u32 DHCP_State=1; //保存DHCP状态 1表示没有分配成功 0表示分配成功
/*
函数功能: LWIP轮询任务
*/
void lwip_periodic_handle()
{
//每250ms调用一次tcp_tmr()函数
if(TCPTimer >= TCP_TMR_INTERVAL)
{
TCPTimer=0;
tcp_tmr(); //处理TCP协议请求
}
//ARP每5s周期性调用一次
if(ARPTimer >= ARP_TMR_INTERVAL)
{
ARPTimer=0;
etharp_tmr();
}
//每500ms调用一次dhcp_fine_tmr()
if(DHCPfineTimer >= DHCP_FINE_TIMER_MSECS)
{
DHCPfineTimer=0;
dhcp_fine_tmr(); //动态IP地址分配的事物处理
if(DHCP_State)lwip_dhcp_process_handle(); //DHCP处理
}
//每60s执行一次DHCP粗糙处理
if(DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS)
{
DHCPcoarseTimer=0;
dhcp_coarse_tmr();
}
}
//lwip控制结构体
typedef struct
{
u8 remoteip[4]; //服务器主机IP地址
u8 ip[4]; //本机IP地址
u8 netmask[4]; //子网掩码
u8 gateway[4]; //默认网关的IP地址
}__lwip_dev;
extern __lwip_dev lwipdev; //lwip信息结构体
__lwip_dev lwipdev; //lwip信息结构体
/*
函数功能: DHCP处理任务
*/
void lwip_dhcp_process_handle(void)
{
u32 ip=0,netmask=0,gw=0;
ip=lwip_netif.ip_addr.addr; //读取新IP地址
netmask=lwip_netif.netmask.addr; //读取子网掩码
gw=lwip_netif.gw.addr; //读取默认网关
if(ip!=0) //正确获取到IP地址的时候
{
DHCP_State=0; //表示分配成功
//解析出通过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("动态分配
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("子网掩
码............%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("网
关.........%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
}
}
4.7 配置一个定时器提供时间基准
4.8 初始化lwip动态获取IP地址
4.9 LWIP内存配置选择
五、LWIP函数使用(RAW编程接口)
5.1 LWIP初始化配置
ip_addr_t ipaddr; //IP地址
ip_addr_t netmask; //子网掩码
ip_addr_t gw; //网关
//全部初始化为0 -因为使用了动态IP地址分配
ipaddr.addr=0;
netmask.addr=0;
gw.addr=0;
/*1. 初始化LWIP内核*/
lwip_init();
/*2. 向网卡列表中添加一个网络设备*/
netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,ðernet_input);
/*3. 开启DHCP服务 */
dhcp_start(&lwip_netif);
/*4. 设置netif为默认网口*/
netif_set_default(&lwip_netif);
/*5. 打开netif网口*/
netif_set_up(&lwip_netif);
5.2 LWIP轮询函数处理
5.3 LWIP编程RAW接口函数
tcp_new() 创建一个 TCP 的 PCB 控制块
tcp_bind() 为 TCP 的 PCB 控制块绑定一个本地 IP 地址和端口号
tcp_listen() 开始 TCP 的 PCB 监听
tcp_accept() 控制块 accept字段注册的回调函数,侦听到连接时被调用
tcp_accepted() 通知 LWIP 协议栈一个 TCP 连接被接受了
tcp_conect() 连接远端主机
tcp_write() 构造一个报文并放到控制块的发送缓冲队列中
tcp_sent() 控制块 sent 字段注册的回调函数,数据发送成功后被回调
tcp_output() 将发送缓冲队列中的数据发送出去
tcp_recv()控制块 recv 字段注册的回调函数,当接收到新数据时被调用
tcp_recved()当程序处理完数据后一定要调用这个函数,通知内核更新接收窗口
tcp_poll() 控制块 poll 字段注册的回调函数,该函数周期性调用
tcp_close() 关闭一个 TCP 连接
tcp_err() 控制块 err 字段注册的回调函数,遇到错误时被调用
tcp_abort() 中断 TCP 连接
5.4 创建TCP服务器示例
u8 TCP_Create(u16_t port)
{
struct tcp_pcb *pcb=NULL;
pcb=tcp_new(); //创建套接字
if(pcb==NULL)return 1;
if(tcp_bind(pcb,IP_ADDR_ANY,port)!=ERR_OK)return 2; //绑定端口号
pcb=tcp_listen(pcb); //开始监听
tcp_accept(pcb,TCP_accept);//等待连接
return 0;
}
err_t TCP_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
u8 addr[4];
//tcp_setprio(newpcb, TCP_PRIO_MIN); 设置优先级
printf("有新的客户端连接!\n");
addr[3]=(newpcb->remote_ip.addr>>24)&0xFF;
addr[2]=(newpcb->remote_ip.addr>>16)&0xFF;
addr[1]=(newpcb->remote_ip.addr>>8)&0xFF;
addr[0]=(newpcb->remote_ip.addr>>0)&0xFF;
printf("ip地址:%d.%d.%d.%d\n",addr[0],addr[1],addr[2],addr[3]);
printf("端口号:%d\n",newpcb->remote_port);
printf("当前队列剩余字节:%d\n",tcp_sndbuf(newpcb));
tcp_write(newpcb,"1234567890",10,1); //将要发送的数据提交到发送队列(不会立即发送)
tcp_output(newpcb); //提示系统现在,发送数据
tcp_sent(newpcb,TCP_sent); //发送成功的回调函数
tcp_recv(newpcb,TCP_recv);
return ERR_OK;
}
err_t TCP_sent(void *arg, struct tcp_pcb *tpcb,u16_t len)
{
printf("成功发送:%d字节\n",len);
//tcp_close(tpcb); //关闭客户端连接
return ERR_OK;
}
u8 rx_buff[1024];
err_t TCP_recv(void *arg, struct tcp_pcb *tpcb,struct pbuf *p, err_t err)
{
u32 rx_cnt=0;
struct pbuf *q;
memset(rx_buff,0,sizeof(rx_buff));
if(p==NULL)
{
printf("客户端已经断开连接!\n");
}
else
{
for(q=p;q!=NULL;q=q->next)
{
memcpy(rx_buff+rx_cnt,q->payload,q->len);
rx_cnt+=q->len;
}
pbuf_free(p); //释放PUFF
printf("成功接收:%d字节\n",rx_cnt);
printf("收到的数据=%s\n",rx_buff);
}
return ERR_OK;
}
5.5 创建TCP客户端示例
u8 TCP_Create(u16_t port)
{
struct tcp_pcb *pcb=NULL;
pcb=tcp_new(); //创建套接字
ip_addr_t ipaddr;
if(pcb==NULL)return 1;
IP4_ADDR(&ipaddr,192,168,31,54); //在ip_addr.h里定义
tcp_connect(pcb,&ipaddr,port,TCP_connected);
return 0;
}
err_t TCP_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
u8 addr[4];
//tcp_setprio(newpcb, TCP_PRIO_MIN); 设置优先级
printf("服务器连接成功!\n");
addr[3]=(tpcb->remote_ip.addr>>24)&0xFF;
addr[2]=(tpcb->remote_ip.addr>>16)&0xFF;
addr[1]=(tpcb->remote_ip.addr>>8)&0xFF;
addr[0]=(tpcb->remote_ip.addr>>0)&0xFF;
printf("服务器ip地址:%d.%d.%d.%d\n",addr[0],addr[1],addr[2],addr[3]);
printf("服务器端口号:%d\n",tpcb->remote_port);
printf("当前队列剩余字节:%d\n",tcp_sndbuf(tpcb));
tcp_write(tpcb,"1234567890",10,1); //将要发送的数据提交到发送队列(不会立即发送)
tcp_output(tpcb); //提示系统现在,发送数据
tcp_sent(tpcb,TCP_sent); //发送成功的回调函数
tcp_recv(tpcb,TCP_recv);
return ERR_OK;
}
err_t TCP_sent(void *arg, struct tcp_pcb *tpcb,u16_t len)
{
printf("成功发送:%d字节\n",len);
//tcp_close(tpcb); //关闭客户端连接
return ERR_OK;
}
u8 rx_buff[1024];
err_t TCP_recv(void *arg, struct tcp_pcb *tpcb,struct pbuf *p, err_t err)
{
u32 rx_cnt=0;
struct pbuf *q;
memset(rx_buff,0,sizeof(rx_buff));
if(p==NULL)
{
printf("服务器已经断开连接!\n");
}
else
{
for(q=p;q!=NULL;q=q->next)
{
memcpy(rx_buff+rx_cnt,q->payload,q->len);
rx_cnt+=q->len;
}
printf("成功接收:%d字节\n",rx_cnt);
printf("收到的数据=%s\n",rx_buff);
}
return ERR_OK;
}
边栏推荐
- MATLAB实战Sobel边缘检测(Edge Detection)
- Inventorying Four Entry-Level SSL Certificates
- 游戏服务器中集群网关的设计
- HStreamDB v0.9 released: Partition model extension, support for integration with external systems
- Quickly submit a PR (Web) for OpenHarmony in 5 minutes
- 2022-08-09 顾宇佳 学习笔记
- IPQ4019/IPQ4029 support WiFi6 MiniPCIe Module 2T2R 2×2.4GHz 2x5GHz MT7915 MT7975
- 利用mindspore下面mindzoo里面的yolov3-darknet53进行目标识别,模型训练不收敛
- VideoScribe卡死解决方案
- Continuous Integration/Continuous Deployment (2) Jenkins & SonarQube
猜你喜欢
pycharm 取消msyql表达式高亮
深度学习100例 —— 卷积神经网络(CNN)识别验证码
Typora和基本的Markdown语法
音视频+AI,中关村科金助力某银行探索发展新路径 | 案例研究
HDRP Custom Pass Shader Get world coordinates and near clipping plane coordinates
新一代开源免费的轻量级 SSH 终端,非常炫酷好用!
Primavera Unifier advanced formula usage sharing
SDUT 2877: angry_birds_again_and_again
疫情当前,如何提高远程办公的效率,远程办公工具分享
wordpress插件开发03-简单的all in one seo 插件开发
随机推荐
Three handshakes and four waves
Primavera Unifier 高级公式使用分享
Primavera P6 Professional 21.12 Login exception case sharing
[wxGlade learning] wxGlade environment configuration
中国电子学会五级考点详解(一)-string类型字符串
最强大脑(9)
Typora and basic Markdown syntax
模型训练出现NAN
安装ES7.x集群
WooCommerce电子商务WordPress插件-赚美国人的钱
2022-08-10:为了给刷题的同学一些奖励,力扣团队引入了一个弹簧游戏机, 游戏机由 N 个特殊弹簧排成一排,编号为 0 到 N-1, 初始有一个小球在编号
盘点四个入门级SSL证书
基于 VIVADO 的 AM 调制解调(3)仿真验证
使用树莓派和OAK相机部署机器人视觉模型
Halcon算子解释
【wxGlade学习】wxGlade环境配置
Primavera Unifier - AEM Form Designer Essentials
Adobe LiveCycle Designer report designer
Segmentation Learning (loss and Evaluation)
snapshot standby switch