<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > Keil創(chuàng)建新的STM32工程以及CortexM3的位帶操作

          Keil創(chuàng)建新的STM32工程以及CortexM3的位帶操作

          作者: 時(shí)間:2016-11-19 來源:網(wǎng)絡(luò) 收藏
            上周實(shí)驗(yàn)課照例很水,首先是準(zhǔn)備工作沒做好,J-Link的驅(qū)動(dòng)沒裝好,而且由于機(jī)房電腦本身的問題好多機(jī)子無法正確裝驅(qū)動(dòng),或者在進(jìn)入keil后會(huì)彈出莫名錯(cuò)誤、閃退等情況,方老師說得好,當(dāng)我們浪費(fèi)時(shí)間再做這些事情的時(shí)候(浪費(fèi)時(shí)間很大程度上是因?yàn)闄C(jī)房電腦造成的),好一點(diǎn)的學(xué)校早就在寫程序了。這么多時(shí)間已經(jīng)浪費(fèi)了,還有多少能剩下來看代碼進(jìn)而理解它呢?

            從新建一個(gè)工程開始學(xué)習(xí),再加上上周實(shí)驗(yàn)課的位帶操作相關(guān)內(nèi)容,有需要的同學(xué)可以看看,也希望指正相關(guān)錯(cuò)誤:)

          本文引用地址:http://www.ex-cimer.com/article/201611/318090.htm

          1.新建工程

            在keil中新建一個(gè)基于51的工程挺簡單,不過新建一個(gè)STM32工程要復(fù)雜一些,多了一些步驟,需要建立更詳細(xì)的工程目錄,導(dǎo)入一些CMSIS(Cortex Microcontroller Software Interface Standard)文件、標(biāo)準(zhǔn)外設(shè)驅(qū)動(dòng)文件、啟動(dòng)文件等等,并要進(jìn)行一些參數(shù)設(shè)置。下面這篇博客已經(jīng)說明得挺好了(在SITP中我也是參考的這篇博客),因此不再贅述。

          http://www.cnblogs.com/emouse/archive/2012/03/01/2375146.html

            至于相關(guān)的文件,可以在網(wǎng)上找到,我也在百度網(wǎng)盤里面?zhèn)髁艘粋€(gè)上學(xué)期總線課用到的無線模塊通信的工程,可以在里面找到所需的文件。

          2.位帶操作

            先摘抄一些書上的內(nèi)容,再結(jié)合代碼分析



          圖2.1 Cortex ‐ M3預(yù)定義的存儲(chǔ)器映射

            注:根據(jù)我的理解,右邊那一大塊,對應(yīng)的地址 從0x0 0 到0xFFFF FFFF 是存儲(chǔ)器映射地址,通俗一點(diǎn)說就是序號(hào),每一個(gè)地址(序號(hào))對應(yīng)內(nèi)存中的一個(gè)字節(jié)的區(qū)域

          2.1

            在Cortex-M3中,有兩個(gè)區(qū)中實(shí)現(xiàn)了位帶(Bit Band)操作,其一是內(nèi)部SRAM區(qū)最低的1MB范圍,其二是片內(nèi)外設(shè)去的最低1MB范圍,這兩個(gè)區(qū)中的地址還有自己的位帶別名區(qū)(Bit Band Alias Region)。位帶別名區(qū)把每個(gè)比特膨脹成一個(gè)32位的字,當(dāng)通過位帶別名區(qū)訪問這些字時(shí),就可以達(dá)到訪問原始比特的目的。

          圖2.2位帶區(qū)與位帶別名區(qū)的膨脹映射關(guān)系

            注1:和圖2.1一樣,圖上顯示的是地址,也就是序號(hào),就像是第幾號(hào)房間,而每個(gè)房間里面有8張床 (8個(gè)格子) (8bit 可以存東西的空間) 可以放0/1

            注2:上半部分位帶別名區(qū),從起始地址開始,每4個(gè)序號(hào)(如0x2200 0~ 0x2200 3)對應(yīng)下半部分位帶區(qū)的一個(gè)序號(hào)中的一個(gè)位(如0x2 0. 0),這樣就把位帶區(qū)的1位擴(kuò)展成了32位(4個(gè)序號(hào) ,每個(gè)序號(hào)對應(yīng)內(nèi)存中的1字節(jié)=8位,4×8=32,更具體一點(diǎn),就是0x2200 0.0 ~ 0x2200 3.7的空間),即1字。

          /*

            在位帶區(qū),每個(gè)比特都映射到別名地址區(qū)的一個(gè)字,該字只有最低位有效。當(dāng)一個(gè)別名地址被訪問時(shí),會(huì)先把該地址變換成位帶地址。對于讀操作,讀取位帶地址中的一個(gè)字,在把需要的位右移到需要的最低位并把最低位返回。對于寫操作,把需要寫的位左移至對應(yīng)的位序號(hào)出,然后執(zhí)行一個(gè)原子的“讀——改——寫”過程。

          */

            對于內(nèi)部SRAM位帶區(qū)的某個(gè)比特,記它所在的字節(jié)的地址為Addr,字節(jié)中位序號(hào)為n(0≤n≤7),則該比特在別名區(qū)的地址為:

          AliasAddr = 0x2200 0 + ( ( Addr – 0x2 0 ) × 8 + n ) × 4

               = 0x2200 0 + ( Addr - 0x2 0 )×32 + n × 4          (轉(zhuǎn)換公式)

            上式中 × 4 是因?yàn)?1字 = 4字節(jié), × 8 表示 1字節(jié) = 8比特。

          2.2

            2.2.1

            下面放上代碼再具體說明

          1 /* Private define */2 #define RAM_BASE       0x203 #define RAM_BB_BASE    0x224  5 /* Private macro -*/6 #define  Var_ResetBit_BB(VarAddr, BitNumber)    7           (*(__IO uint32_t *) (RAM_BB_BASE  ((VarAddr - RAM_BASE) << 5)  ((BitNumber) << 2)) = 0)8    9 #define Var_SetBit_BB(VarAddr, BitNumber)       10           (*(__IO uint32_t *) (RAM_BB_BASE  ((VarAddr - RAM_BASE) << 5)  ((BitNumber) << 2)) = 1)11 12 #define Var_GetBit_BB(VarAddr, BitNumber)       13           (*(__IO uint32_t *) (RAM_BB_BASE  ((VarAddr - RAM_BASE) << 5)  ((BitNumber) << 2)))14    15 /* Private variables */16 17 __IO uint32_t Var, VarAddr = 0, VarBitValue = 0;18 19 Var = 0x05AA5;20 21 VarAddr = (uint32_t)&Var;

            首先是定義基址,RAM_BASE是位帶區(qū)的起始地址,RAM_BB_BASE是位帶別名區(qū)的起始地址,這也可以從圖2.1 看出。

            然后是三個(gè)宏定義#define,可以看成是三個(gè)函數(shù),其中是續(xù)行符,表示下面一行是緊接著當(dāng)前行的,一般用于將很長的代碼語句分幾段寫,但要注意 后面除了換行回車不能有任何字符,空格也不行。

            接下來以Var_ResetBit_BB為例:

            #define  Var_ResetBit_BB(VarAddr, BitNumber)  (*(__IO uint32_t *) (RAM_BB_BASE ((VarAddr - RAM_BASE) << 5) ((BitNumber) << 2)) = 0)

            這一行第一眼看去不明覺厲。。需要仔細(xì)想想。把Var_ResetBit_BB 定義成這樣一個(gè)函數(shù),它的兩個(gè)參數(shù)是(VarAddr, BitNumber)

          //用這種形式表示

          void Reset(VarAddr, BitNumber){

            首先是 (RAM_BB_BASE ((VarAddr - RAM_BASE) << 5) ((BitNumber) << 2))  假設(shè)得到的結(jié)果是A

            然后是 (__IO uint32_t *) A  這是強(qiáng)制類型轉(zhuǎn)換,即把A 的類型轉(zhuǎn)換成__IO uint32_t的指針,假設(shè)結(jié)果是B,即B =(__IO uint32_t *) A

            接下來 * B  即取 B的內(nèi)容,假設(shè)C = *B

            最后是 C = 0

          }

            接著再來具體分析一下函數(shù)里面的過程,也就是轉(zhuǎn)換公式的實(shí)現(xiàn)部分( RAM_BB_BASE ( (VarAddr - RAM_BASE) << 5) ( (BitNumber) << 2) )

          VarAddr對應(yīng)于公式中的 Addr ,BitNumber 對應(yīng)于 n

          (1) 首先,二進(jìn)制數(shù)左移n位,就相當(dāng)于乘以2的n次方(D),因此

            (VarAddr - RAM_BASE) << 5 就相當(dāng)于(VarAddr - RAM_BASE) × 32 ,(BitNumber) << 2相當(dāng)于(BitNumber) × 4

            對照轉(zhuǎn)換公式可以發(fā)現(xiàn)兩者很像了,如果 按位或 和 公式中的 + 在這里能得到相同的結(jié)果,那么這個(gè)函數(shù)就可以用了。下面來看看是不是這樣。

          (2)

            RAM_BB_BASE=0x2200 0,轉(zhuǎn)換成二進(jìn)制就是

            0010 0010 0 0  0 0 0 0

            由于低25位全都是0(加上一個(gè)數(shù)肯定不會(huì)產(chǎn)生進(jìn)位),因此 與一個(gè)數(shù)相加 和 與一個(gè)數(shù)按位或 的結(jié)果是一樣的(但是 或 更快),當(dāng)然這個(gè)數(shù)不能超過25位。

            而位帶區(qū)地址最多是0x2 0開始的1MB=2^20范圍,也就是VarAddr-RAM_BASE涉及到的范圍是在0x00 ~ 0xFFFFF,也即

            0  0 0 0 0  ~  1  1 1 1 1 (20位) 

            左移5位后也就是最多25位,因此符合上面的條件,按位或和加法等效,不存在進(jìn)位問題,精妙的設(shè)計(jì)!

            每一個(gè)序號(hào)(地址)對應(yīng)的內(nèi)存中有8位,也就是說BitNumber范圍是0~7 (0 ~ 0),左移2位后是 00 ~ 00,不超過VarAddr-RAM_BASE左移5位  后多出來的0,因此也符合條件,按位或和加法等效。

          因此( RAM_BB_BASE ( (VarAddr - RAM_BASE) << 5) ( (BitNumber) << 2) ) = 轉(zhuǎn)換公式的右邊

            再回到(*(__IO uint32_t *) (RAM_BB_BASE ((VarAddr - RAM_BASE) << 5) ((BitNumber) << 2)) = 0) ,在剛才假設(shè)的那個(gè)void Reset(VarAddr, BitNumber) 函數(shù)中,A(或者說是B)代表變量在位帶別名區(qū)的對應(yīng)地址(序號(hào)),C=*B,也就是取內(nèi)容,即取這個(gè)序號(hào)的內(nèi)容(其實(shí)是由此開始的4個(gè)序號(hào),即 32位 )。

            若要Reset,則C = 0,若要Set 則 C = 1(不用 =31,因?yàn)樵撟种挥凶畹臀挥行?。由此實(shí)現(xiàn)了:若要對位帶別名區(qū)的地址(序號(hào))中的內(nèi)容進(jìn)行復(fù)位/置位(其實(shí)是寫一個(gè)字進(jìn)去),就可以改變位帶區(qū)中對應(yīng)的地址中的某一位的值(比如),而這個(gè)位帶別名區(qū)的地址只需通過VarAddr = (uint32_t)&Var得到在位帶區(qū)中的地址,并把要改動(dòng)該地址中的哪一位(BitNumber)一起作為參數(shù)給 Reset /Set 函數(shù)即可 的功能。而GetBit則是到*B為止,即return位帶別名區(qū)地址的內(nèi)容(32位)。

          但是現(xiàn)在有個(gè)問題:VarAddr = (uint32_t)&Var只是從Var的開始地址算起,Offset = VarAddr - BB_BASE已經(jīng)固定了,無法轉(zhuǎn)到下一個(gè)字(n=0~7),這要是Var這個(gè)變量過大,那不就夠不著對應(yīng)的位帶別名區(qū)了。。?

            2.2.2

            結(jié)合代碼中調(diào)用這幾個(gè)“函數(shù)”的部分來看,會(huì)有進(jìn)一步發(fā)現(xiàn)。

            //Var = 0x05AA5;

            //VarAddr = (uint32_t)&Var;

          (1)

          1  /* Modify Var variable bit 0 --*/2   Var_ResetBit_BB(VarAddr, 0);  /* Var = 0x05AA4 = 0 0 0 0  0101 1010 1010 0100 */3   printf("VAR=0x%xn",Var);4 5   Var_SetBit_BB(VarAddr, 0);    /* Var = 0x05AA5 = 0 0 0 0  0101 1010 1010 0101 */6   printf("VAR=0x%xn",Var);
          位帶區(qū)地址(序號(hào))……0x2 3.(7 ~ 0)0x2 2.(7 ~ 0)0x2 1.(7 ~ 0)0x2 0.(7 ~ 0)
          位帶區(qū)地址對應(yīng)的內(nèi)容.(7 ~ 0)……0 00 00101 10101010 0101
          Reset(VarAddr, 0)之后0 00 00101 10101010 0100
          再Set(VarAddr, 0)之后0 00 00101 10101010 0101

            BitNumber=0,Reset得到的結(jié)果是把第一個(gè)地址的.0位(第1位)變成了0,Set后又回到了1。

          (2)

          1   /* Modify Var variable bit 11 --*/2    Var_ResetBit_BB(VarAddr, 11);             /* Var = 0x052A5 = 0 0 0 0 0101 0010 1010 0101*/3    printf("VAR=0x%x",Var);4  5    /* Get Var variable bit 11 value */6    VarBitValue = Var_GetBit_BB(VarAddr, 11); /* VarBitValue = 0x00 */7    printf(" Bit11=%xn",VarBitValue);8 9    //******************************************10    Var_SetBit_BB(VarAddr, 11);               /* Var = 0x05AA5 = 0 0 0 0 0101 1010 1010 0101*/11    printf("VAR=0x%x",Var);12  13    /* Get Var variable bit 11 value */14    VarBitValue = Var_GetBit_BB(VarAddr, 11);    /* VarBitValue = 0x01 */15    printf(" Bit11=%xn",VarBitValue);
          位帶區(qū)地址(序號(hào))……0x2 3.(7 ~ 0)0x2 2.(7 ~ 0)0x2 1.(7 ~ 0)0x2 0.(7 ~ 0)
          位帶區(qū)地址對應(yīng)的內(nèi)容.(7 ~ 0)……0 00 00101 10101010 0101
          Reset(VarAddr, 11)之后0 00 0010100101010 0101
          再Set(VarAddr, 11)之后0 00 0010110101010 0101

            BitNumber=0,Reset得到的結(jié)果是把第一個(gè)地址的.11位(第12位)變成了0,Set后又回到了1。從這里這里看到,當(dāng)要改變下一個(gè)地址中的內(nèi)容時(shí),不是用VarAddr+1,而是用更大的BitNumber,即n不限制在0~7,而是0~31,或者更大。這樣又出現(xiàn)了一個(gè)問題:之前說到的

            ( RAM_BB_BASE ( (VarAddr - RAM_BASE) << 5) ( (BitNumber) << 2) ) = 轉(zhuǎn)換公式的右邊

            前提之一是 n=0~7,即只與低位的0按位或,這樣才和加法是等效的。

            結(jié)合2.2.1最后提到的問題,其實(shí)這也是一種等效。當(dāng)BitNumber > 7 時(shí),可以看成是一次進(jìn)位。

          假設(shè)VarAddr = 0x2 0 ,當(dāng)BitNumber = 7, 則指的是 0x2 0.7 即第1個(gè)地址的第8位

                          當(dāng)BitNumber = 11 = 7 + 4 ,相當(dāng)于VarAddr+1,指的是0x2 1.3 即第2個(gè)地址的第四位

            因此BitNumber 和 VarAddr就像個(gè)位和十位的關(guān)系一樣,不過是逢八進(jìn)一。而且這樣做有個(gè)好處,只需要改BitNumber就行,而不需要同時(shí)改BitNumber和VarAddr,比如在Var_ResetBit_BB(VarAddr, BitNumber)函數(shù)中不用Var_ResetBit_BB(VarAddr+1, BitNumber)了,而是直接根據(jù)需要,修改BitNumber就行。

            另外,在Set以后也可以看到,VarBitValue變成了1(即只有一個(gè)字中的最低位變成了1)。

          (3)

          1  /* Modify Var variable bit 31 --*/2   Var_SetBit_BB(VarAddr, 31);               /* Var = 0x85AA5 = 0x1 0 0 0  0101 1010 1010 0101 */3   printf("VAR=0x%x",Var);4 5   /* Get Var variable bit 31 value */6   VarBitValue = Var_GetBit_BB(VarAddr, 31); /* VarBitValue = 0x01 */7   printf(" Bit31=%xn",VarBitValue);8 9   //******************************************10   Var_ResetBit_BB(VarAddr, 31);             /* Var = 0x05AA5 = 0x0 0 0 0  0101 1010 1010 0101 */11   printf("VAR=0x%x",Var);12 13   /* Get Var variable bit 31 value */14   VarBitValue = Var_GetBit_BB(VarAddr, 31); /* VarBitValue = 0x00 */15   printf(" Bit31=%x",VarBitValue);
          位帶區(qū)地址(序號(hào))……0x2 3.(7 ~ 0)0x2 2.(7 ~ 0)0x2 1.(7 ~ 0)0x2 0.(7 ~ 0)
          位帶區(qū)地址對應(yīng)的內(nèi)容.(7 ~ 0)……0 00 00101 10101010 0101
          Set(VarAddr, 31)之后100 0010110101010 0101
          再Reset(VarAddr, 31)之后000 0010110101010 0101

          結(jié)果和(2)中得到的結(jié)論相符。

            2.2.3

            到這兒基本上把位帶操作及其實(shí)現(xiàn)的基礎(chǔ)部分寫完了,最后再來一發(fā)從位帶別名區(qū)地址反推回位帶區(qū)地址的過程,備忘。

          假設(shè)  AliasAddr = 0x2200 002C = 0010 0010 0 0101  0 0 0010 1100

          可以把AliasAddr分成3段,1[0010 001]  2[0 0 0101 0 0 001]  3[0 1100]

          第1段,在后面補(bǔ)上25個(gè)0,可以看成是位帶別名區(qū)的起始0x2200 0

          第2段,就是offset=VarAddr - BB_Base ,也即相對位帶區(qū)的偏移0 0 0101 0 0 001 =0 0010 1 0 1 = 0x02801

          第3段,右移兩位后就是BitNumber啦 右移后得到011 = 3

          這樣可以得到相應(yīng)的位帶區(qū) 地址.位 是  (0x2 0 + 0x0 2801) . 3= (0x2 2801) . 3

          再加一點(diǎn)內(nèi)容吧,關(guān)于內(nèi)存對齊,來自C語言吧

            如果你了解體系結(jié)構(gòu),就會(huì)知道,計(jì)算機(jī)內(nèi)存尋址并不是一個(gè)字節(jié)一個(gè)字節(jié)讀取的,而是一次讀取多個(gè),比如32bit數(shù)據(jù)線的計(jì)算機(jī)就可一次讀取4字節(jié),既一個(gè)int值.這時(shí)就出現(xiàn)問題了,比如你在結(jié)構(gòu)體中定義如下:

            struct a{
              char c;
              int i;
            }
          那么計(jì)算機(jī)在內(nèi)存中該如何存放呢?

            比較笨的辦法是c占第一個(gè)字節(jié),i占用2-5字節(jié).那么假設(shè)你的程序正好處于尋址邊界,比如0x0這樣的位置,那么計(jì)算機(jī)為了獲取i,就必須先獲取1-4字節(jié),然后左移8位,再獲取5-8字節(jié),右移24位,然后再相加,才能得到i,無意這種辦法是比較傻的.所以計(jì)算機(jī)在處理這種問題的時(shí)候,往往會(huì)將內(nèi)存按4字節(jié)對齊,比如c占用第一字節(jié),i占用5-8字節(jié).這樣i就和c在4位上對齊了.相當(dāng)于我們寫字一行不夠了,干脆就不寫在一行,直接重起一行.主要是方便尋址,提高性能.

          沒想到寫這篇博客花了這么長時(shí)間,對于位帶操作及其實(shí)現(xiàn)的認(rèn)識(shí)也是反反復(fù)復(fù),寫的時(shí)候再一思考發(fā)現(xiàn)部分原來的理解是錯(cuò)誤的。

          如果還有其他錯(cuò)誤,還希望讀到這一篇文章的你能夠幫忙指正,也幫助我學(xué)習(xí) :)

          參考資料:1http://www.cnblogs.com/emouse/archive/2012/03/01/2375146.html

               2http://blog.chinaunix.net/uid-26285146-id-3071387.html

               3http://www.amobbs.com/thread-5464765-1-1.html

               4http://tieba.baidu.com/p/2138813

               5 《嵌入式系統(tǒng)及其應(yīng)用》_同濟(jì)大學(xué)出版社 P37~P42

          補(bǔ)充一句:

          其實(shí)一般來說,初學(xué)不需要掌握函數(shù)內(nèi)部的知識(shí)、過程,只需要知道怎么用就好了,《碼農(nóng)的原罪》里面有一句“沒必要就不用學(xué),有必要的時(shí)候你自然就會(huì)了。”

          剛?cè)腴T時(shí)學(xué)會(huì)新建一個(gè)工程、導(dǎo)入文件、相關(guān)設(shè)置才是更重要的。

          2014.5.20補(bǔ)充

          由同學(xué)指出,新發(fā)現(xiàn)一點(diǎn)疑問,就是這段話

          在位帶區(qū),每個(gè)比特都映射到別名地址區(qū)的一個(gè)字,該字只有最低位有效。當(dāng)一個(gè)別名地址被訪問時(shí),會(huì)先把該地址變換成位帶地址。對于讀操作,讀取位帶地址中的一個(gè)字,在把需要的位右移到需要的最低位并把最低位返回。對于寫操作,把需要寫的位左移至對應(yīng)的位序號(hào)出,然后執(zhí)行一個(gè)原子的“讀——改——寫”過程。

          參考下面兩篇

          STM32位帶操作

          S?T?M?3?2?位?帶?介?紹

          還沒重看程序,我覺著我們只需要改相應(yīng)地址的位帶別名區(qū)的內(nèi)容(最低位)就好,而改完之后,就由ARM內(nèi)核自動(dòng)完成了“位帶”功能,即在發(fā)現(xiàn)位帶別名區(qū)改變之后,自動(dòng)改變相應(yīng)的位帶區(qū)內(nèi)容。

          以后看有時(shí)間能不能再仔細(xì)研究一下



          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();