目录
寄存器定义
百度百科对寄存器的解释如下:
寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。其实寄存器就是一种常用的时序逻辑电路,但这种时序逻辑电路只包含存储电路。寄存器的存储电路是由锁存器或触发器构成的,因为一个锁存器或触发器能存储1位二进制数,所以由N个锁存器或触发器可以构成N位寄存器。寄存器是中央处理器内的组成部分。寄存器是有限存储容量的高速存储部件,它们可用来暂存指令、数据和位址
可以理解为寄存器就是存放东西的区域,理解成一个变量。
寄存器的地址在手册中没有完全列出,需要计算偏移量
寄存器地址
例如:GPIOA的地址已经在手册中给出,这是GPIOA的基址,从0x4001 0800到0x4001 0BFF这个区域都是关于GPIOA的寄存器
而手册中端口配置低寄存器CRL的偏移地址是0x00
所以可以得到,GPIOA_CRL的地址是0x4001 0800 + 0x00,前者是GPIOA的起始地址
同样的,端口配置高寄存器的CRH的偏移地址是0x04
啧GPIOA_CRH的地址是0x4001 0800 + 0x04
在知道地址之后,想要读写这个地址的数据,就需要用将地址转化为指针,然后用*解引用,取空间操作,找到这个地址所指向的空间,因为stm32是32位的寄存器,所以这是unsigned int类型的指针
在代码中是这样的:
*(unsigned int*)(0x40010800+0x00)|=0x11; //PA0推挽输出,速度10M
0x40010800+0x00得到CRL的地址,用(unsigned int*)进行类型转换,把int型的地址转换成指针类型,再用*取空间就可以对其进行读写操作
寄存器映射
STM32的寄存器非常多,为了方便写代码,以及提高代码的可读性,可以进行寄存器映射。在头文件stm32f10x.h头文件中已经做了寄存器映射
这相当于给地址起个名字
如果不进行寄存器映射,那么点灯代码将会是这样:
*(unsigned int*)(0x40021000+0x18)|= 1<<2; //使能GPIOA
*(unsigned int*)(0x40010800+0x00)&=0xFFFFFF00; //清空PA0的端口位
*(unsigned int*)(0x40010800+0x00)|=0x11; //PA0推挽输出,速度10M
*(unsigned int*)(0x40010800+0x0C)&=~(3<<0); //输出低电平
做了寄存器映射之后,点灯代码会是这样:
RCC->APB2ENR |= 1<<2; //使能GPIOA的时钟
GPIOA->CRL &= 0xFFFFFFF0; //清空控制PA0的端口位
GPIOA->CRL |= 0x00000001; //配置PA0为通用推挽输出,速度为10M
GPIOA->ODR |=(0<<0); //配置PA0输出为低电平
当然,如果对比起库函数,寄存器的代码可读性依旧差很多
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //PIN0/1/2/3
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出速度最大50Mhz
GPIO_Init(GPIOA , &GPIO_InitStructure);
GPIO_ResetBits(GPIOA , GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); //将PIN0/1/2/3置低电平