補充:html
你們應該知道咱的單片機程序都是存在flash裏面的數組
你們應該知道ISP吧,就是咱用串口下載51單片機,32單片機等等app
你們知道過程不函數
咱用ISP的時候是下載的hex文件測試
知道下載的時候爲何須要從新啓動單片機不?????spa
其實單片機在啓動的時候內部會先執行個程序:檢測是否是給我下載程序,若是是,我就接收,而後看看寫到哪一個地址裏面.net
固然總體就是這樣,其實單片機和燒錄他的軟件通訊的時候,他倆還有具體協議,設計
本身想一想就明白,,,你直接發給他,你不問問寫好了沒,能夠發下一條了不....也就這麼回事3d
單片機寫好了,而後就是讀取運行唄....指針
接着看IAP
淨整一些名詞,看着就犯迷糊
您直接說:本身寫一個程序控制把程序寫到flash的哪一個位置,而後告訴單片機從哪裏開始讀取運行
既然是本身寫,簡單的就是這樣:把程序放到0x8004000(假設位置是這個)
首先明確一點,單片機一開始運行仍是從頭開始的,默認就是下載到這個位置
因此咱本身寫的那個控制程序(IAP程序),就寫到這裏,而後用串口或者下載器下載到裏面
假設咱如今單片機的串口接受的就是單片機程序,咱(本身寫的IAP程序的功能)就是把串口接收的程序從flash的(0x8004000)那個地址開始寫入
支持IAP的單片機都有函數支持寫Flash的
而後寫完以後呢,調用單片機給的API函數就能夠了,
好比32的,,,那個appxaddr就是 0x8004000
這樣就跳轉過去執行去了
說是這樣說,不過呢,說說其餘問題
1,程序文件呢通常都用BIN文件,其實就是hex文件只保留數據部分,其它的不要....
由於是咱本身規定的寫到flash的哪一個位置....要地址就沒有用了
咱編寫單片機的軟件都支持生成bin文件,具體看下面
2,咱的普通的程序文件並非直接寫到0x8004000個位置,而後調用跳轉就能夠運行,其實須要讓軟件配置一下文件才能夠,要想讓其運行,必須告訴編寫單片機的軟件我這個程序是在哪一個位置運行,這樣他生成的程序文件才能在在那個位置運行.具體看下面
這個是設置的在0x8006000運行
3,還有一箇中斷向量表的問題
假設
0x80000004 默認是定時器0的中斷函數地址,並且中斷的地址往上加
我把程序文件寫到了0x8004000了,,若是裏面寫個定時器0的中斷..難道要到0x80000004那裏去取中斷函數的地址去????
下面的位置是放了咱控制ISP的程序......豈不是有點亂了......
通常程序從哪裏開始,那麼總體的中斷地址就從哪裏開始,全部還須要設置一下中斷地址,也就是總體偏移一下
其實也就這幾個須要瞭解和注意的地方
不過上面的那個不實用,真正的項目,通常都是設計成兩套,而後每次更新的時候切換寫入,而後切換運行
轉眼間天亮了......
而後就想起了一個朋友QQ的個性簽名:年輕人老是要爲一些本身認爲有意義的事情而廢寢忘食,通宵達旦,直至白髮方休........
對了這篇文章必定會介紹的很詳細,請細嚼慢嚥.......嗯,我是這樣認爲的,,,,,,
上面是昨天寫的,應該說是今天寫的,,今天發現發現博客又不能複製粘貼圖片了!!!!而後就睡了一覺,,麻煩。。。。是否是由於我寫的博客有太多的圖片而把我屏蔽了。能讓人一目瞭然的就是圖片。。。。
說一下本身是如何作的,,,
先說一下實現的功能
IAP程序的功能
再看本身的用戶程序--用戶程序本身也作了些設置
對了關於我爲何拷貝到Flash裏面------本身用的單片機的RAM不夠用,存不了用戶程序,因此本身就定義了一個小點的數組(環形隊列),串口一邊接收,一邊往Flash裏面寫,環形隊列但是幫了大忙了,,,,
把IAP升級程序下進去,之後就直接經過串口發送本身的用戶程序就好了...什麼都不須要作了,先說一下操做過程吧!最後有本身的源碼
IAP程序軟件不須要任何配置
波特率太快的話,數據來不及寫入Flash,環形隊列容易溢出,,太慢的話,程序發送的慢。。。
用戶程序軟件須要一些配置
8006000告訴編譯器個人用戶程序打算在這裏開始,你幫我設置一下吧,默認是在8000000開始的
0x1A000,就是告訴編譯器個人程序空間有這麼大。
個人用戶程序裏面也是設置的6000,這個必定要和程序設置的同樣哈
關於這個我後面會說爲何這樣設置。。。固然也能夠百度一下。
其實個人原本是
20000換成十進制就是131072個字節 除以1024 等於128
因爲我先把IAP程序下進去了,IAP程序也須要空間來運行,,,我就給了他6000 換成十進制就是24576 除以1024就是24K
個人總共是128K而後去掉IAP暫用的24K就是 128-24 = 104K = 106496個字節 換成16進制就是 1A000
因此我上面寫了1A000
對了若是您的板子是大容量的若是您很是明白就本身隨意修改把,別忘了修改程序裏的那個,,,
若是不是很明白按照上面修改就行,後面會讓您明白
這個呢就是讓Keil軟件幫忙生成bin文件
F:\Keil4&&MDK4.70A\ARM\ARMCC\bin\fromelf.exe --bin -o .\Progect\Progect.bin ..\Progect\output\Progect.axf
F:\Keil4&&MDK4.70A\ARM\ARMCC\bin\fromelf.exe --bin -o 這個是執行的命令,就是生成bin文件,根據本身的安裝路徑找哈
.\Progect\Progect.bin 就是告訴他把生成的bin文件放在哪一個地方
..\Progect\output\Progect.axf 這個就是本身工程編譯的時候產生的.axf文件,根據本身的找到
./當前目錄
../上一級目錄
../../上上一級目錄
關於Bin文件和Hex文件
http://blog.sina.com.cn/s/blog_6b94d5680100lo2h.html
這是個人用戶程序的Hex與Bin
我們本身設置好寫到哪裏了,因此前頭的就不須要了,後面的校驗也不須要了,,不過呢應該向他那樣加上校驗,數據對了再寫進去!!!!
好生成了bin文件
而後
假設修改了程序了,再升級
您再升級就再升級把!!
再升級
不要總是升級哈!!!玩壞了Flash可就很差玩了
本身用的F103RBT6單片機的RAM只有 5000 也就是20480個字節,,可是本身的程序已經超過了這個字節數
因此本身就不能先定義一個很大的數組而後而後把程序先存在裏面了,列如不少都是:
u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收緩衝,最大USART_REC_LEN個字節,起始地址爲0X20001000.//把數據固定的存在以0X20001000爲起始地址的RAM裏面
本身呢就是用的環形隊列一邊接收,一邊寫入,,,關於環形隊列能夠看個人環形隊列的文章,,,
http://www.cnblogs.com/yangfengwu/p/6822984.html
就再說一下本身的程序的一些地方
串口接收的
void USART1_IRQHandler(void) //串口1中斷服務程序 { u8 Res; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾) { Res =USART_ReceiveData(USART1); //讀取接收到的數據 PutData(&Res,1); //把數據存入隊列 Usart1RecCnt ++; //數據個數 } }
用的系統定時器中斷來檢測的串口空閒,判斷接沒接收到一條完整的數據--方法呢是看到人家的一種方法,感受比本身之前的好,因此直接拿過來用了
關於單片機空閒中斷能夠看一下本身之前的
http://www.cnblogs.com/yangfengwu/p/6746403.html
/*系統定時器中斷*/ void SysTick_Handler(void) { SysTickCnt ++; SysTickCnt1++; SysTickCnt2++; if(SysTickCnt1>=10)//每隔10毫秒檢測一次 { SysTickCnt1 = 0; if(Usart1RecCnt)//若是接收到數據了 { if(IdleCnt == Usart1RecCnt)//10ms時間數據沒了變化 { Usart1RecCntCopy = Usart1RecCnt;//拷貝數據個數 Usart1RecCnt = 0;//清零數據個數 IdleCnt = 0;//清零 Usart1Flage = 1;//接收到一條數據 // rbDelete(&pRb);測試的時候銷燬 // rbCreate(&pRb,ReceBuff,USART_REC_LEN);//建立接收環形隊列 } else { IdleCnt = Usart1RecCnt; } } } }
個人IAP的接收的數據往Flash裏面寫和用戶程序的往Flash裏面寫有一點不一樣,其實用戶程序的往Flash裏面寫的程序是後期的改進...
先看個人IAP的
if(rbCanRead(&pRb)>1)//若是環形隊列裏面的數據個數大於1 { rbRead(&pRb, &ReadDat, 2);//讀取兩個數據 ReadDat16 = (u16)ReadDat[1]<<8; ReadDat16 = ReadDat16|ReadDat[0]; STMFLASH_Write(addr2,&ReadDat16,1); addr2+=2; }
if(rbCanRead(&pRb)>1)
由於一次性要往Flash裏面寫16位數據,因此纔會判斷數據個數大於一個的時候再往裏面寫.
雖然如今程序沒有問題,可是我還在想若是程序的個數是奇數個就完啦!可是好像程序的個數老是偶數個....其實能夠在判斷接收完成的裏面
作一下判斷,若是數據還殘留一個,那就寫進去....
好,那就看一下判斷接收完程序
if(Usart1Flage == 1)//數據接收完成 { addr2 = FLASH_APP2_ADDR;//存儲數據的地址 Usart1Flage =0;//清零 if(((*(vu32*)(FLASH_APP2_ADDR+4))&0xFF000000)==0x08000000)//判斷是否爲0X08XXXXXX. { printf("準備執行APP代碼!!\r\n"); UserDataAddr = FLASH_DATA_ADDR;//存儲其他的數據地址 ReadDat16 = 0x55;//寫入標誌告訴IAP程序有可更新的用戶程序 STMFLASH_Write(UserDataAddr,&ReadDat16,1); UserDataAddr+=2; printf("寫入0x55標誌!!\r\n"); ReadDat16 = (u16)((Usart1RecCntCopy>>16)&0xffff);//存儲接收到多少數據高位 STMFLASH_Write(UserDataAddr,&ReadDat16,1); UserDataAddr+=2; ReadDat16 = (u16)(Usart1RecCntCopy&0xffff);//存儲接收到多少數據低位 STMFLASH_Write(UserDataAddr,&ReadDat16,1); UserDataAddr+=2; Usart1RecCntCopy = 0; printf("開始復位重啓!!\r\n"); Reset_MCU(); } else { printf("非FLASH應用程序,沒法執行!\r\n"); } // printf("Cnt=%d\r\n",Usart1RecCntCopy); // for(i=0;i<Usart1RecCntCopy/2;i++) // { // STMFLASH_Read(addr1,&ReadDat16,1); // addr1+=2;//偏移2048 16=2*8.因此要乘以2. // if((ReadDat16&0x00ff)<=15) // { // printf("0%x ",ReadDat16&0x00ff); // } // else // { // printf("%x ",ReadDat16&0x00ff); // } // // if((ReadDat16>>8)<=15) // { // printf("0%x ",ReadDat16>>8); // } // else // { // printf("%x ",ReadDat16>>8); // } // } // addr1 = FLASH_APP1_ADDR; // for(i=0;i<40;i++) // { // STMFLASH_Erase(addr1,1024);//擦除FLASH_APP1_ADDR地址以及以上40頁 // addr1 +=2048; // } // addr1 = FLASH_APP1_ADDR; }
後邊屏蔽的是測試的時候,看一下寫入的數據,而後和源數據對比一下,看一下寫入的對不對
if(((*(vu32*)(FLASH_APP2_ADDR+4))&0xFF000000)==0x08000000)
這句話
先問一個問題,怎麼知道接收過來的是用戶程序呢????要是別的數據怎麼辦???,,必須有一個判斷依據才行對吧!!
就必須找到用戶程序中永恆不變的變量....
而後呢,我是看別人的程序說,數據的第一個4個字節爲棧頂地址,數據的第二個4字節爲復位中斷向量的入口地址
FLASH_APP2_ADDR+4指針就移動到了IAP升級程序的E9或者說電壓電流採集程序的D5上
(*(vu32*)(FLASH_APP2_ADDR+4))而後強制型的轉成32位的,而後取出來,就是IAP升級程序的E9 20 00 08
或者說電壓電流採集程序的D5 7E 00 08
還有一件事就是STM32是小端模式,,,,所謂小端模式就是低位在低地址,高位在高地址
舉個例子
把60000存到STM32的Flash的,60000轉換成16進制是EA60 EA是高8位,60是低八位,,存到Flash裏面就是60EA這樣存的
60存到了低地址,EA存到了高地址,,,,,固然有小端模式就有大端模式,,,大端模式就是低位在高地址,高位在低地址,電腦就是這樣...
說到這裏就要說一下共用體
typedef union Resolver_I { long Data; char Data_Table[4]; }Resolver_iData; typedef union Resolver_f { float Data; char Data_Table[4]; }Resolver_fData;
Resolver_iData Resolver_7758; //解析7758數據
Resolver_fData Resolver_Usart; //解析串口數據
一個整形數據快速的轉換成16進制存到數組裏面
Resolver_7758.Data = 60000;
那麼Resolver_7758.Data_Table[0] = 0x60;
Resolver_7758.Data_Table[1] = 0xEA;
Resolver_7758.Data_Table[2] = 0x00;
Resolver_7758.Data_Table[3] = 0x00;
一個浮點型的數據轉換成16進制存到數組裏面--其實也是按照IEEE754規約來計算的
Resolver_Usart.Data = 220.5;
那麼Resolver_Usart.Data_Table[0] = 0x00;
Resolver_Usart.Data_Table[1] = 0x80;
Resolver_Usart.Data_Table[2] = 0x5C;
Resolver_Usart.Data_Table[3] = 0x43;
因此說上面的數據取出來就是08 00 20 E9而後&0xFF000000 確定就等於 0x08000000啦
其實這樣還有一個緣由是由於32的地址是從08000000開始的,復位中斷向量的地址的最高兩位確定是08啦!!!!
而後還有個if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //檢查棧頂地址是否合法.
這個......難道棧頂地址的最高位就是20......之後等看到相關的資料再說吧
有人這樣介紹的--仔細看,細細品味....
http://blog.csdn.net/yx_l128125/article/details/12992773
對了說一下中斷向量表
就從個人MSP430的文章中摘抄過來
原文地址
http://www.cnblogs.com/yangfengwu/p/6064129.html
忽然想起來一句話,知識是相通的....
32也有中斷向量表,就像上面的430的似的,
只不過呢!32的中斷函數的入口地址是能夠改變的!!!(F0好像固定,可是看過一篇文章好像也能經過某種方式改改)
咱們寫入IAP程序後單片機內部就有了一箇中斷向量表,這個呢是其內部一開始的固定的,
若是跳轉到用戶程序,用戶程序確定會有本身的中斷函數吧!如串口,,定時器,,等等,,,,若是不改變中斷向量表的話!!!產生的中斷
豈不是跑到了IAP那邊去了,IAP那邊有本身的中斷函數,,,亂了,完全亂了,,,,,因此必須得讓中斷向量表改變改變,好讓本身產生的
中斷,執行本身的中斷函數......
那就加一句話,或者修改一個地方
再看個人用戶程序的一個地方,,感受本身羅嗦了
if(AppFlage == 1)//接收到更新程序 { if(rbCanRead(&pRb)>1) { rbRead(&pRb, &ReadDat, 2);//讀取兩個數據 ReadDat16 = (u16)ReadDat[1]<<8; ReadDat16 = ReadDat16|ReadDat[0]; STMFLASH_Write(addr2,&ReadDat16,1); addr2+=2; } } else if(AppFlage == 0) { if(rbCanRead(&pRb)==8) { rbRead(&pRb, &TestData, 8);//讀取數據 if(TestData[3] == 0x20 && TestData[7] == 0x08)//判斷是不是更新程序 { AppFlage = 1;//要更新程序 for(i=0;i<4;i++)//先寫入這八位數據 { ReadDat16 = (u16)TestData[(i<<1)+1]<<8; ReadDat16 = ReadDat16|TestData[i<<1]; STMFLASH_Write(addr2,&ReadDat16,1); addr2+=2; } } } }
畢竟是用戶程序,串口1可能要參與別的通訊,,,因此本身加了一個判斷是不是要更新程序的數據,,,是的話才往Flash裏面寫
本身的源碼
連接:http://pan.baidu.com/s/1bJtc78 密碼:nobu
這兩天發現了本身程序的Bug
1,若是用戶程序主函數加入延時,那麼程序就來不及讀出而後寫到Flash裏面,串口卻不停的往環形隊列裏面寫,從而形成環形隊列溢出....
再者若是寫入的時候,設置的串口助手的波特率太快,,,,一樣也會形成環形隊列溢出(就是往環形隊列寫的太快了)....
本身把寫Flash的程序放在了定時器裏面,50Us進入一次的定時器,看着網上說往Flash寫一個字節大約16Us,,,,加上其他的程序總體應該不會超過50Us
若是有溢出程序不在往環形隊列裏面寫了
void USART1_IRQHandler(void) //串口1中斷服務程序 { u8 Res; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾) { Res =USART_ReceiveData(USART1); //讀取接收到的數據 if(Overflow==0) { if(PutData(&Res,1) == -1) { Overflow = 1; } } Usart1RecCnt ++; } }
最後接收完若是判斷溢出過,直接復位重啓,復位重啓更方便直接...
if(Usart1Flage == 1)//串口1接收完成 { addr2 = FLASH_APP2_ADDR; Usart1Flage =0; if(Overflow==1)//若是中途溢出了 { printf("程序中途溢出,準備復位重啓!!\r\n"); Reset_MCU();//復位重啓CPU }
2,若是寫了一些後,忽然由於某些緣由中止了寫入,,,,,本想在程序中觀察末尾有什麼固定的數據沒有,,,,或者本身最後加一些標誌位
可是這個如今程序好像能判斷出來....可是本身一直沒有明白程序爲何能夠判斷出來............應該判斷不出來的......
後來一想如今反正是本身去更新程序,真不行能夠直接燒,,,,就先放一放,,,,
更改後的
連接:http://pan.baidu.com/s/1slnWFVJ 密碼:mts7