zoukankan      html  css  js  c++  java
  • 位带操作

    一、位带操作

    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();            
    
        }
    
    }
     
     
  • 相关阅读:
    【leetcode】图像渲染
    【leetcode】不邻接植花
    052-75
    052-74
    052-73
    052-71
    052-70
    052-69
    052-67
    052-66
  • 原文地址:https://www.cnblogs.com/xiangtingshen/p/10960181.html
Copyright © 2011-2022 走看看