STM32系列基于專為要求高性能、低成本、低功耗的嵌入式應(yīng)用專門設(shè)計的ARM Cortex-M3內(nèi)核(ST‘s product portfolio contains a comprehensive range of microcontrollers, from robust, low-cost 8-bit MCUs up to 32-bit ARM-based Cortex?-M0 and M0+, Cortex?-M3, Cortex?-M4 Flash microcontrollers with a great choice of peripherals. ST has also extended this range to include an ultra-low-power MCU platform)[1] 。按內(nèi)核架構(gòu)分為不同產(chǎn)品:
其中STM32F系列有:
STM32F103“增強(qiáng)型”系列
STM32F101“基本型”系列
STM32F105、STM32F107“互聯(lián)型”系列
增強(qiáng)型系列時鐘頻率達(dá)到72MHz,是同類產(chǎn)品中性能最高的產(chǎn)品;基本型時鐘頻率為36MHz,以16位產(chǎn)品的價格得到比16位產(chǎn)品大幅提升的性能,是32位產(chǎn)品用戶的最佳選擇。兩個系列都內(nèi)置32K到128K的閃存,不同的是SRAM的最大容量和外設(shè)接口的組合。時鐘頻率72MHz時,從閃存執(zhí)行代碼,STM32功耗36mA,相當(dāng)于0.5mA/MHz。
在CM3中,有兩個區(qū)中實(shí)現(xiàn)了位帶。其中一個是 SRAM 區(qū)的最低 1MB 范圍,第二個則是片內(nèi)外設(shè)區(qū)的最低 1MB 范圍。這兩個區(qū)中的地址除了可以像普通的 RAM 一樣使用外,它們還都有自己的“位帶別名區(qū)”,位帶別名區(qū)把每個比特膨脹成一個 32 位的字。當(dāng)你通過位帶別名區(qū)訪問這些字時,就可以達(dá)到訪問原始比特的目的。
在位帶區(qū)中,每個比特都映射到別名地址區(qū)的一個字(這是只有 LSB 有效的字)。當(dāng)一個別名地址被訪問時,會先把該地址變換成位帶地址。對于讀操作,讀取位帶地址中的一個字,再把需要的位右移到 LSB,并把 LSB 返回。對于寫操作,把需要寫的位左移至對應(yīng)的位序號處,然后執(zhí)行一個原子的“讀-改-寫”過程。
支持位帶操作的兩個內(nèi)存區(qū)的范圍是:
0x2000_0000‐0x200F_FFFF(SRAM 區(qū)中的最低1MB) 0x4000_0000‐0x400F_FFFF(片上外設(shè)區(qū)中的最低 1MB) 位帶(Bit-band)操作是Cortex-M3提供的特殊操作:位帶區(qū)的每個位都有位帶別名區(qū)的一個字與之對應(yīng)。Bit-band區(qū)域的存儲器以32位方式進(jìn)行訪問,其中有效的僅僅是BIT0位,只有BIT0的值才對應(yīng)到相應(yīng)的普通區(qū)域的比特位上,其他位無效。STM32F系列芯片為所有外設(shè)寄存器和SRAM提供相對應(yīng)的Bit-band區(qū)域,以簡化對外設(shè)寄存器和SRAM的操作位帶操作最重要的一環(huán)就是尋址,即為需要操作的“目標(biāo)位”找到位帶別名區(qū)相對應(yīng)的地址:
位帶別名區(qū)首地址+(操作字節(jié)的偏移量*32) +(操作位的偏移量*4) 內(nèi)置SRAM區(qū)的位帶別名區(qū)首地址 = 0x2200,0000 外設(shè)寄存器區(qū)的位帶別名區(qū)首地址 = 0x4200,0000
例如:GPIOA的端口輸出數(shù)據(jù)寄存器地址0x4001080c(stm32f1xx系列),對于PA.0來說控制其輸出電平的比特位的位帶操作地址為:
0x42000000+(0x1080c*32)+(0*4) = 0x42021018 以stm32f207為例,介紹stm32的位帶操作。
在《ARM Cortex M3權(quán)威指南》92頁介紹了如何在C語言中使用位帶操作。個人在使用位帶操作過程中將其大致總結(jié)為三種:
① 查找需要定義的位所在的地址,將地址強(qiáng)制轉(zhuǎn)換為指針,通過取指針內(nèi)容的方式使用。
例如:
#define PD12 ((volatile unsigned long *)(0x424182b0))
//0x424182b0為GPIOD_Pin12引腳對應(yīng)的映射地址
*PD12=0x01; //PD12引腳置高
② 直接使用地址進(jìn)行操作
?。?((u32*)0x424182b0))=0x01; //PD12引腳置高 當(dāng)然也可以通過如下定義使用 例如:
#define PD12 *((volatile unsigned long *)(0x424182b0))
//0x424182b0為GPIOD_Pin12引腳對應(yīng)的映射地址
PD12=0x01; //PD12引腳置高
③在多個引腳需要定義時,顯然上面兩種方法都比較繁瑣。為簡化位帶操作,也可以定義一些宏。比如,我們可以建立一個把“位帶地址+位序號”換成別名地址的宏,再建立一個把別名地址轉(zhuǎn)換成指針類型的宏。
例如
#define GPIOD_ODR_Addr ((uint32_t)(GPIOD_BASE+0x14)) // GPIOD_BASE已經(jīng)定義過 #define BITBAND(addr,bitnum) ((addr&0xF0000000)+0x2000000+((addr&0xFFFFF)《《5)+(bitnum《《2)) #define MEM_ADDR(addr) (*((volatile unsigned long *)(addr)) ) #define BIT_ADDR(addr, bitnum) MEM_ADDR( BITBAND(addr, bitnum) ) #define PD12 BIT_ADDR(GPIOD_ODR_Addr, 12) //12為GPIOD對應(yīng)的引腳號 #define PD12 BIT_ADDR(GPIOD_ODR_Addr, 13) //13為GPIOD對應(yīng)的引腳號 PD12=0x01; //PD12引腳置高 PD13=0x01; //PD13引腳置高
說明:GPIOD_BASE已經(jīng)在庫文件stm32f2xx.h中定義過,直接使用即可,對應(yīng)地址是GPIOD的基地址,0x14是ODR的偏移地址。第二條語句是把“位帶地址+序號”轉(zhuǎn)換為對應(yīng)的位帶別名區(qū)地址。第三條語句MEM_ADDR(addr)代表的是(addr)地址中的內(nèi)容。volatile必須使用,原因如下。
volatile 是易變的、不穩(wěn)定的意思。用它修飾的變量表示可以被某些編譯器未知的因素更改,比如操作系統(tǒng)、硬件或者其它線程等。遇到這個關(guān)鍵字聲明的變量,編譯器對訪問該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對特殊地址的穩(wěn)定訪問。
先看看下面的例子: int i=10; int j = i;//(1)語句 int k = i;//(2)語句
這時候編譯器對代碼進(jìn)行優(yōu)化,因?yàn)樵冢?)、(2)兩條語句中,i 沒有被用作左值。這時候編譯器認(rèn)為 i 的值沒有發(fā)生改變,所以在(1)語句時從內(nèi)存中取出 i 的值賦給 j 之后,這個值并沒有被丟掉,而是在(2)語句時繼續(xù)用這個值給 k 賦值。編譯器不會生成出匯編代碼重新從內(nèi)存里取 i 的值,這樣提高了效率。但要注意:(1)、(2)語句之間 i 沒有被用作左值才行。
再看另一個例子: volatile int i=10; int j = i;//(3)語句 int k = i;//(4)語句
volatile 關(guān)鍵字告訴編譯器 i 是隨時可能發(fā)生變化的,每次使用它的時候必須從內(nèi)存中取出 i的值,因而編譯器生成的匯編代碼會重新從 i 的地址處讀取數(shù)據(jù)放在 k 中。這樣看來,如果 i 是一個寄存器變量或者表示一個端口數(shù)據(jù)或者是多個線程的共享數(shù)據(jù),就容易出錯,所以說 volatile 可以保證對特殊地址的穩(wěn)定訪問。
電子發(fā)燒友App












評論