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

这篇具有很好参考价值的文章主要介绍了bootloader串口更新程序[瑕疵学习板]。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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


前言

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


一、储备知识

bootloader串口更新程序[瑕疵学习板],STM32高级功能,学习
在学习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串口更新程序[瑕疵学习板],STM32高级功能,学习

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

这是程序的分区。
我们的流程主要是
bootloader程序
bootloader串口更新程序[瑕疵学习板],STM32高级功能,学习
运行程序
主要就是串口更新程序将程序写到0x800C000里面去并且将程序更新标志位置1
这里要注意程序烧录的地址哈,bootloader烧录到0x8000000,运行程序烧录到0x8004000里面去
bootloader串口更新程序[瑕疵学习板],STM32高级功能,学习

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
bootloader串口更新程序[瑕疵学习板],STM32高级功能,学习
然后是main.c源码
这里下载地址要选择0x8004000
bootloader串口更新程序[瑕疵学习板],STM32高级功能,学习


#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串口更新程序[瑕疵学习板],STM32高级功能,学习


总结

这就是bootloader 的主要程序了。不是很难,这个程序后期还要完善。文章来源地址https://www.toymoban.com/news/detail-683679.html

到了这里,关于bootloader串口更新程序[瑕疵学习板]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 OTA Bootloader部分 demo流程学习

    学习依据的源文链接:STM32 OTA应用开发——自制BootLoader 什么是OTA? OTA是“Over-the-Air”(空中升级)的缩写,指的是通过无线通信网络(如Wi-Fi、蓝牙、LoRa等)对嵌入式系统进行远程升级或更新。 在嵌入式系统中,OTA技术可以用于更新固件、软件或配置文件等。通过OTA技术,

    2024年02月04日
    浏览(35)
  • STM32 usart bootloader 源代码 STM32 usart bootloader 源代码 STM32 usart bootloader 原代源码

    STM32 usart bootloader 源代码   STM32 usart bootloader 源代码  STM32 usart bootloader 原代源码,上位机C#,下位机c。 简单修改可以支持stm32全系列芯片。 支持串口升级 该版本为优化过的版本, 1.支持代码段保护; 2.支持烧写失败重置; 3.兼容我公司生产的配套wifi模块和w5500模块远程更新

    2024年01月23日
    浏览(47)
  • STM32duino-bootloader:STM32的开源Bootloader深入解析

    STM32微控制器广泛应用于各种嵌入式系统。一个常见的需求是能够远程更新固件,而这通常是通过Bootloader来实现的。在本文中,我们将深入解析一个叫做STM32duino-bootloader的开源项目,它为STM32微控制器提供了一个USB DFU (Device Firmware Upgrade) bootloader。 STM32duino-bootloader简介 STM32d

    2024年02月11日
    浏览(44)
  • STM32_串口下载程序

    使用调试器下载程序又快有稳定还能使用调试功能,当然是下载调试的首选。但是拓展下串口下载程序的知识作为下载的备选方案也是很有必要的。 ICP :使用SWD接口进行烧录,如J-Link烧录器和J-Flash软件配合使用。 ISP :使用引导程序(Bootload)加上外围UART/USB等接口进行烧录

    2024年01月16日
    浏览(51)
  • STM32 使用串口下载程序

    在ROM区的0x0800 0000位置,存储的就是编译后的程序代码,你把什么程序写入到这个位置,STM32就执行什么样的程序。如果想使用串口下载程序的话,我们只需要把数据通过串口发送给STM32,STM32接收数据,然后刷新到0x0800 0000这一块位置就行了。但是接收并转存数据,这个过程本

    2024年02月05日
    浏览(42)
  • STM32 学习————串口通信

    众所周知,串口通信是MCU最基本的通信方式,对于STM32来说也是如此。本文重点讲述STM32单片机的串口通信,主要包括的内容是:通信基础知识、串口通信原理、USART有关寄存器和自定义编写串口通信函数。 通信目的:的将一个设备数据传送到另一个设备,扩展硬件系统。 通

    2024年02月01日
    浏览(43)
  • 【STM32】STM32学习笔记-USART串口协议(25)

    按数据传送的方式,通讯可分为串行通讯与并行通讯,串行通讯是指设备之间通过少量数据信号线(一般是8根以下), 地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。而并行通讯一般是指使用8、16、32及64根或更多的数据线进行传输的通讯方式, 它们的通

    2024年01月19日
    浏览(47)
  • 【STM32】学习笔记(串口通信)

    串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。 电平标准

    2024年02月10日
    浏览(42)
  • STM32 串口实验(学习一)

    本章将实现如下功能:STM32通过串口和上位机对话,STM32在收到上位机发过来的字符串后,原原本本返回给上位机。 串口作为MCU的重要外部接口,同时也是软件开发重要的调试手段,其重要性不言而喻。现在基本上所有的MCU都会带有串口,STM32自然也不例外。 串口最基本的设

    2024年02月15日
    浏览(29)
  • 【STM32】STM32学习笔记-USART串口数据包(28)

    串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式, 电子工程师在调试设备时也经常使用该通讯方式输出调试信息。 在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和

    2024年01月18日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包