STM32学习笔记(3)-点亮LED(使用结构体访问寄存器)

仅供个人学习,记录学习STM32中的要点,加深对知识的巩固

前言

上一节我们了解到如何使用寄存器来访问PA1-PA4。今天我们就换一种写法。 使用结构体访问寄存器。

一般我们定义结构体都会在头文件中去定义,然后再.c文件中使用它。

创建并编写头文件

创建一个main.h的文件

alt

在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));

解释:

  1. 代码 RCC->APB2ENR |= (1 << 2) 用于启用 GPIOA 端口的时钟,具体来说是通过 RCC(APB2 Peripheral Clock Enable Register) 寄存器的第 2 位启用 GPIOA 端口的时钟,使其能够正常工作。

  2. GPIOA -> CRL &= 0xFFF0000F;GPIOA -> CRL |= 0x00033330;用于使能GPIOA的PA1-PA4管脚。

  3. 代码 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的目标就完成了。

全部评论
一直都学不会结构体访问寄存器这里
点赞 回复 分享
发布于 2023-03-05 10:06 甘肃
哇,谢谢大佬分享,很实用
点赞 回复 分享
发布于 2023-03-05 09:42 湖南

相关推荐

自由水:这HR已经很好了,多的是已读不回和不读了
点赞 评论 收藏
分享
评论
1
3
分享

创作者周榜

更多
牛客网
牛客企业服务