modbus tcp 在线OTA升级407

BootLoader端

BootLoader\Core\Src\stm_flash.c

c 复制代码
#include "stm_flash.h"

struct stm_flash_dev Stm_Flash_Data;

// 读取指定地址的字(32位数据)
// faddr:读地址
// 返回值:对应数据.
uint32_t STMFLASH_ReadWord(uint32_t faddr)
{
    return *(uint32_t *)faddr;
}

// 获取某个地址所在的flash扇区
// addr:flash地址
// 返回值:0~11,即addr所在的扇区
uint8_t STMFLASH_GetFlashSector(uint32_t addr)
{
    if (addr < ADDR_FLASH_SECTOR_1)
        return FLASH_SECTOR_0;
    else if (addr < ADDR_FLASH_SECTOR_2)
        return FLASH_SECTOR_1;
    else if (addr < ADDR_FLASH_SECTOR_3)
        return FLASH_SECTOR_2;
    else if (addr < ADDR_FLASH_SECTOR_4)
        return FLASH_SECTOR_3;
    else if (addr < ADDR_FLASH_SECTOR_5)
        return FLASH_SECTOR_4;
    else if (addr < ADDR_FLASH_SECTOR_6)
        return FLASH_SECTOR_5;
    else if (addr < ADDR_FLASH_SECTOR_7)
        return FLASH_SECTOR_6;
    else if (addr < ADDR_FLASH_SECTOR_8)
        return FLASH_SECTOR_7;
    else if (addr < ADDR_FLASH_SECTOR_9)
        return FLASH_SECTOR_8;
    else if (addr < ADDR_FLASH_SECTOR_10)
        return FLASH_SECTOR_9;
    else if (addr < ADDR_FLASH_SECTOR_11)
        return FLASH_SECTOR_10;
    return FLASH_SECTOR_11;
}

void STMFLASH_Earse_Sector(uint32_t addr)
{
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);

    FLASH_EraseInitTypeDef FlashEraseInit;
    uint32_t SectorError = 0;
    
    FlashEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;     // 擦除类型,扇区擦除
    FlashEraseInit.Sector = STMFLASH_GetFlashSector(addr);  // 要擦除的扇区
    FlashEraseInit.NbSectors = 1;                           // 一次只擦除一个扇区
    FlashEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;    // 电压范围,VCC=2.7~3.6V之间!!
    
    HAL_FLASHEx_Erase(&FlashEraseInit, &SectorError);
    HAL_FLASH_Lock();
}

// 从指定地址开始写入指定长度的数据
// 特别注意:因为STM32F4的扇区实在太大,没办法本地保存扇区数据,所以本函数
//          写地址如果非0xFF,那么会先擦除整个扇区且不保存扇区数据.所以
//          写非0xFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
//          没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写.
// 该函数对OTP区域也有效!可以用来写OTP区!
// OTP区域地址范围:0x1FFF7800~0x1FFF7A0F(注意:最后16字节,用于OTP数据块锁定,别乱写!!)
// WriteAddr:起始地址(此地址必须为4的倍数!!)
// pBuffer:数据指针
// NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
void STMFLASH_Write(uint32_t WriteAddr, uint32_t *pBuffer, uint32_t NumToWrite)
{
    FLASH_EraseInitTypeDef FlashEraseInit;
    HAL_StatusTypeDef FlashStatus = HAL_OK;
    uint32_t SectorError = 0;
    uint32_t addrx = 0;
    uint32_t endaddr = 0;
    if (WriteAddr < STM32_FLASH_BASE || WriteAddr % 4)
        return; // 非法地址

    HAL_FLASH_Unlock();                   // 解锁
    addrx = WriteAddr;                    // 写入的起始地址
    endaddr = WriteAddr + NumToWrite * 4; // 写入的结束地址

    if (addrx < 0x1FFF0000)
    {
        while (addrx < endaddr) // 扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
        {
            if (STMFLASH_ReadWord(addrx) != 0xFFFFFFFF) // 有非0xFFFFFFFF的地方,要擦除这个扇区
            {
                __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
                
                FlashEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;     // 擦除类型,扇区擦除
                FlashEraseInit.Sector = STMFLASH_GetFlashSector(addrx); // 要擦除的扇区
                FlashEraseInit.NbSectors = 1;                           // 一次只擦除一个扇区
                FlashEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;    // 电压范围,VCC=2.7~3.6V之间!!
                if (HAL_FLASHEx_Erase(&FlashEraseInit, &SectorError) != HAL_OK)
                {
                    break; // 发生错误了
                }
            }
            else
                addrx += 4;
            FLASH_WaitForLastOperation(FLASH_WAITETIME); // 等待上次操作完成
        }
    }
    FlashStatus = FLASH_WaitForLastOperation(FLASH_WAITETIME); // 等待上次操作完成
    if (FlashStatus == HAL_OK)
    {
        while (WriteAddr < endaddr) // 写数据
        {
            if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, WriteAddr, *pBuffer) != HAL_OK) // 写入数据
            {
                break; // 写入异常
            }
            WriteAddr += 4;
            pBuffer++;
        }
    }
    HAL_FLASH_Lock(); // 上锁
}

// 从指定地址开始读出指定长度的数据
// ReadAddr:起始地址
// pBuffer:数据指针
// NumToRead:字(32位)数
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个字节.
    }
}

//-------------------------------测试用-------------------------------//
// WriteAddr:起始地址
// WriteData:要写入的数据
void Test_Write(uint32_t WriteAddr, uint32_t WriteData)
{
    STMFLASH_Write(WriteAddr, &WriteData, 1); // 写入一个字
}

BootLoader\Core\Inc\stm_flash.h

c 复制代码
#ifndef __STM_FLASH_H__
#define __STM_FLASH_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32f4xx_hal.h"

//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 	//STM32 FLASH的起始地址
#define FLASH_WAITETIME  50000          //FLASH等待超时时间

//FLASH 扇区的起始地址
#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) 	//扇区0起始地址, 16 Kbytes  
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) 	//扇区1起始地址, 16 Kbytes  
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) 	//扇区2起始地址, 16 Kbytes  
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) 	//扇区3起始地址, 16 Kbytes  
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) 	//扇区4起始地址, 64 Kbytes  
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) 	//扇区5起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) 	//扇区6起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) 	//扇区7起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) 	//扇区8起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) 	//扇区9起始地址, 128 Kbytes  
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) 	//扇区10起始地址,128 Kbytes  
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) 	//扇区11起始地址,128 Kbytes 

#define APP1_ADDRESS ADDR_FLASH_SECTOR_5
#define APP2_ADDRESS ADDR_FLASH_SECTOR_7
#define UPDATE_CODE_ADDRESS ADDR_FLASH_SECTOR_7
#define UPDATE_FLAG_ADDRESS ADDR_FLASH_SECTOR_10

struct stm_flash_dev
{
    uint32_t bootloader_flag;   // 值为0xA5表示进入bootloader进行代码升级
};

extern struct stm_flash_dev Stm_Flash_Data;

uint32_t STMFLASH_ReadWord(uint32_t faddr);		  	    //读出字  
void STMFLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite);		//从指定地址开始写入指定长度的数据
void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead);   		//从指定地址开始读出指定长度的数据
void Test_Write(uint32_t WriteAddr,uint32_t WriteData); //测试写入
void STMFLASH_Earse_Sector(uint32_t addr);

#ifdef __cplusplus
}
#endif
#endif /*__STM_FLASH_H__ */

BootLoader\Core\Src\main.c

c 复制代码
/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN 5 */

  // earse flash
  STMFLASH_Earse_Sector(APP1_ADDRESS);
  STMFLASH_Earse_Sector(APP1_ADDRESS + 0x00020000);

  // 将接收到的固件写入运行区
  STMFLASH_Read(APP2_ADDRESS, (uint32_t*) APP_Code, sizeof(APP_Code) / sizeof(uint32_t));
  STMFLASH_Write(APP1_ADDRESS, (uint32_t*) APP_Code, sizeof(APP_Code) / sizeof(uint32_t)); // 64Kbyte code
  
  STMFLASH_Read(APP2_ADDRESS + 1 * sizeof(APP_Code), (uint32_t*) APP_Code, sizeof(APP_Code) / sizeof(uint32_t));
  STMFLASH_Write(APP1_ADDRESS + 1 * sizeof(APP_Code), (uint32_t*) APP_Code, sizeof(APP_Code) / sizeof(uint32_t)); // 64Kbyte code
  
  STMFLASH_Read(APP2_ADDRESS + 2 * sizeof(APP_Code), (uint32_t*) APP_Code, sizeof(APP_Code) / sizeof(uint32_t));
  STMFLASH_Write(APP1_ADDRESS + 2 * sizeof(APP_Code), (uint32_t*) APP_Code, sizeof(APP_Code) / sizeof(uint32_t)); // 64Kbyte code
  
  STMFLASH_Read(APP2_ADDRESS + 3 * sizeof(APP_Code), (uint32_t*) APP_Code, sizeof(APP_Code) / sizeof(uint32_t));
  STMFLASH_Write(APP1_ADDRESS + 3 * sizeof(APP_Code), (uint32_t*) APP_Code, sizeof(APP_Code) / sizeof(uint32_t)); // 64Kbyte code
  
  // 擦除升级标志,使得MCU复位后程序运行到运行区
  STMFLASH_Earse_Sector(UPDATE_FLAG_ADDRESS);

  __set_FAULTMASK(1);	// 关闭所有中断
  HAL_NVIC_SystemReset();
  
  /* Infinite loop */
  for(;;)
  {
    osDelay(10);
  }
  /* USER CODE END 5 */
}

APP端:

\Core\Src\tasks\slave_modbus_task.c

c 复制代码
void Slave_Modbus_Task(void *pvParameters)
{
	uint32_t total_length = 0;
	uint32_t receive_length = 0;
	uint32_t length = 0;	
	uint16_t step = 0;
	uint32_t destination_address = 0;
	uint32_t check_sum = 0;
	uint32_t elapse_time_start = 0;
	uint32_t elapse_time = 0;
	uint8_t backup_flag = 0;
	uint8_t update_flag = 0;

	// Slave_Data_Init();

	// eMBTCPInit(502);
	// eMBEnable();
	xMBPortEventInit();

	MX_IWDG_Init();	
	while (1)
	{
		eMBPoll();

		// 固件更新升级代码	
		if ((Slave_station.usSRegHoldBuf[3100] == MAIN_BOARD_ID) && (Slave_station.usSRegHoldBuf[3103] == 0xAAAA))
		{
			if ((Slave_station.usSRegHoldBuf[3101] == 0xFFFF) && (Slave_station.usSRegHoldBuf[3102] == 0xFFFF))
			{
				step = 0;
			}

			if (step == 0)
			{ 
				{
					step = 1;
					Slave_station.usSRegHoldBuf[3103] = 0x5555;
				}
			}
			else if (step == 1)
			{
				total_length = (uint32_t)Slave_station.usSRegHoldBuf[3101] + ((uint32_t)Slave_station.usSRegHoldBuf[3102] << 16);
				receive_length = 0;
				
				if (total_length > (256 * 1024))
				{
					Slave_station.usSRegHoldBuf[3103] = 0xFFFF;
					step = 0;
				}
				else
				{
					vTaskSuspendAll();
					// earse flash
					STMFLASH_Earse_Sector(UPDATE_CODE_ADDRESS);
					STMFLASH_Earse_Sector(UPDATE_CODE_ADDRESS + 0x00020000);
					xTaskResumeAll();

					destination_address = UPDATE_CODE_ADDRESS;
					step = 2;
					Slave_station.usSRegHoldBuf[3103] = 0x5555;
				}
			}
			else if (step == 2)
			{
				length = total_length - receive_length;

				if (length >= 128)
				{
					receive_length += 128;
					length = 128;
				}
				else
				{
					receive_length += length;
				}

				vTaskSuspendAll();
				// write flash
				for (uint32_t i = 0; i < length / 2; i += 2)
				{
					HAL_FLASH_Unlock();
					__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
					HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, destination_address, *(uint32_t *)&Slave_station.usSRegHoldBuf[3104 + i]);
					HAL_FLASH_Lock();

					destination_address += 4;
				}
				xTaskResumeAll();
 
				// equ total length?
				if (receive_length == total_length)
				{
					// judge checksum
					check_sum = crc32((uint8_t *)UPDATE_CODE_ADDRESS, total_length - 4);
					
					if (check_sum == *(uint32_t *)(UPDATE_CODE_ADDRESS + total_length - 4))
					{
						vTaskSuspendAll();
						/* Unlock the Flash to enable the flash control register access *************/
						STMFLASH_Earse_Sector(UPDATE_FLAG_ADDRESS);
						HAL_FLASH_Unlock();
						HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, UPDATE_FLAG_ADDRESS, 0x000000A5);
						/* Lock the Flash to disable the flash control register access (recommended
							to protect the FLASH memory against possible unwanted operation) *********/
						HAL_FLASH_Lock();
						xTaskResumeAll();

						// 升级成功
						Slave_station.usSRegHoldBuf[3103] = 0x2222;
						step = 3;
					}
					else
					{
						// 升级失败
						Slave_station.usSRegHoldBuf[3103] = 0xFFFF;
						step = 0;
					}
				}
				else if (receive_length > total_length)
				{
					// 升级失败
					Slave_station.usSRegHoldBuf[3103] = 0xFFFF;
					step = 0;
				}
				else
				{
					Slave_station.usSRegHoldBuf[3103] = 0x5555;
				}
			}
			else if (step == 3)
			{
                __set_FAULTMASK(1);	// 关闭所有中断
                HAL_NVIC_SystemReset();

				step = 4;
			}
		}

	}
}
相关推荐
茯苓gao37 分钟前
嵌入式开发笔记:电机参数辨识与自学习完全指南——从电气参数到机械特性的深度解析
笔记·嵌入式硬件·学习
西安景驰电子1 小时前
PCIe 授时卡原理及应用
运维·服务器·windows·单片机·嵌入式硬件·fpga开发
派勤电子2 小时前
5G物联网网关工控主板选型指南(超小尺寸工业级嵌入式硬件解决方案)
嵌入式硬件·工业网关·工控主板·工控主板选型·嵌入式工控主板·5g物联网网关·超小尺寸工控主板
&Hello Code2 小时前
2.一套可落地的 STM32 六层架构:基础工程框架搭建
stm32·嵌入式硬件·架构
sramdram3 小时前
高速低功耗stt-mram工业级存储方案
嵌入式硬件·mram·stt-mram·低功耗stt-mram
躺不平的理查德4 小时前
STM32 上拉和下拉
stm32·单片机·嵌入式硬件
嘶哈哈哈5 小时前
雷迅 CAN Babel 新品测评:一个小巧的 CAN/UART 双功能 USB 调试适配器
单片机·嵌入式硬件
Zevalin爱灰灰6 小时前
模拟信号链电路——感应触摸电路
单片机·嵌入式硬件
国科安芯6 小时前
还是4位?ASC8T245S 与 ASC4T245S 的双向电平转换选型实战
单片机·嵌入式硬件·物联网·fpga开发·机器人·云计算
Freak嵌入式7 小时前
RP2040 内核电源调节器:软件可调电压 + 三种模式,低功耗设计的利器
人工智能·单片机·嵌入式硬件·机器人·开源