python入門python的基本語法
894
2025-04-03
目錄
1、ICP、ISP和IAP的概念
2、IAP升級程序的原理
3、IAP升級程序的流程
4、IAR環(huán)境下IAP的實(shí)現(xiàn)
4.2、User Application程序設(shè)計(jì)
4.3、IAR地址配置及文件輸出
5、拓展:解析HEX文件
1、ICP、ISP和IAP的概念
在項(xiàng)目開發(fā)過程中通常使用SWD、JTAG等工具進(jìn)行程序燒錄和仿真,若產(chǎn)品節(jié)點(diǎn)較少還是比較方便,但是當(dāng)設(shè)備節(jié)點(diǎn)量產(chǎn)時(shí),就需要使用IAP的方式進(jìn)行程序燒錄。
簡單說明幾個(gè)概念I(lǐng)CP、ISP和IAP。
ICP In-circuit programmer
ICP:在電路編程,MCU內(nèi)部不需要有程序,上電就能夠?qū)Τ绦虼鎯^(qū)域進(jìn)行編程,例如平時(shí)使用JTAG、SWD等方式。
ISP?In-system?programer
ISP:在系統(tǒng)編程,通過MCU專用的串行編程接口進(jìn)行編程,MCU需要具有運(yùn)行的外部條件,例如有晶振等。
例如STM32通過設(shè)置BOOT引腳設(shè)置對應(yīng)啟動模式,然后通過串口等對內(nèi)部Flash進(jìn)行升級,可以說這種方式就是廠家在芯片內(nèi)部固化了一個(gè)BootLoader程序。
IAP In-application?programer
IAP:在應(yīng)用編程,開發(fā)者設(shè)計(jì)BootLoader程序,通過串口、CAN、以太網(wǎng)等通信方式實(shí)現(xiàn)程序升級。
2、IAP升級程序的原理
通常一塊MCU芯片的Code(代碼)區(qū)內(nèi)只有一個(gè)用戶程序,而IAP方案則是將代碼區(qū)劃分為兩部分,兩部分區(qū)域各存放一個(gè)程序,一個(gè)為BootLoader(引導(dǎo)加載程序),另一個(gè)為User Application(用戶應(yīng)用程序)。
BootLoader在出廠時(shí)就固定下來了,在需要變更User Application時(shí)只需要通過觸發(fā)BootLoader對User Application的擦除和重新寫入即可完成用戶應(yīng)用的更換。
程序執(zhí)行初始化后首先會進(jìn)入BootLoader,在BootLoader里面檢測條件是否被觸發(fā)(可通過按鍵是否被按下、串口是否接收到特定的數(shù)據(jù)、U盤是否插入等),如果有則進(jìn)行對User Application進(jìn)行擦除和重新寫入操作新程序,如果沒有則直接跳轉(zhuǎn)到BootLoader執(zhí)行User Application。
3、IAP升級程序的流程
假設(shè)設(shè)備僅有User Application,以STM32F103ZET6為例,其啟動方式有三種:內(nèi)置FLASH啟動、內(nèi)置SRAM啟動、系統(tǒng)存儲器ROM啟動。通過BOOT0和BOOT1引腳的設(shè)置可以選擇從哪中方式啟動,這里選擇內(nèi)置的FLASH啟動,STM32F103ZET6?FLASH的地址為0x08000000—0x0807FFFF,共512KB。
通常STM32發(fā)生中斷的過程為以下五步:
1、發(fā)生中斷(中斷請求);
2、到中斷向量表查找中斷函數(shù)入口地址;
3、跳轉(zhuǎn)到中斷函數(shù);
4、執(zhí)行中斷函數(shù);
5、中斷返回。
也就是說,STM32的內(nèi)置的Flash中有一個(gè)中斷向量表來存放各個(gè)中斷服務(wù)函數(shù)的入口地址,內(nèi)置Flash的分配情況如下圖所示:
所以當(dāng)只有一個(gè)程序的情況下(僅有User Applicatio時(shí)),程序執(zhí)行的走向如下所示:
解析上圖:
STM32F103ZET6有一個(gè)中斷向量表,這個(gè)中斷向量表存放在代碼開始部分的后4個(gè)字節(jié)處(即0x08000004),代碼開始的4個(gè)字節(jié)存放的是堆棧棧頂?shù)牡刂罚?dāng)發(fā)生中斷后程序通過查找該表得到相應(yīng)的中斷服務(wù)程序入口地址,然后再跳到相應(yīng)的中斷服務(wù)程序中執(zhí)行。
設(shè)備上電后從0x08000004處取出復(fù)位中斷向量的地址,然后跳轉(zhuǎn)到復(fù)位中斷程序的入口(標(biāo)號①所示),執(zhí)行結(jié)束后跳轉(zhuǎn)到main函數(shù)中(標(biāo)號②所示)。在執(zhí)行main函數(shù)的過程中發(fā)生中斷,則STM32強(qiáng)制將PC指針指回中斷向量表處(標(biāo)號③所示),從中斷向量表中找到相應(yīng)的中斷函數(shù)入口地址,跳轉(zhuǎn)到相應(yīng)的中斷服務(wù)函數(shù)(標(biāo)號④所示),執(zhí)行完中斷函數(shù)后再返回到main函數(shù)中來(標(biāo)號⑤所示)。
下面要講正題了。
若將STM32F103ZET6在內(nèi)置的Flash里面添加User Application和BootLoader程序,則Flash分配情況大致如下圖所示:
此時(shí),User Application和BootLoader程序各有一個(gè)中斷向量表,假設(shè)BootLoader程序占用的空間為N+M字節(jié),則程序的走向應(yīng)該如下圖所示:
解析上圖:
設(shè)備上電初始程序依然從0x08000004處取出復(fù)位中斷向量地址,執(zhí)行復(fù)位中斷函數(shù)后跳轉(zhuǎn)到IAP的main(標(biāo)號①所示),在IAP的main函數(shù)執(zhí)行完成后(在BootLoader里面檢測條件是否被觸發(fā)(可通過按鍵是否被按下、串口是否接收到特定的數(shù)據(jù)、U盤是否插入等),如果有則進(jìn)行對User Application進(jìn)行擦除和重新寫入操作新程序,如果沒有則直接跳轉(zhuǎn)到BootLoader執(zhí)行User Application)強(qiáng)制跳轉(zhuǎn)到0x08000004+N+M處(標(biāo)號②所示),最后跳轉(zhuǎn)到新的main函數(shù)中來(標(biāo)號③所示),當(dāng)發(fā)生中斷請求后,程序跳轉(zhuǎn)到新的中斷向量表中取出新的中斷函數(shù)入口地址,再跳轉(zhuǎn)到新的中斷服務(wù)函數(shù)中執(zhí)行(標(biāo)號④⑤所示),執(zhí)行完中斷函數(shù)后再返回到main函數(shù)中來(標(biāo)號⑥所示)。
4、IAR環(huán)境下IAP的實(shí)現(xiàn)
以IAR環(huán)境為例,簡單講述IAP的實(shí)現(xiàn)步驟。這里MCU以華大HC32L130為例,因?yàn)槭褂玫腗CU不同,所以實(shí)現(xiàn)的細(xì)節(jié)也不一致,但是基本上官方都會提供Demo例程。
本示例Flash分配情況為:BootLoader地址:0x00000000~0x00000DFF,User Application地址:0x00001000~0x0000FFFF。
4.1、BootLoader程序設(shè)計(jì)
第1步:設(shè)計(jì)總體架構(gòu),包含三個(gè)功能函數(shù):檢測BootLoader標(biāo)志程序、IAP配置程序和IAP燒錄功能程序。
/**
*******************************************************************************
** \brief IAP 主函數(shù)
**
** \param None
**
** \retval int32_t Return value, if needed
**
******************************************************************************/
int32_t main(void)
{
IAP_UpdateCheck();
IAP_Init();
IAP_Main();
}
第2步:檢查BootPara標(biāo)記區(qū)數(shù)據(jù)值,判斷是否需要升級APP程序,若需要升級則才會執(zhí)行IAP_Init()和IAP_Main()函數(shù),否則會直接跳轉(zhuǎn)到User Application程序。
/**
*******************************************************************************
** \brief 檢查BootPara標(biāo)記區(qū)數(shù)據(jù)值,判斷是否需要升級APP程序.
**
** \param None
**
** \retval None
**
******************************************************************************/
void IAP_UpdateCheck(void)
{
uint32_t u32AppFlag;
u32AppFlag = *(__IO uint32_t *)BOOT_PARA_ADDRESS; //讀出BootLoader para區(qū)標(biāo)記值
if (APP_FLAG != u32AppFlag) //如果標(biāo)記值不等于APP_FLAG,表示不需要升級APP程序
{
IAP_JumpToApp(APP_ADDRESS); //則直接跳轉(zhuǎn)至APP
}
}
第3步:IAP_Init()函數(shù)的實(shí)現(xiàn),主要包括外圍模塊初始化和IAP通信協(xié)議標(biāo)志初始化。
/**
*******************************************************************************
** \brief IAP 初始化
**
** \param [in] None
**
** \retval None
**
******************************************************************************/
void IAP_Init(void)
{
PreiModule_Init();
Modem_RamInit();
}
/**
*******************************************************************************
** \brief CPU外圍模塊初始化
**
** \param [in] None
**
** \retval None
**
******************************************************************************/
void PreiModule_Init(void)
{
HC32_SetSystemClockToRCH22_12MHz();
HC32_InitUart();
HC32_InitCRC();
HC32_InitTIM();
HC32_InitFlash(FLASH_CONFIG_FREQ_22_12MHZ);
}
/**
*******************************************************************************
** \brief modem文件中相關(guān)變量參數(shù)初始化
**
** \param [out] None
** \param [in] None
**
** \retval None
**
******************************************************************************/
void Modem_RamInit(void)
{
uint32_t i;
enFrameRecvStatus = FRAME_RECV_IDLE_STATUS; //幀狀態(tài)初始化為空閑狀態(tài)
for (i=0; i { u8FrameData[i] = 0; //幀數(shù)據(jù)緩存初始化為零 } u32FrameDataIndex = 0; //幀緩存數(shù)組索引值初始化為零 } 第4步:IAP_Main()函數(shù)的實(shí)現(xiàn),主要包含對User Application程序更新處理。 /** ******************************************************************************* ** \brief IAP APP程序升級主函數(shù). ** ** \param None ** ** \retval None ** ******************************************************************************/ void IAP_Main(void) { en_result_t enRet; while (1) { enRet = Modem_Process(); //APP程序更新處理 if (Ok == enRet) { IAP_ResetConfig(); //復(fù)位所有外設(shè)模塊 if (Error == IAP_JumpToApp(APP_ADDRESS)) //如果跳轉(zhuǎn)失敗 { while(1); } } } } /** ******************************************************************************* ** \brief 上位機(jī)數(shù)據(jù)幀解析及處理 ** ** \param [in] None ** ** \retval Ok APP程序升級完成,并接受到跳轉(zhuǎn)至APP命令 ** \retval OperationInProgress 數(shù)據(jù)處理中 ** \retval Error 通訊錯(cuò)誤 ** ******************************************************************************/ en_result_t Modem_Process(void) { uint8_t u8Cmd, u8FlashAddrValid, u8Cnt, u8Ret; uint16_t u16DataLength, u16PageNum, u16Ret; uint32_t u32FlashAddr, u32FlashLength, u32Temp; if (enFrameRecvStatus == FRAME_RECV_PROC_STATUS) //有數(shù)據(jù)幀待處理, enFrameRecvStatus值在串口中斷中調(diào)整 { u8Cmd = u8FrameData[PACKET_CMD_INDEX]; //獲取幀指令碼 if (PACKET_CMD_TYPE_DATA == u8FrameData[PACKET_TYPE_INDEX]) //如果是數(shù)據(jù)指令 { u8FlashAddrValid = 0u; u32FlashAddr = u8FrameData[PACKET_ADDRESS_INDEX] + //讀取地址值 (u8FrameData[PACKET_ADDRESS_INDEX + 1] << 8) + (u8FrameData[PACKET_ADDRESS_INDEX + 2] << 16) + (u8FrameData[PACKET_ADDRESS_INDEX + 3] << 24); if ((u32FlashAddr >= (FLASH_BASE + BOOT_SIZE)) && (u32FlashAddr < (FLASH_BASE + FLASH_SIZE))) //如果地址值在有效范圍內(nèi) { u8FlashAddrValid = 1u; //標(biāo)記地址有效 } } switch (u8Cmd) //根據(jù)指令碼跳轉(zhuǎn)執(zhí)行 { case PACKET_CMD_HANDSHAKE : //握手幀 指令碼 u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //返回狀態(tài)為:正確 Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀給上位機(jī) break; case PACKET_CMD_ERASE_FLASH : //擦除flash 指令碼 if ((u32FlashAddr % FLASH_SECTOR_SIZE) != 0) //如果擦除地址不是頁首地址 { u8FlashAddrValid = 0u; //標(biāo)記地址無效 } if (1u == u8FlashAddrValid) //如果地址有效 { u32Temp = u8FrameData[PACKET_DATA_INDEX] + //獲取待擦除flash尺寸 (u8FrameData[PACKET_DATA_INDEX + 1] << 8) + (u8FrameData[PACKET_DATA_INDEX + 2] << 16) + (u8FrameData[PACKET_DATA_INDEX + 3] << 24); u16PageNum = FLASH_PageNumber(u32Temp); //計(jì)算需擦除多少頁 for (u8Cnt=0; u8Cnt { u8Ret = Flash_EraseSector(u32FlashAddr + (u8Cnt * FLASH_SECTOR_SIZE)); if (Ok != u8Ret) //如果擦除失敗,反饋上位機(jī)錯(cuò)誤代碼 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR; break; } } if (Ok == u8Ret) //如果全部擦除成功,反饋上位機(jī)成功 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; }else //如果擦除失敗,反饋上位機(jī)錯(cuò)誤超時(shí)標(biāo)志 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_TIMEOUT; } } else //地址無效,反饋上位機(jī)地址錯(cuò)誤 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; } Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī) break; case PACKET_CMD_APP_DOWNLOAD : //數(shù)據(jù)下載 指令碼 if (1u == u8FlashAddrValid) //如果地址有效 { u16DataLength = u8FrameData[FRAME_LENGTH_INDEX] + (u8FrameData[FRAME_LENGTH_INDEX + 1] << 8) - PACKET_INSTRUCT_SEGMENT_SIZE; //獲取數(shù)據(jù)包中的數(shù)據(jù)長度(不包含指令碼指令類型等等) if (u16DataLength > PACKET_DATA_SEGMENT_SIZE) //如果數(shù)據(jù)長度大于最大長度 { u16DataLength = PACKET_DATA_SEGMENT_SIZE; //設(shè)置數(shù)據(jù)最大值 } u8Ret = Flash_WriteBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u16DataLength); //把所有數(shù)據(jù)寫入flash if (Ok != u8Ret) //如果寫數(shù)據(jù)失敗 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR; //反饋上位機(jī)錯(cuò)誤 標(biāo)志 } else //如果寫數(shù)據(jù)成功 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 標(biāo)志 } } else //如果地址無效 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反饋上位機(jī)地址錯(cuò)誤 } Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī) break; case PACKET_CMD_CRC_FLASH : //查詢flash校驗(yàn)值 指令碼 if (1u == u8FlashAddrValid) //如果地址有效 { u32FlashLength = u8FrameData[PACKET_DATA_INDEX] + (u8FrameData[PACKET_DATA_INDEX + 1] << 8) + (u8FrameData[PACKET_DATA_INDEX + 2] << 16) + (u8FrameData[PACKET_DATA_INDEX + 3] << 24); //獲取待校驗(yàn)flash大小 if ((u32FlashLength + u32FlashAddr) > (FLASH_BASE + FLASH_SIZE)) //如果flash長度超出有效范圍 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_FLASH_SIZE_ERROR; //反饋上位機(jī)flash尺寸錯(cuò)誤 }else { u16Ret = Cal_CRC16(((unsigned char *)u32FlashAddr), u32FlashLength);//讀取flash指定區(qū)域的值并計(jì)算crc值 u8FrameData[PACKET_FLASH_CRC_INDEX] = (uint8_t)u16Ret; //把crc值存儲到應(yīng)答幀 u8FrameData[PACKET_FLASH_CRC_INDEX+1] = (uint8_t)(u16Ret>>8); u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 標(biāo)志 } } else //如果地址無效 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反饋上位機(jī)地址錯(cuò)誤 } Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE+2); //發(fā)送應(yīng)答幀到上位機(jī) break; case PACKET_CMD_JUMP_TO_APP : //跳轉(zhuǎn)至APP 指令碼 Flash_EraseSector(BOOT_PARA_ADDRESS); //擦除BOOT parameter 扇區(qū) u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī) return Ok; //APP更新完成,返回OK,接下來執(zhí)行跳轉(zhuǎn)函數(shù),跳轉(zhuǎn)至APP case PACKET_CMD_APP_UPLOAD : //數(shù)據(jù)上傳 if (1u == u8FlashAddrValid) //如果地址有效 { u32Temp = u8FrameData[PACKET_DATA_INDEX] + (u8FrameData[PACKET_DATA_INDEX + 1] << 8) + (u8FrameData[PACKET_DATA_INDEX + 2] << 16) + (u8FrameData[PACKET_DATA_INDEX + 3] << 24); //讀取上傳數(shù)據(jù)長度 if (u32Temp > PACKET_DATA_SEGMENT_SIZE) //如果數(shù)據(jù)長度大于最大值 { u32Temp = PACKET_DATA_SEGMENT_SIZE; //設(shè)置數(shù)據(jù)長度為最大值 } Flash_ReadBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u32Temp); //讀flash數(shù)據(jù) u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 標(biāo)志 Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE + u32Temp);//發(fā)送應(yīng)答幀到上位機(jī) } else //如果地址無效 { u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反饋上位機(jī)地址錯(cuò)誤 標(biāo)志 Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī) } break; case PACKET_CMD_START_UPDATE : //啟動APP更新(此指令正常在APP程序中調(diào)用) u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反饋上位機(jī)成功 標(biāo)志 Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //發(fā)送應(yīng)答幀到上位機(jī) break; } enFrameRecvStatus = FRAME_RECV_IDLE_STATUS; //幀數(shù)據(jù)處理完成,幀接收狀態(tài)恢復(fù)到空閑狀態(tài) } return OperationInProgress; //返回,APP更新中。。。 } 4.2、User Application程序設(shè)計(jì) 在本示例User Application中,觸發(fā)BootLoader更新程序的標(biāo)志在串口接收中實(shí)現(xiàn)。 //UART0中斷函數(shù) void Uart0_IRQHandler(void) { if(Uart_GetStatus(M0P_UART0, UartRC)) //UART0數(shù)據(jù)接收 { Uart_ClrStatus(M0P_UART0, UartRC); //清中斷狀態(tài)位 u8RxData[u8RxCnt] = Uart_ReceiveData(M0P_UART0); //接收數(shù)據(jù)字節(jié) u8RxCnt++; if(u8RxCnt>=18) { u8RxCnt = 0; if ((u8RxData[0]==0x6D)&&(u8RxData[1]==0xAC)&&(u8RxData[6]==0x26)&&(u8RxData[16]==0xA6)&&(u8RxData[17]==0xDA)) //是APP更新幀 { for(uint32_t i=0;i<18;i++) { Uart_SendDataPoll(M0P_UART0,u8TxData[i]); //查詢方式發(fā)送數(shù)據(jù) } //boot para區(qū)域?qū)憳?biāo)記值,通知BootLoader要更新程序了 Flash_SectorErase(0xF00); Flash_WriteWord(0xF00, 0x12345678); NVIC_SystemReset(); //軟件復(fù)位MCU } } } if(Uart_GetStatus(M0P_UART0, UartTC)) //UART0數(shù)據(jù)發(fā)送 { Uart_ClrStatus(M0P_UART0, UartTC); //清中斷狀態(tài)位 } } 4.3、IAR地址配置及文件輸出 最后還需要簡答配置下IAR環(huán)境。 第1步:確定輸出的Linker配置地址,因?yàn)樾枰谶@里程序修改地址。 第2步:找到Linker配置文件,修改BootLoader程序地址:0x00000000~0x00000DFF,User Application程序地址:0x00001000~0x0000FFFF。 第3步:找到User Application程序的配置文件(后綴為.s的文件),添加程序中斷向量偏移長度:0x00001000,和BootLoader程序配置文件相比有兩處不同之處,如下所示: 第4步:將這兩個(gè)程序按照ICP方式(SWD、JTAG等)燒錄后,此后就可以使用IAP方式通過串口燒錄HEX文件程序或者BIN文件程序。輸出及燒錄HEX文件程序或者BIN文件程序方式如下圖所示: 5、拓展:解析HEX文件 HEX文件可以通過UltraEdit、Notepad++、記事本等工具打開,用Notepad++打開之后會看到以下數(shù)據(jù)內(nèi)容: 使用Notepad++打開后會不同含義的數(shù)據(jù)其顏色不同。每行數(shù)據(jù)都會有一個(gè)冒號開始,后面的數(shù)據(jù)由:數(shù)據(jù)長度、地址、標(biāo)識符、有效數(shù)據(jù)、校驗(yàn)數(shù)據(jù)等構(gòu)成。以上圖的第一行為例,進(jìn)行解析: 第1個(gè)字節(jié)10,表示該行具有0x10個(gè)數(shù)據(jù),即16個(gè)字節(jié)的數(shù)據(jù); 第2、3個(gè)字節(jié)3E00,表示該行的起始地址為0x3E00; 第4個(gè)字節(jié)00,表示該行記錄的是數(shù)據(jù); 第5-20個(gè)字節(jié),表示的是有效數(shù)據(jù); 第21個(gè)字節(jié)EB,表示前面數(shù)據(jù)的校驗(yàn)數(shù)據(jù),校驗(yàn)方法:0x100-前面字節(jié)累加和; 其中,第4個(gè)字節(jié)具有5種類型:00-05,含義如下: 單片機(jī)的hex文件以00居多,都用來表示數(shù)據(jù)。hex文件的結(jié)束部分如下圖所示: 最后一行的01表示文件結(jié)束了,最后的FF表示校驗(yàn)數(shù)據(jù),由0x100-0x01=0xFF得來。 資源下載:IAR環(huán)境下STM32+IAP方案的實(shí)現(xiàn) ARM 單片機(jī)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。