STM32学习笔记(3)-点亮LED(使用结构体访问寄存器)
仅供个人学习,记录学习STM32中的要点,加深对知识的巩固
前言
上一节我们了解到如何使用寄存器来访问PA1-PA4。今天我们就换一种写法。 使用结构体访问寄存器。
一般我们定义结构体都会在头文件中去定义,然后再.c文件中使用它。
创建并编写头文件
创建一个main.h的文件
在main.c中包含头文件:
#include "main.h"
接着在新建的main.h文件中写如下代码:
标准头文件的写法
#ifndef _MAIN_H_
#define _MAIN_H_
#endif
解释:这段代码是一个头文件 main.h 的内容,主要作用是在编译时避免头文件被重复引用而导致的编译错误。
#ifndef 是一个条件编译指令,用于检查某个标识符是否已经被定义。如果该标识符已经被定义,则跳过 #ifndef 到 #endif 之间的所有代码。否则,将执行 #ifndef 到 #endif 之间的所有代码。
在这段代码中,_MAIN_H_
是一个预定义的标识符,用于避免 main.h 文件被重复引用。如果在编译过程中,main.h 文件已经被引用了,则_MAIN_H_
标识符已经被定义,#ifndef 条件不成立,因此整个文件都会被跳过。否则,#ifndef 条件成立,整个文件将被编译。
写入结构体
接着在里面写入结构体
typedef struct
{
unsigned int CR;
unsigned int CFGR;
unsigned int CIR;
unsigned int APB2RSTR;
unsigned int APB1RSTR;
unsigned int AHBENR;
unsigned int APB2ENR;
unsigned int APB1ENR;
unsigned int BDCR;
unsigned int CSR;
unsigned int AHBRSTR;
unsigned int CFGR2;
}RCC_Typedef;
解释: 这段代码定义了一个名为 RCC_Typedef 的结构体类型,用于表示 STM32 微控制器的 RCC 寄存器集合。该结构体包含了 STM32 微控制器 RCC 寄存器中的所有成员变量,方便对这些寄存器进行批量访问和操作。
具体来说,结构体中的每个成员变量对应一个 RCC 寄存器,成员变量的类型为无符号整型。这些成员变量的名称和类型与寄存器的名称和类型相对应,以便在代码中进行快速查找和访问。例如,CR 对应 RCC_CR 寄存器,CFGR 对应 RCC_CFGR 寄存器,以此类推。
使用结构体可以将多个变量打包在一起,方便进行逻辑组织和统一管理。在这段代码中,RCC_Typedef 结构体的成员变量按照 STM32 微控制器 RCC 寄存器的顺序排列,方便进行批量访问和操作。
同时编写下行代码
#define RCC ((RCC_Typedef *)0x40021000)
解释:这段代码定义了一个名为 RCC 的宏,用于将 STM32 微控制器的 RCC 寄存器集合映射到内存地址 0x40021000 处,以方便对这些寄存器进行访问和操作。 这样就可在main.c文件中进行访问了。
同理可以写GPIOA的代码
typedef struct
{
unsigned int CRL;
unsigned int CRH;
unsigned int IDR;
unsigned int ODR;
unsigned int BSRR;
unsigned int BRR;
unsigned int LCKR;
}GPIOA_Typedef;
同时编写下行代码
#define GPIOA ((GPIOA_Typedef *)0x40010800)
main.h完整代码如下:
#ifndef __MAIN_H__
#define __MAIN_H__
typedef struct
{
unsigned int CR;
unsigned int CFGR;
unsigned int CIR;
unsigned int APB2RSTR;
unsigned int APB1RSTR;
unsigned int AHBENR;
unsigned int APB2ENR;
unsigned int APB1ENR;
unsigned int BDCR;
unsigned int CSR;
unsigned int AHBRSTR;
unsigned int CFGR2;
}RCC_Typedef;
typedef struct
{
unsigned int CRL;
unsigned int CRH;
unsigned int IDR;
unsigned int ODR;
unsigned int BSRR;
unsigned int BRR;
unsigned int LCKR;
}GPIOA_Typedef;
#define RCC ((RCC_Typedef *)0x40021000)
#define GPIOA ((GPIOA_Typedef *)0x40010800)
#endif
main.c函数内容
当在头文件中写完以上内容,此时转到main.c文件中,
RCC -> APB2ENR |= (1 << 2);
GPIOA -> CRL &= 0xFFF0000F;
GPIOA -> CRL |= 0x00033330;
GPIOA -> ODR &= ~((1 << 1) | (1 << 2) | (1 << 3) | (1 << 4));
解释:
-
代码 RCC->APB2ENR |= (1 << 2) 用于启用 GPIOA 端口的时钟,具体来说是通过 RCC(APB2 Peripheral Clock Enable Register) 寄存器的第 2 位启用 GPIOA 端口的时钟,使其能够正常工作。
-
GPIOA -> CRL &= 0xFFF0000F;GPIOA -> CRL |= 0x00033330;用于使能GPIOA的PA1-PA4管脚。
-
代码 GPIOA->ODR &= ~((1 << 1) | (1 << 2) | (1 << 3) | (1 << 4)) 用于将 GPIOA 端口的前 4 个引脚的输出电平设置为低电平,具体来说是通过 GPIOA_ODR 寄存器的第 1 到 4 位将前 4 个引脚的输出电平设置为 0,保留其他位的状态。
之后的内容都可以通过指针来访问结构体成员来完成。
main.c完整代码如下:
#include "main.h"
void Delay(unsigned long nCount);
int main(void)
{
RCC->APB2ENR |= (1<<2);//RCCAPB2ENR |= (1<<2);
GPIOA->CRL &= 0xFFF0000F;//GPIOACRL &= 0xFFF0000F;
GPIOA->CRL |= 0x00033330;//GPIOACRL |= 0x00033330;
GPIOA->ODR &= ~((1<<1)|(1<<2)|(1<<3)|(1<<4)); //GPIOAODR &= ~((1<<1)|(1<<2)|(1<<3)|(1<<4));
while(1)
{
//点亮LED1~LED4 PA1~PA4输出高电平
GPIOA->ODR |= (1<<1)|(1<<2)|(1<<3)|(1<<4);//GPIOAODR |= (1<<1)|(1<<2)|(1<<3)|(1<<4);
Delay(0xffff);
//熄灭LED1~LED4 PA1~PA4输出低电平
GPIOA->ODR &= ~((1<<1)|(1<<2)|(1<<3)|(1<<4));//GPIOAODR &= ~((1<<1)|(1<<2)|(1<<3)|(1<<4));
Delay(0xffff);
}
}
void Delay(unsigned long nCount)
{
while(nCount--)
{
}
}
至此,使用结构体访问寄存器循环点亮LED的目标就完成了。