OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 19383|回复: 36

手把手在原子F407开发板上搭建TCP服务器及TFTP服务器实现IAP功能

[复制链接]

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
发表于 2017-7-24 10:05:55 | 显示全部楼层 |阅读模式
本帖最后由 513393302@qq.com 于 2017-7-24 10:22 编辑

声明:原贴http://www.stmcu.org/module/foru ... 1&extra=#pid2273351
也是本人写的,为了方便原子F407的朋友,所以在这里重新写了一遍,修改了部分错别字。

非常感谢STM32以太网在线培训,真的让我学到非常多,没培训前很想搞以太网但有种无从下手的感觉,经过这次培训让我从这个架构上有个从上到下的了解,再借助官方神器STM32CubeMX,开发和学习起来还是非常快的!我手上没有官方的板子,带以太网的只有原子的F407的板子,所以也是在这个平台上做的,板子上的PHY为LAN8720A,没有用到显示屏全为串口输出调试信息。
实现功能:通过STM32CubeMX在原子F407板子上搭建TCP Server 及 TFTP Server (实现IAP功能),实现这些功能非常简单。
一、功能介绍
  1、TCP Server 实现显示连接上的客户端IP及对客户端发来的数据回传
  2、TFTP Server 实现IAP功能及读取MCU内部FLASH数据。这里用到的是文件传输协议,主要是参考官方文档UM1709,及官方STM324xG_EVAL的LWIP IAP例子。
二、工程搭建
1、外设配置打开STM32CubeMX,点NEW PROJECT,在左侧的输入框中输入407ZG,在右侧会显示STM32F407ZGT6的MCU列表,选择此列表,双击。
设置时钟源为外部时钟
QQ截图20170724092452.png
开启SWD调试接口,开启ETH外设,选择RMII接口(LAN8720A为RMII接口),中间件里时能LWIP。使用RMII接口的时候软件会自动配置对应的引脚,但是一定要仔细对比软件配置的引脚是否为板子上硬件所连接的。
QQ截图20170724100521.png

原子F407板子上的LAN8720A的RMII接口引脚如下图,

而软件配置的引脚如下:

显然与实际板子上的硬件连接不同,这里需要手动调整3个引脚,分别为:
  ETH_TX_EN -> PG11
  ETH_TXD0  ->  PG13
  ETH_TXD1  ->  PG14
  PD3配置成GPIO输出(LAN8720A的硬件复位引脚)
时能串口1,用于调试

2、配置时钟
外部高速晶振选择为8M,PLL SORCE MUX 选择为HSE,在HCLK处输入168点回车,软件会自动配置好。




3、中间件配置
这里我们主要配置,ETH、LWIP、串口1、GPIO


因为PD3为PHY的输入引脚,所有我们这里直接配置它为输出高,这样就为正常工作状态

串口1我们设置它的波特率为115200,添加发送DMA,注意!如果添加了发送或者接收DMA则必须开启串口中断。

ETH设置,这里主要是注意PHY的地址,原子407的PHY地址引脚为悬空,地址为0。

ETH设置的 Advanced Parameters 选项里面,我们选择PHY为USER PHY,名字我们取 LAN7820A,其它的设置全用默认即可。要改的寄存器基本就只有ExternalPHY Configuration,但是我看了下官方的例子,基本没有用到这寄存器里面的值,只有在使用操作系统并且时能了连接状态变更回调或者其它检测的时候才用到,所有这里我们也不管,都用默认值。


LWIP设置 ,我们关闭DHCP,采用静态IP,因为我所在的网关为192.168.0.XXX,所以IP必须为192.168.0.XXX。这里我设置IP为192.168.0.120。因为要用到TCP 和 UDP(TFTP就是用的UDP),所以这两个都是时能的.其它设置选项我们先不管,直接点OK。

4、生成工程代码
点左上角的Project,选择Settings ...(不建议直接点黄色的齿轮)


取个非中文的工程名,选择IDE为KEIL MDK V5


点击OK。再点右上方的黄色齿轮即可生成工程代码。
三、工程代码
生成的代码的试图如下:
QQ截图20170724085618.png
1、TCP Server 的实现
在math.h中 加入
#include "stm32f4xx_hal.h"
#include "stdio.h"

用于支持printf 及 一些 HAL 定义的 数据结构

在uart.c 文件中加入如下代码,用于把printf输出到串口1
[mw_shl_code=c,true]/* USER CODE BEGIN 0 */
int fputc(int c,FILE * f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&c,1,20);
return c;
}
/* USER CODE END 0 */[/mw_shl_code]


注意一定要在 /* USER CODE BEGIN X */ 与 /* USER CODE END X */ 中间添加代码,不然重新用STM32CubeMX生成代码后就会被覆盖。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

3

帖子

0

精华

新手入门

积分
11
金钱
11
注册时间
2019-1-17
在线时间
2 小时
发表于 2019-1-17 19:30:53 | 显示全部楼层
搭建好了之后,客户端点put按钮有时候没有反应,服务器没有接受到数据,不知道咋回事
回复 支持 1 反对 0

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-7-24 10:09:56 | 显示全部楼层
本帖最后由 513393302@qq.com 于 2017-7-24 10:25 编辑

在LWIP.C 中添加如下代码:
[mw_shl_code=c,true]void User_notification(void)
{
if (netif_is_up(&gnetif))
{
uint8_t iptxt[20];
sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&gnetif.ip_addr));
printf ("Static IP address: %s\r\n", iptxt);
}
}[/mw_shl_code]

这个代码的功能是把网卡的IP地址通过串口输出,这个是从官方例子里面COPY出来的,例子里面是输出到LCD上,这个是通过串口输出
接下来重点:
新建 tcp_echoserver.c 文件,并在文件中添加如下代码:
[mw_shl_code=c,true]/**
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of and a contribution to the lwIP TCP/IP stack.
*
* Credits go to Adam Dunkels (and the current maintainers) of this software.
*
* Christiaan Simons rewrote this file to get a more stable echo application.
*
**/

/* This file was modified by ST */


#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/tcp.h"
#include "usart.h"

#if LWIP_TCP

static struct tcp_pcb *tcp_echoserver_pcb;

/* ECHO protocol states */
enum tcp_echoserver_states
{
ES_NONE = 0,
ES_ACCEPTED,
ES_RECEIVED,
ES_CLOSING
};

/* structure for maintaing connection infos to be passed as argument
to LwIP callbacks*/
struct tcp_echoserver_struct
{
u8_t state; /* current connection state */
u8_t retries;
struct tcp_pcb *pcb; /* pointer on the current tcp_pcb */
struct pbuf *p; /* pointer on the received/to be transmitted pbuf */
};


static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err);
static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
static void tcp_echoserver_error(void *arg, err_t err);
static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb);
static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);
static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es);
static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es);


/**
* @brief Initializes the tcp echo server
* @param None
* @retval None
*/
void tcp_echoserver_init(void)
{
/* create new tcp pcb */
tcp_echoserver_pcb = tcp_new();

if (tcp_echoserver_pcb != NULL)
{
err_t err;

/* bind echo_pcb to port 7 (ECHO protocol) */
err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);

if (err == ERR_OK)
{
/* start tcp listening for echo_pcb */
tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
printf("开始监听 \r\n");
/* initialize LwIP tcp_accept callback function */
tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);
printf("挂载客户端连接回调函数 \r\n");
}
else
{
/* deallocate the pcb */
memp_free(MEMP_TCP_PCB, tcp_echoserver_pcb);
printf("TCP PCB 内存申请失败 \r\n");
}
}
}

/**
* @brief This function is the implementation of tcp_accept LwIP callback
* @param arg: not used
* @param newpcb: pointer on tcp_pcb struct for the newly created tcp connection
* @param err: not used
* @retval err_t: error status
*/
static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
err_t ret_err;
struct tcp_echoserver_struct *es;

LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(err);

/* set priority for the newly accepted tcp connection newpcb */
tcp_setprio(newpcb, TCP_PRIO_MIN);
printf("收到客户端连接请求,设置刚连接的客户端为最低优先级 \r\n");

uint8_t iptxt[20];
sprintf((char *)iptxt, "%s", ip4addr_ntoa((const ip4_addr_t *)&newpcb->remote_ip));
printf ("客户端 IP address: %s\r\n", iptxt);

/* allocate structure es to maintain tcp connection informations */
es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));
if (es != NULL)
{
es->state = ES_ACCEPTED;
es->pcb = newpcb;
es->retries = 0;
es->p = NULL;

printf("为新连接的客户端挂载需要的回调函数及 调用参数 \r\n");

/* pass newly allocated es structure as argument to newpcb */
tcp_arg(newpcb, es);

/* initialize lwip tcp_recv callback function for newpcb */
tcp_recv(newpcb, tcp_echoserver_recv);

/* initialize lwip tcp_err callback function for newpcb */
tcp_err(newpcb, tcp_echoserver_error);

/* initialize lwip tcp_poll callback function for newpcb */
tcp_poll(newpcb, tcp_echoserver_poll, 0);
ret_err = ERR_OK;
}
else
{
/* close tcp connection */
tcp_echoserver_connection_close(newpcb, es);
printf("tcp_echoserver_struct 内存申请失败 关闭连接 \r\n");
/* return memory error */
ret_err = ERR_MEM;
}
return ret_err;
}


/**
* @brief This function is the implementation for tcp_recv LwIP callback
* @param arg: pointer on a argument for the tcp_pcb connection
* @param tpcb: pointer on the tcp_pcb connection
* @param pbuf: pointer on the received pbuf
* @param err: error information regarding the reveived pbuf
* @retval err_t: error code
*/
static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
struct tcp_echoserver_struct *es;
err_t ret_err;

LWIP_ASSERT("arg != NULL",arg != NULL);
printf("收到客户端数据\r\n");
es = (struct tcp_echoserver_struct *)arg;

/* if we receive an empty tcp frame from client => close connection */
if (p == NULL)
{
printf("收到断开连接请求 \r\n");
/* remote host closed connection */
es->state = ES_CLOSING;
if(es->p == NULL)
{
/* we're done sending, close connection */
tcp_echoserver_connection_close(tpcb, es);
}
else
{
printf("发送的数据还未发送完成 \r\n");
/* we're not done yet */
/* acknowledge received packet */
tcp_sent(tpcb, tcp_echoserver_sent);
printf("装载发送完成回调函数 \r\n");
/* send remaining data*/
tcp_echoserver_send(tpcb, es);
}
ret_err = ERR_OK;
}
/* else : a non empty frame was received from client but for some reason err != ERR_OK */
else if(err != ERR_OK)
{
/* free received pbuf*/
if (p != NULL)
{
es->p = NULL;
pbuf_free(p);
}
ret_err = err;
}
else if(es->state == ES_ACCEPTED)
{
/* first data chunk in p->payload */
es->state = ES_RECEIVED;

/* store reference to incoming pbuf (chain) */
es->p = p;

/* initialize LwIP tcp_sent callback function */
tcp_sent(tpcb, tcp_echoserver_sent);
printf("设备刚连接,挂载刚才接收到的数据,设置发送完成回调 \r\n");
/* send back the received data (echo) */
tcp_echoserver_send(tpcb, es);

ret_err = ERR_OK;
}
else if (es->state == ES_RECEIVED)
{
/* more data received from client and previous data has been already sent*/
if(es->p == NULL)
{
es->p = p;
printf("接收到的数据直接回传 \r\n");
/* send back received data */
tcp_echoserver_send(tpcb, es);
}
else
{
struct pbuf *ptr;
printf("上次的数据还未发送完成,把新数据拼接在后面 \r\n");
/* chain pbufs to the end of what we recv'ed previously */
ptr = es->p;
pbuf_chain(ptr,p);
}
ret_err = ERR_OK;
}
else if(es->state == ES_CLOSING)
{
printf("当前已经是关闭连接了,但还是受到数据 \r\n");
/* odd case, remote side closing twice, trash data */
tcp_recved(tpcb, p->tot_len);
es->p = NULL;
pbuf_free(p);
ret_err = ERR_OK;
}
else
{
/* unkown es->state, trash data */
tcp_recved(tpcb, p->tot_len);
es->p = NULL;
pbuf_free(p);
ret_err = ERR_OK;
}
return ret_err;
}

/**
* @brief This function implements the tcp_err callback function (called
* when a fatal tcp_connection error occurs.
* @param arg: pointer on argument parameter
* @param err: not used
* @retval None
*/
static void tcp_echoserver_error(void *arg, err_t err)
{
struct tcp_echoserver_struct *es;

LWIP_UNUSED_ARG(err);
printf("错误 : %d \r\n",err);
es = (struct tcp_echoserver_struct *)arg;
if (es != NULL)
{
/* free es structure */
mem_free(es);
}
}

/**
* @brief This function implements the tcp_poll LwIP callback function
* @param arg: pointer on argument passed to callback
* @param tpcb: pointer on the tcp_pcb for the current tcp connection
* @retval err_t: error code
*/
static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb)
{
err_t ret_err;
struct tcp_echoserver_struct *es;

es = (struct tcp_echoserver_struct *)arg;
if (es != NULL)
{
if (es->p != NULL)
{
tcp_sent(tpcb, tcp_echoserver_sent);
/* there is a remaining pbuf (chain) , try to send data */
tcp_echoserver_send(tpcb, es);
}
else
{
/* no remaining pbuf (chain) */
if(es->state == ES_CLOSING)
{
/* close tcp connection */
tcp_echoserver_connection_close(tpcb, es);
}
}
ret_err = ERR_OK;
}
else
{
/* nothing to be done */
tcp_abort(tpcb);
ret_err = ERR_ABRT;
}
return ret_err;
}

/**
* @brief This function implements the tcp_sent LwIP callback (called when ACK
* is received from remote host for sent data)
* @param None
* @retval None
*/
static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
struct tcp_echoserver_struct *es;

LWIP_UNUSED_ARG(len);

es = (struct tcp_echoserver_struct *)arg;
es->retries = 0;

if(es->p != NULL)
{
/* still got pbufs to send */
tcp_sent(tpcb, tcp_echoserver_sent);
tcp_echoserver_send(tpcb, es);
}
else
{
/* if no more data to send and client closed connection*/
if(es->state == ES_CLOSING)
tcp_echoserver_connection_close(tpcb, es);
}
return ERR_OK;
}


/**
* @brief This function is used to send data for tcp connection
* @param tpcb: pointer on the tcp_pcb connection
* @param es: pointer on echo_state structure
* @retval None
*/
static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
{
struct pbuf *ptr;
err_t wr_err = ERR_OK;
uint16_t Count ;
printf("发送数据的总长度 : %ld \r\n",es->p->tot_len);
printf("发送数据: \r\n");
while ((wr_err == ERR_OK) &&
(es->p != NULL) &&
(es->p->len <= tcp_sndbuf(tpcb)))
{

/* get pointer on pbuf from es structure */
ptr = es->p; //得到当前需要发送的数据缓存 pbuf

for( Count =0 ; Count < ptr->len; Count++ )
{
printf("0X%02X ",((uint8_t *)ptr->payload)[Count]);
}
/* enqueue data for transmission */
wr_err = tcp_write(tpcb, ptr->payload, ptr->len, TCP_WRITE_FLAG_COPY); //发送

if (wr_err == ERR_OK)
{
u16_t plen;
u8_t freed;

plen = ptr->len; //得到当前节点的数据长度

/* continue with next pbuf in chain (if any) */
es->p = ptr->next; //得到链表的下一个节点

if(es->p != NULL) //如果节点不为空
{
/* increment reference count for es->p */
pbuf_ref(es->p); //成员 ref 的值加1 当收到数据时,pbuf成员ref的值为1 ,在pbuf_free()函数中是对成员ref减一 如果结果为0则释放此节点内存并进入下一个节点 否则只是把成员ref的值减一
}

/* chop first pbuf from chain */
do
{
/* try hard to free pbuf */
freed = pbuf_free(ptr); //每执行一个 pbuf中的成员ref的值减一 当它这个节点没有释放时,返回一直为0
}
while(freed == 0); //执行直到释放这个节点内存为止 pbuf_free()函数的返回值表示释放的内存的节点数
/* we can read more data now */
tcp_recved(tpcb, plen); //增加可以接收数据的大小
}
else if(wr_err == ERR_MEM)
{
/* we are low on memory, try later / harder, defer to poll */
es->p = ptr; //重发
}
else
{
/* other problem ?? */
}
}
printf("\r\n");
}

/**
* @brief This functions closes the tcp connection
* @param tcp_pcb: pointer on the tcp connection
* @param es: pointer on echo_state structure
* @retval None
*/
static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
{

/* remove all callbacks */
tcp_arg(tpcb, NULL);
tcp_sent(tpcb, NULL);
tcp_recv(tpcb, NULL);
tcp_err(tpcb, NULL);
tcp_poll(tpcb, NULL, 0);

/* delete es structure */
if (es != NULL)
{
mem_free(es);
}

/* close tcp connection */
tcp_close(tpcb);
printf("已关闭连接 \r\n");
}

#endif /* LWIP_TCP */[/mw_shl_code]


这个代码也是从官方的 TCP echoserver 例子COPY出来的,不过里面加入了串口输出信息,及一些注释,感觉写的很好的,就没怎么改了
看起来代码很多,其实真的不复杂,听了之前的以太网培训,这个理解起来还是很简单的。大致流程如下,先新建一个TCB_PCB、开始监听、挂载客户端连接请求回调函数、挂载接收数据回调函数、挂载发送完成回调函数、挂载出错回调函数、挂载心跳包回调函数、关闭连接等,最重要的是释放内存!每收到一个数据链,它已经被申请了内存,所有在处理完这个数据后一定要释放。
在mian.c文件中加入如下代码:

编译,下载到开发板中。
串口输出如下数据,表示服务器已经开始监听
Start
开始监听
挂载客户端连接回调函数
Static IP address: 192.168.0.120


点击电脑开始菜单,输入CMD点回车,出现命令行,在命令行中输入 :"ping 192.168.0.120"
能ping通,显示如下:
QQ截图20170724085406.png
命令行使用技巧: 直接输入 “help” 可以返回支持的命令, 在命令后面输入  "/?"  可以返回命令的使用说明,比如输入 “ping /?”

2、TCP服务器 测试
用网络调试助手连接TCP服务器(两者必须在一个网关里面,192.168.000.XXX),网络调试助手里选择TCP客户端,远程主机IP为 192.168.0.120 , 端口号为 7,

串口调试助手显示如下信息:
   Start
   开始监听
   挂载客户端连接回调函数
   Static IP address: 192.168.0.120
   收到客户端连接请求,设置刚连接的客户端为最低优先级
   客户端 IP address: 192.168.0.101
   为新连接的客户端挂载需要的回调函数及 调用参数

则表示连接正常,这时网络调试助手输出什么数据就会收到什么数据,同时串口也会输出相关信息
网络调试助手 输出: http://www.cmsoft.cn QQ:10865600
网络调试助手 显示: 【Receive from 192.168.0.120 :7】:http://www.cmsoft.cnQQ:10865600


串口调试助手显示如下:
Start
开始监听
挂载客户端连接回调函数
Static IP address: 192.168.0.120
收到客户端连接请求,设置刚连接的客户端为最低优先级
客户端 IP address: 192.168.0.101
为新连接的客户端挂载需要的回调函数及 调用参数
收到客户端数据
设备刚连接,挂载刚才接收到的数据,设置发送完成回调
发送数据的总长度 : 32
发送数据:
0X68 0X74 0X74 0X70 0X3A 0X2F 0X2F 0X77 0X77 0X77 0X2E 0X63 0X6D 0X73 0X6F 0X66 0X74 0X2E 0X63 0X6E 0X20 0X51 0X51 0X3A 0X31 0X30 0X38 0X36 0X35 0X36 0X30 0X30


网络调试助手点击“断开”按键,串口调试助手输出如下信息:
Start
开始监听
挂载客户端连接回调函数
Static IP address: 192.168.0.120
收到客户端连接请求,设置刚连接的客户端为最低优先级
客户端 IP address: 192.168.0.101
为新连接的客户端挂载需要的回调函数及 调用参数
收到客户端数据
设备刚连接,挂载刚才接收到的数据,设置发送完成回调
发送数据的总长度 : 32
发送数据:
0X68 0X74 0X74 0X70 0X3A 0X2F 0X2F 0X77 0X77 0X77 0X2E 0X63 0X6D 0X73 0X6F 0X66 0X74 0X2E 0X63 0X6E 0X20 0X51 0X51 0X3A 0X31 0X30 0X38 0X36 0X35 0X36 0X30 0X30
收到客户端数据
收到断开连接请求
已关闭连接



通过上面的测试,则表示我们的TCP服务器已经完成。您也可以通过手机连接这个路由器的WIFI,通过 网络调试助手APP 来连接这个开发板的服务器,电脑可以通过 TCP客户端与开发板的TCP服务器连接同时也可以用手机做TCP客户端连接开发板的TCP服务器。



回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-7-24 10:13:59 | 显示全部楼层
3、TFTP Server 的实现
还是在上面的代码上来实现此功能,即在TCP 服务器的代码上实现 TFTP 服务器。
打开刚才STM32CubeMX工程,在原来的基础上来配置 LWIP,配置如下,时能TFTP

点击OK,然后点黄色的齿轮,重新生成代码。因为我们之前写的代码要么是在 /* USER CODE BEGIN X */ 与 /* USER CODE END X */ 中间添加代码要么是在新建的文件中添加代码,所以不会覆盖之前的代码。



注意到了吗?LWIP文件夹下多了一个新的.c,tftp_server.c,既然是在LWIP文件夹下,言外之意就是别人帮我们写好了,不要动这里面的文件内容,相当于库,但是我们还是要知道去怎么用。这里我们直接偷懒,直接打开tptp_server.h,因为如果要给别的文件提供功能函数,那么必须会在.h文件中存在声明。打开tptp_server.h文件,内容如下:
[mw_shl_code=c,true]/** @ingroup tftp
* TFTP context containing callback functions for TFTP transfers
*/
struct tftp_context {
  /**
   * Open file for read/write.
   * @param fname Filename
   * @param mode Mode string from TFTP RFC 1350 (netascii, octet, mail)
   * @param write Flag indicating read (0) or write (!= 0) access
   * @returns File handle supplied to other functions
   */
  void* (*open)(const char* fname, const char* mode, u8_t write);
  /**
   * Close file handle
   * @param handle File handle returned by open()
   */
  void (*close)(void* handle);
  /**
   * Read from file
   * @param handle File handle returned by open()
   * @param buf Target buffer to copy read data to
   * @param bytes Number of bytes to copy to buf
   * @returns >= 0: Success; < 0: Error
   */
  int (*read)(void* handle, void* buf, int bytes);
  /**
   * Write to file
   * @param handle File handle returned by open()
   * @param pbuf PBUF adjusted such that payload pointer points
   *             to the beginning of write data. In other words,
   *             TFTP headers are stripped off.
   * @returns >= 0: Success; < 0: Error
   */
  int (*write)(void* handle, struct pbuf* p);
};

err_t tftp_init(const struct tftp_context* ctx);[/mw_shl_code]


里面就一个 err_t tftp_init(const struct tftp_context* ctx);  从函数名的意思就是初始化,形参是一个结构体指针,而这个结构体里面定义的全是函数指针,那么很明确了,这个文件就是要我们填写轮子。
我们新建一个文件 mytftpserverif.c ,这个文件里面的内容 就是用来写那些轮子的实现函数,内容如下:


[mw_shl_code=c,true]#include "mytftpserverif.h"
#include <string.h>
#include <stdio.h>
#include "main.h"
#include "flash_if.h"
#include "stdlib.h"
#include "string.h"
  
  
__packed typedef struct
{
uint8_t InitFlat;
uint8_t W_R_Mode; //读写模式 1写 0读
uint32_t Flash_Addr ;
}Iap_FlashCont;
  
  
static Iap_FlashCont Iapflashcont ;
  
  
  
static void * OpenFile(const char* fname, const char* mode, u8_t write);
static void Close_File(void* handle);
static int Read_File(void* handle, void* buf, int bytes);
static int Write_File(void* handle, struct pbuf* p);
  
const struct tftp_context tftpContext={ //TFTP SERVER 对接接口
OpenFile,
Close_File,
Read_File,
Write_File,
};
  
  
/**
* 打开文件 返回文件句柄
* @param const char* fname 文件名
* @param const char* mode
* @param u8_t write 模式 1写 0读
* @returns 文件句柄
*/
static void * OpenFile(const char* fname, const char* mode, u8_t write)
{
printf("打开文件 %s \r\n",fname);
printf("打开模式 %s \r\n",mode);
Iapflashcont.W_R_Mode = write ;
Iapflashcont.W_R_Mode ? printf("写文件\r\n") : printf("读文件\r\n");
  
if(Iapflashcont.W_R_Mode == 1)
{
FLASH_If_Init(); //解锁
Iapflashcont.Flash_Addr = USER_FLASH_FIRST_PAGE_ADDRESS ; //FLASH起始地址
printf("开始擦除Flash 时间较长 \r\n");
if( FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS) == 0 ) //擦除用户区FLASH数据
{
Iapflashcont.InitFlat =1 ; //标记初始化完成
printf("Write File Init Succes \r\n");
}
} //如果为读文件模式
else if(memcmp(fname,"flash.bin",strlen("flash.bin"))==0) //可以读内部FLASH
{
Iapflashcont.InitFlat =1 ; //标记初始化完成
printf("Read File Init Succes \r\n");
Iapflashcont.Flash_Addr = FLASH_BASE ; //FLASH起始地址
}
return (Iapflashcont.InitFlat) ? (&Iapflashcont) : NULL ; //如果初始化成功 返回有效句柄
}
  
/**
* 关闭文件句柄
* @param None
* @param None
* @param None
* @returns None
*/
static void Close_File(void* handle)
{
Iap_FlashCont * Filehandle = (Iap_FlashCont *) handle ;
  
FLASH_If_UnInit(); //FLASH上锁
Filehandle->InitFlat = 0 ;
printf("关闭文件\r\n");
if(Filehandle->W_R_Mode) //如果之前是写文件
{
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;
/* Check if valid stack address (RAM address) then jump to user application */
if (((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (USER_FLASH_FIRST_PAGE_ADDRESS + 4);
Jump_To_Application = (pFunction)JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) USER_FLASH_FIRST_PAGE_ADDRESS);
Jump_To_Application();
/* do nothing */
while(1);
}
}
}
  
/**
* 读取文件数据
* @param handle 文件句柄
* @param *buf 保存数据的缓存
* @param bytes 读取的数据长度
* @returns 返回读取数据长度 小于0则错误
*/
static int Read_File(void* handle, void* buf, int bytes)
{
Iap_FlashCont * Filehandle = (Iap_FlashCont *) handle ;
printf("读取文件数据 读取长度: %ld \r\n",bytes);
if(!Filehandle->InitFlat) //未初始化
{
return ERR_MEM ;
}
uint16_t Count ;
for( Count = 0 ; (Count < bytes)&&(Filehandle->Flash_Addr<=FLASH_END) ; Count++ ,Filehandle->Flash_Addr++ )
{
((uint8_t *)buf)[Count] = *((__IO uint8_t *) Filehandle->Flash_Addr);
}
return Count;
}
  
  
/**
* 写文件数据
* @param handle 文件句柄
* @param struct pbuf* p 数据缓存结构体 里面的数据缓存全为实际需要写入的数据
* @returns 小于0为错误
*/
static int Write_File(void* handle, struct pbuf* p)
{
uint16_t Count ;
Iap_FlashCont * Filehandle = (Iap_FlashCont *) handle ;
printf("写文件数据 数据长度 %ld \r\n",p->len);
if(!Filehandle->InitFlat)
{
printf("写文件 没有初始化 \r\n");
return ERR_MEM ;
}
Count = p->len/4 +((p->len%4)>0); //得到要写入的数据
printf("开始写FLASH 地址 :0X%08X \r\n",Filehandle->Flash_Addr);
if( FLASH_If_Write((__IO uint32_t*)&Filehandle->Flash_Addr,(uint32_t *)p->payload,Count) == 0 )
{
printf("写FLASH成功 下一次地址 :0X%08X \r\n",Filehandle->Flash_Addr);
return ERR_OK;
}
else
{
printf("写FLASH 失败 出错地址 : 0X%08X \r\n",Filehandle->Flash_Addr);
}
return ERR_MEM;
}[/mw_shl_code]

这上面的代码我都写了注释,理解应该难度不大,就是根据tptp_server.h中的结构体实现对应的轮子函数,分别为:
static void * OpenFile(const char* fname, const char* mode, u8_t write);
static void Close_File(void* handle);
static int Read_File(void* handle, void* buf, int bytes);
static int Write_File(void* handle, struct pbuf* p);


mytftpserverif.h文件内容如下:

#ifndef     _MYTFTPSERVERIF_H_
#define     _MYTFTPSERVERIF_H_
#include "lwip/mem.h"
#include "lwip/udp.h"
#include "lwip/apps/tftp_server.h"
extern const struct tftp_context  tftpContext;
#endif



因为我们是要通过TFTP 来实现 IAP 功能 ,所有一定有FLASH 的操作,这里我们之间COPY官方例子的里面的flash_if.c和flash_if.h,flash_if.c文件内容如下:
[mw_shl_code=c,true]#include "flash_if.h"
  
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
  
/* Private functions ---------------------------------------------------------*/
  
/**
* @brief Unlocks Flash for write access
* @param None
* @retval None
*/
void FLASH_If_Init(void)
{
HAL_FLASH_Unlock();
}
  
/**
* @brief This function does an erase of all user flash area
* @param StartSector: start of user flash area
* @retval 0: user flash area successfully erased
* 1: error occurred
*/
int8_t FLASH_If_Erase(uint32_t StartSector)
{
uint32_t FlashAddress;
  
FlashAddress = StartSector;
  
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
  
if (FlashAddress <= (uint32_t) USER_FLASH_LAST_PAGE_ADDRESS)
{
FLASH_EraseInitTypeDef FLASH_EraseInitStruct;
uint32_t sectornb = 0;
  
FLASH_EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
FLASH_EraseInitStruct.Sector = FLASH_SECTOR_5;
FLASH_EraseInitStruct.NbSectors = 7;
FLASH_EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
  
if (HAL_FLASHEx_Erase(&FLASH_EraseInitStruct, §ornb) != HAL_OK)
{
return (1);
}
}
else
{
return (1);
}
  
return (0);
}
/**
* @brief This function writes a data buffer in flash (data are 32-bit aligned).
* @note After writing data buffer, the flash content is checked.
* @param FlashAddress: start address for writing data buffer
* @param Data: pointer on data buffer
* @param DataLength: length of data buffer (unit is 32-bit word)
* @retval 0: Data successfully written to Flash memory
* 1: Error occurred while writing data in Flash memory
* 2: Written Data in flash memory is different from expected one
*/
uint32_t FLASH_If_Write(__IO uint32_t* FlashAddress, uint32_t* Data ,uint16_t DataLength)
{
uint32_t i = 0;
  
for (i = 0; (i < DataLength) && (*FlashAddress <= (USER_FLASH_END_ADDRESS-4)); i++)
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, *FlashAddress, *(uint32_t*)(Data+i)) == HAL_OK)
{
/* Check the written value */
if (*(uint32_t*)*FlashAddress != *(uint32_t*)(Data+i))
{
/* Flash content doesn't match SRAM content */
return(2);
}
/* Increment FLASH destination address */
*FlashAddress += 4;
}
else
{
/* Error occurred while writing data in Flash memory */
return (1);
}
}
  
return (0);
}
  
  
/**
* @brief locks Flash for write access
* @param None
* @retval None
*/
void FLASH_If_UnInit(void)
{
HAL_FLASH_Lock();
}[/mw_shl_code]

这里面就是FLASH 解锁、FLASH 扇区擦除、写FLASH、FLASH上锁(自己添加的)

flash_if.h内容如下:
[mw_shl_code=c,true]#ifndef __FLASH_IF_H
#define __FLASH_IF_H
  
/* Includes ------------------------------------------------------------------*/
#include "main.h"
  
  
//起始地址
#define USER_FLASH_FIRST_PAGE_ADDRESS 0x08020000 /* Only as example see comment */
  
#define USER_FLASH_LAST_PAGE_ADDRESS 0x080E0000
  
//结束地址
#define USER_FLASH_END_ADDRESS FLASH_END
  
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
#define USER_FLASH_SIZE (USER_FLASH_END_ADDRESS - USER_FLASH_FIRST_PAGE_ADDRESS)
  
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
uint32_t FLASH_If_Write(__IO uint32_t* Address, uint32_t* Data, uint16_t DataLength);
int8_t FLASH_If_Erase(uint32_t StartSector);
void FLASH_If_Init(void);
void FLASH_If_UnInit(void);
  
#endif /* __FLASH_IF_H */[/mw_shl_code]

mian.c添加如下代码:



至此,TFTP服务器搭建完成,下载到开发板
串口显示如下信息:
Start
开始监听
挂载客户端连接回调函数
TFTP初始化成功
Static IP address: 192.168.0.120



可以通过上面的 TCP服务器测试步骤来测试TCP服务器功能,我这边已经测试了,工作正常,ping 也能通




回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-7-24 10:15:28 | 显示全部楼层
本帖最后由 513393302@qq.com 于 2017-7-24 13:45 编辑

TFTP 服务器测试
先在网上下载 tftpd32.exe 软件,选择tftp Client,输入主机IP地址,选择需要下载的bin文件,BLOCK SIZE 选择 512。这里在FLASH_IF.H里面定义APP的起始地址
#define USER_FLASH_FIRST_PAGE_ADDRESS 0x08020000
所以APP的起始地址要设置成0x08020000,这里选择原子的RTC 的APP,在里面我把地址重新改了下
tftpd32.exe 软件 设置如下:


点put即可发送文件数据给MCU,MCU会把这个文件的数据写入到内部FLASH里面,完成后会跳转到APP代码。

点击Put按钮后,串口输出如下:



执行完成后会运行APP代码



重新复位后又会返回服务器的代码,在TFTP 服务器中,加入了读取内部FLASH数据的功能,但只能读取全部的数据,在tftpd32.exe软件中选择flash.bin,点击Get按钮,即会在你选择的目录下创建一个flash.bin文件,内容为MCU内部FLASH数据。注意,块大小一定要为512.



点击Get后,串口助手显示如下:



开始读的第1块和第2块非常慢,而且读取完成后返回说有2个块好像有问题,因为我是从0X08000000开始读的,我想可能是有冲突或者不允许读,这个问题也很好解决,只要把读取的地址不设置成FLASH的基地址就可以了。

工程代码:

HAL_LWIP_TFTP_TEST.zip (848.12 KB, 下载次数: 1366)
回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-7-24 10:17:06 | 显示全部楼层
请求原子哥 把发帖里面关于20000字的约束去掉, 或者在文本框下面 显示还可以输入的字符数,当超过约束时可以把显示超过的字符数变成红色。 写这个帖子因为这个字符数约束 写了几遍,又不能保持草稿,草稿也有这个字符约束。  @正点原子
还有我不知道保存的草稿能直接发布吗?我这个帖子都是保存几个草稿然后一个个复制过来的,感觉有点麻烦
回复 支持 反对

使用道具 举报

6

主题

69

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1786
金钱
1786
注册时间
2015-4-20
在线时间
258 小时
发表于 2017-7-24 10:26:00 | 显示全部楼层
6666666
回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-7-24 10:34:37 | 显示全部楼层
回复 支持 反对

使用道具 举报

11

主题

216

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1124
金钱
1124
注册时间
2015-2-27
在线时间
485 小时
发表于 2017-7-24 12:29:02 | 显示全部楼层
为什么不能做成一个PDF啊,这没法看呀
回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-7-24 12:48:01 | 显示全部楼层
enan 发表于 2017-7-24 12:29
为什么不能做成一个PDF啊,这没法看呀

好的,谢谢。我处理下
回复 支持 反对

使用道具 举报

70

主题

6758

帖子

0

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
12991
金钱
12991
注册时间
2012-11-26
在线时间
3789 小时
发表于 2017-7-24 13:22:34 | 显示全部楼层
不错   很赞  支持下
学无止境
回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-7-24 13:45:44 | 显示全部楼层
enan 发表于 2017-7-24 12:29
为什么不能做成一个PDF啊,这没法看呀

已上传PDF附件
回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-7-24 13:45:58 | 显示全部楼层
jermy_z 发表于 2017-7-24 13:22
不错   很赞  支持下

谢谢大神
回复 支持 反对

使用道具 举报

11

主题

216

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1124
金钱
1124
注册时间
2015-2-27
在线时间
485 小时
发表于 2017-7-24 14:58:24 | 显示全部楼层

非常棒,感谢
回复 支持 反对

使用道具 举报

4

主题

32

帖子

0

精华

初级会员

Rank: 2

积分
102
金钱
102
注册时间
2017-7-18
在线时间
29 小时
发表于 2017-8-2 09:25:01 | 显示全部楼层
其实我更想知道怎么搭建FTP服务器
回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2017-8-2 10:17:17 | 显示全部楼层
Szep 发表于 2017-8-2 09:25
其实我更想知道怎么搭建FTP服务器

上面不是已经实现了么?
只要实现那几个轮子函数就可以了 ,在这几个函数中加入文件操作
TFTP服务器 就是一个文件传输协议  LWIP 已经实现了这个通信过程
回复 支持 反对

使用道具 举报

1

主题

50

帖子

0

精华

高级会员

Rank: 4

积分
805
金钱
805
注册时间
2012-7-19
在线时间
111 小时
发表于 2017-8-3 09:49:55 | 显示全部楼层
支持,66666
回复 支持 反对

使用道具 举报

4

主题

93

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
262
金钱
262
注册时间
2016-10-27
在线时间
47 小时
发表于 2017-8-3 11:17:19 | 显示全部楼层
cool                    
回复 支持 反对

使用道具 举报

12

主题

36

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
202
金钱
202
注册时间
2013-10-27
在线时间
34 小时
发表于 2017-9-21 14:35:10 | 显示全部楼层
支持,很有用
回复 支持 反对

使用道具 举报

12

主题

68

帖子

0

精华

初级会员

Rank: 2

积分
176
金钱
176
注册时间
2016-9-20
在线时间
56 小时
发表于 2017-10-9 15:50:46 | 显示全部楼层
膜拜一波!
回复 支持 反对

使用道具 举报

7

主题

88

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
273
金钱
273
注册时间
2015-1-10
在线时间
69 小时
发表于 2017-10-14 12:50:23 | 显示全部楼层
非常详细,,感谢
回复 支持 反对

使用道具 举报

2

主题

7

帖子

0

精华

新手上路

积分
44
金钱
44
注册时间
2017-11-3
在线时间
8 小时
发表于 2018-3-16 16:20:01 | 显示全部楼层
513393302@qq.co 发表于 2017-7-24 10:15
TFTP 服务器测试
先在网上下载 tftpd32.exe 软件,选择tftp Client,输入主机IP地址,选择需要下载的bin ...

楼主,如果我采用的是DP84838芯片,我需要怎么改呢?谢谢
回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
 楼主| 发表于 2018-3-18 12:08:19 | 显示全部楼层
wsqh 发表于 2018-3-16 16:20
楼主,如果我采用的是DP84838芯片,我需要怎么改呢?谢谢

修改与PHY对接的接口,还有PHY的地址即可
回复 支持 反对

使用道具 举报

37

主题

246

帖子

0

精华

高级会员

Rank: 4

积分
766
金钱
766
注册时间
2016-9-10
在线时间
306 小时
发表于 2018-3-20 15:35:13 | 显示全部楼层
LM,或许以后用的到
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2017-9-19
在线时间
50 小时
发表于 2018-9-14 20:42:12 | 显示全部楼层
楼主有尝试过http方式的吗?
回复 支持 反对

使用道具 举报

5

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
199
金钱
199
注册时间
2017-7-18
在线时间
60 小时
发表于 2019-3-29 15:42:23 | 显示全部楼层
这种方式是不是没有限制bin文件大小,传输比较大的文件是不是也是可以的保证完整性的(当然要在芯片总存储大小减去iap的空间之内)?????
回复 支持 反对

使用道具 举报

0

主题

8

帖子

0

精华

新手上路

积分
27
金钱
27
注册时间
2019-4-8
在线时间
5 小时
发表于 2019-4-10 10:43:42 | 显示全部楼层
有用!!!!!!谢谢!!收藏了!
回复 支持 反对

使用道具 举报

10

主题

49

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
285
金钱
285
注册时间
2017-8-10
在线时间
86 小时
发表于 2019-5-5 11:42:58 | 显示全部楼层
这个项目实现完工,除了以下知识,还需要哪些知识?
1.LWIP
2.Flash SRAM
3.STM32CubeMX
4.精通STM32基础,uart,SPI
回复 支持 反对

使用道具 举报

10

主题

49

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
285
金钱
285
注册时间
2017-8-10
在线时间
86 小时
发表于 2019-5-7 12:14:15 | 显示全部楼层
朋友,你发的附件工程,没有LWIP和ST库文件,,这些文件没有呀,附件请看29楼
回复 支持 反对

使用道具 举报

10

主题

49

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
285
金钱
285
注册时间
2017-8-10
在线时间
86 小时
发表于 2019-5-7 12:16:02 | 显示全部楼层
朋友,你发的附件工程,没有LWIP和ST库文件,,这些文件没有呀,请看附件

HAL库文件

HAL库文件

LWIP库文件

LWIP库文件
回复 支持 反对

使用道具 举报

10

主题

49

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
285
金钱
285
注册时间
2017-8-10
在线时间
86 小时
发表于 2019-5-7 12:16:53 | 显示全部楼层
tao2008 发表于 2019-5-7 12:16
朋友,你发的附件工程,没有LWIP和ST库文件,,这些文件没有呀,请看附件

按照您的文档指示,移植了好几天都没搞出来。不知道是哪里原因,然后用您的工程,发现您的工程缺少文件
回复 支持 反对

使用道具 举报

2

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
88
金钱
88
注册时间
2019-4-20
在线时间
11 小时
发表于 2019-8-20 14:25:59 | 显示全部楼层
server已经成功移植f107板子  楼主有TFTP做客户端的相关资料有吗
回复 支持 反对

使用道具 举报

1

主题

3

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-11-7
在线时间
8 小时
发表于 2019-11-8 14:01:42 | 显示全部楼层
楼主是用keil开发的吗 ,感觉配置界面和keil不一样?
回复 支持 反对

使用道具 举报

9

主题

30

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
224
金钱
224
注册时间
2014-5-2
在线时间
49 小时
发表于 2020-12-27 19:52:02 | 显示全部楼层
513393302@qq.co 发表于 2017-8-2 10:17
上面不是已经实现了么?
只要实现那几个轮子函数就可以了 ,在这几个函数中加入文件操作
TFTP服务器 就 ...

不一样,ftp是tcp协议层,tftp用的是udp协议,还要调整其它位置
回复 支持 反对

使用道具 举报

0

主题

6

帖子

0

精华

初级会员

Rank: 2

积分
112
金钱
112
注册时间
2020-11-18
在线时间
59 小时
发表于 2021-4-28 13:38:38 | 显示全部楼层
学习一下
回复 支持 反对

使用道具 举报

3

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
125
金钱
125
注册时间
2019-5-26
在线时间
40 小时
发表于 2021-5-28 15:38:38 | 显示全部楼层
TFTP不是基于UDP协议的吗?TFTP32上位机也支持TCP吗?
回复 支持 反对

使用道具 举报

5

主题

20

帖子

0

精华

初级会员

Rank: 2

积分
139
金钱
139
注册时间
2018-11-19
在线时间
114 小时
发表于 2021-9-18 08:49:43 | 显示全部楼层
感谢分享。不知道 能不能在ENC28J60上移植成功!!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-26 23:11

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表