一、位帶操作
1.意義
回想以前寫51代碼P0 = 0x10; //將P0端口設置為0x10
P1_0=1; //將P1端口1號引腳設置為高電平
a = P2_2; //獲取P2端口2號引腳的電平
根據上述的方法,我們可以發現快速定位修改某個引腳的電平還有獲取引腳的狀態
GPIO_SetBits、GPIO_ResetBits操作IO口的性能沒有達到極致,因為這些函數都需要進行現場保護和現場恢復的動作,比較耗時間,沒有進行一步到位。
GPIO_SetBits、GPIO_ResetBits操作IO口的性能沒有達到極致,因為這些函數都需要進行現場保護和現場恢復的動作,比較耗時間,沒有進行一步到位。
GPIO_SetBits(GPIOF,GPIO_Pin_9);
修改為
PFout(9)=1;
修改為
PFout(9)=1;
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
修改為
PFout(9)=0;
修改為
PFout(9)=0;
可以理解為LCD編程的文件描述符映射到內存,相當於mmap函數。
2.參考資料
STM32F3與F4系列Cortex M4內核編程手冊.pdf 第31頁 2.2.5 Bit-banding
3.相關寄存器的地址
a.查找GPIOF相關的地址
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region #define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400) //0x40001400
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) #define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400) #define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000) #define PERIPH_BASE ((uint32_t)0x40000000) GPIOF端口的地址0x40000000+20000+0x1400=0x40021400 typedef struct { __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */ __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */ __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ } GPIO_TypeDef; GPIOF->ODR的訪問地址0x40021400+0x14 =0x40021414
b.了解GPIOF相關寄存器
typedef struct { __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ __IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */ __IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */ __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ } GPIO_TypeDef; #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
c.最終操作GPIOF的時候,操作GPIO_TypeDef里的成員變量,參考例子為GPIO_SetBits與GPIO_ResetBits函數的內容。
d.如果要對GPIO端口進行讀寫數據的時候,要對GPIO_TypeDef->ODR寫入數據則對端口輸出對應的電平;要獲取端口的引腳電平,GPIO_TypeDef->IDR進行讀取。
GPIO_TypeDef->ODR,地址為0x40001400+0x14 //端口輸出數據地址 GPIO_TypeDef->IDR,地址為0x40001400+0x10 //端口輸入數據地址
公式如下:
寄存器的位帶別名 = 0x42000000 + (寄存器的地址-0x40000000)*8*4 + 引腳編號*4
寄存器的位帶別名 = 0x42000000 + (寄存器的地址-0x40000000)*8*4 + 引腳編號*4
譬如GPIOF的ODR寄存器可以編寫如下:
__IO uint32_t *pPF9_BitBand = (__IO uint32_t *)(0x42000000 + (GPIOF_BASE+0x14-0x40000000)*8*4 + 9*4); #define __I volatile /*!< Defines 'read only' permissions */ #else #define __I volatile const /*!< Defines 'read only' permissions */ #endif #define __O volatile /*!< Defines 'write only' permissions */ #define __IO volatile /*!< Defines 'read / write' permissions */
volatile關鍵字分析,往往應用在三種場合
1)多線程編程共享全局變量的時候,該全局變量要加上volatile進行修飾,讓編譯器不要優化該變量。
2)裸機編程的時候,某函數與中斷服務函數共享全局變量的時候,該全局變量要加上volatile進行修飾,讓編譯器不要優化該變量。
3)ARM定義寄存器的時候,寄存器是指向一個地址,要加上volatile進行修飾,讓編譯器不要優化該變量。
編譯器不要優化該變量也就是不對該資源進行保護,讓任何程序隨時都可以對它修改。
#include <stdio.h> #include "stm32f4xx.h" GPIO_InitTypeDef GPIO_InitStructure; void delay(void) { int i=0x500000; while(i--); } int main(void) { //獲取PF端口的ODR地址 uint32_t PF_ODR_ADDR = GPIOF_BASE+0x14; //轉換為訪問PF端口的ODR別名地址(映射地址,該地址能夠直接操作硬件) __IO uint32_t *PF9_BitBand = (__IO uint32_t *)(0x42000000+(PF_ODR_ADDR-0x40000000)*8*4+9*4); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); /* 配置PF9引腳為輸出模式 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //第9根引腳 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //設置輸出模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽模式,增加驅動電流 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //設置IO的速度為100MHz,頻率越高性能越好,頻率越低,功耗越低 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //不需要上拉電阻 GPIO_Init(GPIOF, &GPIO_InitStructure); while(1) { //PF9引腳變為低電平 *PF9_BitBand = 0; delay(); //PF9引腳變為高電平 *PF9_BitBand = 1; delay(); } }
#ifndef __SYS_H__ #define __SYS_H__ //位帶操作,實現51類似的GPIO控制功能 //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+20) //0x40020014 #define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414 #define GPIOC_ODR_Addr (GPIOC_BASE+20) //0x40020814 #define GPIOD_ODR_Addr (GPIOD_BASE+20) //0x40020C14 #define GPIOE_ODR_Addr (GPIOE_BASE+20) //0x40021014 #define GPIOF_ODR_Addr (GPIOF_BASE+20) //0x40021414 #define GPIOG_ODR_Addr (GPIOG_BASE+20) //0x40021814 #define GPIOH_ODR_Addr (GPIOH_BASE+20) //0x40021C14 #define GPIOI_ODR_Addr (GPIOI_BASE+20) //0x40022014 #define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010 #define GPIOB_IDR_Addr (GPIOB_BASE+16) //0x40020410 #define GPIOC_IDR_Addr (GPIOC_BASE+16) //0x40020810 #define GPIOD_IDR_Addr (GPIOD_BASE+16) //0x40020C10 #define GPIOE_IDR_Addr (GPIOE_BASE+16) //0x40021010 #define GPIOF_IDR_Addr (GPIOF_BASE+16) //0x40021410 #define GPIOG_IDR_Addr (GPIOG_BASE+16) //0x40021810 #define GPIOH_IDR_Addr (GPIOH_BASE+16) //0x40021C10 #define GPIOI_IDR_Addr (GPIOI_BASE+16) //0x40022010 //IO口操作,只對單一的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 PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //輸出 #define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //輸入 #define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //輸出 #define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //輸入 #endif
#include <stdio.h> #include "stm32f4xx.h" #include "sys.h" GPIO_InitTypeDef GPIO_InitStructure; void delay(void) { int i=0x500000; while(i--); } int main(void) { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); /* 配置PF9引腳為輸出模式 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //第9根引腳 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //設置輸出模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽模式,增加驅動電流 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //設置IO的速度為100MHz,頻率越高性能越好,頻率越低,功耗越低 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //不需要上拉電阻 GPIO_Init(GPIOF, &GPIO_InitStructure); while(1) { //PF9引腳變為低電平 //*PF9_BitBand = 0; PFout(9)=0; delay(); //PF9引腳變為高電平 PFout(9)=1; delay(); } }
