STM32F103ZE点亮LED灯(二)
1. 宏定义优化
ST公司提前把每个外设寄存器的地址提前给我们用宏定义的方式定义在stm32f10x.h
文件中,我们只需要直接使用即可。
- 外设基址定义, 比如:
#define PERIPH_BASE ((uint32_t)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
- 寄存器结构体映射, 例如 GPIO:
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;
} GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
这样我们就可以写:
GPIOA->ODR |= (1 << 0); // 设置 PA0 输出高电平
- 位定义(宏), 比如 RCC 里的时钟使能位:
#define RCC_APB2ENR_IOPAEN ((uint32_t)0x00000004)
这样就不用记住 0x40021018
了。
- 因此,之前的点LED1代码可以修改为:
#include "stm32f10x.h"
int main(void)
{
RCC->APB2ENR = 4;
GPIOA->CRL = 3;
GPIOA->ODR = 0xfffe;
while (1)
{
}
}
2. 特定位修改优化
在STM32中一个寄存器是32位的,我们在编写代码的时候只是需要给某位或某几位赋值。由于STM32不支持位寻址,所以在前面的操作中,我们其实是修改了所有位。这是非常不合理的,也许其他位在其他地方有赋值,我们重新赋值势必会覆盖了其他值,带来的后果也是很严重的。
把代码进化为下面的形式:
#include "stm32f10x.h"
int main(void)
{
/* 开启GPIOA的时钟 第2位置1*/
RCC->APB2ENR |= (0x1 << 2);
/* GPIOA_CRL的最后4位置 0011 */
GPIOA->CRL &= ~(0x1 << 3);
GPIOA->CRL &= ~(0x1 << 2);
GPIOA->CRL |= 0x1 << 1;
GPIOA->CRL |= 0x1 << 0;
/* GPIOA_ODR的第0位置0 */
GPIOA->ODR &= ~(0x1 << 0);
while (1)
{
}
}
3. 使用宏定义中移位后的值
上次优化中,我们是给寄存器“或等”和“与等”了一些值,这些值都是通过相应的“移位”操作得到的。比如要操作第2位,就需要把0x1左移2位得到。我们需要查找手册才能知道要移位几。也是很不方便。
其实ST公司也把我们需要的移位后的值给提前计算好了,用宏定义的方式供我们使用。
比如前面的开启时钟,已经定义了好了这个值。正好就是1<<2
#define RCC_APB2ENR_IOPAEN ((uint32_t)0x00000004)
利用ST公司提前预定义的这些值,可以进一步进化代码为下面的形式:
#include "stm32f10x.h"
int main(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
GPIOA->CRL &= ~GPIO_CRL_CNF0_1;
GPIOA->CRL &= ~GPIO_CRL_CNF0_0;
GPIOA->CRL |= GPIO_CRL_MODE0_1;
GPIOA->CRL |= GPIO_CRL_MODE0_0;
GPIOA->ODR &= ~GPIO_ODR_ODR0;
while (1)
{
}
}
1 游客 2025-09-01 11:26 回复
1
1 游客 2025-09-01 12:04 回复
1
@@uQJVd 游客 2025-09-01 12:05 回复
1
1 游客 2025-09-01 12:07 回复
1
1 游客 2025-09-01 12:08 回复
1