因为unit32_t是32位的,也就是四个字节,CRL就从GPIOB_BASE的地址开始占四个字节的位置,CRH存放的地址就自动+4个字节了,正好是偏移量,就免去了每次都用#define去根据基地址去偏移了
//基地址+偏移地址 就能得到寄存器的地址
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef struct{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
}GPIO_TypeDef;
//强制类型转换 表示在代码中使用GPIOB时,会被替换为指向GPIOB外设的指针。
#define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)
这段程序的目的是为了方便地访问特定微控制器的 GPIOB 外设
下面是这段程序的工作原理:
-
首先,通过宏定义的方式,确定了 GPIOB 外设寄存器在内存中的基地址。具体的计算方法是通过对应的外设基地址(如 APB2PERIPH_BASE)加上偏移地址(如 0x0C00)来得到 GPIOB 的基地址。
-
接下来,定义了一个名为
GPIO_TypeDef
的结构体,其中包含了 GPIO 外设的不同寄存器,如 CRL、CRH、IDR、ODR、BSRR、BRR 和 LCKR。 -
最后,通过宏定义
#define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)
,将 GPIOB 外设基地址强制类型转换为GPIO_TypeDef*
类型的指针,也就是将其解释为指向GPIO_TypeDef
结构体的指针。
通过这样的宏定义,以后在代码中使用 GPIOB
时,就可以直接通过指针访问 GPIOB 外设的寄存器,从而方便地对 GPIOB 进行配置和操作。例如:
GPIOB->CRL = 0x00000001; // 对 GPIOB 的 CRL 寄存器进行配置
GPIOB->ODR |= (1 << 5); // 设置 GPIOB 的第 5 位输出
为什么用typedef而不用define?
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
在 C/C++ 中,可以使用 #define
来创建简单的别名,但是使用 typedef
更适合创建类型别名。虽然两者都能达到类似的效果,但 typedef
在某些情况下更灵活且更安全。
使用 #define
创建别名会直接进行文本替换,这可能导致一些潜在的问题。例如:
#define A int
A x, y; // 替换后:int x, y;
这样会将 A完全替换为 int
,但是有时候可能意外地影响到其他代码,尤其是在代码规模较大的情况下。
而 typedef
则会在编译器级别创建类型别名,它更安全且更易于维护。例如:
typedef int A;
A x, y; // 此时 A 是 int 的别名
使用 typedef
,编译器会将 A视为 int
的别名,这样做不会引起不必要的替换或潜在错误。
因此,虽然 #define
可以用于创建别名,但建议在 C/C++ 中使用 typedef
来定义类型别名,因为它更安全、更符合语义,而且更容易维护。
结构体:
typedef struct{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
}GPIO_TypeDef;
这是一个典型的 C typedef
结构体定义,用于表示特定的 GPIO(General Purpose Input/Output)外设寄存器映射。
让我来解释一下这段代码:
-
typedef
: 这是 C中用于定义类型别名的关键字。 -
struct
: 这是 C 中用于定义结构体的关键字。 -
GPIO_TypeDef
: 这是结构体的名称,代表这个结构体类型的别名。 -
大括号
{}
中是结构体的成员,每个成员都是一个无符号32位整数(uint32_t
)。这些成员代表了 GPIO 外设的不同寄存器,其中包括:CRL
:GPIO控制寄存器低位(Control Register Low)CRH
:GPIO控制寄存器高位(Control Register High)IDR
:GPIO输入数据寄存器(Input Data Register)ODR
:GPIO输出数据寄存器(Output Data Register)BSRR
:GPIO位设置/复位寄存器(Bit Set/Reset Register)BRR
:GPIO位复位寄存器(Bit Reset RegisterLCKR
:GPIO锁定寄存器(Lock Register)
这样的结构体定义可以很方便地访问和控制特定微控制器中的GPIO外设寄存器,使得对GPIO的配置和操作更加简洁和可读。例如,可以通过访问 GPIO_TypeDef
结构体的成员来控制和读取 GPIO 的不同功能和状态。
#define GPIOB((GPIO_TypeDef*)GPIOB_BASE)
((GPIO_TypeDef*)GPIOB_BASE)
这部分代码是一个类型转换(type-casting)的表达式。让我解释一下:
-
GPIO_TypeDef
: 这是一个类型名称,是在特定的微控制器或嵌入式系统中定义的结构体类型,用于表示GPIO外设的寄存器映射。 -
GPIOB_BASE
: 这是一个宏或符号常量,表示GPIOB外设在内存中的基地址。基地址是指外设寄存器的起始地址,通过访问这个地址,可以读写控制外设的寄存器。 -
(GPIO_TypeDef*)
: 这是C/C++中的类型转换语法。通过将GPIOB_BASE
强制转换为GPIO_TypeDef
类型的指针,可以将该地址解释为GPIO外设的寄存器映射结构。
综合来说,((GPIO_TypeDef*)GPIOB_BASE)
这个表达式的作用是将GPIOB外设的基地址转换为指向该外设的结构体指针,使得程序可以通过指针访问该外设的寄存器,并进行相关的控制和配置。