stm32之iap实现应用(基于串口,上位机,详细源码)
開發(fā)環(huán)境:Window 7
 開發(fā)工具:Keil uVision4
 硬件:stm32f103c8t6
篇幅略長,前面文字很多,主要是希望能讓小白們理解,后面就是實現(xiàn)步驟,包括實現(xiàn)的代碼。
在研發(fā)調(diào)試的時候我們一般用燒錄器下載代碼,對于stm32f103c8t6來說,還可以用串口下載,步驟如下:
1.PC端下載一個上位機Flash Loader Demo
 2.芯片的串口引腳Tx、Rx(PA.9、PA.10)通過USB>TTL連接到電腦上
 3.將芯片的boot0引腳接高電平、boot1引腳接低電平。這是為了讓芯片上電的時候從系統(tǒng)存儲區(qū)啟動,原廠的isp程序保存在那里,地址是0x1FFF 000 ~ 0x1FFF 77FF。系統(tǒng)存儲區(qū)是用戶不能訪問的區(qū)域,它在芯片出廠時已經(jīng)固化了啟動程序,它負責實現(xiàn)串口、USB以及CAN等isp燒錄功能。
 4.打開上位機,配置如圖,波特率可多選,這是因為上位機在發(fā)送握手0x7F時,芯片接收到0x7F后,就能將波特率算出來,然后給自身串口初始化,跟上位機設置一樣的波特率。接著給芯片上電,上位機選擇bin文件,下載到芯片里面后,將boot0和boot1引腳接低,重新上電,就能運行剛下載的程序了。
 這樣就可以在沒有燒錄器的情況下下載程序了,當然如果要進行調(diào)試的話,還是需要燒錄器。
除了上面這兩種給芯片下載新程序的方法,還可以在芯片運行中給自身flash存儲器寫入新程序。這就是iap(In application Programing在應用編程)。做一個產(chǎn)品,研發(fā)時一般都是在PC端借助燒錄器升級,但到客戶手里一般是用U盤升級,只要把U盤插入到機器中,就能給自身升級。其中,在插入U盤后,芯片檢測到需要升級,就會跳轉(zhuǎn)到iap程序段里面去,然后讀取U盤里面的程序,再將U盤的程序文件拷貝到自身的flash里,拷貝完成之后,跳轉(zhuǎn)到新的程序中運行。下面是通過串口給自身升級的iap案例,將實現(xiàn)的過程代碼詳細說明。
stc32f103c8t6內(nèi)部有一個64k的flash存儲器,用于儲存代碼,在電腦上編譯好的程序,通過燒錄器把它燒錄到內(nèi)部flash中。Flash里面的內(nèi)容掉電不會丟失,燒錄完,芯片重新上電,就可以從內(nèi)部flash中加載代碼(起始地址一般是0x0800 0000)。
內(nèi)部falsh除了用燒錄器讀寫外,還可以在芯片運行時,對自身的內(nèi)部flash進行讀寫。如果flash儲存了程序后還有剩余的空間,那么可以把它用來保存程序運行時產(chǎn)生需要掉電保存的數(shù)據(jù);也可以在芯片運行時將另一個編譯后的二進制程序文件寫到剩余的flash,然后進行跳轉(zhuǎn)到新的程序上面運行。這也是iap的實現(xiàn)原理。
1.先介紹怎么利用stm庫對flash進行操作
所有flash操作相關的函數(shù)接口在stm32f10x_flash.h里面。讀flash里面的數(shù)據(jù)直接根據(jù)地址讀出來就行了。往寫flash里面寫數(shù)據(jù),需要解鎖,擦除,寫入數(shù)據(jù),上鎖;擦除后存儲單元都變成1,因為儲存單元不能由0變1,所以在寫入之前一定要先擦除,不然會寫入失敗。
 操作代碼如下:
要注意,address的地址不能指向自身的代碼區(qū)域,因為修改了自身的程序會造成不可預測的效果,所以要指向自身程序后面的空余區(qū)域,一般來說從0x08000000+加上程序的大小,后面的就是空余區(qū)域。下圖是對于芯片stc32f103c8t6的工程配置:
 
2.流程圖不太會寫,簡單地將過程描述一遍,iap策略如下:
 對于升級的方式,可以選擇以下幾種,如USART,IIC,CAN,USB,以太網(wǎng)接口甚至是無線射頻通道,將程序文件發(fā)送到iap。
存儲區(qū)劃分:
Bootloader工程:0x800 0000-0x800 2BFF (11k)
 升級標志:0x800 2C00-0x800 2FFF (1K)
 App工程:0x800 3000 -0x801 0000 (52k)
芯片上電首先是進入bootloader工程的,所以把bootloader放在前面。升級標志可能會有人問為啥要1k這么大,一個字節(jié)不行了嗎?首先,bootloader和app都可能會對升級標志的值進行修改或讀取,所以不能保存在RAM,只能保存在ROM,那么對ROM的數(shù)據(jù)進行修改就是flash讀寫操作,上面提了要先擦除,而且擦除是按頁擦,一頁就是1k,所以就算標志位不需要1k這么大,只用其一個字節(jié),那剩下的也不能用到其他地方,因為它隨時會被擦除。
下面開始說明這兩部分的代碼實現(xiàn),其中的一些配置也要細心注意。
3.Bootloader工程
Bootloader程序開機引導app程序,在運行app程序中,若收到升級信號,則從app跳轉(zhuǎn)到bootloader里,然后boorloader通過串口接收新的程序文件,對app進行升級。所以,我們還需要一個上位機將程序文件通過串口發(fā)送給bootloader,為了方便,我沒自己做上位機,直接用Flash Loader Demo,這個可以網(wǎng)上下載。那么上位機有了,還要了解它的通訊協(xié)議, 到底數(shù)據(jù)是怎么從上位機發(fā)送過來了,bootloader該怎么接收數(shù)據(jù)。
 其實我們要做的bootloader工程就是要實現(xiàn)原廠isp的功能,跟上位機同步,接受上位機數(shù)據(jù)。我們無法得到人家的isp代碼,但是可以上st的官網(wǎng)下載它的isp協(xié)議。了解了它的協(xié)議就能自己寫單片機端的代碼了。協(xié)議下載鏈接。這里不對這個協(xié)議進行細說,直接說明實現(xiàn)的代碼,下圖是原廠isp所支持的命令。
 
 建立bootloader工程,打開一個新的帶stm32標準庫的keil工程,對工程進行如下配置。
 
第一步,初始化USART1外設,這里不做波特率自適應,把波特率固定為115200,那么上位機配置就要跟其保持一致。
創(chuàng)建<USART.h>
#ifndef __USART1_INIT_H__ #define __USART1_INIT_H__#include "stm32f10x.h" #include <stdio.h>void USART1_Configuration(void);//打印輸出串口初始化 void sengdata(unsigned char data); unsigned char waitdata(void); #endif創(chuàng)建<USART.c>
#include "USART1.h" #include "Queue.h"void USART1_Configuration(void)//打印輸出串口初始化{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;//配置串口1 (USART1) 時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);//配置串口1接收終端的優(yōu)先級NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//配置串口1 發(fā)送引腳(PA.09)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //配置串口1 接收引腳 (PA.10)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);//串口1工作模式(USART1 mode)配置 USART_InitStructure.USART_BaudRate = 115200;//設置波特率;USART_InitStructure.USART_WordLength = USART_WordLength_8b; //數(shù)據(jù)位為8個字節(jié)USART_InitStructure.USART_StopBits = USART_StopBits_1; //一位停止位USART_InitStructure.USART_Parity = USART_Parity_No ; //無校驗位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不需要流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收發(fā)送模式USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟中斷USART_Cmd(USART1, ENABLE);//使能串口 }void sengdata(unsigned char data) {USART_SendData(USART1, (unsigned char) data);while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET); }extern QueueT RxQueueEntity; unsigned char waitdata(void) //阻塞等待一個數(shù)據(jù)到來 {while(1){if(getDataCount(&RxQueueEntity)!=0){return outQueue(&RxQueueEntity);}} }第二步,創(chuàng)建接收隊列,因為上位機發(fā)送過來的數(shù)據(jù)很多,芯片不能及時處理,那么就要先把數(shù)據(jù)放進隊列,然后逐個拿出來處理。這樣就不會丟失數(shù)據(jù)。直接復制下面代碼就行,可以先不用理解。
創(chuàng)建<Queue.h>
#ifndef __QUEUE__H__ #define __QUEUE__H__ #include "core_cm3.h"typedef struct {u16 in;u16 out;u16 cntMax;u8* pBuf; }QueueT;/*隊列的特點:先進先出,若隊列滿了,不能再放數(shù)據(jù)。可循環(huán)使用隊列的位置*/void QueueCreate(QueueT* thiz,u8* BufAddress,u16 BufSize); //創(chuàng)建一個隊列,初始化結(jié)構(gòu)體里面的成員 u16 getDataCount(QueueT* thiz); //獲取隊列里面有效的數(shù)據(jù)的大小 u16 getEmptyCount(QueueT* thiz); //獲取隊列里面還剩余多少空的位置 u8 inQueue(QueueT* thiz,u8 data); //將一個數(shù)據(jù)放進隊列 u8 outQueue(QueueT* thiz); //從隊列里面拿一個數(shù)據(jù)出來#endif //__QUEUE__H__創(chuàng)建<Queue.c>
#include "Queue.h" void QueueCreate(QueueT* thiz,u8* BufAddress,u16 BufSize) {thiz->in=0;thiz->out=0;thiz->cntMax=BufSize;thiz->pBuf=BufAddress; }u16 getDataCount(QueueT* thiz) {if (thiz->in >= thiz->out){return (thiz->in - thiz->out);}else{return (thiz->in + thiz->cntMax - thiz->out);} }u16 getEmptyCount(QueueT* thiz) {u16 dataCnt;if (thiz->in >= thiz->out){dataCnt=(thiz->in - thiz->out);}else{dataCnt=(thiz->in + thiz->cntMax - thiz->out);}if ((dataCnt+1u) >= thiz->cntMax){return 0; //fifo full}return (thiz->cntMax-dataCnt-1u);}u8 inQueue(QueueT* thiz,u8 data) {u16 in;in = thiz->in + 1u;if (in >= thiz->cntMax){in = 0;}if (in == thiz->out){ //full fiforeturn 0;}thiz->pBuf[thiz->in] = data;thiz->in = in;return 1; }u8 outQueue(QueueT* thiz) {u8 data;u16 out;if (thiz->in == thiz->out){ //empty fiforeturn 0;}out = thiz->out;data = thiz->pBuf[out];out++;if (out >= thiz->cntMax){out = 0;}thiz->out = out;return data; }第三步,建立與上位機的通訊協(xié)議,接收上位機的命令并作出相應的應答。下面代碼是根據(jù)協(xié)議AN2606來寫給上位機的應答的。
創(chuàng)建<iap.h>
#ifndef __IAP_H__ #define __IAP_H__ #include "core_cm3.h"#define updata_flagaddr 0x8002C00 //升級標志 #define verify_flagaddr 0x800FF10 //在app里面 #define ACK 0x79 //肯定應答 #define NACK 0x1F //否定應答typedef struct{unsigned char cmd;void (*pfunction)(void); }CommandHandleStruct;typedef void (*iapfun)(void); void jump_to_app(u32 appxaddr); extern const CommandHandleStruct CmdHdlStr[11];#endif //__IAP_H__創(chuàng)建<iap.c>
#include "iap.h" #include "USART1.h"static unsigned char bootloaderversion=0x22; static unsigned char cmd_count=11; static unsigned char cmd[11]={0x00,0x01,0x02,0x11,0x21,0x31,0x43,0x63,0x73,0x82,0x92};void jump_to_app(u32 appaddr) //跳轉(zhuǎn)函數(shù) {iapfun jump2app; if(((*(vu32*)appaddr)&0x2FFE0000)==0x20000000){ __set_PRIMASK(1);__set_MSP(*(vu32*)appaddr);jump2app=(iapfun)*(vu32*)(appaddr+4);jump2app(); } }unsigned char checksum(unsigned char *data, int len) //計算p開始len個字節(jié)的checksum,也就是計算異或 {int i;unsigned char cs;cs = 0;for ( i=0; i<len; i++ )cs ^= data[i];return cs; }void Getcommand(void) //AN2606 page10 { unsigned char i;sengdata(cmd_count);sengdata(bootloaderversion);for(i=0;i<cmd_count;i++){sengdata(cmd[i]);}sengdata(ACK); }void GetVersion(void) //AN2606 page12 {sengdata(bootloaderversion);sengdata(0x01);sengdata(0x01);sengdata(ACK); }void GetID(void) //AN2606 page14 {sengdata(0x01);sengdata(0x04);sengdata(0x10);sengdata(ACK); } unsigned int addr=0,temp1; unsigned int* flashdata; void ReadMemorycommand(void) //AN2606 page16 {unsigned char temp[4],recchecksum,len,tempsum=0,i;temp[0]= waitdata();temp[1]= waitdata();temp[2]= waitdata();temp[3]= waitdata();addr=(temp[0]<<24)|(temp[1]<<16)|(temp[2]<<8)|temp[3];recchecksum = waitdata();if(recchecksum==checksum(temp,4)&&(addr>=0x08000000)||(addr<0x08010000)){sengdata(ACK);}else{sengdata(NACK);return;}len=waitdata();recchecksum=waitdata();tempsum=~len;if(recchecksum==tempsum){sengdata(ACK);}else{sengdata(NACK);return;}for(i=0;i<len+1;i++){flashdata=(unsigned int*)(addr+i);temp1=*flashdata;sengdata(*flashdata);} } void Gocommand(void) //AN2606 page18 {unsigned int addr;unsigned char temp[4],recchecksum;temp[0]= waitdata();temp[1]= waitdata();temp[2]= waitdata();temp[3]= waitdata();addr=(temp[0]<<24)|(temp[1]<<16)|(temp[2]<<8)|temp[3];recchecksum = waitdata();if(recchecksum==checksum(temp,4)&&(addr>=0x08000000)&&(addr<0x08010000)){sengdata(ACK);}else{sengdata(NACK);return;}//清除升級標志FLASH_Unlock(); //解鎖FLASH_ErasePage(updata_flagaddr); FLASH_ProgramWord(updata_flagaddr,0x00);FLASH_Lock(); //上鎖FLASH_WaitForLastOperation(0xFFFFFF);//等待擦除結(jié)束jump_to_app(addr); }void WriteMemorycommand(void) //AN2606 page20 {unsigned int Status;unsigned int addr,recdata_32bit,i;unsigned char temp[4],recchecksum=0,len,recdata[0xFF];temp[0]= waitdata();temp[1]= waitdata();temp[2]= waitdata();temp[3]= waitdata();addr=(temp[0]<<24)|(temp[1]<<16)|(temp[2]<<8)|temp[3];recchecksum = waitdata();if(recchecksum==checksum(temp,4)&&(addr>=0x08000000)&&(addr<0x08010000)){sengdata(ACK);}else{sengdata(NACK);return;}len=waitdata();for(i=0;i<len+1;i++){recdata[i]=waitdata();}recchecksum=waitdata();if(recchecksum==(len^checksum(recdata,len+1))){//寫flashFLASH_Unlock(); //解鎖for(i=0;i<len+1-3;i+=4){recdata_32bit=recdata[i+0]|(recdata[i+1]<<8)|(recdata[i+2]<<16)|(recdata[i+3]<<24);FLASH_ProgramWord(addr,recdata_32bit); addr+=4;}FLASH_Lock(); //上鎖Status=FLASH_WaitForLastOperation(0xFFFFFF);//等待燒寫結(jié)束if(Status==FLASH_COMPLETE){//寫入成功}sengdata(ACK);}else{sengdata(NACK);return;} } void Erasecommand(void) //AN2606 page24 {unsigned int Status;unsigned char pagecount,erasepagearry[50],recchecksum,i,tm;pagecount=waitdata();for(i=0;i<pagecount+1;i++){erasepagearry[i]=waitdata();}recchecksum=waitdata();tm=checksum(erasepagearry,pagecount+1)^pagecount;if(recchecksum==tm){//擦除頁FLASH_Unlock(); //解鎖for(i=0;i<pagecount+1;i++){FLASH_ErasePage(0x08000000+erasepagearry[i]*1024); }FLASH_Lock(); //上鎖Status=FLASH_WaitForLastOperation(0xFFFFFF);//等待擦除結(jié)束if(Status==FLASH_COMPLETE){//擦除成功}sengdata(ACK);}else{sengdata(NACK);return;} } //下面四個暫時沒用到,所以我沒做實現(xiàn) void WriteProtectcommand(void) { } void WriteUnprotectcommand(void) { } void ReadoutProtectcommand(void) { } void ReadoutUnprotectcommand(void) { }/* Byte 4: 0x00 – Get command Byte 5: 0x01 – Get Version and Read Protection Status Byte 6: 0x02 – Get ID Byte 7: 0x11 – Read Memory command Byte 8: 0x21 – Go command Byte 9: 0x31 – Write Memory command Byte 10: 0x43 – Erase command Byte 11: 0x63 – Write Protect command Byte 12: 0x73 – Write Unprotect command Byte 13: 0x82 – Readout Protect command Byte 14: 0x92 – Readout Unprotect command */ //把所有應答函數(shù)的函數(shù)地址保存到數(shù)組里面,方便后續(xù)遍歷定位執(zhí)行命令相對應的函數(shù) const CommandHandleStruct CmdHdlStr[11]={{0x00,Getcommand},{0x01,GetVersion},{0x02,GetID},{0x11,ReadMemorycommand},{0x21,Gocommand},{0x31,WriteMemorycommand},{0x43,Erasecommand},{0x63,WriteProtectcommand},{0x73,WriteUnprotectcommand},{0x82,ReadoutProtectcommand},{0x92,ReadoutUnprotectcommand} , };第四步,創(chuàng)建main.c
#include "stm32f10x.h" #include "USART1.h" #include "iap.h" #include "Queue.h"void GPIO_Configuration(void);#define RxbufSize 500 QueueT RxQueueEntity; //包含了隊列內(nèi)信息的結(jié)構(gòu)體 u8 rxbuf[RxbufSize];//隊列緩存int main(void) { u8 ch=0,checksum,recchecksum,i;RCC_DeInit();SystemInit();__set_PRIMASK(0);USART1_Configuration();//串口初始化GPIO_Configuration(); //將接到PC.13的lcd點亮QueueCreate(&RxQueueEntity,&rxbuf[0],RxbufSize); //創(chuàng)建串口1的接收隊列if(*(uint16_t*)updata_flagaddr==0){ //不需要升級if((*(unsigned int*)verify_flagaddr)==0xaabbccdd){jump_to_app(0x8003000);//跳轉(zhuǎn)到app}}//等待升級while(1){if(getDataCount(&RxQueueEntity)!=0){ch=outQueue(&RxQueueEntity);if(ch==0x7f){ //接收到上位機發(fā)送的同步信號7fsengdata(ACK);}else{ //接收到上位機的命令for(i=0;i<11;i++){if(CmdHdlStr[i].cmd==ch){checksum=~ch;recchecksum=waitdata();//接收校驗位if(recchecksum==checksum){sengdata(ACK);CmdHdlStr[i].pfunction(); //調(diào)用相應的應答函數(shù)break;}}}}}}} void GPIO_Configuration(void) //在運行bootloader時,設置pin13的led常亮 {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC , ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStructure);GPIO_ResetBits(GPIOC,GPIO_Pin_13); }最后,找到stm32f10x_it.c,加入:
 將串口接收到的數(shù)據(jù)放進隊列。
建立好的工程如下圖:
 
 然后進行編譯下載到芯片里面去。
4.建立app工程,打開一個新的帶stm32標準庫的keil工程,對工程進行如下配置。
 第一步,初始化USART1外設,把波特率固定為115200。
創(chuàng)建USART1.h
#ifndef __USART1_INIT_H__ #define __USART1_INIT_H__#include "stm32f10x.h" #include <stdio.h>int fputc(int ch, FILE *f); void USART1_Configuration(void);//打印輸出串口初始化#endif創(chuàng)建USART1.c
#include "USART1.h"void USART1_Configuration(void)//打印輸出串口初始化{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;//配置串口1 (USART1) 時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);//配置串口1接收終端的優(yōu)先級NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//配置串口1 發(fā)送引腳(PA.09)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //配置串口1 接收引腳 (PA.10)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);//串口1工作模式(USART1 mode)配置 USART_InitStructure.USART_BaudRate = 115200;//配置波特率;USART_InitStructure.USART_WordLength = USART_WordLength_8b; //數(shù)據(jù)位為8個字節(jié)USART_InitStructure.USART_StopBits = USART_StopBits_1; //一位停止位USART_InitStructure.USART_Parity = USART_Parity_No ; //無校驗位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不需要流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收發(fā)送模式USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟中斷USART_Cmd(USART1, ENABLE);//使能串口 }int fputc(int ch, FILE *f) //重定向c庫里面的fputc到串口,那么使用printf時就能將打印的信息從串口發(fā)送出去,在PC上同串口助手接收信息 {//將Printf內(nèi)容發(fā)往串口USART_SendData(USART1, (unsigned char) ch);while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET); return (ch); }第二步,找到stm32f10x_it.c,加入:
int updatareq=0; void USART1_IRQHandler(void) { u16 code,Status;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){ code=USART_ReceiveData(USART1);Status = USART_GetFlagStatus(USART1, USART_FLAG_NE|USART_FLAG_PE|USART_FLAG_NE);if(Status!=RESET){//如果發(fā)生錯誤接忽略接收的數(shù)據(jù)USART_ClearFlag(USART1,Status);//把錯誤標志清楚return;}if(code==0x7F){ //如果接受到0x7F,那么跳轉(zhuǎn)到bootloader,等待升級updatareq=1;} } }第三步,找到system_stm32f10x.h,修改中斷列表的偏移地址:
 #define VECT_TAB_OFFSET 0x3000
 最后加入main.c:
最后,設置輸出文件的保存路徑,不更改也可以,保持默認,那么等一下就到這個文件夾下找到輸出文件:
 
 生成app的bin文件:
 fromelf --bin --output “$Ltest.bin” “#L”
 
5.下載驗證:
前面已經(jīng)將boot下載到芯片里面去;現(xiàn)在,把usb>ttl 接口接到電腦上,打開上位機,上位機就能檢測到連上串口的設備,設置波特率115200,無校驗位。配置完了之后,先給芯片上電(boot0=0,boot1=0正常啟動),正常來說,接到PC.13的led會常亮,因為上電運行的是boot。那么點擊上位機的Next進行同步,若同步成功就會跳轉(zhuǎn)到下一個界面,若是同步不成功,就會卡著,鼠標光標轉(zhuǎn)圈;同步失敗的話檢查線路是否連接正確,COM口選擇是否正確。
 
 設置flash大小,然后點next繼續(xù):
 
 這里選擇剛才編譯出來的app工程bin文件,注意紅圈的配置,點擊Next繼續(xù):
 
 開始app下載,下載完成后會自動跳轉(zhuǎn)到app程序,這時候可以看到led閃爍。
 
 如果成功下載了之后,芯片重新上電,首先是進入boot,檢測app完整存在,那么就會自動跳轉(zhuǎn)到app。
 再把芯片串口接到電腦上,打開串口助手,檢測通訊正常,給芯片發(fā)送7F,如圖,app收到7F就會跳轉(zhuǎn)到boot,等待升級;然后把串口助手關閉,打開Flash Loader Demo上位機,給芯片升級app。其實也可以不用串口助手發(fā)送7F,下載上位機同步的時候就會給芯片發(fā)送7F,只不過在操作的時候,要點擊兩次Next,點擊第一次的時候忽略提示就行,再點擊一次。
 
 到最后,終于寫完了,這個iap還有很多要完善的地方,比如加入超時機制等。希望以上能起到提供一些參考的作用。有錯誤或者疑問都可以提出來,謝謝。
總結(jié)
以上是生活随笔為你收集整理的stm32之iap实现应用(基于串口,上位机,详细源码)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: stm32之TIM-高级定时器应用实例二
- 下一篇: stm32之USB应用实例(官方例程资料
