前言:從51開始,單片機玩了很長時間了,有51,PIC,AVR等等,早就想跟潮流玩玩ARM,但一直沒有開始,原因-----不知道玩了ARM可以做什么(對我自己而言)。如果為學習而學習,肯定學不好。然后cortex-m3出來了,據說,這東西可以替代單片機,于是馬上開始關注。也在第一時間開始學習,可惜一開始就有點站錯了隊,選錯了型(仍是對我自己而言)。我希望這種芯片應該是滿大街都是,隨便哪里都可以買得到,但我選的第一種顯然做不到。為此,大概浪費了一年多時間吧,現在,回到對我來說是正確的道路上來啦,邊學邊寫點東西。
這里寫的是我的學習的過程,顯然,很多時候會是不全面的,不系統的,感悟式的,甚至有時會是錯誤的,有些做法會是不專業的。那么,為什么我還要寫呢?這是一個有趣的問題,它甚至涉及到博客為什么要存在的問題。顯然,博客里面的寫的東西,其正確性、權威性大多沒法和書比,可為什么博客會存在呢?理由很多,我非專家,只說我的感慨。
我們讀武俠小說,總會有一些創出獨門功夫的宗師,功夫極高,然后他的弟子則基本上無法超越他。我在想,這位宗師在創造他自己的獨門功夫時,必然會有很多的次的曲折、彎路、甚至失敗,會浪費他的很多時間,而他教給弟子時,則已去掉了這些曲折和彎路,當然更不會把失敗教給弟子,按理說,效率應該更高,可是沒用,弟子大都不如師。為什么呢?也許知識本身并不是最重要的,獲取知識的過程才是最重要的?也許所謂的知識,并不僅僅是一條條的結論,而是附帶著很多說不清道不明的東西?如植物的根,一條主根上必帶有大量的小小的觸須?
閑話多了些,就權當前言了。下面準備開始。
一、條件的準備
我的習慣,第一步是先搭建一個學習的平臺。原來學51,PIC,AVR時,都是想方設法自己做些工具,實驗板之類,現在人懶了,直接購買成品了。
硬件電路板:火牛板
軟件:有keil和iar可供選擇。網上的口水仗不少,我選keil,理由很簡單,這個我熟。目前要學的知識中,軟、硬件我都不熟,所以找一個我有點熟的東西就很重要。在我相當熟練之前,肯定不會用到IAR,如果真的有一天不得不用IAR,相信學起來也很容易,因為這個時候硬件部分我肯定很熟了,再加上有 keil的基礎,所以應該很容易學會了。
調試工具:JLINK V8。這個不多說了,價格便宜又好用,就是它了。
?
二、熱身
細細端詳,做工精良,尤其那上面的3.2吋屏,越看越喜歡。接下來就是一陣折騰了,裝JLINK軟件,給板子通電,先試試JLINK能不能與電腦和板子通信上了。真順,一點問題也沒有。于是準備將附帶的程序一個一個地寫進去試一試。一檢查,大部分例子的HEX文件并沒有給出,這要下一步自己生成,但是幾個大工程的例子都有HEX文件,如MP3,如UCCGI測試等,寫完以后觀察程序運行的效果。因為之前也做過彩屏的東西,知道那玩藝代碼量很大,要流暢地顯示并不容,當時是用AVR做的,在1.8吋屏上顯示一幅畫要有一段時間。現在看起來,用STM32做的驅動顯示出來的畫面還是很快的,不過這里顯示的大部分是自畫圖,并沒有完整地顯示一整幅的照片,所以到底快到什么程度還不好說,看來不久以后這可以作為一個學習點的。
一個晚上過去了,下一篇就是要開始keil軟件的學習了。
三、開始編程
硬件調通后,就要開始編程了。
編程的方法有兩種,一種是用st提供的庫,另一種是從最底層開始編程,網上關于使用哪種方法編程的討論很多,據說用庫的效率要低一些。但是用庫編程非常方便,所以我還是從庫開始啦。庫是ST提供的,怎么說也不會差到哪里,再說了,用32位ARM的話,開發的觀念也要隨之改變一點了。
說說我怎么學的吧。
找個例子,如GPIO,可以看到其結構如下:
SOURCE(文件夾)
- APP(文件夾)
-CMSIS(文件夾)
-STM32F10x_StdPeriph_Driver(文件夾)
Lis(文件夾)
OBJ(文件夾)
其中SOURCE中保存的是應用程序,其中又有好多子文件夾,而CMSIS文件夾中和STM32F10x_StdPeriph_Driver文件夾中是ST提供的庫,這樣,如果要做新的工程只要將這個文件夾整個復制過來就行,其中APP中保存自己的代碼。
因為我們用51單片機時一般比較簡單,有時就一個文件,所以通常不設置專門的輸出文件夾,而這里做開發,通常會有很多個文件加入一個工程中,編譯過程中會產生很多中間文件,因此設置專門的文件夾LIS和OBJ用來保存中間文件。
?
下面就將設置簡單描述一下。
將復到過來的GPIO根目錄下的所有文件刪除,因為我們要學著自己建立工程。
用菜單Project--》New uVision Porject.。.建立新的工程,選擇目標器件為STM32103VC,這個過程與建立51單片機的工程沒有什么區別,這里就偷點懶,不上圖了。接下來看一看怎么設置。
點那個品字形,打開對話框
這里就給個圖了,相信有一定操作基礎的人應該會用。順便提一下,原來用VC或者IAR時總覺得它們的一個功能:就是建立一個是Debug組和Release組,這個功能挺好的,從這個圖可在Keil里也是一樣可以建的。
將剛才那個文件夾圖中CMSIS中的文件加入CMSIS組,一共3個,其中\Source\CMSIS\Core\CM3有兩個C語言源程序文件全部加入,另外還有一個在
\Source\CMSIS\Core\CM3\startup\arm文件夾中,這個文件夾中有4個.s文件, 我們選擇其中的 startup_stm32f10x_hd.s文件。這是根據項目所用CPU來選擇的,我們用的CPU是103VC的,屬于高密度的芯片,所以選這個。
至于LIB中的文件,就在這兒:\Source\STM32F10x_StdPeriph_Driver\src啦。這里有很多個文件,把什么文件加進去呢?怕麻煩的話,把所有文件全部加進去,這并不會增加編譯后的代碼量,但會增加很多的編譯時間。
接下來設定目標輸出文件夾。上面這個圖怎么出來的就不說啦,單擊“Select Foler for Objects。”,在彈出來的對話框中選擇OBJ文件夾。
同樣方法,選擇List文件的輸出文件夾。
?
設置好后,如果直接編譯是不行的,會出錯。還需要提供頭文件所在位置。單擊c/C++標簽頁。
第一次進入時Include Paths 文本框中是空白的,點擊其后的按鈕打開對話框,增加相應的路徑
這樣路徑就設好了。單擊OK,回到上一界面,然后再單擊OK,退出設置,即可編譯、鏈接。
下一會要試一試新的3.12版的庫效果如何了。
?
升級庫
光盤中所帶的例子是3.10的,另外還有一個3.12的,我 試著將3.12的庫替代原來的庫,還真有問題,下面就簡述問題及解決方法。
(1)將庫文件解壓
庫文件名是:stm32f10x_stdperiph_lib.zip,解壓后放在任意一個文件夾中。
(2)由于原作者做了很好的規劃,每一個項目中都分成三個文件夾,并且在source文件夾中又做了3個文件夾,其中APP文件夾是放自己寫的文件的,其他的兩個是從庫中復制過來的,因此,想當然地把3.1.2版本中相同的兩個文件夾:
CMSIS和STM32F10x_StdPeriph_Driver直接復制過來,以為一切OK,結果一編譯,出來一堆錯誤。
其中有錯誤:
Source\App\main.c(7): error: #20: identifier “GPIO_InitTypeDef” is undefined
。..。
還有大量的警告:
Source\STM32F10x_StdPeriph_Driver\src\stm32f10x_flash.c(130): warning: #223-D: function “assert_param” declared implicitly
看了看,在APP文件夾中還有一些不屬于自己的東西:
stm32f10x_conf.h,stm32f10x_it.h,stm32f10x_it.c,打開一看,果然是3.10版本的,沒說的,換。。。。,找到STM32F10x_StdPeriph_Lib_V3.1.2\Project\Template文件夾,用里面的同樣的文件替換掉這幾個文件,這回應該萬事大吉了吧。
再一看,依然如故,,沒辦法了,只好細細研究了。通過觀察,發現原來可以編譯通過的工程,在main.c下面掛滿了.h文件,而這個通不過的,則少得很。
這是編譯能通過的工程
這是編譯通不過的工程
顯然,有些文件沒有被包含進來。一點一點跟蹤,發現大部分的頭文件都包含在stm32f10x_conf.h中,而這個文件又出現在stm32f10x.h中,其中有這樣的一行:
#ifdef USE_STDPERIPH_DRIVER
#include “stm32f10x_conf.h”
#endif
看來,是這個USE_STDPERIPH_DRIVER沒有被定義啊,于是,人為地去掉條件:
//#ifdef USE_STDPERIPH_DRIVER
#include “stm32f10x_conf.h”
//#endif
再次編譯,果然就OK了。可是,可是,也不能就這么去掉啊,怎么辦呢?萬能的網啊,一搜果然就有了。
到設置 C/C++頁面
在那個define中加入“USE_STDPERIPH_DRIVER,STM32F10X_HD”
當然,去掉條件編譯前面的注釋,回到原樣。
再次編譯,一切順利。可是,原來的工程例子也沒有加這個啊,怎么回事呢?再次打開原來的例子,找到stm32f10x.h,可以看到有這么一行:
而新的stm32f10x.h中則是這樣的:
原來那個3.1.0版的stm32f10x.h被人為地修改了一下,所以,不在define中定義也不要緊,而新升級的3.1.2則不行了。
至此,簡單的升級搞定。
?
內存學習
ARM中的RO、RW和ZI DATA
一直以來對于ARM體系中所描述的RO,RW和ZI數據存在似是而非的理解,這段時間對其仔細了解了一番,發現了一些規律,理解了一些以前書本上有的但是不理解的東西,我想應該有不少人也有和我同樣的困惑,因此將我的一些關于RO,RW和ZI的理解寫出來,希望能對大家有所幫助。
要了解RO,RW和ZI需要首先了解以下知識:
ARM程序的組成
此處所說的“ARM程序”是指在ARM系統中正在執行的程序,而非保存在ROM中的bin映像(image)文件,這一點清注意區別。
一個ARM程序包含3部分:RO,RW和ZI
RO是程序中的指令和常量
RW是程序中的已初始化變量
ZI是程序中的未初始化的變量
由以上3點說明可以理解為:
RO就是readonly,
RW就是read/write,
ZI就是zero
ARM映像文件的組成
所謂ARM映像文件就是指燒錄到ROM中的bin文件,也稱為image文件。以下用Image文件來稱呼它。
Image文件包含了RO和RW數據。
之所以Image文件不包含ZI數據,是因為ZI數據都是0,沒必要包含,只要程序運行之前將ZI數據所在的區域一律清零即可。包含進去反而浪費存儲空間。
Q:為什么Image中必須包含RO和RW?
A:因為RO中的指令和常量以及RW中初始化過的變量是不能像ZI那樣“無中生有”的。
ARM程序的執行過程
從以上兩點可以知道,燒錄到ROM中的image文件與實際運行時的ARM程序之間并不是完全一樣的。因此就有必要了解ARM程序是如何從ROM中的image到達實際運行狀態的。
實際上,RO中的指令至少應該有這樣的功能:
1. 將RW從ROM中搬到RAM中,因為RW是變量,變量不能存在ROM中。
2. 將ZI所在的RAM區域全部清零,因為ZI區域并不在Image中,所以需要程序根據編譯器給出的ZI地址及大小來將相應得RAM區域清零。ZI中也是變量,同理:變量不能存在ROM中
在程序運行的最初階段,RO中的指令完成了這兩項工作后C程序才能正常訪問變量。否則只能運行不含變量的代碼。
?
說了上面的可能還是有些迷糊,RO,RW和ZI到底是什么,下面我將給出幾個例子,最直觀的來說明RO,RW,ZI在C中是什么意思。
1; RO
看下面兩段程序,他們之間差了一條語句,這條語句就是聲明一個字符常量。因此按照我們之前說的,他們之間應該只會在RO數據中相差一個字節(字符常量為1字節)。
Prog1:
#include 《stdio.h》
void main(void)
{
;
}
Prog2:
#include 《stdio.h》
const char a = 5;
void main(void)
{
;
}
Prog1編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog2編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 61 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1009 ( 0.99kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
以上兩個程序編譯出來后的信息可以看出:
Prog1和Prog2的RO包含了Code和RO Data兩類數據。他們的唯一區別就是Prog2的RO Data比Prog1多了1個字節。這正和之前的推測一致。
如果增加的是一條指令而不是一個常量,則結果應該是Code數據大小有差別。
2; RW
同樣再看兩個程序,他們之間只相差一個“已初始化的變量”,按照之前所講的,已初始化的變量應該是算在RW中的,所以兩個程序之間應該是RW大小有區別。
Prog3:
#include 《stdio.h》
void main(void)
{
;
}
Prog4:
#include 《stdio.h》
char a = 5;
void main(void)
{
;
}
Prog3編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 1 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
可以看出Prog3和Prog4之間確實只有RW Data之間相差了1個字節,這個字節正是被初始化過的一個字符型變量“a”所引起的。
3; ZI
再看兩個程序,他們之間的差別是一個未初始化的變量“a”,從之前的了解中,應該可以推測,這兩個程序之間應該只有ZI大小有差別。
Prog3:
#include 《stdio.h》
void main(void)
{
;
}
Prog4:
#include 《stdio.h》
char a;
void main(void)
{
;
}
Prog3編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4編譯出來后的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 97 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
編譯的結果完全符合推測,只有ZI數據相差了1個字節。這個字節正是未初始化的一個字符型變量“a”所引起的。
注意:如果一個變量被初始化為0,則該變量的處理方法與未初始化華變量一樣放在ZI區域。
即:ARM C程序中,所有的未初始化變量都會被自動初始化為0。
總結:
1; C中的指令以及常量被編譯后是RO類型數據。
2; C中的未被初始化或初始化為0的變量編譯后是ZI類型數據。
3; C中的已被初始化成非0值的變量編譯后市RW類型數據。
附:
程序的編譯命令(假定C程序名為tst.c):
armcc -c -o tst.o tst.c
armlink -noremove -elf -nodebug -info totals -info sizes -map -list aa.map -o tst.elf tst.o
編譯后的信息就在aa.map文件中。
ROM主要指:NAND Flash,Nor Flash
評論
查看更多