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;
			}
		}

	}
}
相关推荐
嵌入式阿蔡41 分钟前
OTA 实战四:安全启动与固件签名(Secure Boot)—— 拒绝非法固件
网络·stm32·单片机·嵌入式硬件·嵌入式实时数据库
LCMICRO-133108477461 小时前
国产长芯微LP8628完全P2P替代AD8628,零漂移、单电源、输入输出轨到轨高精度运放
arm开发·嵌入式硬件·fpga开发·硬件工程·精密运放·ad8628
DevHub1 小时前
电视盒子刷 Armbian 教程:30 元旧盒子变 NAS 跑 Docker,200+ 机型可刷
linux·嵌入式硬件·docker·容器·开源·电视盒子
wuyk5559 小时前
4.树:一对多的层次数据结构
开发语言·数据结构·stm32·单片机
天空'之城12 小时前
单片机基础核心知识点汇总(五)
单片机·嵌入式硬件
恒锐丰-罗生17 小时前
SS8103 芯片解析:45V 同步整流降压半桥恒流驱动控制器 大功率光源国产优选 告别低灰闪烁!
驱动开发·嵌入式硬件·硬件工程
FakeOccupational18 小时前
【电路笔记 STM32】stm32f103 实现HAL_UART_Transmit_DMA + 普通模式/连续模式的 UART DMA 收发
笔记·stm32·嵌入式硬件
wuyk55518 小时前
第 6 章 FOC 完整系统整合:从算法到真机跑起来
c语言·开发语言·stm32·单片机·嵌入式硬件
恒锐丰科技韩生18 小时前
率能 SS6810H|10‑32V 双通道 H 桥步进电机驱动 ETSSOP20 打印机安防舞台灯光专用
嵌入式硬件·硬件工程
1560820721919 小时前
直流耦合采集卡前端电路调试
嵌入式硬件·fpga开发