使用寄存器控制LED灯的步骤:
1. 配置时钟——单独针对gpio组外设的时钟
2. 配置gpio工作模式
3. 控制引脚输出电压
4. 用一个死循环保持状态
在此之前,需要明确一下寄存器是什么?
寄存器是CPU中的一部分,是一些容量小但是访问速度极快的存储单元,比内存快
寄存器在CPU中有自己的名字和固定位置的,通过硬件电路直接固定在CPU里面,当输入一个寄存器的名字,译码电路读取到之后,就会转到名字对应的寄存器电路
当然有些架构是会把寄存器映射到内存地址空间,为了方便访问,但CPU内部还是通过硬件电路进行访问的
而我们所用到的寄存器则和CPU中的寄存器又有所区别,CPU中的寄存器是通用寄存器。而我们所使用的寄存器是特殊功能寄存器以及映射方式
#include "stm32f10x.h" // Device header int main(void) { // 1. 配置时钟——单独针对gpioA组外设的时钟 /* 在启动文件startup中的; Reset handler下已经定义了启动后的默认操作 先系统初始化,然后才是main函数的运行,在系统初始化中已经初始化过系统时钟了 为什么还要再次开启时钟呢? 这次的开启时钟是针对单独的一个片上外设,stm32为了高效,低功耗;因此只有当你使用到这个 片上外设的时候,需要手动开启该片上外设对应的时钟开关,这样做是为了低功耗和高效管理 */ /* 0x40021000:RCC复位和时钟控制外设的起始地址 0x18:APB2外设时钟使能寄存器的偏移地址 两个地址相加就能得到APB2外设时钟使能寄存器的地址了 将其赋值4也就是0000 0000 0000 0000 0000 0000 0000 0100,对应就是GPIOA开启 (uint32_t *),引用<stdint.h>,是因为在我们看来,前面的加法就是寄存器地址, 但是编译器不会,他只看到了一个用十六进制来表示的整数,因此需要加上(uint32_t *)转为指针类型 最前面的*,表示对前面的指针地址解引用取内容,然后赋值更改其内容 */ // *(uint32_t *)(0x40021000 + 0x18) = 4; // RCC->APB2ENR = 4; // 第一次进化:使用宏定义寄存器 // RCC->APB2ENR |= 0x04; // 第二次进化:使用按位与、或操作寄存器中每一位 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 第三次进化:使用宏定义按位与、或操作寄存器中每一位 // 2. 配置gpioA工作模式 /* * 和 (uint32_t *)上面已经解释过了,以后不在注释 0x40010800是GPIOA的起始地址, 为什么没有加呢,因为端口配置低寄存器的偏移地址是0x00,也就是不偏移 51,是因为要对PA1,PA0进行初始化,而每个引脚分两部分CNFx工作模式MODEx输入输出模式 每个部分占2个bit,MODEx = 11(输出模式,50MHz) CNFx = 00(通用推挽输出模式) 因此给寄存器赋值就是0000 0000 0000 0000 0000 0000 0011 0011,就是51 */ // *(uint32_t *)(0x40010800 + 0x00) = 51; // GPIOA -> CRL = 51; // GPIOA -> CRL |= 0x33; GPIOA -> CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1); // ~(1100) = 0011 &=保证2/3位一定是0,0/1位则不变 GPIOA -> CRL |= (GPIO_CRL_MODE0 | GPIO_CRL_MODE1); // 0011 |=保证0/1位一定是1,2/3位不变 // 3. PA0和PA1输出高电平点亮 /* 还是只用到了最后的两位,置一之后就是3 */ // *(uint32_t *)(0x40010800 + 0x0C) = 3; // GPIOA->ODR = 3; // GPIOA->ODR |= 0x03; GPIOA->ODR |= (GPIO_ODR_ODR0 | GPIO_ODR_ODR1); // 4. 用一个死循环保持状态 while(1) {} }
左右位运算:<<左移 >>右移
按位运算:|按位或 &按位与 ~按位取反
按位或:有1则1,全0为0
按位与:有0则0,全1为1
按位取反:1变0,0变1
其实有一个一直没有搞明白的地方,就是|和&的作用?
想置1用 |=,想清0用 &= 和 ~,判断某位用 &。
浙公网安备 33010602011771号