我相信很多朋友在學(xué)習(xí)單片機(jī)之前都學(xué)習(xí)過(guò)51單片機(jī),假設(shè)在51單片機(jī)的P1.1的IO口上掛了一個(gè)LED,那么你單獨(dú)對(duì)LED的操作就是P1.1 = 0或P1.1 = 1,這樣你就可以單獨(dú)的對(duì)P1端的第一個(gè)IO口進(jìn)行上下拉操作,然而對(duì)于STM32,是沒(méi)有這種操作的,那么為了像51單片機(jī)一樣能夠單獨(dú)的對(duì)某個(gè)端的某一個(gè)IO單獨(dú)操作,就引入了__位帶操作__,簡(jiǎn)而言之,就是為了去單獨(dú)操作STM32里面PA的第1個(gè)IO口,所以才有了位帶這樣的操作機(jī)制。
1 什么是位帶操作
在講解位帶操作之前,首先要搞清楚什么是位帶操作。我們知道,32位的處理器的32位地址總線提供了4G的地址空間,幾乎所有的嵌入式產(chǎn)品是足夠用的。 Cortex-M就利用了額外的空間實(shí)現(xiàn)了稱為位帶(Bit-Banding)操作的硬件屬性,該技術(shù)使用地址空間的兩個(gè)不同區(qū)域來(lái)指向同一物理地址 。在主位帶區(qū)域,每個(gè)地址對(duì)應(yīng)一個(gè)字節(jié)的數(shù)據(jù),在“位帶別名”區(qū)域中,每個(gè)地址對(duì)應(yīng)同一個(gè)數(shù)據(jù)的一個(gè)位。
如下圖所示。在CM3的寄存器映射圖中有1MB的 bit band區(qū),這里被稱為位帶區(qū),與之對(duì)應(yīng)的是32MB的bit band別名區(qū),這里被稱為位帶別名區(qū)。
STM32的位帶別名區(qū)會(huì)把位帶區(qū)中的每一位膨脹成一個(gè)32位的字,所以相應(yīng)的別名區(qū)的內(nèi)存也會(huì)是位帶區(qū)的32倍。從上圖可以看出,位帶操作同時(shí)支持SRAM和片上外設(shè),支持位帶操作的兩個(gè)內(nèi)存區(qū)域范圍如下:
SRAM區(qū):0x20000000 ~ 0x200FFFFF,最低1M的范圍;
片上外設(shè)區(qū): 0x40000000 ~ 0x400FFFFF,最低1M的范圍;
位帶操作就是把位帶區(qū)中一個(gè)地址的8個(gè)位分別映射到位帶別名區(qū)的8個(gè)地址(LSB有效,即最低位有效),通過(guò)操作相應(yīng)地址的方式實(shí)現(xiàn)操作某個(gè)位。以SRAM為例,位帶區(qū)和位帶別名區(qū)的映射如下圖所示:
位帶區(qū)里每個(gè)地址的每1位膨脹為別名區(qū)里一個(gè)32位的字(32位處理器中,1字=4字節(jié)),例如:0x20000000的第0位對(duì)應(yīng)0x22000000,第1位對(duì)應(yīng)0x22000004等。
2 位帶操作的計(jì)算公式
既然位帶操作屬于Cortex-M內(nèi)核的一部分,那么在Cortex-M官方手冊(cè)也是給出了相應(yīng)的計(jì)算公式的,其通用公式如下:
別名區(qū)地址 = 別名區(qū)起始地址 + (位字節(jié)地址偏移量 * 8 + n) * 4
其中,8表示一個(gè)字節(jié)有8位,4表示膨脹了4個(gè)字節(jié),因此位帶區(qū)和位帶別名區(qū)也就是32倍的關(guān)系。
兩個(gè)區(qū)的計(jì)算公式為:
SRAM區(qū):AliasAddr = 0x22000000 + (A - 0x20000000) * 32 + n * 4
片上外設(shè)區(qū):AliasAddr = 0x42000000 + (A - 0x40000000)* 32 + n * 4
其中,AliasAddr是別名區(qū)的地址,A是位帶區(qū)的地址,n是該端口的上的某一位。
接下來(lái)就是對(duì)這個(gè)地址進(jìn)行操作了,寫(xiě)1,該位輸出1,寫(xiě)0,就輸出0。
3 位帶操作代碼實(shí)現(xiàn)
這里STM32F1為例,根據(jù)STM32的《RM0008 Reference manual》手冊(cè),其GPIO的地址映射如下:
GPIOx_ODR 寄存器如下:
每個(gè)寄存器32位,占4個(gè)地址,在訪問(wèn)或修改某個(gè)寄存器時(shí),是從首地址開(kāi)始的,邏輯運(yùn)算則是直接可涵蓋到32bit,offset 為 0x0C。GPIOA 的首地址為0x40010800,因此GPIOx_ODR 寄存器的地址為0x4001080C。則所有的GPIO映射如下:
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
上述只是位帶區(qū)的地址,根據(jù)位帶操作的計(jì)算公式,則操作位帶別名區(qū)的地址方法如下:
//IO口操作宏定義
#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))
以上代碼的第一句是轉(zhuǎn)換的關(guān)鍵,當(dāng)然相對(duì)的前面的計(jì)算公式做了優(yōu)化,也就是將SRAM和片上外設(shè)合并在一起。addr & 0XF0000000 得到SRAM和片上外設(shè)的首地址,然后加0x2000000表示位帶別名區(qū)相對(duì)位帶區(qū)的偏移量,(addr &0xFFFFF)<<5)和(bitnum<<2)就是前面“*32”和“*4”,只是換成了移位操作,因?yàn)橐莆徊僮飨鄬?duì)乘法運(yùn)算速度更快。
好了,接下來(lái)使用位帶操作來(lái)寫(xiě)一個(gè)GPIO流水燈,同時(shí)使用庫(kù)函數(shù)來(lái)做比較。
【main.c】
/* Includes ------------------------------------------------------------------*/
#include "stm32f1_bsp_led.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/*簡(jiǎn)單延時(shí)函數(shù)*/
void Delay(uint32_t xms);
/* Private functions ---------------------------------------------------------*/
/**
* @brief 主函數(shù)
* @param None
* @retval
*/
int main(void)
{
/* LED 初始化 */
LED_GPIO_Config();
while (1)
{
#if 0
GPIO_SetBits(GPIOB,GPIO_Pin_0); // 亮
Delay(0xfFfff);
GPIO_ResetBits(GPIOB,GPIO_Pin_0); // 滅
GPIO_SetBits(GPIOG,GPIO_Pin_6); // 亮
Delay(0xfFfff);
GPIO_ResetBits(GPIOG,GPIO_Pin_6); // 滅
GPIO_SetBits(GPIOG,GPIO_Pin_7); // 亮
Delay(0xffFff);
GPIO_ResetBits(GPIOG,GPIO_Pin_7); // 滅
#else
PBout(0) = 1; // 亮
Delay(0xfFfff);
PBout(0) = 0; // 滅
PGout(6) = 1; // 亮
Delay(0xfFfff);
PGout(6) = 0; // 滅
PGout(7) = 1; // 亮
Delay(0xffFff);
PGout(7) = 0; // 滅
#endif
}
}
/**
* @brief 延時(shí)函數(shù)
* @param
xms 延時(shí)長(zhǎng)度
* @retval None
*/
void Delay( uint32_t xms)
{
//for(; nCount != 0; nCount--);(方法一)
while(xms--);//(方法二)
}
【stm32f1_bsp_led.c】
/* Includes ------------------------------------------------------------------*/
#include "stm32f1_bsp_led.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
* @brief 初始化LED的GPIO
* @param None
* @retval None
*/
void LED_GPIO_Config(void)
{
/*定義一個(gè)GPIO_InitTypeDef類型的結(jié)構(gòu)體*/
GPIO_InitTypeDef GPIO_InitStructure;
/*開(kāi)啟LED的外設(shè)時(shí)鐘*/
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE);
/*設(shè)置IO口*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //設(shè)置引腳模式為通用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //設(shè)置引腳速率為50MHz
/*調(diào)用庫(kù)函數(shù),初始化GPIOB0*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //選擇要控制的GPIOB引腳
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;/*選擇要控制的引腳*/
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* 開(kāi)啟所有l(wèi)ed燈*/
GPIO_SetBits(GPIOB, GPIO_Pin_0);
GPIO_SetBits(GPIOG, GPIO_Pin_6|GPIO_Pin_7);
}
【stm32f1_bsp_led.h】
#ifndef __STM32F1_BSP_LED_H__
#define __STM32F1_BSP_LED_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
//位帶操作,實(shí)現(xiàn)51類似的GPIO控制功能
//具體實(shí)現(xiàn)思想,參考<
//IO口操作宏定義
#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))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//IO口操作,只對(duì)單一的IO口!
//確保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //輸出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //輸入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //輸出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //輸入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //輸出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //輸入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //輸出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //輸入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //輸出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //輸入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //輸出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //輸入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //輸出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //輸入
#define ON 1
#define OFF 0
/* 帶參宏,可以像內(nèi)聯(lián)函數(shù)一樣使用 */
#define LED1(a) if (a) \\
GPIO_SetBits(GPIOB,GPIO_Pin_0);\\
else \\
GPIO_ResetBits(GPIOB,GPIO_Pin_0)
#define LED2(a) if (a) \\
GPIO_SetBits(GPIOG,GPIO_Pin_6);\\
else \\
GPIO_ResetBits(GPIOG,GPIO_Pin_6)
#define LED3(a) if (a) \\
GPIO_SetBits(GPIOG,GPIO_Pin_7);\\
else \\
GPIO_ResetBits(GPIOG,GPIO_Pin_7)
/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i) {p->BSRR=i;} //設(shè)置為高電平
#define digitalLo(p,i) {p->BRR=i;} //輸出低電平
#define digitalToggle(p,i) {p->ODR ^=i;} //輸出反轉(zhuǎn)狀態(tài)
/* 定義控制IO的宏 */
#define LED1_TOGGLE digitalToggle(GPIOB,GPIO_Pin_0)
#define LED1_ON digitalHi(GPIOB,GPIO_Pin_0)
#define LED1_OFF digitalLo(GPIOB,GPIO_Pin_0)
#define LED2_TOGGLE digitalToggle(GPIOC,GPIO_Pin_4)
#define LED2_ON digitalHi(GPIOG,GPIO_Pin_6)
#define LED2_OFF digitalLo(GPIOG,GPIO_Pin_6)
#define LED3_TOGGLE digitalToggle(GPIOC,GPIO_Pin_3)
#define LED3_ON digitalHi(GPIOG,GPIO_Pin_7)
#define LED3_OFF digitalLo(GPIOG,GPIO_Pin_7)
/* Exported functions ------------------------------------------------------- */
void LED_GPIO_Config(void);
#ifdef cplusplus
}
#endif
#endif /* __STM32F1_BSP_LED_H__ */
不管使用哪種方式,其實(shí)驗(yàn)現(xiàn)象都是一樣的,但是使用位帶操作更方便些,操作者步驟更少,下面舉例說(shuō)明。
實(shí)例:欲設(shè)置地址 0x2000_0000 中的比特 2,則使用普通操作和位帶操作的設(shè)置過(guò)程如下圖所示:
普通操作和位帶操作的匯編對(duì)比代碼如下:
位帶讀操作相對(duì)簡(jiǎn)單,普通操作和位帶操作的設(shè)置過(guò)程如下圖所示:
普通操作和位帶操作的匯編對(duì)比代碼如下:
可以看出位帶操作的步驟更少,相對(duì)普通操作更簡(jiǎn)潔。
而且位帶操作屬于原子操作,在多任務(wù)系統(tǒng)中,位帶操作可以解決共享資源中的紊亂危象,關(guān)于該部分內(nèi)容可以參看《Cortex-M3權(quán)威指南》。
__總的來(lái)說(shuō),位帶的主要優(yōu)點(diǎn)__是數(shù)據(jù)的一個(gè)單獨(dú)位可以通過(guò)一條指令來(lái)讀或者寫(xiě),而不需要操作一些利的寄存器。例如,一條從位帶別名區(qū)域地址進(jìn)行讀操作的LDR指令會(huì)將值0或者1加1載入寄存器。類似的,一條STR指令在向位帶別名區(qū)的地址寫(xiě)入時(shí),只是修改主區(qū)域中數(shù)據(jù)的一位。當(dāng)然修改需要由硬件來(lái)執(zhí)行讀寫(xiě)操作,但是只有一條指令(STR)被取指并執(zhí)行。
審核編輯:湯梓紅
-
處理器
+關(guān)注
關(guān)注
68文章
19259瀏覽量
229648 -
單片機(jī)
+關(guān)注
關(guān)注
6035文章
44554瀏覽量
634614 -
嵌入式
+關(guān)注
關(guān)注
5082文章
19104瀏覽量
304791 -
寄存器
+關(guān)注
關(guān)注
31文章
5336瀏覽量
120228 -
Cortex-M
+關(guān)注
關(guān)注
2文章
229瀏覽量
29752
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論