基于正点原子阿波罗F429开发板的LWIP应用(5)——TFTP在线升级功能

说在开头

        正点原子F429开发板主芯片采用的是STM32F429IGT6,网络PHY芯片采用的是LAN8720A(V1)和YT8512C(V2),采用的是RMII连接,PHY_ADDR为0;在代码中将会对不同的芯片做出适配。

        CubeMX版本:6.6.1;

        F4芯片组Pack包版本:STM32Cube FW_F4 V1.27.0;

        本实验代码以《基于正点原子阿波罗F429开发板的LWIP应用(3)——Netbiosns功能》一章的代码作为基础;

        TFTP(简单文件传输协议)是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,底层使用的是UDP通讯,默认端口号为69。

        虽然UDP是不可靠的传输协议,但TFTP协议在应用层中进行了二次检验以保证接收的内容正确:在传输前会将文件按照设置的Block Size将文件分割并且在组成TFTP包时会加上Block编号(从1开始);在传输时会按顺序将每一个Block发送同时要求接收方每接收一个block就返回一个ACK,接收方在接收到每一个Block后会判断Block的顺序是否错误(是否是上一个Block加1);在传输完成后会核对Block的数量和总的字节数是否正确。

TFTP-boot实现过程如下

1、在“LWIP/App”中新建“flash_if.c ”、“flash_if.ch”、“tftpserver.c”、“tftpserver.h”4个文件,并分别添加以下代码:

“flash_if.c ”代码如下:

/**
  ******************************************************************************
  * @file    flash_if.c 
  * @author  MCD Application Team
  * @version V1.0.0
  * @date    31-October-2011
  * @brief   This file provides high level routines to manage internal Flash 
  *          programming (erase and write). 
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#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, &sectornb) != 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);
}
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

“flash_if.h ”代码如下:

/**
  ******************************************************************************
  * @file    flash_if.h 
  * @author  MCD Application Team
  * @version V1.0.0
  * @date    31-October-2011
  * @brief   Header for flash_if.c module
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __FLASH_IF_H
#define __FLASH_IF_H

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* 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);

#endif /* __FLASH_IF_H */

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

“tftpserver.c ”代码如下:

/**
  ******************************************************************************
  * @file    tftpserver.c
  * @author  MCD Application Team
  * @version V1.0.0
  * @date    31-October-2011   
  * @brief   basic tftp server implementation for IAP (only Write Req supported)
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************  
  */

/* Includes ------------------------------------------------------------------*/
#include "tftpserver.h"
#include "flash_if.h"
#include <string.h>
#include <stdio.h>
#include "main.h"

#ifdef USE_IAP_TFTP

/* Private variables ---------------------------------------------------------*/
static uint32_t Flash_Write_Address;
static struct udp_pcb *UDPpcb;
//static __IO uint32_t total_count=0;


/* Private function prototypes -----------------------------------------------*/
static void IAP_wrq_recv_callback(void *_args, struct udp_pcb *upcb, struct pbuf *pkt_buf, 
                        const ip_addr_t *addr, u16_t port);

static int IAP_tftp_process_write(struct udp_pcb *upcb, const ip_addr_t *to, int to_port);

static void IAP_tftp_recv_callback(void *arg, struct udp_pcb *Upcb, struct pbuf *pkt_buf,
                        const ip_addr_t *addr, u16_t port);

static void IAP_tftp_cleanup_wr(struct udp_pcb *upcb, tftp_connection_args *args);
static tftp_opcode IAP_tftp_decode_op(char *buf);
static u16_t IAP_tftp_extract_block(char *buf);
static void IAP_tftp_set_opcode(char *buffer, tftp_opcode opcode);
static void IAP_tftp_set_block(char* packet, u16_t block);
static err_t IAP_tftp_send_ack_packet(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, int block);

/* Private functions ---------------------------------------------------------*/


/**
  * @brief Returns the TFTP opcode 
  * @param buf: pointer on the TFTP packet 
  * @retval none
  */
static tftp_opcode IAP_tftp_decode_op(char *buf)
{
  return (tftp_opcode)(buf[1]);
}

/**
  * @brief  Extracts the block number
  * @param  buf: pointer on the TFTP packet 
  * @retval block number
  */
static u16_t IAP_tftp_extract_block(char *buf)
{
  u16_t *b = (u16_t*)buf;
  return ntohs(b[1]);
}

/**
  * @brief Sets the TFTP opcode 
  * @param  buffer: pointer on the TFTP packet
  * @param  opcode: TFTP opcode
  * @retval none
  */
static void IAP_tftp_set_opcode(char *buffer, tftp_opcode opcode)
{
  buffer[0] = 0;
  buffer[1] = (u8_t)opcode;
}

/**
  * @brief Sets the TFTP block number 
  * @param packet: pointer on the TFTP packet 
  * @param  block: block number
  * @retval none
  */
static void IAP_tftp_set_block(char* packet, u16_t block)
{
  u16_t *p = (u16_t *)packet;
  p[1] = htons(block);
}

/**
  * @brief Sends TFTP ACK packet  
  * @param upcb: pointer on udp_pcb structure
  * @param to: pointer on the receive IP address structure
  * @param to_port: receive port number
  * @param block: block number
  * @retval: err_t: error code 
  */
static err_t IAP_tftp_send_ack_packet(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, int block)
{
  err_t err;
  struct pbuf *pkt_buf; /* Chain of pbuf's to be sent */

  /* create the maximum possible size packet that a TFTP ACK packet can be */
  char packet[TFTP_ACK_PKT_LEN];

  /* define the first two bytes of the packet */
  IAP_tftp_set_opcode(packet, TFTP_ACK);

  /* Specify the block number being ACK'd.
   * If we are ACK'ing a DATA pkt then the block number echoes that of the DATA pkt being ACK'd (duh)
   * If we are ACK'ing a WRQ pkt then the block number is always 0
   * RRQ packets are never sent ACK pkts by the server, instead the server sends DATA pkts to the
   * host which are, obviously, used as the "acknowledgement".  This saves from having to sEndTransferboth
   * an ACK packet and a DATA packet for RRQs - see RFC1350 for more info.  */
  IAP_tftp_set_block(packet, block);

  /* PBUF_TRANSPORT - specifies the transport layer */
  pkt_buf = pbuf_alloc(PBUF_TRANSPORT, TFTP_ACK_PKT_LEN, PBUF_POOL);

  if (!pkt_buf)      /*if the packet pbuf == NULL exit and EndTransfertransmission */
  {
    return ERR_MEM;
  }

  /* Copy the original data buffer over to the packet buffer's payload */
  memcpy(pkt_buf->payload, packet, TFTP_ACK_PKT_LEN);

  /* Sending packet by UDP protocol */
  err = udp_sendto(upcb, pkt_buf, to, to_port);

  /* free the buffer pbuf */
  pbuf_free(pkt_buf);

  return err;
}

/**
  * @brief  Processes data transfers after a TFTP write request
  * @param  _args: used as pointer on TFTP connection args
  * @param  upcb: pointer on udp_pcb structure
  * @param pkt_buf: pointer on a pbuf stucture
  * @param ip_addr: pointer on the receive IP_address structure
  * @param port: receive port address
  * @retval none
  */
/*写请求回调函数*/
static void IAP_wrq_recv_callback(void *_args, struct udp_pcb *upcb, struct pbuf *pkt_buf, const ip_addr_t *addr, u16_t port)
{
  tftp_connection_args *args = (tftp_connection_args *)_args;
  uint32_t data_buffer[128];/*特别注意这里的单位是字,总长度512字节*/
	uint16_t rec_date_len = 0;
	uint16_t write_count = 0;


	/*验证数据包来源和端口是否匹配 */
  if (pkt_buf->len != pkt_buf->tot_len)
  {
    IAP_tftp_cleanup_wr(upcb, args);//如果分配处理空间失败
		pbuf_free(pkt_buf);
		return;
  }

  /* Does this packet have any valid data to write? */
  if ((pkt_buf->len > TFTP_DATA_PKT_HDR_LEN) &&
      (IAP_tftp_extract_block(pkt_buf->payload) == (args->block + 1)))
  {
		rec_date_len = pkt_buf->len - TFTP_DATA_PKT_HDR_LEN;/*保存本次接收到有效数据的长度*/
		
    /* copy packet payload to data_buffer */
    pbuf_copy_partial(pkt_buf, data_buffer, rec_date_len, TFTP_DATA_PKT_HDR_LEN);/*拷贝接收到的数据*/
		
		
    
    write_count = rec_date_len/4;
    if ((rec_date_len%4)!=0)   write_count++;
//		printf("rec_date_len = %d, write count = %d\n",rec_date_len, write_count);
     
    /* Write received data in Flash */
    if(FLASH_If_Write(&Flash_Write_Address, data_buffer ,write_count) != 0)
		{
			printf("write failed\n");

			IAP_tftp_cleanup_wr(upcb, args);
			pbuf_free(pkt_buf);
			/* init flash */
			FLASH_If_Init();//flash解锁
			/* erase user flash area */
			FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS);
			HAL_NVIC_SystemReset();//系统复位
		}
       
    /* update our block number to match the block number just received */
    args->block++;//更新端口号
    /* update total bytes  */
    (args->tot_bytes) += (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);//更新已经接收字节总数

    /* This is a valid pkt but it has no data.  This would occur if the file being
       written is an exact multiple of 512 bytes.  In this case, the args->block
       value must still be updated, but we can skip everything else.    */
  }
  else if (IAP_tftp_extract_block(pkt_buf->payload) == (args->block + 1))//如果数据包中不含数据
  {
    /* update our block number to match the block number just received  */
		/*当要发送的字节总数是512的整数倍时会出现*/
    args->block++;//数据包总数+1,但是接收的数据不加
  }
	else
	{
			printf("ZZZSL	ERROR	=	%d\n"	,(args->block	+	1));//打印错误的数据包的编号
	}
  
  /* Send the appropriate ACK pkt*/
  IAP_tftp_send_ack_packet(upcb, addr, port, args->block);   

  /* If the last write returned less than the maximum TFTP data pkt length,
   * then we've received the whole file and so we can quit (this is how TFTP
   * signals the EndTransferof a transfer!)
   */
	//判断数据包是否接收完毕 ,如果数据包长度小于512则认为是最后一个数据包
  if (pkt_buf->len < TFTP_DATA_PKT_LEN_MAX)
  {
    IAP_tftp_cleanup_wr(upcb, args);
    pbuf_free(pkt_buf);
		printf("total block num =	%d, total len = %d\n"	,(args->block), (args->tot_bytes));//打印错误的数据包的编号
		HAL_Delay(10);
		//写入完成,重启
		HAL_NVIC_SystemReset();//系统复位
  }
  else
  {
    pbuf_free(pkt_buf);
		memset(data_buffer, 0, sizeof(data_buffer));
    return;
  }
}


/**
  * @brief  Processes TFTP write request
  * @param  to: pointer on the receive IP address
  * @param  to_port: receive port number
  * @retval none
  */
/*写请求处理函数*/
static int IAP_tftp_process_write(struct udp_pcb *upcb, const ip_addr_t *to, int to_port)
{
  tftp_connection_args *args = NULL;//建立控制块
  /* This function is called from a callback,
  * therefore interrupts are disabled,
  * therefore we can use regular malloc   */
  args = mem_malloc(sizeof *args);//分配处理空间
  if (!args)
  {
    IAP_tftp_cleanup_wr(upcb, args);//如果分配处理空间失败
    return 0;
  }

  args->op = TFTP_WRQ;
  args->to_ip.addr = to->addr;
  args->to_port = to_port;
  /* the block # used as a positive response to a WRQ is _always_ 0!!! (see RFC1350)  */
  args->block = 0;
  args->tot_bytes = 0;

  /* set callback for receives on this UDP PCB (Protocol Control Block) */
  udp_recv(upcb, IAP_wrq_recv_callback, args);

  /* init flash */
  FLASH_If_Init();//flash解锁
  
  /* erase user flash area */
  FLASH_If_Erase(USER_FLASH_FIRST_PAGE_ADDRESS);
	
	printf("FLASH_If_Erase finished\n");
 
  Flash_Write_Address = USER_FLASH_FIRST_PAGE_ADDRESS;    
  /* initiate the write transaction by sending the first ack */
  IAP_tftp_send_ack_packet(upcb, to, to_port, args->block);

  return 0;
}


/**
  * @brief  Processes traffic received on UDP port 69
  * @param  args: pointer on tftp_connection arguments
  * @param  upcb: pointer on udp_pcb structure
  * @param  pbuf: pointer on packet buffer
  * @param  addr: pointer on the receive IP address
  * @param  port: receive port number
  * @retval none
  */
/*处理UDP端口69上接收的流量*/
static void IAP_tftp_recv_callback(void *arg, struct udp_pcb *upcb, struct pbuf *pkt_buf,
                        const ip_addr_t *addr, u16_t port)
{
  tftp_opcode op;
  struct udp_pcb *upcb_tftp_data;
  err_t err;

	uint32_t	IPaddress	=	addr->addr;

	/*打印客户端的IP地址和端口号*/
	printf("\n\rTFTP	RRQ	from:	[%d.%d.%d.%d]:[%d]\n\r",	(u8_t)(IPaddress),	\
							(u8_t)(IPaddress	>>	8),(u8_t)(IPaddress	>>	16),(u8_t)(IPaddress	>>	24),	port);
	
  /* create new UDP PCB structure */
  upcb_tftp_data = udp_new();
  if (!upcb_tftp_data)
  {
    /* Error creating PCB. Out of Memory  */
    return;
  }

  /* bind to port 0 to receive next available free port */
  /* NOTE:  This is how TFTP works.  There is a UDP PCB for the standard port
  * 69 which al transactions begin communication on, however, _all_ subsequent
  * transactions for a given "stream" occur on another port  */
  err = udp_bind(upcb_tftp_data, IP_ADDR_ANY, 0);
  if (err != ERR_OK)
  {
    /* Unable to bind to port */
    return;
  }

  op = IAP_tftp_decode_op(pkt_buf->payload);/*pkt_buf->payload储存着文件名称*/
	/*判断是否是TFTP写请求,如果是就调用写请求回调函数,不是就断开*/
  if (op != TFTP_WRQ)
  {
    /* remove PCB */
    udp_remove(upcb_tftp_data);
  }
  else
  {  
    /* Start the TFTP write mode*/
    IAP_tftp_process_write(upcb_tftp_data, addr, port);
  }
  pbuf_free(pkt_buf);
}


/**
  * @brief  disconnect and close the connection 
  * @param  upcb: pointer on udp_pcb structure
  * @param  args: pointer on tftp_connection arguments
  * @retval none
  */
static void IAP_tftp_cleanup_wr(struct udp_pcb *upcb, tftp_connection_args *args)
{
  /* Free the tftp_connection_args structure */
  mem_free(args);

  /* Disconnect the udp_pcb */
  udp_disconnect(upcb);
  
  /* close the connection */
  udp_remove(upcb);
  
  /* reset the callback function */
  udp_recv(UDPpcb, IAP_tftp_recv_callback, NULL);
 
}

/* Global functions ---------------------------------------------------------*/

/**
  * @brief  Creates and initializes a UDP PCB for TFTP receive operation  
  * @param  none  
  * @retval none
  */
void IAP_tftpd_init(void)
{
  err_t err;
  unsigned port = 69; /* 69 is the port used for TFTP protocol initial transaction */

  /* create a new UDP PCB structure  */
  UDPpcb = udp_new();
  if (!UDPpcb)
  {
    /* Error creating PCB. Out of Memory  */
    return ;
  }

  /* Bind this PCB to port 69  */
  err = udp_bind(UDPpcb, IP_ADDR_ANY, port);/*绑定控制块*/
  if (err == ERR_OK)
  {
    /* Initialize receive callback function  注册服务器数据处理回调函数*/
    udp_recv(UDPpcb, IAP_tftp_recv_callback, NULL);
  } 
}

#endif /* USE_IAP_TFTP */

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

“tftpserver.h ”代码如下:


#ifndef __TFTPSERVER_H_
#define __TFTPSERVER_H_

#include "lwip/mem.h"
#include "lwip/udp.h"

#define USE_IAP_TFTP   /* enable IAP using TFTP */

#define TFTP_OPCODE_LEN         2
#define TFTP_BLKNUM_LEN         2
#define TFTP_DATA_LEN_MAX       512
#define TFTP_DATA_PKT_HDR_LEN   (TFTP_OPCODE_LEN + TFTP_BLKNUM_LEN)
#define TFTP_ERR_PKT_HDR_LEN    (TFTP_OPCODE_LEN + TFTP_ERRCODE_LEN)
#define TFTP_ACK_PKT_LEN        (TFTP_OPCODE_LEN + TFTP_BLKNUM_LEN)
#define TFTP_DATA_PKT_LEN_MAX   (TFTP_DATA_PKT_HDR_LEN + TFTP_DATA_LEN_MAX)
#define TFTP_MAX_RETRIES        3
#define TFTP_TIMEOUT_INTERVAL   5


typedef struct
{
  int op;    /*WRQ */
  /* last block read */
  char data[TFTP_DATA_PKT_LEN_MAX];
  int  data_len;
  /* destination ip:port */
//  struct ip_addr to_ip;
	ip_addr_t to_ip;
  int to_port;
  /* next block number */
  int block;
  /* total number of bytes transferred */
  int tot_bytes;
  /* timer interrupt count when last packet was sent */
  /* this should be used to resend packets on timeout */
  unsigned long long last_time;
 
}tftp_connection_args;


/* TFTP opcodes as specified in RFC1350   */
typedef enum {
  TFTP_RRQ = 1,
  TFTP_WRQ = 2,
  TFTP_DATA = 3,
  TFTP_ACK = 4,
  TFTP_ERROR = 5
} tftp_opcode;


/* TFTP error codes as specified in RFC1350  */
typedef enum {
  TFTP_ERR_NOTDEFINED,
  TFTP_ERR_FILE_NOT_FOUND,
  TFTP_ERR_ACCESS_VIOLATION,
  TFTP_ERR_DISKFULL,
  TFTP_ERR_ILLEGALOP,
  TFTP_ERR_UKNOWN_TRANSFER_ID,
  TFTP_ERR_FILE_ALREADY_EXISTS,
  TFTP_ERR_NO_SUCH_USER,
} tftp_errorcode;


void IAP_tftpd_init(void);


#endif

2、将“flash_if.c ”、“tftpserver.c”添加进MDK的分组“Application/User/LWIP/App”;

3、gpio.c和gpio.h文件添加以下内容:

/*gpio.h文件添加以下内容*/
#define KEY0_Pin GPIO_PIN_3
#define KEY0_GPIO_Port GPIOH

void Key_Init(void);

/*gpio.c文件添加以下内容*/
void Key_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
	
  /*Configure GPIO pins : PHPin PHPin */
  GPIO_InitStruct.Pin = KEY0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(KEY0_GPIO_Port, &GPIO_InitStruct);	
}

4、main.c和main.h文件添加以下内容:

/*main.h文件添加以下内容*/

/*STM32F429的flash大小为1M,共12个扇区,前4个扇区每个16k,第5个64k,后7个每个128k
前5个扇区做boot,大小128k,后7个扇区做APP*/
#define USER_FLASH_FIRST_PAGE_ADDRESS 0x08020000 /* Only as example see comment */
#define USER_FLASH_LAST_PAGE_ADDRESS  0x080E0000
#define USER_FLASH_END_ADDRESS        0x080FFFFF 


/*main.c文件开头添加以下内容*/

#include "tftpserver.h"

/********************************IAP跳转部分代码********************************************/
typedef void (*iapfun)(void);                   /* 定义一个函数类型的参数 */

iapfun jump2app;

/**
 * @brief       跳转到应用程序段(执行APP)
 * @param       appxaddr : 应用程序的起始地址
 * @retval      无
 */
void iap_load_app(uint32_t appxaddr)
{
    __set_CONTROL(0);//CONTROL[0]选择特权级别,0为特权级,1为敌用户级。CONTROL[1]用于选择堆栈指针,0为MSP,1为PSP。
		if (((*(volatile  uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000)     /* 检查栈顶地址是否合法.可以放在内部SRAM共64KB(0x20000000) */
    {
				 __disable_irq();

				/* 用户代码区第二个字为程序开始地址(复位地址) */
        jump2app = (iapfun) * (volatile uint32_t *)(appxaddr + 4);
        
        /* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
        __set_MSP(*(volatile uint32_t *)appxaddr);
        
        /* 跳转到APP */
        jump2app();
    }
}

/********************************IAP跳转部分代码********************************************/

/*main函数替换为以下内容*/
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
	Key_Init();
	
	/*按键0按下就不进行跳转*/
	if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) != GPIO_PIN_RESET)
	{
		if (((*(volatile uint32_t *)(USER_FLASH_FIRST_PAGE_ADDRESS + 4)) & 0xFF000000) == 0x08000000) /* 判断FLASH里面是否有APP,有的话执行 */
		{
			iap_load_app(USER_FLASH_FIRST_PAGE_ADDRESS);/* 执行FLASH APP代码 */
		}			
	}
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C2_Init();
  MX_USART1_UART_Init();
  MX_LWIP_Init();
  MX_RTC_Init();
  /* USER CODE BEGIN 2 */
#if(LWIP_DHCP == 1)
	while(gnetif.ip_addr.addr == 0)
	{
		MX_LWIP_Process();
	}
	
	printf("DHCP succeed:%d.%d.%d.%d\n\n", 
	 ((gnetif.ip_addr.addr)&0x000000ff), (((gnetif.ip_addr.addr)&0x0000ff00)>>8), 
	 (((gnetif.ip_addr.addr)&0x00ff0000)>>16), ((gnetif.ip_addr.addr)&0xff000000)>>24);
#endif

#ifdef USE_IAP_TFTP    
    /* Initialize the TFTP server */
    IAP_tftpd_init();
#endif 
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		MX_LWIP_Process();
  }
  /* USER CODE END 3 */
}

至此就修改完成了,编译0警告0错误,下载时将芯片全擦除,烧录后可以看到串口打印开发板IP,开发板DS0一直在闪烁,串口打印信息如下图:

APP修改过程

这里就使用正点原子F429开发板标准例程中的《实验30 触摸屏实验》为例子进行讲解:

1、main函数开头添加以下内容:

__enable_irq();
SCB->VTOR = FLASH_BASE | 0x20000;	
HAL_RCC_DeInit();

2、修改MDK设置中的起始地址和大小:

3、添加生成bin文件脚本

"fromelf.exe"   --bin -o  ..\..\Output\APP.bin   ..\..\Output\atk_f429.axf

至此就修改完成了,编译0警告0错误,同时在过程根目录的“Output”文件夹中会生成一个“APP.bin”文件,这就是待会升级需要使用的文件。

使用“TFTPD32”软件进行TFTP升级

1、双击运行“Tftpd32.exe”(会在源码中一起提供)。

2、点击“Tftp Client”选项卡进入Tftp Client模式。

3、在Host栏输入开发板IP,在Port栏输入69;

4、点击“Local file”栏最右边的三个点选中上面生成的“APP.bin”文件;

5、修改Block Size的值为512;

6、最后点击Put将文件发送到开发板;步骤2到6的操作如下图:

在点击完“Put”后开发板串口会打印电脑的IP和端口号:“TFTP    RRQ    from:    [192.168.2.19]:[60511]”,之后会打印“FLASH_If_Erase finished”提示“PP占用的Flash已擦除完成;在全部接收完电脑发送过去的数据后一共接收的block的数量和总的文件的大小,同时Tftpd32软件也会弹出发送完成的界面,上面会显示发送耗时和block的数量,如下图:

        开发板在接收完成后会自动重启,重启之后就会运行APP程序,其就是正点原子的触摸屏实验例程;

        在开机时如果想要留在boot中不跳转APP仅需要在重启前按下开发板的“KEY0”键即可。在boot中就可以使用tftpd32软件再次进行更新。

        本实验的源代码如下:https://download.csdn.net/download/qq_44712722/90957074

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值