位帶操作


一、位帶操作

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();            

    }

}

 

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM