一、位带操作
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-banding3.相关寄存器的地址
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(); } }