wordpress 主题站,深圳市建设工程交易服务网宝安分中心,武功县住房与城乡建设局网站,绍兴模板建站公司0. 项目移植
对于不想知道其执行过程的朋友来说#xff0c;可以直接移植#xff0c;我的板子是STM32F411CER6, 512K M4内核 项目地址#xff1a; Bootloader#xff08;可以自己写标志位用于自测#xff0c;项目中这部分代码已经被注释#xff0c;可以打开自行测试…0. 项目移植
对于不想知道其执行过程的朋友来说可以直接移植我的板子是STM32F411CER6, 512K M4内核 项目地址 Bootloader可以自己写标志位用于自测项目中这部分代码已经被注释可以打开自行测试配套测试程序 0.1 Bootloader移植 修改刷写大小我用的Cubeide我使用第一个扇区当作bootloader其为16k 修改指示灯引脚 修改扇区开始地址根据自己的芯片的内部FLASH扇区分配分配对应的起始地址 修改分区开始地址 分区参考1.1中的分区表进行分区 修改刷写大小
0.2 应用程序和Bootloaer配合 应用程序只要正确的将程序刷写到对应的分区开始地址即可刷写示例程序参照 2.2 应用程序可以选择性包含以下两个文件VernonBL_Compatible.h文件用于指示Settings分区中各个变量的枚举值便于和Bootloader交互partition_table.h则保存分区表 最为重要的一步 重定义向量表设置向量表偏移量0x0000_8000是因为前两个分区占据了32k大小换成十六进制为0x8000 魔术棒修改刷写地址
1. 整体思路
正常的裸机STM32直接开始执行程序为了能够正式启动应用程序之前能过做更多的功能比如固件更新等因此我们需要Bootloader
在正常的STM32启动流程中其实也有Bootloader的身影存在即我们在开发的时候所看到的启动文件。
我们先来大致过一下正常的STM32是如何进行启动的。
STM32首先将ROM的0x0800_0000映射成0x0000_0000STM32获取0x0800_0000的第一地址内的内容连续取32位此内容即为MSP堆栈指针此后单片机便从此地址开始读取数据STM32获取从0x0800_0000偏移四个地址的内容0x0800_0004因为上面读取了32位此内容则为PC指针的内容至此单片机跳转到0x0800_0004中所代表地址因为此地址的值给了PC指针PC指针的地址刚好是函数SystemInit的地址SystemInit中负责相关时钟初始化等工作。
具体的启动细节这里不在解释读者可自行查阅其他文章
1.1 分区介绍 分区有好几种分区方式具体可以参见这个文章https://blog.csdn.net/ShenZhen_zixian/article/details/129064681 要想实现Bootloader启动我们就应该先给ROM 进行分区这里我们采用一种全新的方式这种方式我们就得采用奇数和偶数更新法就是奇数版本号更新到Application偶数版本更新到Application_2因为我们两个分区的程序中断向量表映射位置是不同的。优点就是有一个版本的备份。
下图对STM32F411CEU6 512K的ROM进行分配的STM32F1系列可以分配到1k一个扇区 我们采用Bootloader分区设置参数双分区的形式和其他教程不同的是Application_2也用于运行程序即Bootloader只识别BOOT_PARTITION中的内容用来识别跳转到第Application分区还是Application_2分区这样做的好处是即使新版本任何错误我们Bootloader可以自动切换回旧的版本运行。防止造成设备故障。
1.2 启动过程 阅读本章之前请先阅读这个文章讲的很好很清楚https://shatang.github.io/2020/08/12/IAP%E5%8D%87%E7%BA%A7-Bootloader%E5%88%B6%E4%BD%9C/ 使用Bootloader之后我们的启动过程为先启动BootloaderBootloader再来启动应用程序。具体在Bootloader内应该
判断栈顶指针是否合规获得应用程序的PC指针设置应用程序MSP堆栈指针通过PC地址跳转到应用程序开始执行应用程序
具体在应用程序内应该
重定向向量表–设置向量表偏移量(注意一定要设置偏移量(VECT_TAB_OFFSET)来完成重定向向量表而不是设置FLASH_BASE来达到重定向向量表的效果不然DMA之类的中断无法使用后面会详细讲到)检查是否有用户更新用户更新的时候刷写到Application的另外一个分区写Settings中的信息用于指示Bootloader下一步启动时启动哪个Application
2. 代码编写
2.1 Boot loader编写
2.1.0 CubeMX配置
cubemx里面的这些引脚我相信各位一看就知道我配置了什么简单地说除了必要的配置我另外配置了串口、还有一个指示灯(PA0)指示灯使用Systick提供闪烁功能。 要注意的是STM32F411CEU6 HAL库Systick的中断回调默认官方对其进行了关闭按照如下方式打开
如果你的Systick_Handler打开是这样子的只有一个HAL_IncTick() 那么改成这样把HAL_SYSTICK_IRQHandler()加进去
//stm32f4xx_it.c
/*** brief This function handles System tick timer.*/
void SysTick_Handler(void)
{/* USER CODE BEGIN SysTick_IRQn 0 *//* USER CODE END SysTick_IRQn 0 */HAL_IncTick();/* USER CODE BEGIN SysTick_IRQn 1 */HAL_SYSTICK_IRQHandler();/* USER CODE END SysTick_IRQn 1 */
}这样你main.c中才能写Systick回调
//mainc.c
void HAL_SYSTICK_Callback(void){bootloader_run_notify_led_count ;if(bootloader_run_notify_led_count 600){bootloader_run_notify_led_count 0;led_blink_on ~led_blink_on;}
}2.1.1 跳转函数
在编写跳转函数之前我们应该先根据手册将扇区定义好我这里用的STM32F411CEU6其有512K Flash因此根据图表列出定义 //partation_table.h
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) //sector0 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) //sector1 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) //sector2 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) //sector3 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) //sector4 addr, 64 Kbytes
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) //sector5 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) //sector6 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) //sector7 addr, 128 Kbytes 编写其跳转函数我们根据官方的IAP程序中的示例我们直接拿过来。
//main.c
typedef void (*pFunction)(void);static pFunction JumpToApplication;
static uint32_t JumpAddress;uint8_t IAP_LoadAPP(uint32_t AppxAddr)
{if (((*(__IO uint32_t*)AppxAddr) 0x2FFE0000 ) 0x20000000){/* Jump to user application */JumpAddress *(__IO uint32_t*) (AppxAddr 4); //PC指针地址JumpToApplication (pFunction) JumpAddress;/* Initialize user applications Stack Pointer */__set_MSP(*(__IO uint32_t*) AppxAddr); //设置MSP指针JumpToApplication();return 0;}return -1;
}2.2.2 日志
打印点东西表示我进入Bootloader了
void print_boot_message(void)
{printf(\r\n--------- Enter Vernon BootLoader --------\r\n);printf(\r\n);printf( flash partition table \r\n);printf(| name | offset | size |\r\n);printf(--------------------------------------\r\n);printf(| bootloader | 0x%08lx | 0x%08x |\r\n, BOOT_SECTOR_ADDR, BOOT_SECTOR_SIZE);printf(| setting | 0x%08lx | 0x%08x |\r\n, SETTING_SECTOR_ADDR, SETTING_SECTOR_SIZE);printf(| application1 | 0x%08lx | 0x%08x |\r\n, APP_SECTOR_ADDR, APP_SECTOR_SIZE);printf(| application2 | 0x%08lx | 0x%08x |\r\n, APP2_SECTOR_ADDR, APP2_SECTOR_SIZE);printf(\r\n);printf(\r\n);
}2.2.3 Flash刷写函数
下面这几个函数是用来写Settings这个分区里的标志位的即BOOT_STATE和BOOT_PARTITION
//flash_fun.c
int8_t read_settings_boot_state(void)
{return *(__IO uint8_t *)(SETTING_SECTOR_ADDR);
}int8_t write_settings_boot_state(uint8_t state)
{uint32_t sector_index;HAL_StatusTypeDef res;uint32_t read_buf;read_buf *(__IO uint32_t *)(SETTING_SECTOR_ADDR); // 先把前四个字节数据读出来res HAL_FLASH_Unlock();if (res ! HAL_OK){printf(FLASH_UNLOCK ERROR\r\n);return -1;}sector_index get_sector_from_addr(SETTING_SECTOR_ADDR);printf([Bootloader]Erase ADDR 0x%08lx; Sector No.%ld...\r\n, SETTING_SECTOR_ADDR, sector_index);FLASH_Erase_Sector(sector_index, FLASH_VOLTAGE_RANGE_3);printf([Bootloader]Flash ADDR 0x%08lx ...\r\n, SETTING_SECTOR_ADDR);// 把想要写入的信息写入readbuf中去最后把32位信息一起写进去read_buf 0xffffff00; // 先把第一个字节数据清0,FLASH是高字节存储在高位低字节存储在低位所以应该把低字节设置为0read_buf | state;res HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, SETTING_SECTOR_ADDR, read_buf);if (res ! HAL_OK){printf([Bootloader]FLASH_WRITE ERROR\r\n);return -1;}res HAL_FLASH_Lock();if (res ! HAL_OK){printf([Bootloader]FLASH_LOCK ERROR\r\n);return -1;}return 0;
}int8_t read_settings_boot_partition(void)
{return *(__IO uint8_t *)(SETTING_SECTOR_ADDR SETTING_BOOT_PARTITION_OFFSET);
}int8_t write_settings_boot_partition(int8_t state)
{int sector_index;HAL_StatusTypeDef res;uint32_t read_buf;read_buf *(__IO uint32_t *)(SETTING_SECTOR_ADDR); // 先把前四个字节数据读出来res HAL_FLASH_Unlock();if (res ! HAL_OK){printf(FLASH_UNLOCK ERROR\r\n);return -1;}sector_index get_sector_from_addr(SETTING_SECTOR_ADDR);printf([Bootloader]Erase ADDR 0x%08lx; Sector No.%d...\r\n, SETTING_SECTOR_ADDR, sector_index);FLASH_Erase_Sector(sector_index, FLASH_VOLTAGE_RANGE_3);printf([Bootloader]Flash ADDR 0x%08lx ...\r\n, SETTING_SECTOR_ADDR);// 把想要写入的信息写入readbuf中去最后把32位信息一起写进去read_buf 0xffff00ff; // 先把第二个字节数据清0,FLASH是高字节存储在高位低字节存储在低位所以应该把低字节设置为0read_buf | (state 8); // 放在第二个字节的位置res HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, SETTING_SECTOR_ADDR, read_buf);if (res ! HAL_OK){printf([Bootloader]FLASH_WRITE ERROR\r\n);return -1;}res HAL_FLASH_Lock();if (res ! HAL_OK){printf([Bootloader]FLASH_LOCK ERROR\r\n);return -1;}return 0;
}2.2.4 根据标志位启动对应应用程序
BOOT_STATE有三个状态分别为运行状态更新应用程序状态和应用程序更新完成状态应用程序更新由用户编写的应用程序完成这里只负责根据对应标志位跳转。
//main.c
boot_state read_settings_boot_state();boot_partition_select read_settings_boot_partition();switch(boot_state){case RUN_APP_STATE:printf([Bootloader]Start to run APP[%d] ...\r\n, boot_partition_select);if(boot_partition_select RUN_APP1_partition){err IAP_LoadAPP(APP_SECTOR_ADDR);if(err ! 0){printf([Bootloader]Run App error, please flash the new bin...\r\n);}}else if(boot_partition_select RUN_APP2_partition){err IAP_LoadAPP(APP2_SECTOR_ADDR);if(err ! 0){printf([Bootloader]Run App error, please flash the new bin...\r\n);}}else{printf([Bootloader]Can not find the select settings of the partition\r\n);}break;case UPDATE_APP_STATE:printf([Bootloader]Update APP...\r\n);break;case SUCCESS_UPDATE_APP_STATE:printf([Bootloader]Success Update APP...Then Reboot System\r\n);err write_settings_boot_state(RUN_APP_STATE);if(err ! 0){printf(FLASH ERROR!\r\n);}__ASM volatile (cpsid i); //关闭总中断HAL_NVIC_SystemReset();break;default:printf([Bootloader]Unknown Update APP...Error Code : %x\r\n, boot_state);}2.2 用户程序编写-测试
在应用程序中使用YModem协议进行数据传输写入新的固件之后再由应用程序写入Setttings配置信息重启之后Bootloader即可自动启动新更新的应用。
2.2.1 Flash和Ymodem函数的实现
这两个部分在Cubemx的实例中有但是其Flash函数个人测试无法使用还有Ymodem函数有bug个人对其进行了修改和适配篇幅限制就不说改了哪里了大家直接在Gitee克隆下来用吧具体代码可以去库里面查看
2.2.1.1 Flash函数
在Bootloader中的函数中再添加
//flash_func.c
// FLash Function
/*** brief This function does an erase of all user flash area* param StartSector: start of user flash area* retval 0: user flash area successfully erased* 1: error occurred*/
uint32_t flash_erase(uint32_t StartAdd)
{uint32_t UserStartSector;uint32_t SectorError;FLASH_EraseInitTypeDef pEraseInit;HAL_FLASH_Unlock();/* Get the sector where start the user flash area */UserStartSector get_sector_from_addr(StartAdd);pEraseInit.TypeErase TYPEERASE_SECTORS;pEraseInit.Sector UserStartSector;pEraseInit.NbSectors 5;pEraseInit.VoltageRange VOLTAGE_RANGE_3;if (HAL_FLASHEx_Erase(pEraseInit, SectorError) ! HAL_OK){/* Error occurred while page erase */return (1);}HAL_FLASH_Lock();return (0);
}/*** brief This function writes a data buffer in flash (data are 32-bit aligned).* note After writing data buffer, the flash content is checked.* param StartAddress: start address for writing data buffer* param EndAddress: end address for writing data buffer* param Data: pointer on data buffer* param DataLength: length of data buffer (unit is 32-bit word)* retval 0: Data successfully written to Flash memory* -2: Error occurred while writing data in Flash memory* -1: Written Data in flash memory is different from expected one*/
int8_t flash_write_continue(uint32_t StartAddress, uint32_t EndAddress,uint32_t *Data, uint32_t DataLength)
{int32_t i 0;HAL_FLASH_Unlock();for (i 0; (i DataLength) (StartAddress (EndAddress - 4)); i){/* Device voltage range supposed to be [2.7V to 3.6V], the operation willbe done by word */if (HAL_FLASH_Program(TYPEPROGRAM_WORD, StartAddress, *(uint32_t *)(Data i)) HAL_OK){/* Check the written value */if (*(uint32_t *)StartAddress ! *(uint32_t *)(Data i)){/* Flash content doesnt match SRAM content */return (-1);}/* Increment FLASH destination address */StartAddress 4;}else{/* Error occurred while writing data in Flash memory */return (-2);}}HAL_FLASH_Lock();return (0);
}2.2.1.2 Ymodem函数 关于协议可以看这篇https://blog.csdn.net/weixin_41865104/article/details/107388202 //ymedom.c
/********************************************************************************* file IAP/IAP_Main/Src/ymodem.c* author MCD Application Team* brief This file provides all the software functions related to the ymodem* protocol.******************************************************************************* attention** Copyright (c) 2017 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.********************************************************************************/
/** addtogroup STM32F4xx_IAP_Main* {*//* Includes ------------------------------------------------------------------*/
#include common.h
#include ymodem.h
#include string.h
#include main.h
#include usart.h
#include VernonBL_Compatible.h
#include flash_func.h/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define CRC16_F /* activate the CRC16 integrity */
#define UartHandle huart1
#define APPLICATION_ADDRESS APP2_SECTOR_ADDR
#define APPLICATION_PARTITION_SIZE APP_SECTOR_SIZE
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
__IO uint32_t flashdestination;
/* note ATTENTION - please keep this variable 32bit aligned */
uint8_t aPacketData[PACKET_1K_SIZE PACKET_DATA_INDEX PACKET_TRAILER_SIZE];
uint8_t aFileName[FILE_NAME_LENGTH];
/* Private function prototypes -----------------------------------------------*/
static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout);
uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte);
uint16_t Cal_CRC16(const uint8_t *p_data, uint32_t size);
uint8_t CalcChecksum(const uint8_t *p_data, uint32_t size);/* Private functions ---------------------------------------------------------*//*** brief Receive a packet from sender* param data* param length* 0: end of transmission* 2: abort by sender* 0: packet length* param timeout* retval HAL_OK: normally return* HAL_BUSY: abort by user*/
static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout)
{uint32_t crc;uint32_t packet_size 0;HAL_StatusTypeDef status;uint8_t char1;*p_length 0;status HAL_UART_Receive(UartHandle, char1, 1, timeout);if (status HAL_OK){switch (char1){case SOH:packet_size PACKET_SIZE;break;case STX:packet_size PACKET_1K_SIZE;break;case EOT:break;case CA:if ((HAL_UART_Receive(UartHandle, char1, 1, timeout) HAL_OK) (char1 CA)){packet_size 2;}else{status HAL_ERROR;}break;case ABORT1:case ABORT2:status HAL_BUSY;break;default:status HAL_ERROR;break;}*p_data char1;if (packet_size PACKET_SIZE){status HAL_UART_Receive(UartHandle, p_data[PACKET_NUMBER_INDEX], packet_size PACKET_OVERHEAD_SIZE, timeout);/* Simple packet sanity check */if (status HAL_OK){if (p_data[PACKET_NUMBER_INDEX] ! ((p_data[PACKET_CNUMBER_INDEX]) ^ NEGATIVE_BYTE)){packet_size 0;status HAL_ERROR;}else{/* Check packet CRC */crc p_data[packet_size PACKET_DATA_INDEX] 8;crc p_data[packet_size PACKET_DATA_INDEX 1];if (Cal_CRC16(p_data[PACKET_DATA_INDEX], packet_size) ! crc){packet_size 0;status HAL_ERROR;}}}else{packet_size 0;}}}*p_length packet_size;return status;
}/*** brief Update CRC16 for input byte* param crc_in input value* param input byte* retval None*/
uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte)
{uint32_t crc crc_in;uint32_t in byte | 0x100;do{crc 1;in 1;if (in 0x100)crc;if (crc 0x10000)crc ^ 0x1021;}while (!(in 0x10000));return crc 0xffffu;
}/*** brief Cal CRC16 for YModem Packet* param data* param length* retval None*/
uint16_t Cal_CRC16(const uint8_t *p_data, uint32_t size)
{uint32_t crc 0;const uint8_t *dataEnd p_data size;while (p_data dataEnd)crc UpdateCRC16(crc, *p_data);crc UpdateCRC16(crc, 0);crc UpdateCRC16(crc, 0);return crc 0xffffu;
}/*** brief Calculate Check sum for YModem Packet* param p_data Pointer to input data* param size length of input data* retval uint8_t checksum value*/
uint8_t CalcChecksum(const uint8_t *p_data, uint32_t size)
{uint32_t sum 0;const uint8_t *p_data_end p_data size;while (p_data p_data_end){sum *p_data;}return (sum 0xffu);
}/* Public functions ---------------------------------------------------------*/
/*** brief Receive a file using the ymodem protocol with CRC16.* param p_size The size of the file.* retval COM_StatusTypeDef result of reception/programming*/
COM_StatusTypeDef Ymodem_Receive(uint32_t *p_size)
{uint32_t i, packet_length, session_done 0, file_done, errors 0, session_begin 0;// uint32_t flashdestination;uint32_t ramsource, filesize, packets_received;uint8_t *file_ptr;uint8_t file_size[FILE_SIZE_LENGTH], tmp;COM_StatusTypeDef result COM_OK;*p_size 0; //it may be a random value if you not assigned value in out of the function/* Initialize flashdestination variable */flashdestination APP2_SECTOR_ADDR;while ((session_done 0) (result COM_OK)){packets_received 0;file_done 0;while ((file_done 0) (result COM_OK)){switch (ReceivePacket(aPacketData, packet_length, DOWNLOAD_TIMEOUT)){case HAL_OK:errors 0;switch (packet_length){case 2:/* Abort by sender */Serial_PutByte(ACK);result COM_ABORT;break;case 0:/* End of transmission */Serial_PutByte(ACK);file_done 1;break;default:/* Normal packet */if (aPacketData[PACKET_NUMBER_INDEX] ! (uint8_t)packets_received){Serial_PutByte(NAK);}else{if (packets_received 0){/* File name packet */if (aPacketData[PACKET_DATA_INDEX] ! 0){/* File name extraction */i 0;file_ptr aPacketData PACKET_DATA_INDEX;while ((*file_ptr ! 0) (i FILE_NAME_LENGTH)){aFileName[i] *file_ptr;}/* File size extraction */aFileName[i] \0;i 0;file_ptr;while ((*file_ptr ! ) (i FILE_SIZE_LENGTH)){file_size[i] *file_ptr;}file_size[i] \0;Str2Int(file_size, filesize);/* Test the size of the image to be sent *//* Image size is greater than Flash size */if (*p_size (APPLICATION_PARTITION_SIZE 1)){/* End session */tmp CA;HAL_UART_Transmit(UartHandle, tmp, 1, NAK_TIMEOUT);HAL_UART_Transmit(UartHandle, tmp, 1, NAK_TIMEOUT);result COM_LIMIT;}/* erase user application area */flash_erase(APPLICATION_ADDRESS);*p_size filesize;Serial_PutByte(ACK);Serial_PutByte(CRC16);}/* File header packet is empty, end session */else{Serial_PutByte(ACK);file_done 1;session_done 1;break;}}else /* Data packet */{ramsource (uint32_t)aPacketData[PACKET_DATA_INDEX];/* Write received data in Flash */ if (flash_write_continue(flashdestination, APPLICATION_ADDRESS APPLICATION_PARTITION_SIZE,(uint32_t*) ramsource, packet_length/4) 0){//data transforming led blinkint value 3;while(value --){HAL_Delay(50);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);HAL_Delay(50);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);}flashdestination packet_length;Serial_PutByte(ACK);}else /* An error occurred while writing to Flash memory */{/* End session */Serial_PutByte(CA);Serial_PutByte(CA);result COM_DATA;}}packets_received;session_begin 1;}break;}break;case HAL_BUSY: /* Abort actually */Serial_PutByte(CA);Serial_PutByte(CA);result COM_ABORT;break;default:if (session_begin 0){errors;}if (errors MAX_ERRORS){/* Abort communication */Serial_PutByte(CA);Serial_PutByte(CA);}else{Serial_PutByte(CRC16); /* Ask for a packet */}break;}}}return result;
}/*******************(C)COPYRIGHT 2016 STMicroelectronics *****END OF FILE****/最后外部调用即可 2.3 重定义向量表重点看有坑
我们可以知道我们的应用程序是写在了0x0800_8000的那我们程序从这里开始不就可以了吗事实也确实是这样正常情况下应用程序应该从0x0800_0000开始我们看下图 这里定义了FLASH_BASE也确实是这样正常情况从0x0800_0000开始所以我们把这个变量改成0x0800_8000不就可以了吗中断向量表也在从这个地址的开始写着。这不是完美吗有些博主也确实是这么做的。能运行吗能如果不涉及DMA的话不是说只有DMA只是因为我写应用程序的时候用到了DMA它出现了问题。
所以我们坚决不能改这个
所以我们应该改的是偏移值 0x0000_8000是因为前两个分区占据了32k大小换成十六进制为0x8000
其实源码中Note已经写的很清楚了只不过我们不太注意哈哈。
这个小插曲我在学习这部分的时候所有博主都没说过所以难免会出现这种问题正常现象 现在解决以免以后在工作中出现~
2.4 修改刷写地址
魔术棒里面修改地址和大小即可 2.5 刷写测试
这里使用软件Tera Term 5因为其支持1k的Ymodem刷写速度较快 插入开发板打开串口可以发现Bootloader启动了当其出现C字样的时候表示其可以进行刷写。
选择bin文件使用Ymodem发送 等待其刷写完成就可以了 应用程序中设置的烧写在Application_2这个分区里所以我们可以通过keli看0x0804_0000这个地址的内容如果有内容则刷写成功。