1)RCC_APB2ENR |= 1<<3;//该句可以对第3位置1,只能置1时使用,且只能操作一位。
2)RCC_APB2ENR &= ~(1<<2); //该句可以对第2位置0,只能操作一位。
3)RCC->CR = 0x01000000; //这种句式给所有位赋值,不推荐使用
4)RCC->CR |= 0x01000000; // 这种句式只给非0位赋值,0位对的数据不变
5)
*GPIOE_CRL&= 0xFF0F0FFF;//清除第3、5位,不影响其它位
*GPIOE_CRL|= 0x00308000;//为第3、5位写入数值
该段代码用于对寄存器多位同时操作,用于写入数据,不可读取数据。
1)(RCC->CR>>17)&0x01; 这种句式能够读取寄存器的第17位,只能读取一位
2)while((USART1->SR & 0X40)==0);这种句式可以直接判断 USART1->SR 的第6位是1还是0,仅判断一位
3)
unsigned char temp=0;
RCC->CFGR = 0x0000000a;
while(temp!=0x02)
{
temp=RCC->CFGR>>2;//获取寄存器低位数据
temp&=0x03;//清除其它位的干扰
}
上述代码利用了temp的短长度,先把寄存器的需要读取位(第2,3位)放在最低位,而后给temp这个长度短的赋值,由此寄存器把低位数据给temp,temp装满后寄存器其余值不再赋值。
#define APB2 0x40000000
#define GPIO APB2+0x10000
#define GPIOE GPIO+0x1800
#define A (GPIOE+0x08)
#define BIT(addr, num) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(num<<2))
#define BIT(addr, num) ((addr & 0xF0000000)+0x2000000+(addr-(addr & 0xF0000000))*32+num*4)
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BIT(addr, bitnum))
#define PEin(n) BIT_ADDR(A,n)
该段代码也可以实现位操作,它既可以读也可以写,既可以置1也可以置0,具体如何看A的地址。
此段代码源于《Cortex-M3权威指南》第五章的位带操作,在 CM3中,有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围,两个内存区的范围是:
0x2000_0000‐0x200F_FFFF(SRAM 区中的最低 1MB)
0x4000_0000‐0x400F_FFFF(片上外设区中的最低 1MB)
这两个区域的地址的每个位被膨胀成为32位的字,对这些字有了个新的地址名,称位带别名,对别名的操作等同于对该地址的操作。获取别名地址的方式有两种在上述代码中体现。
其中*((volatile unsigned long *)(addr)) 的解释如下:
(unsigned long *)(addr),代表“addr”是一个unsigned long类型的指针;
volatile是一个修饰符,告诉编译器此段代码不要优化;因此,(volatile unsigned long *)(addr),意思是未优化指针类型的addr;
(volatile unsigned long *) (addr),意思是(volatile unsigned long *)指针指向固定的地址 (addr)
*(volatile unsigned long *) (addr)代表 (addr)是一个地址变量,我们既可以给固定地址中赋值,也可以从固定地址中取值。
我们知道STM32的功能基本上就是运算+寄存器控制实现的,运算就是算法,寄存器控制是对STM32的所有外设的直接控制,设定不同的寄存器值可以设定外设进行不同的动作。对寄存器操作前,需要对所有相关寄存器进行定义,即给寄存器的地址定义一个名称,使用时使用名称即可。
以RTC为例,寄存器相互独立,且不可分组,于是,可以单独定义。
具体定义如下,定义一个结构体,结构体内包含所有独立寄存器,__IO表示静态定义,类似与static,uint32_t是因为一个独立寄存器有32位,这里把保留位也算进来了。如此定义,后只需加上开头的两个define就可以实现对寄存器的访问。(mRCC_Type *)意味着把变量 mRCC 转变为指针,指针类型为 mRCC_Type 结构体,指向地址 mRCC_BASE。
#define mRCC_BASE 0x40021000
#define mRCC ((mRCC_Type *)mRCC_BASE)
typedef struct
{
__IO uint32_t CR;
__IO uint32_t CFGR;
__IO uint32_t CIR;
__IO uint32_t APB2RSTR;
__IO uint32_t APB1RSTR;
__IO uint32_t AHBENR;
__IO uint32_t APB2ENR;
__IO uint32_t APB1ENR;
__IO uint32_t BDCR;
__IO uint32_t CSR;
} mRCC_Type;
typedef struct
{
__IO uint32_t EVCR;
__IO uint32_t MAPR;
__IO uint32_t EXTICR[4];
} mAFIO_Type;
以GPIO为例,多个GPIO使用同名寄存器(但是寄存器地址不一样);TIM也是如此。
具体定义如下,如此这般,所有的GPIO组共用一个结构体 mGPIO_Type ,但是在指针定义时不同GPIO组,使用不同的指针变量名,指向不同地址。
#define mGPIOG_BASE 0x40012000
#define mGPIOF_BASE 0x40011C00
#define mGPIOE_BASE 0x40011800
#define mGPIOD_BASE 0x40011400
#define mGPIOC_BASE 0x40011000
#define mGPIOB_BASE 0X40010C00
#define mGPIOA_BASE 0x40010800
#define mGPIOA ((mGPIO_Type *)mGPIOA_BASE)
#define mGPIOB ((mGPIO_Type *)mGPIOB_BASE)
#define mGPIOC ((mGPIO_Type *)mGPIOC_BASE)
#define mGPIOD ((mGPIO_Type *)mGPIOD_BASE)
#define mGPIOE ((mGPIO_Type *)mGPIOE_BASE)
#define mGPIOF ((mGPIO_Type *)mGPIOF_BASE)
#define mGPIOG ((mGPIO_Type *)mGPIOG_BASE)
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
}mGPIO_Type;
以CAN为例,CAN的控制和状态寄存器、过滤器寄存器是独立的,不需要分组,但是邮箱寄存器有3个,虽然每个对应的寄存器是不同的,但是这些寄存器功能相同,名称相同,故而可以分组进行定义。
在定义控制和状态寄存器、过滤器寄存器之后,对邮箱寄存器 mCAN_TxMailBox_Type 和 mCAN_FIFOMailBox_Type 进行单独定义,并在主定义中添加指针数组,指针类型为 mCAN_TxMailBox_Type 和 mCAN_FIFOMailBox_Type。
mCAN_TxMailBox_Type sTxMailBox[3];
mCAN_FIFOMailBox_Type sFIFOMailBox[2];
可以看作如下代码:
__IO uint32_t TIR[0];
__IO uint32_t TDTR[0];
__IO uint32_t TDLR[0];
__IO uint32_t TDHR[0];
__IO uint32_t TIR[1];
__IO uint32_t TDTR[1];
__IO uint32_t TDLR[1];
__IO uint32_t TDHR[1];
__IO uint32_t TIR[2];
__IO uint32_t TDTR[2];
__IO uint32_t TDLR[2];
__IO uint32_t TDHR[2];
__IO uint32_t RIR[0];
__IO uint32_t RDTR[0];
__IO uint32_t RDLR[0];
__IO uint32_t RDHR[0];
__IO uint32_t RIR[1];
__IO uint32_t RDTR[1];
__IO uint32_t RDLR[1];
__IO uint32_t RDHR[1];
#define PERIPH_BASE ((uint32_t)0x40000000)
#define APB1PERIPH_BASE PERIPH_BASE
#define CAN1_BASE (APB1PERIPH_BASE + 0x6400)
#define CAN2_BASE (APB1PERIPH_BASE + 0x6800)
#define CAN1 ((mCAN_Type *) CAN1_BASE)
#define CAN2 ((mCAN_Type *) CAN2_BASE)
typedef struct
{
__IO uint32_t TIR;
__IO uint32_t TDTR;
__IO uint32_t TDLR;
__IO uint32_t TDHR;
} mCAN_TxMailBox_Type;
typedef struct
{
__IO uint32_t RIR;
__IO uint32_t RDTR;
__IO uint32_t RDLR;
__IO uint32_t RDHR;
} mCAN_FIFOMailBox_Type;
typedef struct
{
__IO uint32_t MCR;
__IO uint32_t MSR;
__IO uint32_t TSR;
__IO uint32_t RF0R;
__IO uint32_t RF1R;
__IO uint32_t IER;
__IO uint32_t ESR;
__IO uint32_t BTR;
uint32_t RESERVED0[88];
mCAN_TxMailBox_Type sTxMailBox[3];
mCAN_FIFOMailBox_Type sFIFOMailBox[2];
uint32_t RESERVED1[12];
__IO uint32_t FMR;
__IO uint32_t FM1R;
uint32_t RESERVED2;
__IO uint32_t FS1R;
uint32_t RESERVED3;
__IO uint32_t FFA1R;
uint32_t RESERVED4;
__IO uint32_t FA1R;
uint32_t RESERVED5[8];
} mCAN_Type;
在这里有一个 uint32_t RESERVED0[88]; 与 uint32_t RESERVED1[12];这些仅起到占位符的作用,指针经过这些占位符时,指针地址会相应增加。具体加多少占位符看,寄存器中间保留的位数。占位符不仅限于32位,也可以是16位,如下所示,注意占位符的位置不同,占去的位不同。
typedef struct
{
__IO uint16_t CRH;//低16位
uint16_t RESERVED0;//高16位
__IO uint16_t CRL;
uint16_t RESERVED1;
__IO uint16_t PRLH;
uint16_t RESERVED2;
__IO uint16_t PRLL;
uint16_t RESERVED3;
__IO uint16_t DIVH;
uint16_t RESERVED4;
__IO uint16_t DIVL;
uint16_t RESERVED5;
__IO uint16_t CNTH;
uint16_t RESERVED6;
__IO uint16_t CNTL;
uint16_t RESERVED7;
__IO uint16_t ALRH;
uint16_t RESERVED8;
__IO uint16_t ALRL;
uint16_t RESERVED9;
} mRTC_Type;