bootloader串口更新程序[瑕疵学习板]

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

很久没有更新文章了。最近工作太忙,没有学习很多的知识,然后这两天不忙了,就学习了一下bootloader的程序升级,本文章是使用的STM32F103zet6这个硬件实现的
,本文章的bootloader存在bug哈,本文章只是博主记录一下自己学习bootloader的心得。最终是能成功的用串口更新程序的。


一、储备知识

在这里插入图片描述
在学习bootloader串口升级程序之前要知道stm32的flash和rom,flash就是掉电存储。用来存程序的。ROM是用来存单片机运行的数据的。
然后stm32的程序是从0x8000000开始的,程序运行产生的数据是从0x20000000开始的。然后1 为0x80000,可以知道stm32f103zet6的flash是(0x80000)h=524288/1024=512K,ROM(0x10000)h=65536/1024=64K。
STM32的主程序存储分为256页,每页占2K。

二、程序步骤

在这里插入图片描述

//--------------------------------------------------------------------
//---------------------------bootloader_2分区设置--------------------------------
//bootloader          0x8000000~0x8004000    //16K
//运行程序          0x8004000~0x800C000    //32K   //48K
//程序下载缓存区    0x800C000~0x8014000   //32K   //80K
//程序更新标志位    0x807FC00~0x8080000  //1K
//--------------------------------------------------------------------

这是程序的分区。
我们的流程主要是
bootloader程序
在这里插入图片描述
运行程序
主要就是串口更新程序将程序写到0x800C000里面去并且将程序更新标志位置1
这里要注意程序烧录的地址哈,bootloader烧录到0x8000000,运行程序烧录到0x8004000里面去
在这里插入图片描述

2.程序展示

首先是stm32内部flash的读写
flash.c

#include "flash.h"
#include "usart.h"
//STM32G030F6P6的内部FLASH
//注意STm32G030F6P6有16页,每页2K

uint32_t STMFLASH_ReadWord(uint32_t faddr)
{
	return *(volatile uint32_t*)faddr; 
}
void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead)   		//连续读取
{
	uint32_t i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadWord(ReadAddr);	//读取4个字节.
		ReadAddr+=4;												//偏移4个字节.	
	}
}


HAL_StatusTypeDef flash_write(uint32_t address, uint64_t data) //指定页写入数据
{
    HAL_StatusTypeDef ret = HAL_OK;
    HAL_FLASH_Unlock();   //解锁Flash扫写权限
    ret = HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, address , data);
  	if(ret!=HAL_OK)
  	{
  		printf("flash write error, code=%d...\r\n",ret);
  	}else{
  		printf("flash write succeed, code=%d...\r\n",ret);
  	}
    HAL_FLASH_Lock();     //上锁Flash扫写权限

    return ret;
}

HAL_StatusTypeDef flash_write_buff(uint32_t address, uint64_t data,uint32_t len) //指定页写入数据
{
    HAL_StatusTypeDef ret = HAL_OK;
    HAL_FLASH_Unlock();   //解锁Flash扫写权限
	  for(int inss=0;inss<len;inss+=8){
			
			ret = HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, address , data);
		}
  	if(ret!=HAL_OK)
  	{
  		printf("flash write error, code=%d...\r\n",ret);
  	}else{
  		printf("flash write succeed, code=%d...\r\n",ret);
  	}
    HAL_FLASH_Lock();     //上锁Flash扫写权限

    return ret;
}

void flash_page_erase(uint32_t page)
{
    uint32_t F =0;
     
    FLASH_EraseInitTypeDef  FLash;
    HAL_FLASH_Unlock();
   	//__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR  | FLASH_FLAG_BSY |FLASH_SR_PGERR);
    FLash.TypeErase=FLASH_TYPEERASE_PAGES;
    FLash.Banks=FLASH_BANK_1;
    FLash.PageAddress=page;
    FLash.NbPages=16; //清除后面的16页,32K数据
    HAL_StatusTypeDef ret=HAL_FLASHEx_Erase(&FLash, &F);
	if(ret!=HAL_OK)
	{
		printf("flash erase error, code=%d...\r\n",ret);
	}else{
		printf("flash erase succeed, code=%d...\r\n",ret);		
	}
    HAL_FLASH_Lock();
}

void flash_page_erase_one(uint32_t page)
{
    uint32_t F =0;
     
    FLASH_EraseInitTypeDef  FLash;
    HAL_FLASH_Unlock();
   	//__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR  | FLASH_FLAG_BSY |FLASH_SR_PGERR);
    FLash.TypeErase=FLASH_TYPEERASE_PAGES;
    FLash.Banks=FLASH_BANK_1;
    FLash.PageAddress=page;
    FLash.NbPages=1; //清除后面的16页,32K数据
    HAL_StatusTypeDef ret=HAL_FLASHEx_Erase(&FLash, &F);
	if(ret!=HAL_OK)
	{
		printf("flash erase error, code=%d...\r\n",ret);
	}
    HAL_FLASH_Lock();
}
//擦除写入一起
void flash_page_erase_common(uint32_t address ,uint32_t data)  //擦除页
{
	
    uint32_t F=0;
     

    HAL_FLASH_Unlock(); //解锁Flash扫写权限
	  //清楚Flsh标志位
   	//__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR  | FLASH_FLAG_BSY |FLASH_SR_PGERR);
    FLASH_EraseInitTypeDef  FLash;
    FLash.TypeErase=FLASH_TYPEERASE_PAGES; //擦除模式
    //FLash.Banks=FLASH_BANK_1;     //擦除电压选择
    //FLash.PageAddress=(address - FLASH_BASE) / FLASH_PAGE_SIZE;  //从那页开始擦除 2K/页,一共16页
	  FLash.PageAddress= address;
    FLash.NbPages=1;  //一次擦除几页
    HAL_StatusTypeDef ret=HAL_FLASHEx_Erase(&FLash, &F); //擦除flash
	if(ret!=HAL_OK)
	{
		printf("flash erase error, code=%d...\r\n",ret);
	}else{
		printf("flash erase succeed, code=%d...\r\n",ret);
	} 
	  
    ret = HAL_FLASH_Program(TYPEPROGRAM_DOUBLEWORD, address , data);//写入flash
  	if(ret!=HAL_OK)
  	{
  		printf("flash write error, code=%d...\r\n",ret);
  	}else{
  		printf("flash write succeed, code=%d...\r\n",ret);
  	}	
	
    HAL_FLASH_Lock();//FLash上锁
	
}

//跳转到应用程序段
//appxaddr:用户代码起始地址.
typedef void (*pFunction)(void); 									
pFunction Jump_To_Application;  									// jump function

void JumpToAPP(uint32_t ApplicationAddress)
{
	uint32_t JumpAddress = 0;      									// JumpAddress

	if(((*(uint32_t*)ApplicationAddress) & 0x2FFE0000)==0x20000000) // check if the stack is ok 
	{
		SCB->VTOR = FLASH_BASE | ApplicationAddress;				// redifine the vect table
		JumpAddress = (*(uint32_t*)(ApplicationAddress + 4));
		Jump_To_Application =(pFunction)JumpAddress;
		__set_MSP(*(uint32_t*)ApplicationAddress);
		Jump_To_Application();
	}
	else
	{
		printf("there is no image in app\r\n");
	}
}		

flash.h

#ifndef __flash_H
#define __flash_H

#include "main.h"

#define Flash_addr  0x807FC00
#define FLASH_BASEs 0x8000000
#define FLASH_PAGE_SIZE 2048
typedef  void (*iapfun)(void);				//定义一个函数类型的参数


HAL_StatusTypeDef flash_write(uint32_t address, uint64_t data);
void flash_page_erase_common(uint32_t address ,uint32_t data);
void flash_page_erase(uint32_t page);
void flash_page_erase_one(uint32_t page);
uint32_t STMFLASH_ReadWord(uint32_t faddr);
void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead);
void iap_load_app(uint32_t appxaddr);

#endif

usart.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.c
  * @brief   This file provides code for the configuration
  *          of the USART instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

UART_HandleTypeDef huart1;
usart_buff usart_dat;
/* USART1 init function */
uint32_t buff_number;
void MX_USART1_UART_Init(void)
{


  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
	HAL_UART_Receive_IT(&huart1,&usart_dat.szTemp,1);

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		
		HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn); 

  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
}

void USART1_IRQHandler(void){
	HAL_UART_IRQHandler(&huart1);	
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  
	if(huart->Instance==USART1){
		if(usart_dat.nRec ==5415){	
			printf("size=%d\r\n",usart_dat.nRec); //打印数据
			//usart_dat.nRec =0;
			usart_dat.download_flag =1;
		}
		if(usart_dat.nRec<15*1024)
		{
			usart_dat.szRx[usart_dat.nRec++]=usart_dat.szTemp;
		}
		//usart_dat.download_flag =1;
		HAL_UART_Receive_IT(&huart1,&usart_dat.szTemp,1);	
	
	}
 
}

usart.h

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.h
  * @brief   This file contains all the function prototypes for
  *          the usart.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__

#ifdef __cplusplus
extern "C" {
#endif

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

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */
typedef struct{
	volatile unsigned char  		szRx[15*1024];
	volatile unsigned char  		szTx[32];
	unsigned int 					      nRec;
	unsigned char					      szTemp;  
	unsigned short 		          timeOut;
  unsigned char               download_flag;	
	
}usart_buff;

extern uint32_t buff_number;
extern usart_buff usart_dat;
extern UART_HandleTypeDef huart1;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_USART1_UART_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __USART_H__ */


1.bootloader

首先我们需要将flash下载程序选择0x8000000
在这里插入图片描述
然后是main.c源码
这里下载地址要选择0x8004000
在这里插入图片描述


#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "flash.h"
int fputc(int ch,FILE *f) //串口4的重定向
{
	HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);//hurat1为串口号,根据自己情况进行选择
	return ch;
}


//--------------------------------------------------------------------
//---------------------------bootloader_2分区设置--------------------------------
//bootloader          0x8000000~0x8004000    //16K
//运行程序          0x8004000~0x800C000    //32K   //48K
//程序下载缓存区    0x800C000~0x8014000   //32K   //80K
//程序更新标志位    0x807FC00~0x8080000  //1K
//--------------------------------------------------------------------

void SystemClock_Config(void);
uint32_t flash_read_dat[1024];
#define AppAddr		0x8004000
uint32_t AppAddr_write=0x800C000;
uint32_t erase_count;
void flashDataMove(void)
{
	int i=0;
	int j=0;
	flash_page_erase(AppAddr);//清除0x8004000以后的16页
	for(i=16;i<48;i+=2)
	{

		for(j=0;j<2048;j+=8)
		{
			uint64_t t=0;
			//读取
			t=*(__IO uint64_t*)(FLASH_BASE+48*1024+j+(i-16)*1024);//FLASH_BASE		0x08000000
			flash_write(FLASH_BASE+j+i*1024,t);
			printf("from addr=0x%x,dest addr=0x%x\r\n",FLASH_BASE+48*1024+j+(i-16)*1024,FLASH_BASE+j+i*1024);
		}
		printf("from sector=%d,dest sector=%d\r\n",48+i,i);
	}
}
//unsigned char usart_d[8]={0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18};
int main(void)
{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_USART1_UART_Init();
	
	printf("\r\n\r\n");
  
	printf("	                                 \r\n");
	printf("  /\\  /\\__ _ _ __ ___   ___ ___ _ __ \r\n");
	printf(" / /_/ / _` | '_ ` _ \\ / __/ _ \\ '__|\r\n");
	printf("/ __  / (_| | | | | | | (_|  __/ |   \r\n");
	printf("\\/ /_/ \\__,_|_| |_| |_|\\___\\___|_|   \r\n");
	printf("                                     \r\n");

	printf("bootloader hardware inited...\r\n");
  //flash_page_erase_common(Flash_addr,0xFA);	
	
//	flash_page_erase(AppAddr);
//	STMFLASH_Read(AppAddrs,flash_read_dat,10);
//	flash_write(AppAddrs, 0x00FEFACE);
//	//printf("OUTPUT Flash data=0x%X\n",flash_read_dat[0]);		
//	 for(int ins=0;ins<10;ins++){
//			printf("OUTPUT Flash data=0x%X\n",flash_read_dat[ins]);			 
//	 }
	STMFLASH_Read(Flash_addr,flash_read_dat,1);//读取下载标志位
//	
	printf("OUTPUT Flash data=0x%X\r\n",flash_read_dat[0]);	
	
	//flash_page_erase(AppAddr);
	if(flash_read_dat[0]==0xFA)//判断是否为0X08XXXXXX.
  {
		  flashDataMove();//将下载的程序移植到运行程序里面
      printf("开始执行FLASH用户代码!!\r\n");
      flash_page_erase_common(Flash_addr,0x00);//清空下载标志位		
      JumpToAPP(AppAddr);//执行FLASH APP代码		 
  }else{
      printf("非FLASH应用程序,无法执行!\r\n");     
	}
    if(flash_read_dat[0]==0x00)	
    {
      printf("开始执行FLASH用户代码!!\r\n");			
			JumpToAPP(AppAddr);//执行FLASH APP代码			
		}else{
      printf("非FLASH应用程序,无法执行!\r\n"); 			
		}
		
  while (1)
  {
		 // 01 06  00 00 00 01 
		//4个字节发送文件大小(32位)
//		 if(usart_dat.szRx[0] == 01){
//			 if(usart_dat.szRx[1] == 06){
//				  buff_number=(usart_dat.szRx[2]<<24)+(usart_dat.szRx[3]<<16)+(usart_dat.szRx[4]<<8)+usart_dat.szRx[5];

//				 printf("file size=%d\r\n",buff_number);
//			 }
//			 usart_dat.nRec =0;
//		 }
		 //printf("hellow\r\n");
     if(usart_dat.download_flag ==1){ //表示开始传输程序
			 usart_dat.download_flag =0;
			 int ins=0;
			flash_page_erase(AppAddr_write);
	    for(ins=0;ins<usart_dat.nRec;ins+=8){
				uint64_t t=0;
							   t=(t<<8)+usart_dat.szRx[ins+7];
								 t=(t<<8)+usart_dat.szRx[ins+6];
								 t=(t<<8)+usart_dat.szRx[ins+5];
								 t=(t<<8)+usart_dat.szRx[ins+4];
								 t=(t<<8)+usart_dat.szRx[ins+3];
								 t=(t<<8)+usart_dat.szRx[ins+2];
								 t=(t<<8)+usart_dat.szRx[ins+1]; 
								 t=(t<<8)+usart_dat.szRx[ins];
				int ret=flash_write(AppAddr_write,t);
				printf("addr=0x%X\r\n",AppAddr_write);
				printf("%s\r\n",ret==0?"OK":"Error");
        AppAddr_write=AppAddr_write+8;				
			}
			usart_dat.nRec =0;
			flash_page_erase_common(Flash_addr,0xFA);			
			//AppAddr_write=AppAddr_write+0x800;			
		 }
		 //printf("size=%d\r\n",usart_dat.nRec);
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

2.然后是主运行函数

主函数

#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"
#include "w5500.h"
#include <string.h>
#include "stdio.h"
#include "flash.h"

uint32_t flash_read_dat[1024];
#define AppAddr		0x8004000
uint32_t AppAddr_write=0x800C000;
uint32_t erase_count;

void System_Initialization(void);	//STM32系统初始化函数(初始化STM32时钟及外设)
void Load_Net_Parameters(void);
void W5500_Initialization(void);
void W5500_Socket_Set(void);
void Process_Socket_Data(SOCKET s);
void Process_Socket_Keep_Read_nData(SOCKET s,uint8_t* Tx_Buff_datas);
void Process_Socket_Write_nData(SOCKET s);
uint8_t Tx_Buff_Text[6]={0xA0,0xA0,0xB0,0xB0,0xC0,0xC0};//测试的buffer


static uint16_t data=0;


unsigned char Buff_test[]="Hellow Modbus TCP\n";
void SystemClock_Config(void);


int main(void)
{

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();
	printf("W5500_Begin...\r\n");
  /* USER CODE BEGIN 2 */
	//System_Initialization();	//STM32系统初始化函数(初始化STM32时钟及外设)
	//Load_Net_Parameters();		//装载网络参数	
	//W5500_Hardware_Reset();		//硬件复位W5500
	//W5500_Initialization();		//W5500初始货配置
	


  while (1)
  {
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
		HAL_Delay(1000);
		printf("bootloader Tast OK...\r\n");
		
    if(usart_dat.download_flag ==1){ //表示开始传输程序
			 usart_dat.download_flag =0;
			 int ins=0;
			flash_page_erase(AppAddr_write);
	    for(ins=0;ins<usart_dat.nRec;ins+=8){
				uint64_t t=0;
							   t=(t<<8)+usart_dat.szRx[ins+7];
								 t=(t<<8)+usart_dat.szRx[ins+6];
								 t=(t<<8)+usart_dat.szRx[ins+5];
								 t=(t<<8)+usart_dat.szRx[ins+4];
								 t=(t<<8)+usart_dat.szRx[ins+3];
								 t=(t<<8)+usart_dat.szRx[ins+2];
								 t=(t<<8)+usart_dat.szRx[ins+1]; 
								 t=(t<<8)+usart_dat.szRx[ins];
				int ret=flash_write(AppAddr_write,t);
				printf("addr=0x%X\r\n",AppAddr_write);
				printf("%s\r\n",ret==0?"OK":"Error");
        AppAddr_write=AppAddr_write+8;				
			}
			usart_dat.nRec =0;
			flash_page_erase_common(Flash_addr,0xFA);			
			//AppAddr_write=AppAddr_write+0x800;			
		 }		
		
		
//		W5500_Socket_Set();//W5500端口初始化配置

//		W5500_Interrupt_Process();//W5500中断处理程序框架
//		//printf("Read_Len=%d\r\n",Rx_Buffer);
//		//Read_W5500_SOCK_1Byte(0,)
//	  //printf("S_RECEIVE=0x%X\n",S0_Data);		
//		if((S0_Data & S_RECEIVE) == S_RECEIVE)//如果Socket0接收到数据
//		{
//			S0_Data&=~S_RECEIVE;
//			//Process_Socket_Data(0);//W5500接收并发送接收到的数据
//			Process_Socket_Keep_Read_nData(0,Tx_Buff_Text);
//      //Process_Socket_Write_nData(0);
//		}
//		else //定时发送字符串
//		{
//			if(S0_State == (S_INIT|S_CONN))
//			{
//				S0_Data&=~S_TRANSMITOK;
//				//memcpy(Tx_Buffer, "\r\n你好服务器,我是客户1!\r\n", 23);	
//				Process_Socket_Write_nData(0);
//				//Write_SOCK_Data_Buffer(0, Tx_Buffer, 23);//指定Socket(0~7)发送数据处理,端口0发送23字节数据
//			}
//		}
	//	Write_SOCK_Data_Buffer(0, Buff_test, sizeof(Buff_test));

		//HAL_Delay(500);
		//HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);

//		data++;
    /* USER CODE BEGIN 3 */
  }
  
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */



/*******************************************************************************
* 函数名  : W5500_Initialization
* 描述    : W5500初始货配置
* 输入    : 无
* 输出    : 无
* 返回值  : 无
* 说明    : 无
*******************************************************************************/
void W5500_Initialization(void)
{
	W5500_Init();		//初始化W5500寄存器函数
	if(Detect_Gateway()==TRUE){
		printf("Connect Success\r\n");

	}else{
		printf("Connect Loser\r\n");

	}		//检查网关服务器 
	Socket_Init(0);		//指定Socket(0~7)初始化,初始化端口0
}

/*******************************************************************************
* 函数名  : Load_Net_Parameters
* 描述    : 装载网络参数
* 输入    : 无
* 输出    : 无
* 返回值  : 无
* 说明    : 网关、掩码、物理地址、本机IP地址、端口号、目的IP地址、目的端口号、端口工作模式
*******************************************************************************/
void Load_Net_Parameters(void)
{
	Gateway_IP[0] = 192;//加载网关参数
	Gateway_IP[1] = 168;
	Gateway_IP[2] = 1;
	Gateway_IP[3] = 1;

	Sub_Mask[0]=255;//加载子网掩码
	Sub_Mask[1]=255;
	Sub_Mask[2]=255;
	Sub_Mask[3]=0;

	Phy_Addr[0]=0x08;//加载物理地址
	Phy_Addr[1]=0x97;
	Phy_Addr[2]=0x98;
	Phy_Addr[3]=0xBE;
	Phy_Addr[4]=0x2F;
	Phy_Addr[5]=0x7F;

	IP_Addr[0]=192;//加载本机IP地址
	IP_Addr[1]=168;
	IP_Addr[2]=1;
	IP_Addr[3]=17;

	S0_Port[0] = 0x13;//加载端口0的端口号5000 
	S0_Port[1] = 0x88;

//	S0_DIP[0]=192;//加载端口0的目的IP地址
//	S0_DIP[1]=168;
//	S0_DIP[2]=1;
//	S0_DIP[3]=190;
//	
//	S0_DPort[0] = 0x17;//加载端口0的目的端口号6000
//	S0_DPort[1] = 0x70;

	S0_Mode=TCP_SERVER;//加载端口0的工作模式,TCP服务端模式
}

/*******************************************************************************
* 函数名  : W5500_Socket_Set
* 描述    : W5500端口初始化配置
* 输入    : 无
* 输出    : 无
* 返回值  : 无
* 说明    : 分别设置4个端口,根据端口工作模式,将端口置于TCP服务器、TCP客户端或UDP模式.
*			从端口状态字节Socket_State可以判断端口的工作情况
*******************************************************************************/
void W5500_Socket_Set(void)
{
	if(S0_State==0)//端口0初始化配置
	{
		if(S0_Mode==TCP_SERVER)//TCP服务器模式 
		{
			if(Socket_Listen(0)==TRUE)
				S0_State=S_INIT;
			else
				S0_State=0;
		}
		else if(S0_Mode==TCP_CLIENT)//TCP客户端模式 
		{
			if(Socket_Connect(0)==TRUE)
				S0_State=S_INIT;
			else
				S0_State=0;
		}
		else//UDP模式 
		{
			if(Socket_UDP(0)==TRUE)
				S0_State=S_INIT|S_CONN;
			else
				S0_State=0;
		}
	}
}

/*******************************************************************************
* 函数名  : Process_Socket_Data
* 描述    : W5500接收并发送接收到的数据
* 输入    : s:端口号
* 输出    : 无
* 返回值  : 无
* 说明    : 本过程先调用S_rx_process()从W5500的端口接收数据缓冲区读取数据,
*			然后将读取的数据从Rx_Buffer拷贝到Temp_Buffer缓冲区进行处理。
*			处理完毕,将数据从Temp_Buffer拷贝到Tx_Buffer缓冲区。调用S_tx_process()
*			发送数据。
*******************************************************************************/
void Process_Socket_Data(SOCKET s)
{
	int len;
	unsigned char msg[11]={0x00,0x00,0x00 ,0x00, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00, 0x70};
	
	len=sizeof(msg);
	unsigned short size;
	size=Read_SOCK_Data_Buffer(s, Rx_Buffer);
	printf("Rx_len=%d\r\n",size);
	memcpy(Tx_Buffer, Rx_Buffer, size);
	
	//打印查询报文
	for (int j=0;j<size;j++){
		 printf("0x%02X ",Tx_Buffer[j]);
	}

	//写响应报文
	//检验码
	msg[0]=Tx_Buffer[0];
	msg[1]=Tx_Buffer[1];
	
	//协议
	msg[2]=0x00;
	msg[3]=0x00;
	
	//数据包长度
	msg[4]=0x00;
	msg[5]=0x05;
	
	//设备编号
	msg[6]=Tx_Buffer[6];
	//功能码
	msg[7]=Tx_Buffer[7];
	//数据长度
	msg[8]=0x02;
	
	//低八位
	msg[10]=data&0XFF;
	//高八位
	msg[9]=data>>8;
	
	memcpy(Tx_Buffer, msg, len);	
	//发送响应报文
	Write_SOCK_Data_Buffer(0, Tx_Buffer, len);
	data++;
}
/*******************************************************************************
* 函数名  : Process_Socket_Keep_Read_nData
* 描述    : W5500接收并发送接收到的多个数据
* 输入    : s:端口号
* 输出    : 无
* 返回值  : 无
* 说明    : 本过程先调用S_rx_process()从W5500的端口接收数据缓冲区读取数据,
*			然后将读取的数据从Rx_Buffer拷贝到Temp_Buffer缓冲区进行处理。
*			处理完毕,将数据从Temp_Buffer拷贝到Tx_Buffer缓冲区。调用S_tx_process()
*			发送数据。注意这里是发送多个数据(0x03),写一个数据是(0x06) 
*******************************************************************************/
void Process_Socket_Keep_Read_nData(SOCKET s,uint8_t* Tx_Buff_datas){
	uint16_t size;
	uint8_t Tx_Buffer_CP[128];
	uint8_t p=0;	
	
	size=Read_SOCK_Data_Buffer(s, Rx_Buffer);
/*******************************************************************************	
                             Modbus TCP
主机保持读从机        实物协议    字节长度  从机地址 功能码  起始地址   读取线圈个数
	            主机    05 95 00 00    00 06        01        03     00 00          00 01
	                      实物协议    字节长度  从机地址 功能码  字节个数     上传数据
	            从机    05 95 00 00    00 05        01        03       02           00 04
0x03 读保持寄存器
********************************************************************************/	
	if(Rx_Buffer[6]==0x01){//判读是否是该从机寄存器地址
		if(Rx_Buffer[7]==0x03){//主机读取从机保持读寄存器
//			printf("PC receive Data(0x03)=");
//			for(int i=0;i<size;i++){
//				printf("0x%X,",Rx_Buffer[i]);		
//			}
//			printf("\r\n");
			//判断发送的字节数量
			uint16_t Begin_addr=Rx_Buffer[8]<<8;//读取起始地址高八位
			Begin_addr=Rx_Buffer[9];	//读取起始地址低八位
			uint16_t End_addr=Rx_Buffer[10]<<8;//读取结束地址高八位
			End_addr=Rx_Buffer[11];//读取结束地址低八位
			uint16_t RX_SUM=End_addr-Begin_addr;//主机读取多少个数据
			uint16_t RX_SUM_size=RX_SUM*2;
			//printf("PULL READ Data=%d\r\n",RX_SUM_size); 
			//判断事务协议后的字节数量
			uint16_t start_size=3+RX_SUM_size;
				
			//实物协议码
			Tx_Buffer_CP[0]=Rx_Buffer[0]; 
			Tx_Buffer_CP[1]=Rx_Buffer[1];	
			Tx_Buffer_CP[2]=Rx_Buffer[2]; 	
			Tx_Buffer_CP[3]=Rx_Buffer[3];
			//下发长度
			Tx_Buffer_CP[4]=start_size>>8;
			Tx_Buffer_CP[5]=start_size;
			//从机地址	
			Tx_Buffer_CP[6]=Rx_Buffer[6];
			//功能码
			Tx_Buffer_CP[7]=Rx_Buffer[7];
			//发送的字节长度
			Tx_Buffer_CP[8]=RX_SUM_size;
			p=9;
			for(int j=0;j<RX_SUM_size;j++){
				if((p+j)%2==1){ //填充高8位
					Tx_Buffer_CP[p+j]=Tx_Buff_datas[j];
				}else{         //填充低8位
					Tx_Buffer_CP[p+j]=Tx_Buff_datas[j];  						
				} 		
			}		
			memcpy(Tx_Buffer, Tx_Buffer_CP, p+RX_SUM_size);
			//printf("size=%d\r\n",p+RX_SUM_size);
			//发送响应报文
			Write_SOCK_Data_Buffer(0, Tx_Buffer, p+RX_SUM_size);	
		}else if(Rx_Buffer[7]==0x06){//主机写从机单一寄存器
/*******************************************************************************	
                             Modbus TCP
主机写单一从机        实物协议    字节长度  从机地址 功能码  寄存器地址   下发数据
	            主机    31 C9 00 00     00 06       01       06       00 00          00 20
	                      实物协议    字节长度  从机地址 功能码  寄存器地址   下发数据
	            从机    31 C9 00 00     00 06       01       06       00 00          00 20
0x06 写单一寄存器
********************************************************************************/						
			//写地址
			uint16_t Write_addr=Rx_Buffer[8]>>8;
			Write_addr=Rx_Buffer[9];
			//写数据
			uint16_t Write_data=Rx_Buffer[10]>>8;
			Write_data=Rx_Buffer[11];
			printf("Write_addr=0x%x,Write_data=0x%x\r\n",Write_addr,Write_data);
			
			printf("PC receive Data(0x06)=");
			for(int w=0;w<size;w++){
				printf("0x%X,",Rx_Buffer[w]);		
			}
			printf("\r\n");
		  for(int i=0;i<size;i++){
				Tx_Buffer[i]=Rx_Buffer[i];			
			}
			printf("0x06_data_size=%d\r\n",size);
 			Write_SOCK_Data_Buffer(0, Tx_Buffer, size);
      
			if(Write_addr==0x04){
				if(Write_data==0x01){
					HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
					
				}else if(Write_data==0x02){
					HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);									
				}				
			}

			
		}
	}else{
		printf("Not the address, but=0x%X,\r\n",Rx_Buffer[6]);	
	}
}


void Process_Socket_Write_nData(SOCKET s){
	uint16_t sizes;	
	
	sizes=Read_SOCK_Data_Buffer(s, Rx_Buffer);
  if(Rx_Buffer[6]==0x01){//判断从机地址
		if(Rx_Buffer[7]==0x06){//写单一寄存器
			printf("PC receive Data(0x06)=");
			for(int w=0;w<sizes;w++){
				printf("0x%X,",Rx_Buffer[w]);		
			}
			printf("\r\n");
		  for(int i=0;i<sizes;i++){
				Tx_Buffer[i]=Rx_Buffer[i];			
			}
			printf("0x06_data_size=%d\r\n",sizes);
 			Write_SOCK_Data_Buffer(0, Tx_Buffer, sizes);      			
		}
	}else{
		printf("Not the address, but=0x%X,\r\n",Rx_Buffer[6]);	
	}		
	
	
}


/*******************************************************************************
* 函数名  : System_Initialization
* 描述    : STM32系统初始化函数(初始化STM32时钟及外设)
* 输入    : 无
* 输出    : 无
* 返回    : 无 
* 说明    : 无
*******************************************************************************/
void System_Initialization(void)
{
	//RCC_Configuration();		//设置系统时钟为72MHZ(这个可以根据需要改)
  	//NVIC_Configuration();		//STM32中断向量表配配置
	SPI_Configuration();		//W5500 SPI初始化配置(STM32 SPI1)
	//Timer2_Init_Config();		//Timer2初始化配置
	W5500_GPIO_Configuration();	//W5500 GPIO初始化配置	
}






/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

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

效果展示
在这里插入图片描述


总结

这就是bootloader 的主要程序了。不是很难,这个程序后期还要完善。