采用结构体方式
在上节课我们只采用了GPIOC,但是有的型号引脚比较多,可以分成A、B、C、D…G组。那么根据下图的写法我们要重复写好几次。大家有发现除了A、B、C、D…G不一样之外,别的基本一样。也就是说GPIOA有7个寄存器控制,GPIOB也是有7个寄存器控制,由此联想到采用结构体。举个例子,一个小区里有A幢楼、B幢楼、、、但是每幢楼的结构层数都一样。
定义:
typedef unsigned int uint32_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;
也就是说把32位的寄存器CRL、CRH、、LCKR组成一个整体,名字叫做 GPIO_TypeDef,在内存中是连续排列的,假设CRL的地址为0x00,那么CRH的地址为0x04,依次排列下去。
再定义:
#define GPIOC ((GPIO_TypeDef*)GPIOC_BASE)
看下(GPIO_TypeDef*)GPIOC_BASE 这条语句,意思是将GPIOC_BASE转换成GPIO_TypeDef类型的地址。上节课中可知GPIOC_BASE的值为0x4001 1000。那么以该地址为基础(0x4001 0C00),CRL的地址为0x4001 0C00,CRH地址为0x4001 1004…依次下去。完全跟我们以前的方式符合!
下图中的“GPIO_CRH”的地址即为0x40011004,那么怎么用结构体表示呢?
再回到这条语句
#define GPIOC ((GPIO_TypeDef*)GPIOC_BASE)
可以用GPIOC->CRH的方式来表示GPIOC_CRH。由于程序小,看不出来优势在哪,以后慢慢体会。那么在main函数中都可以用这种方式来代替。时钟也类似!
写法跟配置GPIO一样,可以写相应的结构体!
typedef struct
{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
}RCC_TypeDef;
综上所述,在头文件中进行相应的程序修改,如下:
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
/* AHB总线基地址 */
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
/*GPIOC外设基地址*/
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
/*RCC外设基地址*/
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
typedef unsigned int uint32_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;
typedef struct
{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
}RCC_TypeDef;
#define GPIOC ((GPIO_TypeDef*)GPIOC_BASE)
#define RCC ((RCC_TypeDef*)RCC_BASE)
在main函数中,进行如下修改:
#include "stm32f10x.h"
int main(void)
{
RCC->APB2ENR |= 1<<4;
GPIOC->CRH &=~(0x0F<<(4*5));
GPIOC->CRH |=(1<<(4*5));
GPIOC->ODR &=~(1<<13);
while(1);
}
void SystemInit()
{
}
经测试完全没有问题!