日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stm32之iap实现应用(基于串口,上位机,详细源码)

發布時間:2024/10/14 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32之iap实现应用(基于串口,上位机,详细源码) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

開發環境:Window 7
開發工具:Keil uVision4
硬件:stm32f103c8t6

篇幅略長,前面文字很多,主要是希望能讓小白們理解,后面就是實現步驟,包括實現的代碼。

在研發調試的時候我們一般用燒錄器下載代碼,對于stm32f103c8t6來說,還可以用串口下載,步驟如下:

1.PC端下載一個上位機Flash Loader Demo
2.芯片的串口引腳Tx、Rx(PA.9、PA.10)通過USB>TTL連接到電腦上
3.將芯片的boot0引腳接高電平、boot1引腳接低電平。這是為了讓芯片上電的時候從系統存儲區啟動,原廠的isp程序保存在那里,地址是0x1FFF 000 ~ 0x1FFF 77FF。系統存儲區是用戶不能訪問的區域,它在芯片出廠時已經固化了啟動程序,它負責實現串口、USB以及CAN等isp燒錄功能。
4.打開上位機,配置如圖,波特率可多選,這是因為上位機在發送握手0x7F時,芯片接收到0x7F后,就能將波特率算出來,然后給自身串口初始化,跟上位機設置一樣的波特率。接著給芯片上電,上位機選擇bin文件,下載到芯片里面后,將boot0和boot1引腳接低,重新上電,就能運行剛下載的程序了。


這樣就可以在沒有燒錄器的情況下下載程序了,當然如果要進行調試的話,還是需要燒錄器。

除了上面這兩種給芯片下載新程序的方法,還可以在芯片運行中給自身flash存儲器寫入新程序。這就是iap(In application Programing在應用編程)。做一個產品,研發時一般都是在PC端借助燒錄器升級,但到客戶手里一般是用U盤升級,只要把U盤插入到機器中,就能給自身升級。其中,在插入U盤后,芯片檢測到需要升級,就會跳轉到iap程序段里面去,然后讀取U盤里面的程序,再將U盤的程序文件拷貝到自身的flash里,拷貝完成之后,跳轉到新的程序中運行。下面是通過串口給自身升級的iap案例,將實現的過程代碼詳細說明。

stc32f103c8t6內部有一個64k的flash存儲器,用于儲存代碼,在電腦上編譯好的程序,通過燒錄器把它燒錄到內部flash中。Flash里面的內容掉電不會丟失,燒錄完,芯片重新上電,就可以從內部flash中加載代碼(起始地址一般是0x0800 0000)。

內部falsh除了用燒錄器讀寫外,還可以在芯片運行時,對自身的內部flash進行讀寫。如果flash儲存了程序后還有剩余的空間,那么可以把它用來保存程序運行時產生需要掉電保存的數據;也可以在芯片運行時將另一個編譯后的二進制程序文件寫到剩余的flash,然后進行跳轉到新的程序上面運行。這也是iap的實現原理。

1.先介紹怎么利用stm庫對flash進行操作

所有flash操作相關的函數接口在stm32f10x_flash.h里面。讀flash里面的數據直接根據地址讀出來就行了。往寫flash里面寫數據,需要解鎖,擦除,寫入數據,上鎖;擦除后存儲單元都變成1,因為儲存單元不能由0變1,所以在寫入之前一定要先擦除,不然會寫入失敗。
操作代碼如下:

#define address 0x08006000 //寫入的flash地址 #define value 0x55aa55aa //將要寫的數據 void flash_test(void) {uint32_t *pdata=address;Printf(“data=%d”,*pdata); //先將原來的數據打印出來FLASH_Unlock(); //解鎖FLASH_ErasePage(address); //擦除,擦除只能按頁擦,擦除address地址所在的頁,不同的芯片一頁的大小不一樣,對于stc32f103c8t6來說,一頁就是1024字節,也就是1k。FLASH_ProgramWord(address,value); /*將data寫入address地址里面,除了寫入uint32_t類型,還可以寫uint16_t類型的數據,對于一份很長的代碼來說,只能這樣一個個的寫進去flash,對應接口如下:FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);*/FLASH_Lock(); //上鎖,保護數據 Status=FLASH_WaitForLastOperation(0xFFFFFF);//等待燒寫結束,參數是等待的超時時間if(Status==FLASH_COMPLETE){//寫入成功}Printf(“data=%d”,*pdata);//打印確認是否寫入成功 }

要注意,address的地址不能指向自身的代碼區域,因為修改了自身的程序會造成不可預測的效果,所以要指向自身程序后面的空余區域,一般來說從0x08000000+加上程序的大小,后面的就是空余區域。下圖是對于芯片stc32f103c8t6的工程配置:

2.流程圖不太會寫,簡單地將過程描述一遍,iap策略如下:


對于升級的方式,可以選擇以下幾種,如USART,IIC,CAN,USB,以太網接口甚至是無線射頻通道,將程序文件發送到iap。

存儲區劃分:

Bootloader工程:0x800 0000-0x800 2BFF (11k)
升級標志:0x800 2C00-0x800 2FFF (1K)
App工程:0x800 3000 -0x801 0000 (52k)

芯片上電首先是進入bootloader工程的,所以把bootloader放在前面。升級標志可能會有人問為啥要1k這么大,一個字節不行了嗎?首先,bootloader和app都可能會對升級標志的值進行修改或讀取,所以不能保存在RAM,只能保存在ROM,那么對ROM的數據進行修改就是flash讀寫操作,上面提了要先擦除,而且擦除是按頁擦,一頁就是1k,所以就算標志位不需要1k這么大,只用其一個字節,那剩下的也不能用到其他地方,因為它隨時會被擦除。

下面開始說明這兩部分的代碼實現,其中的一些配置也要細心注意。

3.Bootloader工程

Bootloader程序開機引導app程序,在運行app程序中,若收到升級信號,則從app跳轉到bootloader里,然后boorloader通過串口接收新的程序文件,對app進行升級。所以,我們還需要一個上位機將程序文件通過串口發送給bootloader,為了方便,我沒自己做上位機,直接用Flash Loader Demo,這個可以網上下載。那么上位機有了,還要了解它的通訊協議, 到底數據是怎么從上位機發送過來了,bootloader該怎么接收數據。
其實我們要做的bootloader工程就是要實現原廠isp的功能,跟上位機同步,接受上位機數據。我們無法得到人家的isp代碼,但是可以上st的官網下載它的isp協議。了解了它的協議就能自己寫單片機端的代碼了。協議下載鏈接。這里不對這個協議進行細說,直接說明實現的代碼,下圖是原廠isp所支持的命令。

建立bootloader工程,打開一個新的帶stm32標準庫的keil工程,對工程進行如下配置。

第一步,初始化USART1外設,這里不做波特率自適應,把波特率固定為115200,那么上位機配置就要跟其保持一致。

創建<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

創建<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接收終端的優先級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 發送引腳(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; //數據位為8個字節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; //接收發送模式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) //阻塞等待一個數據到來 {while(1){if(getDataCount(&RxQueueEntity)!=0){return outQueue(&RxQueueEntity);}} }

第二步,創建接收隊列,因為上位機發送過來的數據很多,芯片不能及時處理,那么就要先把數據放進隊列,然后逐個拿出來處理。這樣就不會丟失數據。直接復制下面代碼就行,可以先不用理解。

創建<Queue.h>

#ifndef __QUEUE__H__ #define __QUEUE__H__ #include "core_cm3.h"typedef struct {u16 in;u16 out;u16 cntMax;u8* pBuf; }QueueT;/*隊列的特點:先進先出,若隊列滿了,不能再放數據。可循環使用隊列的位置*/void QueueCreate(QueueT* thiz,u8* BufAddress,u16 BufSize); //創建一個隊列,初始化結構體里面的成員 u16 getDataCount(QueueT* thiz); //獲取隊列里面有效的數據的大小 u16 getEmptyCount(QueueT* thiz); //獲取隊列里面還剩余多少空的位置 u8 inQueue(QueueT* thiz,u8 data); //將一個數據放進隊列 u8 outQueue(QueueT* thiz); //從隊列里面拿一個數據出來#endif //__QUEUE__H__

創建<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; }

第三步,建立與上位機的通訊協議,接收上位機的命令并作出相應的應答。下面代碼是根據協議AN2606來寫給上位機的應答的。

創建<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__

創建<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) //跳轉函數 {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個字節的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);//等待擦除結束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);//等待燒寫結束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);//等待擦除結束if(Status==FLASH_COMPLETE){//擦除成功}sengdata(ACK);}else{sengdata(NACK);return;} } //下面四個暫時沒用到,所以我沒做實現 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 */ //把所有應答函數的函數地址保存到數組里面,方便后續遍歷定位執行命令相對應的函數 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} , };

第四步,創建main.c

#include "stm32f10x.h" #include "USART1.h" #include "iap.h" #include "Queue.h"void GPIO_Configuration(void);#define RxbufSize 500 QueueT RxQueueEntity; //包含了隊列內信息的結構體 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); //創建串口1的接收隊列if(*(uint16_t*)updata_flagaddr==0){ //不需要升級if((*(unsigned int*)verify_flagaddr)==0xaabbccdd){jump_to_app(0x8003000);//跳轉到app}}//等待升級while(1){if(getDataCount(&RxQueueEntity)!=0){ch=outQueue(&RxQueueEntity);if(ch==0x7f){ //接收到上位機發送的同步信號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(); //調用相應的應答函數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,加入:
將串口接收到的數據放進隊列。

#include "Queue.h" extern QueueT RxQueueEntity;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){//如果發生錯誤接忽略接收的數據USART_ClearFlag(USART1,Status);//把錯誤標志清楚return;}if(getEmptyCount(&RxQueueEntity)!=0){ //判斷隊列是否有空的位置,若滿了就丟棄inQueue(&RxQueueEntity,code); //將接受到的數據放進隊列}//printf("%c",code); //將接受到的數據直接返回打印} }

建立好的工程如下圖:

然后進行編譯下載到芯片里面去。

4.建立app工程,打開一個新的帶stm32標準庫的keil工程,對工程進行如下配置。


第一步,初始化USART1外設,把波特率固定為115200。

創建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

創建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接收終端的優先級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 發送引腳(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; //數據位為8個字節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; //接收發送模式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時就能將打印的信息從串口發送出去,在PC上同串口助手接收信息 {//將Printf內容發往串口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){//如果發生錯誤接忽略接收的數據USART_ClearFlag(USART1,Status);//把錯誤標志清楚return;}if(code==0x7F){ //如果接受到0x7F,那么跳轉到bootloader,等待升級updatareq=1;} } }

第三步,找到system_stm32f10x.h,修改中斷列表的偏移地址:
#define VECT_TAB_OFFSET 0x3000
最后加入main.c:

#include "stm32f10x.h" #include "GPIOLIKE51.h" #include "USART1.h"void GPIO_Configuration(void); void Delay(uint32_t nCount);static const unsigned int verifyflag __attribute__((at(0x800FF10)))= 0xaabbccdd;#define updata_flagaddr 0x8002C00 //升級標志 #define verify_flagaddr 0x800FF10 //在app里面 typedef void (*iapfun)(void);extern int updatareq; void jump_to_boot(u32 appaddr) {iapfun jump2boot; if(((*(vu32*)appaddr)&0x2FFE0000)==0x20000000){ printf("jump_to_boot\n");__set_PRIMASK(1);__set_MSP(*(vu32*)appaddr);jump2boot=(iapfun)*(vu32*)(appaddr+4);jump2boot(); } } int main(void) {RCC_DeInit();SystemInit();__set_PRIMASK(0);GPIO_Configuration(); //運行app時,將接到PC.13引腳的lcd閃爍USART1_Configuration();while (1){GPIO_SetBits(GPIOC,GPIO_Pin_13);Delay(0xfffff);Delay(0xfffff); GPIO_ResetBits(GPIOC,GPIO_Pin_13);Delay(0xfffff);Delay(0xfffff); printf("app runing \n");if(updatareq){FLASH_Unlock(); //解鎖FLASH_ErasePage(updata_flagaddr); FLASH_ProgramWord(updata_flagaddr,0x01);FLASH_Lock(); //上鎖FLASH_WaitForLastOperation(0xFFFFFF);//等待擦除結束jump_to_boot(0x8000000);}} }void GPIO_Configuration(void) {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); }void Delay(uint32_t nCount) {for(; nCount != 0; nCount--); }

最后,設置輸出文件的保存路徑,不更改也可以,保持默認,那么等一下就到這個文件夾下找到輸出文件:

生成app的bin文件:
fromelf --bin --output “$Ltest.bin” “#L”

5.下載驗證:

前面已經將boot下載到芯片里面去;現在,把usb>ttl 接口接到電腦上,打開上位機,上位機就能檢測到連上串口的設備,設置波特率115200,無校驗位。配置完了之后,先給芯片上電(boot0=0,boot1=0正常啟動),正常來說,接到PC.13的led會常亮,因為上電運行的是boot。那么點擊上位機的Next進行同步,若同步成功就會跳轉到下一個界面,若是同步不成功,就會卡著,鼠標光標轉圈;同步失敗的話檢查線路是否連接正確,COM口選擇是否正確。

設置flash大小,然后點next繼續:

這里選擇剛才編譯出來的app工程bin文件,注意紅圈的配置,點擊Next繼續:

開始app下載,下載完成后會自動跳轉到app程序,這時候可以看到led閃爍。

如果成功下載了之后,芯片重新上電,首先是進入boot,檢測app完整存在,那么就會自動跳轉到app。
再把芯片串口接到電腦上,打開串口助手,檢測通訊正常,給芯片發送7F,如圖,app收到7F就會跳轉到boot,等待升級;然后把串口助手關閉,打開Flash Loader Demo上位機,給芯片升級app。其實也可以不用串口助手發送7F,下載上位機同步的時候就會給芯片發送7F,只不過在操作的時候,要點擊兩次Next,點擊第一次的時候忽略提示就行,再點擊一次。

到最后,終于寫完了,這個iap還有很多要完善的地方,比如加入超時機制等。希望以上能起到提供一些參考的作用。有錯誤或者疑問都可以提出來,謝謝。

總結

以上是生活随笔為你收集整理的stm32之iap实现应用(基于串口,上位机,详细源码)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。