实验概述:
用配置寄存器的方式,开关一个LED灯,
只用标准库中提供的启动文件,
1> 建立工程
出现错误:导入文件类型错误
keil5编译中出现的错误(6):FCARM - Output Name not specified, please check ‘Options for Target - Utilities
错误原因:文件类型错误;
解决办法:
文件上右击,选择Options for…, 更改文件类型;
2> 向寄存器写数据
向寄存器地址,写数据
// 硬件电路 PB5 - LED0; 1:熄灭, 0点亮
// 根据内存地址映射,得到寄存器地址
#define RCC_APB2ENR_addr 0x40021018
#define GPIOB_CRL_addr 0X40010C00
#define GPIOB_ODR_addr 0X40010C0C
int main(void)
{
*((unsigned int *)RCC_APB2ENR_addr) = 0x00000008; // 打开 GPIOB 端口时钟
*((unsigned int *)GPIOB_CRL_addr) = 0x00300000; // PB5 配置为推挽输出
*((unsigned int *)GPIOB_ODR_addr) = 0x00000020; // 关闭 LED0;
//*((unsigned int *)GPIOB_ODR_addr) = 0x00000000; // 打开 LED0;
while (1) /* Stop! */;
}
}
C语言指针应用
面试题:
用C语言向内存地址【0x40021018】写入0X8?
方法1:
unsigned int *p = (unsigned int *)0x40021018;
*p = 0x8;
方法2:
*(unsigned int *)0x40021018 = 0x8;
3> 宏定义-封装寄存器
// 硬件电路 PB5 - LED0; 1:熄灭, 0点亮
#define RCC_APB2ENR_addr 0x40021018
#define GPIOB_CRL_addr 0X40010C00
#define GPIOB_ODR_addr 0X40010C0C
#define RCC_APB2ENR (*(unsigned int *)RCC_APB2ENR_addr)
#define GPIOB_CRL (*(unsigned int *)GPIOB_CRL_addr)
#define GPIOB_ODR (*(unsigned int *)GPIOB_ODR_addr)
int main(void)
{
RCC_APB2ENR = 0x00000008; // 打开 GPIOB 端口时钟
GPIOB_CRL = 0x00300000; // PB5 配置为推挽输出
//GPIOB_ODR = 0x00000020; // 关闭 LED0;
GPIOB_ODR = 0x00000000; // 打开 LED0;
while (1) /* Stop! */;
}
通过封装,让代码更清爽
发现上电并不需要配置时钟树,这是因为上电默认使用内部高速时钟HSI;
4> 结构体-封装寄存器
(*(unsigned int *)RCC_APB2ENR_addr)
问题:
通过指针变量访问寄存器,有个麻烦,STM32103内部寄存器有大几百个,
有没有好的方法把这些寄存器组织起来,方便使用呢?
解决:
用结构体,数组不行吗?
4.1> 结构体类型定义
地址偏移Offset是4Byte,非常有规律,
用结构体封装 GPIO模块的寄存器;
struct GPIOx {
unsigned int GPIO_CRL;
unsigned int GPIO_CRH;
unsigned int GPIO_IDR;
unsigned int GPIO_ODR;
unsigned int GPIO_BSRR;
unsigned int GPIO_LCKR;
};
// 结构体类型名:GPIOx;
// 结构体成员:GPIO_CRL,GPIO_CRH, GPIO_IDR,
// GPIO_ODR,GPIO_BSRR, GPIO_LCKR
//取别名,使类型简洁点
typedef struct GPIOx {
unsigned int GPIO_CRL;
unsigned int GPIO_CRH;
unsigned int GPIO_IDR;
unsigned int GPIO_ODR;
unsigned int GPIO_BSRR;
unsigned int GPIO_LCKR;
} GPIO_t;
4.2> 结构体指针变量
用结构体指针变量与寄存器地址绑定
// 硬件电路 PB5 - LED0; 1:熄灭, 0点亮
#define RCC_APB2ENR_addr 0x40021018
#define RCC_APB2ENR (*(unsigned int *)RCC_APB2ENR_addr)
typedef struct GPIOx {
unsigned int GPIO_CRL;
unsigned int GPIO_CRH;
unsigned int GPIO_IDR;
unsigned int GPIO_ODR;
unsigned int GPIO_BSRR;
unsigned int GPIO_LCKR;
} GPIO_t;
int main(void)
{
GPIO_t *p = (GPIO_t *)0x40010C00; // GPIOB
RCC_APB2ENR = 0x00000008; // 打开 GPIOB 端口时钟
p->GPIO_CRL = 0x00300000; // PB5 配置为推挽输出
//p->GPIO_ODR = 0x00000020; // 关闭 LED0;
p->GPIO_ODR = 0x00000000; // 打开 LED0;
while (1) /* Stop! */;
}
4.3> 优化程序
// 结构体指针变量的初始化:
GPIO_t *pGPIOA = (GPIO_t *)0x40010800;
// ||等价
#define GPIOA ((GPIO_t *)0x40010800)
#define RCC_APB2ENR_addr 0x40021018
#define RCC_APB2ENR (*(unsigned int *)RCC_APB2ENR_addr)
typedef struct GPIOx {
unsigned int GPIO_CRL;
unsigned int GPIO_CRH;
unsigned int GPIO_IDR;
unsigned int GPIO_ODR;
unsigned int GPIO_BSRR;
unsigned int GPIO_LCKR;
} GPIO_t;
// 把端口寄存器定义为全局变量,方便使用
#define GPIOA ((GPIO_t *)0x40010800)
#define GPIOB ((GPIO_t *)0x40010C00)
#define GPIOC ((GPIO_t *)0x40011000)
#define GPIOD ((GPIO_t *)0x40011400)
#define GPIOE ((GPIO_t *)0x40011800)
#define GPIOF ((GPIO_t *)0x40011C00)
int main(void)
{
RCC_APB2ENR = 0x00000008; // 打开 GPIOB 端口时钟
GPIOB->GPIO_CRL = 0x00300000; // PB5 配置为推挽输出
// GPIOB->GPIO_ODR = 0x00000020; // 关闭 LED0;
GPIOB->GPIO_ODR = 0x00000000; // 打开 LED0;
while (1) /* Stop! */;
}
这样一看,用结构体把寄存器组织起来,
我们就能很方便的访问GPIOA,GPIOB…BPIOF端口的寄存器;