bootloader程序
STM32FLASH读写
1 /** 2 ****************************************************************************** 3 * @file stmflash.c 4 * @brief 读写STM32内部flash(STM32G030F6P6TR) 5 ****************************************************************************** 6 */ 7 8 #include "stmflash.h" 9 10 uint8_t STMFLASH_BUF[FLASH_PAGE_SIZE]; //数据缓存 最多是2KB 11 12 /** 13 * @brief 读取STM内部FLASH数据 14 * @param faddr:FLASH地址 (此地址必须为4的倍数!!) 15 * @param *buffer:数据存放地址 16 * @param length:读取的数据长度(32位数据) 17 * @retval 无 18 */ 19 void STMFLASH_ReadData(uint32_t faddr, uint8_t *buffer, uint32_t length) 20 { 21 for(uint16_t i=0; i < length/4 ; i++ ){ 22 memcpy(buffer+i*4, (uint32_t*)(faddr+i*4), 4); //使用memcpy将32位数据存储到uint8_t数组中 23 } 24 } 25 26 27 /** 28 * @brief 数据无校验写入STM内部FLASH 29 * @param faddr:FLASH起始地址 (此地址必须为4的倍数!!) 30 * @param *buffer:要写入的数据地址 31 * @param length:写入的数据字节数 32 * @retval 无 33 */ 34 #if STM32_FLASH_WREN //如果使能了写 35 void STMFLASH_Write_NoCheck(uint32_t faddr,uint8_t *pBuffer,uint16_t length) 36 { 37 uint64_t temp; 38 for(uint16_t i=0;i <= length/8;i++) 39 { 40 memcpy(&temp, pBuffer + i * 8, sizeof(temp)); 41 HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,faddr,temp); //一次写8字节数据 42 faddr += 8;//地址增加8. 43 } 44 } 45 46 47 static uint32_t GetPage(uint32_t Addr) 48 { 49 return (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;; 50 } 51 52 /** 53 * @brief 擦除一页 54 * @param Addr:页地址 55 * @retval 1:擦除成功 0:擦除失败 56 */ 57 uint8_t STMFLASH_ErasePage(uint32_t Addr) 58 { 59 HAL_FLASH_Unlock(); //FLASH解锁 60 61 FLASH_EraseInitTypeDef EraseInitStruct = {0}; //声明 FLASH_EraseInitTypeDef 结构体 62 uint32_t PageError = 0; 63 EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; //按页擦除 64 EraseInitStruct.Page = GetPage(Addr); //指定初始擦除的 Flash 页 65 EraseInitStruct.NbPages = 1 ; //要擦除的页数 66 67 HAL_StatusTypeDef EraseStatus = HAL_FLASHEx_Erase(&EraseInitStruct, &PageError); 68 if ( EraseStatus != HAL_OK){ //调用擦除函数擦除 69 return 0;//擦擦失败 70 }else 71 return 1;//擦除成功 72 73 HAL_FLASH_Lock(); //上锁 74 75 } 76 77 /** 78 * @brief 从指定地址开始写入指定长度的数据 79 * @param WriteAddr:起始地址(此地址必须为4的倍数!!) 80 * @param *pBuffer:要写入的数据地址 81 * @param length:写入的数据字节数 82 * @retval 1:写入成功 0:写入失败 83 */ 84 85 #include "stdio.h" 86 extern void printfdata(uint8_t *data,uint16_t len); 87 void STMFLASH_Write(uint32_t WriteAddr,uint8_t *pBuffer,uint16_t length) 88 { 89 if(WriteAddr < FLASH_BASE||(WriteAddr>=(FLASH_BASE + FLASH_PAGE_SIZE * STM32_FLASH_SIZE/2))){ 90 return;//非法地址 91 } 92 93 uint32_t sector; //扇区 94 uint16_t secoff; //扇区内偏移地址(按字节计算) 95 uint16_t secremain; //扇区内剩余地址(按字节计算) 96 uint32_t offaddr; //去掉0X08000000后的地址 97 uint16_t i; 98 99 offaddr = WriteAddr - FLASH_BASE; //实际偏移地址 100 sector = offaddr / FLASH_PAGE_SIZE; //地址所在扇区 101 secoff = offaddr % FLASH_PAGE_SIZE; //在扇区内的偏移(字节为基本单位.) 102 secremain = FLASH_PAGE_SIZE - secoff; //扇区剩余空间大小 103 104 HAL_FLASH_Unlock(); //FLASH解锁 105 if( length <= secremain ){ 106 secremain = length; //如果写入数据长度不大于一个扇区 107 } 108 while(1) //循环写入数据 109 { 110 STMFLASH_ReadData((sector*FLASH_PAGE_SIZE + FLASH_BASE),(uint8_t*)STMFLASH_BUF,FLASH_PAGE_SIZE/4);//读出整个扇区的内容 111 112 for( i=0; i < secremain;i++) //校验数据 113 { 114 if(STMFLASH_BUF[secoff+i]!=0XFF) 115 break; //需要擦除 116 } 117 if( i < secremain ) //需要擦除 118 { 119 STMFLASH_ErasePage(sector * FLASH_PAGE_SIZE + FLASH_BASE); /* 擦除这个扇区 */ 120 FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成 121 122 for(i=0;i < secremain;i++)//复制 123 { 124 STMFLASH_BUF[i+secoff] = pBuffer[i]; 125 } 126 STMFLASH_Write_NoCheck(sector*FLASH_PAGE_SIZE+FLASH_BASE,STMFLASH_BUF,secremain+i);//写入整个扇区 127 FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成 128 129 }else //不需要擦除直接写入 130 { 131 STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. 132 FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成 133 } 134 135 if(length == secremain){ 136 break;//写入结束了 137 }else//写入未结束 138 { 139 sector++; //扇区增1 140 secoff = 0; //偏移位置为0 141 pBuffer += secremain; //指针偏移 142 WriteAddr += secremain*8; //写地址偏移 143 length -= secremain; //字节数递减 144 if(length > (FLASH_PAGE_SIZE/8)){ 145 secremain = FLASH_PAGE_SIZE/8;//下一个扇区还是写不完 146 }else 147 secremain = length;//下一个扇区可以写完了 148 } 149 }; 150 151 HAL_FLASH_Lock(); //上锁 152 } 153 154 #endif
1 /** 2 ****************************************************************************** 3 * @file stmflash.h 4 * @brief 读写STM32内部flash(STM32G030F6P6TR) 5 ****************************************************************************** 6 */ 7 8 9 #ifndef __STMFLASH_H__ 10 #define __STMFLASH_H__ 11 #include "main.h" 12 #include "string.h" 13 14 //用户根据自己的需要设置 15 extern void FLASH_PageErase(uint32_t Banks, uint32_t Page); 16 17 #define STM32_FLASH_SIZE 32 //所选STM32的FLASH容量大小(单位为K) 18 #define STM32_FLASH_WREN 1 //使能FLASH写入(0,不是能;1,使能) 19 #define FLASH_WAITETIME 10 //FLASH等待超时时间 20 21 22 void STMFLASH_Write(uint32_t WriteAddr,uint8_t *pBuffer,uint16_t length); 23 void STMFLASH_Write_NoCheck(uint32_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite); 24 void STMFLASH_ReadData(uint32_t faddr, uint8_t *buffer, uint32_t length); 25 uint8_t STMFLASH_ErasePage(uint32_t Addr) ; 26 #endif
IAP
1 /** 2 ****************************************************************************** 3 * @file iap.c 4 * @brief 在线升级APP 5 ****************************************************************************** 6 */ 7 8 #include "iap.h" 9 10 Stm32IapStruct IGK_IAP; 11 //CRC校验表 12 unsigned long CRC32Table[256]; 13 14 uint8_t Uart2_Rx[UART2_RX_LEN]={0}; 15 uint8_t Uart2_Tx[UART2_TX_LEN]={0}; 16 uint16_t Uart2_Rx_length = 0; 17 uint16_t Uart2_Tx_length = 0; 18 19 //定义一个函数指针变量 20 typedef void (*pFunction)(void); 21 22 void App_Jump(uint32_t app_addr) { 23 __disable_irq(); //关闭总中断 24 HAL_DeInit(); 25 if (((*(__IO uint32_t*)app_addr) & 0x2FFE0000 ) == 0x20000000) 26 { 27 /* Jump to user application */ 28 uint32_t JumpAddress = *(__IO uint32_t*) (app_addr + 4); 29 pFunction Jump_To_Application = (pFunction) JumpAddress; 30 /* Initialize user application's Stack Pointer */ 31 __set_MSP(*(__IO uint32_t*) app_addr);//设置栈指针 32 Jump_To_Application(); 33 } 34 } 35 36 void Uart2_Tx_Start(uint16_t len){ 37 HAL_UART_Transmit_DMA(&huart2,(uint8_t *)&Uart2_Tx,len); 38 } 39 40 //IAP初始化 41 void IAP_BootLoad_Init(void) 42 { 43 //CRC校验的表 44 CRC32TableCreate(); 45 //初始化 46 IGK_IAP.UpdataStart = 0; 47 IGK_IAP.UpdataOk = 0; 48 IGK_IAP.WateTime = 0; 49 IGK_IAP.RxBuff = Uart2_Rx;//接收数据指针 50 IGK_IAP.TxBuff = Uart2_Tx;//发送数据指针 51 IGK_IAP.RxLength = &Uart2_Rx_length;//接收长度 52 IGK_IAP.SendFunc = Uart2_Tx_Start;//发送函数 53 } 54 55 //CRC32校验, 56 //pBuf:整包数据 57 //pBufSize:实际长度 58 uint8_t check_data(uint8_t *pBuf ,uint16_t pBufSize) //1:成功 0:错误 59 { 60 uint32_t get_PC_crcVel=0; 61 uint32_t CRC_PC_vel = 0; 62 uint8_t *pbuff; 63 if(pBufSize<5) 64 { 65 return 0; 66 } 67 pbuff = pBuf+pBufSize-4; 68 69 CRC_PC_vel = pbuff[0]<<24|pbuff[1]<<16|pbuff[2]<<8|pbuff[3]; //接收的CRC32 70 71 get_PC_crcVel = CRC32Calculate(pBuf,pBufSize-4); //MCU端计算的CRC32 72 73 if(get_PC_crcVel == CRC_PC_vel) 74 { 75 return 1; 76 } 77 else 78 { 79 return 0; 80 } 81 } 82 83 84 //IAP数据解析,放到串口中断-------------------------------------------------------------------------- 85 void IAP_BootLoad_UpData(void) 86 { 87 unsigned int crc=0; 88 //上位机请求刷固件 89 if((IGK_IAP.RxBuff[0] == 0xF1) && (IGK_IAP.RxBuff[1] == 0x01)) 90 { 91 92 if(check_data(IGK_IAP.RxBuff,*IGK_IAP.RxLength))//crc校验成功 93 { 94 //开始刷固件标志位 95 IGK_IAP.UpdataStart = 1; 96 //获取固件大小 97 IGK_IAP.FirmwareSize = IGK_IAP.RxBuff[2]<<24 | IGK_IAP.RxBuff[3]<<16 | IGK_IAP.RxBuff[4]<<8 | IGK_IAP.RxBuff[5]; 98 //获取总包数 99 IGK_IAP.PackageNum = IGK_IAP.RxBuff[6]<<8 | IGK_IAP.RxBuff[7]; 100 //响应 101 IGK_IAP.TxBuff[0] = 0xF1; //帧头 102 IGK_IAP.TxBuff[1] = 0x81; //功能码 103 104 IGK_IAP.TxBuff[2] = IGK_IAP.RxBuff[2]; 105 IGK_IAP.TxBuff[3] = IGK_IAP.RxBuff[3]; 106 IGK_IAP.TxBuff[4] = IGK_IAP.RxBuff[4]; 107 IGK_IAP.TxBuff[5] = IGK_IAP.RxBuff[5]; 108 109 IGK_IAP.TxBuff[6] = IGK_IAP.RxBuff[6]; 110 IGK_IAP.TxBuff[7] = IGK_IAP.RxBuff[7]; 111 112 crc = CRC32Calculate(IGK_IAP.TxBuff,8); 113 114 IGK_IAP.TxBuff[8] = crc>>24; 115 IGK_IAP.TxBuff[9] = crc>>16; 116 IGK_IAP.TxBuff[10] = crc>>8; 117 IGK_IAP.TxBuff[11] = crc&0xff; 118 119 IGK_IAP.SendFunc(12); 120 } 121 } 122 else if((IGK_IAP.RxBuff[0] == 0xF1) && (IGK_IAP.RxBuff[1] == 0x02)) 123 { 124 //crc校验成功,写入Flash 125 if(check_data(IGK_IAP.RxBuff,*IGK_IAP.RxLength)) 126 { 127 //解析 128 //获取包索引 129 IGK_IAP.PackageIndex = IGK_IAP.RxBuff[2]<<8 | IGK_IAP.RxBuff[3]; 130 //写入flash 131 STMFLASH_Write(STM32_FLASH_USER+IGK_IAP.PackageIndex*PackageSize,&IGK_IAP.RxBuff[4],*IGK_IAP.RxLength-8); 132 //响应 133 IGK_IAP.TxBuff[0] = 0xF1; 134 IGK_IAP.TxBuff[1] = 0x82; 135 136 IGK_IAP.TxBuff[2] = IGK_IAP.RxBuff[2]; 137 IGK_IAP.TxBuff[3] = IGK_IAP.RxBuff[3]; 138 //结果 139 IGK_IAP.TxBuff[4] = 0x01; 140 141 crc = CRC32Calculate(IGK_IAP.TxBuff,5); 142 143 IGK_IAP.TxBuff[5] = crc>>24; 144 IGK_IAP.TxBuff[6] = crc>>16; 145 IGK_IAP.TxBuff[7] = crc>>8; 146 IGK_IAP.TxBuff[8] = crc&0xff; 147 148 IGK_IAP.SendFunc(9); 149 //判断是否接收完成 150 if(IGK_IAP.PackageIndex == (IGK_IAP.PackageNum-1)) 151 { 152 //更新完成标志位 153 IGK_IAP.UpdataOk = 1; 154 } 155 } 156 else//crc校验失败,返回错误类型 157 { 158 //响应 159 IGK_IAP.TxBuff[0] = 0xF1; 160 IGK_IAP.TxBuff[1] = 0x82; 161 162 IGK_IAP.TxBuff[2] = IGK_IAP.RxBuff[2]; 163 IGK_IAP.TxBuff[3] = IGK_IAP.RxBuff[3]; 164 //结果 165 IGK_IAP.TxBuff[4] = 0x02; 166 crc = CRC32Calculate(IGK_IAP.TxBuff,5); 167 IGK_IAP.TxBuff[5] = crc>>24; 168 IGK_IAP.TxBuff[6] = crc>>16; 169 IGK_IAP.TxBuff[7] = crc>>8; 170 IGK_IAP.TxBuff[8] = crc&0xff; 171 IGK_IAP.SendFunc(9); 172 } 173 174 } 175 } 176 177 //生成CRC校验表 178 void CRC32TableCreate(void) 179 { 180 unsigned int c; 181 unsigned int i, j; 182 183 for (i = 0; i < 256; i++) { 184 c = (unsigned int)i; 185 for (j = 0; j < 8; j++) { 186 if (c & 1) 187 c = 0xedb88320L ^ (c >> 1); 188 else 189 c = c >> 1; 190 } 191 CRC32Table[i] = c; 192 } 193 } 194 195 //计算CRC校验 196 unsigned int CRC32Calculate(uint8_t *pBuf ,uint16_t pBufSize) 197 { 198 unsigned int retCRCValue=0xffffffff; 199 unsigned char *pData; 200 pData=(unsigned char *)pBuf; 201 while(pBufSize--) 202 { 203 retCRCValue=CRC32Table[(retCRCValue ^ *pData++) & 0xFF]^ (retCRCValue >> 8); 204 } 205 return retCRCValue^0xffffffff; 206 }
1 /** 2 ****************************************************************************** 3 * @file iap.c 4 * @brief 在线升级APP 5 ****************************************************************************** 6 */ 7 8 #ifndef __IAP_H__ 9 #define __IAP_H__ 10 #include "main.h" 11 #include "stmflash.h" 12 #include "usart.h" 13 #include "stdio.h" 14 15 #define UART2_RX_LEN 1024 + 50 16 #define UART2_TX_LEN 256 17 18 //一包数据的字节数 19 #define PackageSize 1024 20 21 #define STM32_FLASH_USER 0x08004000 //存储数据的FLASH的起始地址 22 23 //定义一个函数指针变量 24 typedef void (*PFunc)(void); 25 typedef void (*PFunc_U16)(uint16_t); 26 27 extern uint16_t Uart2_Rx_length; 28 29 typedef struct 30 { 31 uint8_t UpdataStart; //开始更新标志 32 uint8_t UpdataOk; //更新完成标志 33 uint32_t FirmwareSize; //固件大小 34 uint16_t PackageIndex; //包索引 35 uint16_t PackageNum; //分包数 36 uint16_t WateTime; //等待上位机请求刷固件超时时间 37 uint8_t* RxBuff; //接收数据指针 38 uint8_t* TxBuff; //发送数据指针 39 uint16_t* RxLength; //接收长度 40 PFunc_U16 SendFunc; //发送函数指针 41 }Stm32IapStruct; 42 43 extern Stm32IapStruct IGK_IAP; 44 45 void App_Jump(uint32_t app_addr) ; 46 47 //IAP初始化 48 void IAP_BootLoad_Init(void); 49 void IAP_BootLoad_UpData(void); 50 void iap_load_app(uint32_t appxaddr); 51 //计算CRC校验 52 unsigned int CRC32Calculate(uint8_t *pBuf ,uint16_t pBufSize); 53 //生成CRC校验表 54 void CRC32TableCreate(void); 55 #endif 56 57 /************************************************************************* 58 59 通信协议 60 帧头:0xF1 61 1.请求更新固件 62 上位机发: 63 0x01: 64 帧头【1】+ 功能码【1】+ 固件长度【4】+ 分包数【2】+ CRC32【4】 65 F1 01 00 00 22 84 00 09 65 4F AC 44 66 单片机响应: 67 0x81: 68 帧头【1】+ 功能码【1】+ 固件长度【4】+ 分包数【2】+ CRC32【4】 69 F1 81 00 00 22 84 00 09 ED 59 46 B6 70 2.发送数据包 71 上位机发: 72 0x02: 73 帧头【1】+ 功能码【1】+ 包索引【2】+数据【1024】+ CRC32【4】 74 F1 82 00 00 02 1A 28 91 6C F1 02 00 00 F8 04 ... 75 单片机响应: 76 单片机响应: 77 0x82: 78 帧头【1】+ 功能码【1】+ 包索引【2】+结果【1】+ CRC32【4】 79 F1 82 00 08 01 4B F8 4A DE 80 结果说明: 81 0x01:正常; 82 0x02:CRC 校验错误; 83 0x03:长度不够,丢失数据; 84 85 ************************************************************************/
串口配置
初始化IAP 开启串口DMA
1 /* USER CODE BEGIN 2 */ 2 IAP_BootLoad_Init(); 3 HAL_UARTEx_ReceiveToIdle_DMA(&huart2,(uint8_t *)&Uart2_Rx,UART2_RX_LEN); 4 /* USER CODE END 2 */
STM32串口DMA空闲回调
1 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){ 2 if (huart->Instance == USART2){ 3 Uart2_Rx_length = Size ; 4 IAP_BootLoad_UpData(); 5 HAL_UARTEx_ReceiveToIdle_DMA(&huart2,(uint8_t *)&Uart2_Rx,UART2_RX_LEN); 6 } 7 }
配置bootloader程序空间大小
定义APP地址
1 /* USER CODE BEGIN PD */ 2 #define STM32_FLASH_USER 0x08004000 //存储数据的FLASH的起始地址 3 4 /* USER CODE END PD */
跳转到APP
1 while (1) 2 { 3 //如果更新完成或者等待上位机请求超时,则跳转到主程序 4 if(IGK_IAP.UpdataOk) 5 { 6 App_Jump(APP_ADDR); 7 } 8 }
APP程序
配置flash
配置向量偏移地址开启系统总中断
1 int main(void) 2 { 3 /* USER CODE BEGIN 1 */ 4 SCB->VTOR = FLASH_BASE | 0x4000; // 5 /* USER CODE END 1 */ 6 7 /* MCU Configuration--------------------------------------------------------*/ 8 9 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ 10 HAL_Init(); 11 12 /* USER CODE BEGIN Init */ 13 __enable_irq(); //开启总中断 14 /* USER CODE END Init */
参考: 【STM32 IAP技术实现】适合小白“食用”(以STM32F103C8T6为例)_stm32f103c8t6 串口iap bootloader-CSDN博客
通过百度网盘分享的文件:STM32G030F6P6TR_IAP.zip
链接:https://pan.baidu.com/s/1pncer6ge9Bn56FnWb36YgA?pwd=IAP1
提取码:IAP1