STM32寄存器&HAL库方式的LED程序

STM32寄存器&HAL库方式的LED程序

1.寄存器相关知识

1.1 什么是寄存器

寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。

简单来说,寄存器就是存放东西的东西。从名字来看,跟火车站寄存行李的地方好像是有关系的。只不过火车站行李寄存处,存放的行李;寄存器可能存放的是指令、数据或地址。

存放数据的寄存器是最好理解的,如果你需要读取一个数据,直接到这个寄存器所在的地方来问问他,数据是多少就行了。问寄存器这个动作,叫做访问寄存器。不同的数据会存放在不同的寄存器,例如引脚PA2与PB8的高低电平数据(1或0)肯定放在不同的寄存器里,那么怎么区分不同的寄存器呢?通过地址,不同的寄存器有不同的地址,就像一个人的行李寄存处在101号货柜,另一个人的行李寄存处在233号货柜。

指令、地址寄存器与数据寄存器类似,里边存放的都是0和1,毕竟单片机也只认识机器码,机器码都是0或1,只是特别的规定下,数据寄存器里面存放的0和1表示数据,指令寄存器里存放的表示指令。

1.2 通过数据手册找到某个寄存器的地址

手册中没有直接给出所有的寄存器的地址,需要我们稍加计算。STM32给不同的寄存器分配了不同的地址,有点像划分了片区。在《STM32中文参考手册_V10》的第28页,有不同寄存器的地址范围。
现在,假如我们想读取PB3引脚的电平,该怎么找到相关的寄存器?

1)找到GPIOB的基地址

也就是找到GPIOB的小区。结论是,所有GPIOB相关的寄存器,都住在0x4001 0C00到0x4001 0FFF范围内。

2)找到端口输入寄存器的地址偏移

找到存储数据的单元,结论是0x4001 0C00+8 = 0x4001 0C08

在这里插入图片描述

3)找到具体的屋子

PB3的数据位于从右往左数第4个。

在这里插入图片描述

经过这三步查找,我们可以做出以下结论:
PB3的输入数据位于0x4001 0C08这个地址上,这个地址上存放数据的右起第4个位就是PB3引脚对应的高低电平。

2.直接操作寄存器来点亮LED

本次实验采用GPIOA、B、C三个端口。该三个端口都属于APB2总线

2.1 首先要配置时钟使能。

为什么配置时钟?为了省电,默认的时钟都是关闭的。配置STM32的任何资源前,都必须首先使能时钟。
  配置哪个时钟?
时钟的信息在参考手册里边,参考手册十分巨大,不用通读,就像一个字典,需要什么查什么。
  参考手册,搜索"时钟",在表1里可以看到。
时钟控制名字叫做RCC,属于AHB总线。GPIOA、B、C属于APB2。

在这里插入图片描述

下图系统结构可以看到时钟的从属关系,此图位于手册P25页,十分重要。可以看出AHB总线包含RCC时钟控制,GPIO是属于APB2的。

在这里插入图片描述

我们已经知道,GPIO端口B的地址从0x4001 0C00开始。接下来只寻找时钟使能寄存器的地址:
  复位和时钟控制RCC的地址从0x4002 1000开始;
  可以在6.3.7小节找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。
  看手册RCC_APB2ENR,位2是IOPAEN,位3是IOPBEN,位4是IOPCEN,是IO端口A,B,C时钟使能,就是我们想要的。

使能对应端口时钟

//----------------APB2使能时钟寄存器 ---------------------
#define RCC_APB2ENR		*((unsigned volatile int*)0x40021018)

RCC_APB2ENR|=1<<2|1<<3|1<<4;			//APB2-GPIOA、GPIOB、GPIOC外设时钟使能	

2.2 配置为通用输出

既然叫做IO,那么肯定就是可以输入,可以输出,到底是输入还是输出呢?
控制LED需要输出高电平或是低电平,所以需要配置为输出。
由于STM32的每个IO都需要4个位来配置,所以一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用端口配置低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用端口配置高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。

我们需要使用A5、B9、C13三个引脚。其中A5属于端口配置低寄存器偏移地址为0x00,B9,C14属于端口配置高寄存器偏移地址为0x04。

在这里插入图片描述

在这里插入图片描述

先看标题GPIOx,表示不管是PA,PB还是PE,都能用。
A的偏移地址是0x00,意思是在基地址的基础上再加0x00,GPIOA就是0x40010800。同理,B,C的偏移地址是0x04,意思是在基地址的基础上再加0x04,所以,对于GPIOB和GPIOC来说就是0x40011004。

//----------------GPIOA配置寄存器 -----------------------
#define GPIOA_CRL		*((unsigned volatile int*)0x40010800)
//----------------GPIOB配置寄存器 -----------------------
#define GPIOB_CRL		*((unsigned volatile int*)0x40010C04)
//----------------GPIOC配置寄存器 -----------------------
#define GPIOC_CRH		*((unsigned volatile int*)0x40011004)

找到需要操作的寄存器后,把它配置为通用输出。
复位值是0x4444 4444,并不是0x0000 0000。所谓的复位值,就是指如果没有操作这个寄存器时,寄存器存放的默认值。复位值按位拆分0x4 = 0b0100,0x表示16进制,0b表示二进制,也就是默认CNF 01,MODE 00,是浮空输入。
我们需要的是输出高低电平,所以要设置为输出。输出模式又有好几种输出:

在这里插入图片描述

  • 推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。

  • 开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。

开漏是需要外接上拉电阻才可以输出高电平的,这里并不适合。所以需要设置为推挽输出。
功能是否是复用呢?复用的意思是有别的功能在这个脚上,比如USB,CAN,串口等,所以这些个脚就可能有多个功能。这里我们用不到。

GPIOA_CRL&=0xFF0FFFFF;		//设置位 清零	
GPIOA_CRL|=0x00200000;		//PA5推挽输出,把第23、22、21、20位变为0010

GPIOB_CRL&=0xFFFFFF0F;		//设置位 清零	
GPIOB_CRL|=0x00000020;		//PB9推挽输出,把第7、6、5、4变为0010

GPIOC_CRH&=0xFF0FFFFF;		//设置位 清零	
GPIOC_CRH|=0x00200000;		//PC13推挽输出,把第23、22、21、20变为0010

2.3 点亮LED需要输出低电平

在单片机的编程中,要想做某件事,必须寻找相应的寄存器。在8.2.4小节,可以找到端口输出数据寄存器,就是我们需要的。我们需要输出0。但是中文手册有一个小小的BUG,0x0C写成了0Ch,可以参考英文原版。得知地址的偏移是0x0C,所以这个数据寄存器的地址就是0x4001 0C0C,把第8位写为0就行。默认就是0。

在这里插入图片描述

在这里插入图片描述

移位操作:

#define	GPIOA_ORD	*((unsigned volatile int*)0x4001080C)
GPIOA_ORD|=1<<5;			//设置初始灯为亮

3.C语言实现流水灯

3.1 流水灯原理

本次采用三个灯实现,亮灯状态用1表示,灭灯状态用0表示。
初始状态为0 0 0,
状态一为1 0 0
状态二为0 1 0
状态三为0 0 1
状态三结束后继续进入状态一,一直循环达到流水灯效果。

代码如下:

#include "stm32f10x.h"
//----------------APB2使能时钟寄存器 ---------------------
#define RCC_APB2ENR		*((unsigned volatile int*)0x40021018)
//----------------GPIOA配置寄存器 -----------------------
#define GPIOA_CRL		*((unsigned volatile int*)0x40010800)
#define	GPIOA_ODR		*((unsigned volatile int*)0x4001080C)
//----------------GPIOB配置寄存器 -----------------------
#define GPIOB_CRL		*((unsigned volatile int*)0x40010C04)
#define	GPIOB_ODR		*((unsigned volatile int*)0x40010C0C)
//----------------GPIOC配置寄存器 -----------------------
#define GPIOC_CRH		*((unsigned volatile int*)0x40011004)
#define	GPIOC_ODR		*((unsigned volatile int*)0x4001100C)


//延时函数
 void Delay()
 {
   u32 i=0;
   for(;i<5000000;i++);
 }
 int main(void)
 {	
	RCC_APB2ENR|=1<<2|1<<3|1<<4;			//APB2-GPIOA、GPIOB、GPIOC外设时钟使能	
	

	GPIOA_CRL&=0xFF0FFFFF;		//设置位 清零	
	GPIOA_CRL|=0x00200000;		//PA5推挽输出
	GPIOA_ODR&=~(1<<5);			//设置初始灯为灭
	
	GPIOB_CRL&=0xFFFFFF0F;		//设置位 清零	
	GPIOB_CRL|=0x00000020;		//PB9推挽输出
	GPIOB_ODR&=~(1<<9);			//设置初始灯为灭
	 
	GPIOC_CRH&=0xFF0FFFFF;		//设置位 清零	
	GPIOC_CRH|=0x00200000;		//PC13推挽输出
	GPIOC_ODR&=~(1<<13);			//设置初始灯为灭		


	while(1){
		//A灯
		GPIOA_ODR&=~(1<<5);		//PA5低电平,因为是置0,所以用按位与
	 	Delay();
		GPIOA_ODR|=1<<5;		//PA5高电平

		
		//B灯
		GPIOB_ODR&=~(1<<9);		//PB9低电平,因为是置0,所以用按位与
	 	Delay();
		GPIOB_ODR|=1<<9;		//PB9高电平


		
		
		//C灯
		GPIOC_ODR&=~(1<<13);		//PC13低电平,因为是置0,所以用按位与
	 	Delay();
		GPIOC_ODR|=1<<13;		//PC13高电平		

		}

}

在这里插入图片描述

运行结果:

在这里插入图片描述

4.STM32CubeMX生成代码使用HAL库点亮流水灯

4.1.安装STM32CubeMX

官网下载

在这里插入图片描述

点击next

在这里插入图片描述

勾选第一个即可,第二个选项是是否同意ST公司收集你的个人使用信息等

在这里插入图片描述

点击"I accept the terms of this license agreement",接着选择Next

选择安装位置,默认位置是安装在C盘中(注意:安装位置不要出现中文)

在这里插入图片描述

点击确定

在这里插入图片描述

直接点NEXT,其他不用设置 之后开始安装

在这里插入图片描述

安装完成,点Done退出

在这里插入图片描述

4.2.安装HAL库

STM32 HAL固件库是Hardware Abstraction Layer的缩写,中文名称是:硬件抽象层。HAL库是ST公司为STM32的MCU最新推出的抽象层嵌入式软件,为更方便的实现跨STM32产品的最大可移植性。HAL库的推出,可以说ST也慢慢的抛弃了原来的标准固件库,这也使得很多老用户不满。但是HAL库推出的同时,也加入了很多第三方的中间件,有RTOS,USB,TCP / IP和图形等等。
和标准库对比起来,STM32的HAL库更加的抽象,ST最终的目的是要实现在STM32系列MCU之间无缝移植,甚至在其他MCU也能实现快速移植。
并且从16年开始,ST公司就逐渐停止了对标准固件库的更新,转而倾向于HAL固件库和 Low-layer底层库的更新,停止标准库更新,也就表示了以后使用STM32CubeMX配置HAL/LL库是主流配置环境。

打开安装好的STMCubeMX
在这里插入图片描述

**点击HELP->Manage embedded software packages **

在这里插入图片描述

会跳出来一个选择型号界面 勾选上你要安装的HAL库, 点击“Install Now” 直到安装成功。 如下图:
在这里插入图片描述

4.3.新建项目

回到STMCubeMX的主界面,创建新项目:

在这里插入图片描述

在part name里选择自己的芯片,点击信息栏中的具体芯片信息选中,点击start project:

在这里插入图片描述

在这里插入图片描述

点击system core,进入SYS,在debug下选择serial wire:

在这里插入图片描述

配置时钟,进入上面的rcc,有两个时钟,一个是hse和lse,我们要用是GPIO接口,而这些接口都在APB2里:

在这里插入图片描述

在这里插入图片描述

将hse那里设为Crystal/Ceramic Resonator:

在这里插入图片描述

接下来就是点击相应的引脚设置输出寄存器了,就是output那一项,一共选了三个,是PA5,PB9,PC13:

在这里插入图片描述

点击project manager,配置好自己的路径和项目名,然后IDE那项改为MDK-ARM:

在这里插入图片描述

进入 code generate界面,选择生成初始化.c/.h文件,后面点击generate code,选择open project,然后就到KEIL5了:

在这里插入图片描述

在这里插入图片描述

4.4.keil仿真调试

打开.uvprojx文件(或者在上一步选择open project)

在这里插入图片描述

打开main.c文件,滑倒主函数那一部分:

在这里插入图片描述

将下面代码放入主函数中(替代里面的内容)

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);//PA5亮灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);//PC13熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);//PA5熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);//PB9亮灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);//PC13熄灯
HAL_Delay(1000);//延时1s		
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);//PA5熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);//PC13亮灯
HAL_Delay(1000);//延时1s

在这里插入图片描述

编译成功

在这里插入图片描述

运行结果:
在这里插入图片描述

4.6 观察GPIO端口的输出波形

Target界面中,选择跟正确的晶振大小,我使用的是8MHz的外部晶振。这个选项在软件仿真中起到很重要的作用,如果选择错误,那么波形一定是错误的,因为时间不准确。

Debug页的设置:

在这里插入图片描述

点击Debug,进入调试界面:

在这里插入图片描述

选择逻辑分析仪:

在这里插入图片描述

选择要观察的引脚:

点击Setup Logic Analyzer,按红叉旁边的按钮,添加,注意Display Type选择bit,在ShiftRight里输入引脚。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

相关设置

在这里插入图片描述

运行程序:

在这里插入图片描述

在这里插入图片描述

引脚为低电平的灯亮,高电平的灯不亮,高低电平转换周期(LED闪烁周期)为1s左右。

5.总结

1、学习和理解STM32F103系列芯片的地址映射和寄存器映射原理
2、了解GPIO端口的初始化设置三步骤(时钟配置、输入输出模式设置、最大速率设置)

3、学习和理解了如何用STM32CubeMX完成初始化,并在keil里完成编写。

6.参考

STM32寄存器的简介、地址查找,与直接操作寄存器

stm32cubeMX使用HAL库点亮LED流水灯

stm32寄存器实现流水灯

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值