加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

STM32 Internal Flash DFU芯片内部flash代码升级

发布时间:2020-12-15 17:47:32 所属栏目:百科 来源:网络整理
导读:这次要讲讲如何用USB升级单片机代码。以前曾经做过串口升级,网络升级,升级的基本原理都类似,只不过升级的工具不同罢了,串口升级当然是用串口了,网络升级用的是TCP/IP,USB升级当然用的是USB了。下面就来讲讲USB升级的实现。 修改部分部分都在USB_User组
这次要讲讲如何用USB升级单片机代码。以前曾经做过串口升级,网络升级,升级的基本原理都类似,只不过升级的工具不同罢了,串口升级当然是用串口了,网络升级用的是TCP/IP,USB升级当然用的是USB了。下面就来讲讲USB升级的实现。
修改部分部分都在USB_User组里:

我们一个一个文件讲过来。
首先讲讲hw_config.c,这个文件跟之前工程差不多。由于演示的需要,我们在这个文件里初始化一个按键引脚,并定义按键读取函数,该按键决定代码是否升级,如果程序一开始,该按键按下,则进入升级模式,否则跳转到升级程序代码处:

/******************************************************************************* * Function Name : DFU_Button_Config. * Description : 配置DFU模式选择按键. * Input : None. * Output : None. * Return : None. *******************************************************************************/ void DFU_Button_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DFU, ENABLE); /* 配置DFU按键*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Pin DFU_ENTER_PIN GPIO_InitDFU_ENTER &GPIO_InitStructure); } * Function Name : DFU_Button_Read. * Description : 读取DFU按键是否按下 * Return : 返回0:按键按下;1:按键没有按下 *******************************************************************************/ uint8_t DFU_Button_Read return GPIO_ReadInputDataBit}

usb_desc.c这个文件自然要修改的,USB的功能属性等全在这里定义。首先必须关注下设备描述度符,这里有一点需要强调,就是厂商ID域的值必须为0483,否则电脑不识别USB,产品ID可以自定义。
/* USB标准设备描述符*/
 DFU_DeviceDescriptor[DFU_SIZ_DEVICE_DESC]=
 0x12/*bLength:长度,设备描述符的长度为18字节*/
 USB_DEVICE_DESCRIPTOR_TYPE/*bDescriptorType:类型,设备描述符的编号是0x01*/
0x00/*bcdUSB:所使用的USB版本为2.0*/
0x02/*bDeviceClass:设备所使用的类代码*/
/*bDeviceSubClass:设备所使用的子类代码*/
/*bDeviceProtocol:设备所使用的协议*/
0x40/*bMaxPacketSize:最大包长度为64字节*/
0x83/*idVendor:厂商ID为0x1234*/
0x040x11/*idProduct:产品ID为0x1010*/
0xDF/*bcdDevice:设备的版本号为2.00*/
1/*iManufacturer:厂商字符串的索引*/
2/*iProduct:产品字符串的索引*/
3/*iSerialNumber:设备的序列号字符串索引*/
0x01/*bNumConfiguration:设备有1种配置*/
};/* DFU设备描述符 */

接下去是配置描述符集合,配置描述符不需要修改。但之后的接口描述符的 bNumEndpoints域(该接口所使用的端点数)需要设置成0,因为USB DFU值需要端点0,不不需要其他的端点;接口描述符的bInterfaceClass(该接口所使用的类)域的值为0xFE,表示使用DFU类接口;接口描述符的bInterfaceSubClass(该接口所用的子类)设置成0x01,表示boot用途;接口描述符的nInterfaceProtocol设置成0x2,即DFU模式;最后还要设置接口字符串描述符的索引值为4。接下去是DFU功能描述符,这里设置bmAttribute域USB的属性为0x0B(具体意义看下面代码);DetachTimeOut(超时时间)设置成0XFF,表示超时时间为255ms;接下去设置TransferSize:(传输的长度)为0x400,注意这里是用两字节小端模式表示。
/* USB配置描述符集合(配置、接口、端点、类、厂商)(Configuration,Interface,Endpoint,Class,Vendor */
 DFU_ConfigDescriptorDFU_SIZ_CONFIG_DESC0x09/*bLength:长度,设备字符串的长度为9字节*/
 USB_CONFIGURATION_DESCRIPTOR_TYPE/*bDescriptorType:类型,配置描述符的类型编号为0x2*/
 DFU_SIZ_CONFIG_DESC/*wTotalLength:配置描述符的总长度为41字节*/ 
0x01/*bNumInterfaces:配置所支持的接口数量1个*/
/*bConfigurationValue:该配置的值*/
/*iConfiguration:该配置的字符串的索引值,该值为0表示没有字符串*/0xC0/* bmAttributes:设备的一些特性,0xc0表示自供电,不支持远程唤醒
 D7:保留必须为1,D6:是否自供电,D5:是否支持远程唤醒,D4~D0:保留设置为0*/
0x32/*从总线上获得的最大电流为100mA */
// 0x96,/*MaxPower:设备需要从总线上获取多少电流,单位为2mA,0x96表示300mA*/


/****** Descriptor of DFU interface 0 Alternate setting 0***********/
/*bLength:长度,接口描述符的长度为9字节 */
 USB_INTERFACE_DESCRIPTOR_TYPE/* bDescriptorType:接口描述符的类型为0x4 */
/*bInterfaceNumber:该接口的编号*/
/*bAlternateSetting:该接口的备用编号 */
/*bNumEndpoints:该接口所使用的端点数*/
0xFE/*bInterfaceClass该接口所使用的类为 DFU*/
/*bInterfaceSubClass:该接口所用的子类 1=BOOT,0=no boot */
/*nInterfaceProtocol :DFU模式*/

4 /* iInterface:接口字符串描述符的索引 */ /******************** DFU功能描述符********************/ /*blength:DFU描述符的长度为9字节*/ 0x21/*功能描述符的类型为0x21*/ 0x0B/*bmAttribute bitCanDnload = 1 (bit 0) 下载性能 bitCanUpload = 1 (bit 1) 上传性能 bitManifestationTolerant = 0 (bit 2) 设备在心事阶段后是否可通过USB通讯 bitWillDetach = 1 (bit 3) 接收到DFU_DETACH命令时会进行总线detach-attac Reserved (bit4-6) 保留 bitAcceleratedST = 0 (bit 7) 该为置1,设备将会上传速度增加到4096字节/命令*/ 0xFF/*DetachTimeOut:超时时间 255 ms*/ wTransferSizeB0 wTransferSizeB1/* TransferSize:传输的长度为1024 Byte*/ 0x1A/* bcdDFUVersion:DFU协议版本*/ 0x01

接下去的语言字符串描述符、厂商字符串描述符、产品字符串描述符、序列号字符串描述符都不详细介绍了。
/* 语言ID描述符 */
 DFU_StringLangIdDFU_SIZ_STRING_LANGID DFU_SIZ_STRING_LANGID/*bLength:本描述符的长度为4字节*/
 USB_STRING_DESCRIPTOR_TYPE/*bDescriptorType:字符串描述符的类型为0x03*/
/*bString:语言ID为0x0409,表示美式英语*/
0x04
/* LangID = 0x0409: U.S. English*/

/*厂商字符串描述符*/
 DFU_StringVendorDFU_SIZ_STRING_VENDOR DFU_SIZ_STRING_VENDOR/*bLength:厂商字符串描述符的长度*/
 'B'0'y'':''z''i''e''3''4'0/*自定义*/
};

/*产品的字符串描述符*/
 DFU_StringProductDFU_SIZ_STRING_PRODUCT DFU_SIZ_STRING_PRODUCT/* bLength:产品的字符串描述符*/
/* bDescriptorType:字符串描述符的类型为0x03*/
'z'0/*产品序列号的字符串描述符*/
 DFU_StringSerialDFU_SIZ_STRING_SERIAL DFU_SIZ_STRING_SERIAL/* bLength:产品序列号*/
'1''2''5''6''7'};

重点介绍的是接口字符串描述符,这个字符串描述符定义了升级的硬件信息,如下是我们的接口描述符:
/*接口字符串描述符*/
 DFU_StringInterface0DFU_SIZ_STRING_INTERFACE0=
 DFU_SIZ_STRING_INTERFACE00x03// Interface 0: "@Internal Flash /0x08000000/12*001Ka,500*001Kg"
'@''I''n''t''r''a''l'/* 18 */
' ''F''s''h'/* 16 */

'/''0''x''8'/* 22 */

'*''K'',''g'/* 20 */
};

可以看到上面的注释: // Interface 0: "@Internal Flash /0x08000000/12*001Ka,500*001Kg",这就是这个接口描述符的所表达的信息,我们接下去就详细介绍 接口描述符的数组内容的信息:
—— @:表示这是一个特殊的映射描述符(避免按照标准描述符解码)
—— /:表示不同区之间的分隔符
—— 最大8位的地址,以“0X”开头
—— 最大两位的扇区编号
—— *:扇区数量和扇区大小之间的分隔符
—— 最大3位的扇区大小(0~999)
—— 1位扇区大小单位:有效输入是:B(字节),K(千),M(兆)
—— 1位的扇区类型:
a(0x41):可读
b(0x42):可擦除
c(0x43):可读可擦写
d(0x44):可写
e(0x45):可读可写
f (0x46):可写可擦除
g(0x47):可读可写可擦除
如上面的”? @Internal Flash /0x08000000/12*001Ka,500*001Kg"表示的意思是:存储器的名字为"Internal Flash",起始地址是0x08000000,12*1K的空间可读,500*1K的空间可读可写可擦除。顺便说明下我是用的芯片是STM32F103ZET6,flash空间是512K.

usb_istr.c这个文件和usb_pwr.c这个两个文件不需要修改。
usb_prop.c的文件修改量比较大,这个文件描述了DFU各个状态之间的变化转移,代码我还是点模糊,也不准备详细讲,直接贴代码,部分已将做了注解:

#include"usb_lib.h" "hw_config.h" "usb_conf.h" "usb_prop.h" "usb_desc.h" "usb_pwr.h" "dfu_mal.h" uint32_t wBlockNum wlength ; Manifest_StateManifest_complete; PointerApplicationAddress//程序擦写、读写的基地址 DEVICE Device_Table//端点信息 EP_NUM//已使用的端点数 1//还可以使用的端点数 }; DEVICE_PROP Device_Property//注册一些DFU相关的处理函数 DFU_init//初始化 DFU_Reset//复位 DFU_Status_In//状态输入 DFU_Status_Out//状态输出 DFU_Data_Setup//带数据阶段的建立 DFU_NoData_Setup//不带数据阶段的建立 DFU_Get_Interface_Setting//获取接口值 DFU_GetDeviceDescriptor//获取设备描述符 DFU_GetConfigDescriptor//获取配置描述符 DFU_GetStringDescriptor//获取字符串描述符 //DFU_EP0Buffer bMaxPacketSize0 //最大包大小*/ USER_STANDARD_REQUESTS User_Standard_Requests//标准请求 DFU_GetConfiguration//获取配置值请求 DFU_SetConfiguration//设置配置值请求 DFU_GetInterface//获取接口值请求 DFU_SetInterface//设置接口值请求 DFU_GetStatus//获取状态请求 DFU_ClearFeature//清除特性请求 DFU_SetEndPointFeature//设置断点特性请求 DFU_SetDeviceFeature//设置设备特性请求 DFU_SetDeviceAddress //设置设备地址请求 ONE_DESCRIPTOR Device_Descriptor//注册设备描述符信息 (uint8_t*)DFU_DeviceDescriptor//设备描述符地址 DFU_SIZ_DEVICE_DESC Config_Descriptor//注册配置描述符信息 DFU_ConfigDescriptor//配置描述符地址 DFU_SIZ_CONFIG_DESC }; ONE_DESCRIPTOR DFU_String_Descriptor[5//注册字符串描述符 {DFU_StringLangId DFU_SIZ_STRING_LANGID },0)">//语言字符串 DFU_StringVendor DFU_SIZ_STRING_VENDOR //厂商字符串 DFU_StringProduct DFU_SIZ_STRING_PRODUCT //产品字符串 DFU_StringSerial DFU_SIZ_STRING_SERIAL //序列号字符串 DFU_StringInterface0 DFU_SIZ_STRING_INTERFACE0 }//接口描述符 /* Extern variables ----------------------------------------------------------*/ externDeviceState; DeviceStatus6]; /* Private function prototypes -----------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ * Function Name : DFU_init. * Description : DFU初始化. DEVICE_INFO *pInfo &Device_InfoGet_SerialNum();//获取序列号 pInfo->Current_Configuration//初始化当前配置值 PowerOn//连接设备 USB_SIL_Init//执行基本的设备初始化操作 USB_Interrupts_Config//使能USB中断 bDeviceState UNCONNECTED; * Function Name : DFU_Reset. * Description : DFU 复位 ./* Current Feature initialization */ pInformationCurrent_Feature7];//获取当前的特性 _SetBTABLEBTABLE_ADDRESS/* Initialize Endpoint 0 */_SetEPTypeENDP0 EP_CONTROL);//设置端点0为控制端点 _SetEPTxStatus EP_TX_NAK//设置端点0发送为EP_TX_NAK _SetEPRxAddr ENDP0_RXADDR//设置端点0的接收地址 SetEPRxCountDevice_PropertyMaxPacketSize//设置端点0的最大接收包长度 _SetEPTxAddr ENDP0_TXADDR//设置端点0的发送地址 SetEPTxCount//设置端点0的最大发送包长度 Clear_Status_Out//清除端点0状态 SetEPRxValid//设置端点接收有效 /* Set this device to response on default address */ SetDeviceAddress(//设置设备默认地址 /* Set the new control state of the device to Attached */ ATTACHED} * Function Name : DFU_SetConfiguration. * Description : 更新设备配置状态 ifpInfo!=) /* Device configured */ CONFIGURED} * Description : 更新设备的编址状态 DFU_SetDeviceAddress ADDRESSED* Function Name : DFU_Status_In. * Description : DFU状态输入函数 {} * Function Name : DFU_Status_Out. * Description : DFU状态输出函数 DFU_Status_Out AddrUSBbRequest== DFU_GETSTATUS STATE_dfuDNBUSY)//当前状态为正在状态 wBlockNum //块数为0 ((MAL_Buffer CMD_GETCOMMANDS&&wlength ))//获取更多设备性能以及DFU支持哪些设备 {} else MAL_Buffer CMD_SETADDRESSPOINTER //设置地址指针 ]; +=<<81624} CMD_ERASE ))//发送擦除带地址扇区命令 //获取下载的地址 MAL_ErasePointer//擦除该地址 } >//块数大于1 Addr- wTransferSize+;//获取写入存储器数据的地址 MAL_Write wlength//写入数据 STATE_dfuDNLOAD_SYNC//设置状态为下载同步 DeviceStatereturn STATE_dfuMANIFEST//显示进程 DFU_write_crc//写CRC校验 * Function Name : DFU_Data_Setup. * Description : 处理带数据的特殊类请求 * Input : RequestNb. * Return : USB_SUCCESS or USB_UNSUPPORT. *******************************************************************************/ RESULT DFU_Data_SetupRequestNo*(*CopyRoutine)(uint16_t); CopyRoutine NULLType_RecipientCLASS_REQUEST | INTERFACE_RECIPIENT//类请求,请求的接收者是接口 RequestNo DFU_UPLOAD STATE_dfuIDLE || STATE_dfuUPLOAD_IDLE //设备空闲,上传空闲 UPLOAD//函数指针指向UPLOAD函数 DFU_DNLOAD STATE_dfuDNLOAD_IDLE//设备空闲,下载空闲 //设置设备状态为下载同步 DNLOAD//函数指针指向DNDOWN函数 DFU_GETSTATE//DFU_GETSTATE请求 GETSTATE//函数指针指向GETSTATE函数 //DFU_GETSTATUS请求 GETSTATUS//函数指针指向GETSTATUS函数 else USB_UNSUPPORTCtrl_InfoCopyData//注册这个函数指针 Usb_wOffset(*)(//指向概述指针所指向的函数 USB_SUCCESS* Function Name : DFU_NoData_Setup. * Description : Handle the No data class specific requests. * Input : Request Nb. RESULT DFU_NoData_Setup{ //类请求 /*DFU_NDLOAD*/ DFU_DNLOAD//DFU_DNLOAD请求 /* End of DNLOAD operation*/ STATE_dfuDNLOAD_IDLE STATE_dfuIDLE )//下载空闲或设备空闲 Manifest_In_Progress//设置显示状态为显示进程 STATE_dfuMANIFEST_SYNC//设置显示同步 /*DFU_UPLOAD*/ DFU_UPLOAD//DFU_UPLOAD命令 STATE_dfuIDLE//设置设备空闲 /*DFU_CLRSTATUS*/ DFU_CLRSTATUS//DFU_CLRSTATUS命令 STATE_dfuERROR//dfu错误 //清除错误状态 STATUS_OK/*bStatus*//*bwPollTimeout=0ms*/ /*bState*/ /*iString*/ /*State Error*/ //设置设备状态为升级错误 STATUS_ERRUNKNOWN/*bStatus*/ /*DFU_ABORT*/ DFU_ABORT//DFU_ABORT终止命令 STATE_dfuDNLOAD_SYNC STATE_dfuMANIFEST_SYNC //设别空闲或上传同步或下载空闲或显示同步或上传空闲 //设置设别空闲 } ; /* End of DFU_NoData_Setup */ * Function Name : DFU_GetDeviceDescriptor. * Description : 获取设备描述符 * Input : Length. * Return : 返回设备描述符的地址 DFU_GetDeviceDescriptoruint16_tLengthStandard_GetDescriptorDataDevice_Descriptor* Function Name : DFU_GetConfigDescriptor. * Description : 获取配置描述符 * Return : 返回配置描述符的地址 DFU_GetConfigDescriptorStandard_GetDescriptorDataConfig_Descriptor* Function Name : DFU_GetStringDescriptor. * Description : 根据索引获取字符串描述 * Return : 返回字符串描述符的地址 DFU_GetStringDescriptor wValue0 USBwValue0wValue0 DFU_String_DescriptorwValue0]); * Function Name : DFU_Get_Interface_Setting. * Description : 测试是否支持接口和备用接口 * Input : - Interface : 接口号 * - AlternateSetting : 备用接口号 RESULT DFU_Get_Interface_SettingInterfaceAlternateSettingAlternateSetting/* In this application we don't have more than 3 AlternateSettings */ Interface/* In this application we have only 1 interfaces */ * Function Name : UPLOAD * Description : 上传 * Return : Pointer to data. UPLOAD//获取设备的信息 B1 B0//16bit的高8位和低8位 offset returned*Phy_Addr B0 USBwValuesbwbb0//获取请求的wValue的低8位数据 B1 bb1//获取请求的wValue的高8位数据 B10x100B0//更新wBlockNum USBwLengths//获取请求的wLength的低8位数据 //获取请求的wLength的高8位数据 //更新wLength offset Usb_wOffset//获取数据偏移量 //wBlockNum等于0,表示不传输数据 STATE_dfuUPLOAD_IDLE//Get命令 CMD_SETADDRESSPOINTER CMD_ERASE//擦除含地址的的扇区 LengthUsb_wLength3(&//wBlockNum大于1 //计算要上传的数据在存储器中的地址 MAL_Read);//读取数据 returned //获取上传数据的起始地址 //要上传的长度为0 //即wBlockNum=1,不支持的wBlockNum STATUS_ERRSTALLEDPKT* Function Name : DNLOAD * Description : 下载 * Return : 返回指向数据的指针 DNLOAD //获取设备信息 //指向数据的起始地址 ((MAL_Buffer * Function Name : GETSTATE. * Description : 获取STATE请求函数. GETSTATE(&* Function Name : GETSTATUS. * Description : 获取Status请求函数 GETSTATUSswitchcase: )) MAL_GetStatus/* (wlength==0)*/ break STATE_dfuMANIFEST_SYNC /*bwPollTimeout = 1ms*/ //break; Manifest_completeDescriptor20] default6(&(])); * Function Name : DFU_write_crc. * Description : DFU 写CRC STATE_dfuMANIFEST_WAIT_RESETReset_Device(); }

接下去要讲的是flash_if.c这个文件,这个文件实现的前提是工程已经添加了stm32f10x_flash.c库文件。这个文件只有四个文件:FLASH_If_Init(),FLASH_If_Erase(),FLASH_If_Write(),FLASH_If_Read()。首先讲讲 FLASH_If_Init(),因为flash是程序存储的地方,一上电就已经可以使用,所以用不着初始化了,在这个函数里可以是空函数:

FLASH_If_Init MAL_OK}

然后是FLASH_If_Erase(uint32_t SectorAddress),这个函数调用flash的擦除页函数FLASH_ErasePage():
FLASH_If_EraseSectorAddress FLASH_ErasePage}

至于FLASH_If_Write()这个函数就稍稍复杂点了,这个函数需要判断要写的长度是否子对齐,如果字不对齐,则需要填充成字对齐,然后在写入flash中,这里需要注意的是我们的STM32是32位的,无论是写还是读都要字对齐。
FLASH_If_WriteDataLength idx DataLength0x3/* Not an aligned data */ foridx <0xFFFC idx++) idx/* Data received are Word multiple */ FLASH_ProgramWord*(*)()); SectorAddress}

然后是读函数uint8_t *FLASH_If_Read (uint32_t SectorAddress,uint32_t DataLength),我们这里直接返回要读的地址,这个函数只有在校验的时候会调用。
FLASH_If_Read *)(}

接下去就是dfu_mal.c了。这个文件包括MAL_Init()、MAL_Erase()、MAL_Write()、MAL_Read()、MAL_GetStatus():
MAL_Init//Internal Flash初始化 * Function Name : MAL_Erase * Description : 擦除扇区 * Input : None * Output : None * Return : None MAL_MASK//参看地址 INTERNAL_FLASH_BASE://如果是在INTERNAL FLASH地址池内 pMAL_Erase //擦写函数指针指向FLASH_If_Erase default MAL_FAIL pMAL_Erase//指向擦除函数 * Function Name : MAL_Write * Description : 写扇区 MAL_Write //查看地址 pMAL_Write //写函数指针指向FLASH_If_Write pMAL_Write//调用写扇区函数 * Function Name : MAL_Read * Description : 度扇区 * Return : Buffer pointer MAL_Read pMAL_Read FLASH_If_Read//读函数指针指向FLASH_If_Read //调用如扇区函数 * Function Name : MAL_GetStatus * Description : 获取状态 * Return : MAL_OK Cmdbuffer//更具地址查找定时表的对应的选项 x >>260x03/* 0x000000000 --> 0 */ /* 0x640000000 --> 1 */ /* 0x080000000 --> 2 */ y Cmd SET_POLLING_TIMINGTimingTablex][y]);/* x: 擦除/写 定时 */ /* y: Media */ }

最后要说的main函数了:

typedef(*pFunction)(); pFunction Jump_To_ApplicationJumpAddress;

int main BSP_Init(); printf(" |===============================================|rn"" STM32 DFU 程序开始 rn""|===============================================|rn");

DFU_Button_Read()(((*(__IO uint32_t*)0x2FFE00000x20000000//检验跳转地址区域是否正确 JumpAddress*(ApplicationAddress//获取跳转程序的RESET中断向量地址 Jump_To_Application//强制转换成函数指针 __set_MSP(*(//将该地址写入R14 //调用函数,当函数调用结束时,PC指正指向R14的地址 }

FLASH_Unlock/* Enter DFU mode */ //程序指向到这句话,说明DFU跳转不成功 STATUS_ERRFIRMWARE USB_Configuration//初始化USB while//LED1闪烁 LED1_ToggleDelay_ms1000}

这里有三个地方需要特别说明下:
1、程序跳转的实现。我们在hw_config.h中定义了升级地址:#define ApplicationAddress 0x08005000,在主函数中,首次按会检查该升级地址是否有效if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000),这句话其实是检查该升级地址是否在BANK1的地址范围内,因为flash在BANK1。 可以看到上面定义了一个函数指针类型的函数结构: typedef void (*pFunction)(void);还定了一个跳转地址变量 uint32_t JumpAddress;可以看到 JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);这句话 JumpAddress赋值为 升级地址+4的地址,可以知道该地址指向升级代码的主堆栈,至于有人问道:为什么不直接使用 ApplicationAddress呢?原因很简单,因为 ApplicationAddresss是个常数,无法直接使用,所以这里定义了 JumpAddress这个变量。 然后又用该函数结构定义了一个函数 pFunction Jump_To_Application;,代码 Jump_To_Application = (pFunction) JumpAddress;中函数指针 Jump_To_Application指向 JumpAddress地址处。接着代码调用了__set_MSP(*(__IO uint32_t*) ApplicationAddress);这句话的意思是将 ApplicationAddress的地址写入R14寄存器。最后调用 Jump_To_Application()函数,当该函数调用返回是,PC指针会回去R14的保存的地址,然后程序跳到该地址执行。这就是程序跳转的实现。

2、一定要给flash解锁,否则升级程序无法烧写到flash中,所以必须调用 FLASH_Unlock();这个函数。
3、一定要给定设备的状态。一开始 DeviceState必须设置成 STATE_dfuERROR, DeviceStatus[0]必须设置为 STATUS_ERRFIRMWARE。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读