淺析STM32 中常用的三個函數(sys.c,delay.c,usart.c)之一


1.sys.c(這個主要是定義位帶區地址的宏定義,因此主要的代碼都在sys.h中)

 其實,本質上sys就是實現位帶區映射(每1Bit)到位帶別名區(每32位,即1字),從而我們可以操作位帶別名區里的“別名”,進而操控位帶區對應的位置。

 支持了位帶操作后,可以使用普通的加載/存儲指令來對單一的比特進行讀寫。在CM3
中,有兩個區中實現了位帶。其中一個是SRAM 區的最低1MB 范圍,第二個則是片內外設
區的最低1MB 范圍。

 支持位帶操作的兩個內存區的范圍是:
  0x2000_0000‐0x200F_FFFF(SRAM 區中的最低1MB)
  0x4000_0000‐0x400F_FFFF(片上外設區中的最低1MB)

原理:這兩個區中的地址除了可以像普通的RAM 一樣使用外,它們還都有自己的“位帶別名區”,

位帶別名區把每個比特膨脹成一個32 位的字。當你通過位帶別名區訪問這些字時,就可以達到

訪問原始比特的目的。

我們可以通過下圖來了解地址映射的過程:

接下來把SRAM位帶區和外設位帶區各自映射到的位帶別名區的計算公式列出來:

記位帶區所在字節地址為A,位序號

在別名區的地址為:

內存SRAM公式:SRAMAddr=0x22000000+((A‐0x20000000)*8+n)*4 =0x22000000+ (A‐0x20000000)*32 + n*4

片上外設公式:PERIPHAddr=0x42000000+((A‐0x40000000)*8+n)*4 =0x42000000+ (A‐0x40000000)*32 + n*4

這些可以通過宏定義來實現:

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 

(addr & 0xF0000000)是起始地址,+0x2000000就成了位帶別名區的基地址,相當於取位帶區的地址(字節數)和位數,

然后映射到位帶別名區,其中+((addr &0xFFFFF)<<5)就是偏移地址(原始地址跟原始基地址的差距)*32,(bitnum<<2)就是位偏移*4;

 

//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))                //通過起始位帶地址的地址和位數取位帶別名區的地址的值

 

通過上邊,我們可以基本掌握位帶區到位帶別名區的轉換過程,可以通過下邊的兩個表練習一下。

 

 

 

 

 

通過下圖,我們可知GPIOA_BASE~GPIOG_BASE,即寄存器A~F的基地址

#define PERIPH_BASE           ((uint32_t)0x40000000) 

#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)   //寄存器A的基地址是0x4001 0800
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)   //寄存器B的基地址是0x4001 0C00
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)   //寄存器C的基地址是0x4001 1000
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)  //寄存器D的基地址是0x4001 1400
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)  //寄存器E的基地址是0x4001 1800
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)  //寄存器F的基地址是0x4001 1C00
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)  //寄存器G的基地址是0x4001 2000

我們可以通過GPIO_IDR和GPIO_ODR寄存器控制GPIO的IO口輸入輸出

//IO口地址映射(GPIOA~G中寄存器的基地址加上偏移地址8就是對應寄存器的輸出控制地址)

#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C

(GPIOA~G中寄存器的基地址加上偏移地址12就是對應寄存器的輸入控制地址)

 

 

#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08

知道了各個寄存器的各個位的輸入輸出配置地址以及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) //輸入

 

位帶操作有什么優越性?

最容易想到的就是通過GPIO 的管腳來單獨控制每盞LED 的點亮與熄滅。

另一方面,也對操作串行接口器件提供了很大的方便(典型如74HC165,CD4094)。

總之位帶操作對於硬件I/O 密集型的底層程序最有用處了。

 

 

如何在其他C文件中使用?

直接在對應的C文件的.h文件中引用"#include "sys.h" ",

然后對相應的位重新宏定義一下,方便進一步對位操作。

舉例:#define LED0  PAout(5)        //作用就是點亮PA5口的LED0燈

這時直接給LED0=1,就是BIT_ADDR(GPIOA_ODR_Addr,n) =1,就是BIT_ADDR(GPIOA_BASE+12,n)=1,

就是把寄存器A的輸出配置地址中的n位對應的值置1.(注意n位是0~F,要小於16)

 


免責聲明!

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



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