位带操作

一、位带操作

1.意义

 回想以前写51代码
   P0 = 0x10; //将P0端口设置为0x10
   P1_0=1;  //将P1端口1号引脚设置为高电平
   a = P2_2; //获取P2端口2号引脚的电平
 根据上述的方法,我们可以发现快速定位修改某个引脚的电平还有获取引脚的状态
 GPIO_SetBits、GPIO_ResetBits操作IO口的性能没有达到极致,因为这些函数都需要进行现场保护和现场恢复的动作,比较耗时间,没有进行一步到位。
 GPIO_SetBits(GPIOF,GPIO_Pin_9);
修改为
 PFout(9)=1;   
 GPIO_ResetBits(GPIOF,GPIO_Pin_9);
修改为
 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
譬如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();            

    }

}

 

 
 
posted @ 2019-06-01 16:57  狂奔~  阅读(1752)  评论(0编辑  收藏  举报