STM32寄存器点亮流水灯的三种方法

上一章里说到了BSRR(位设置/清除寄存器)、BRR(位清除寄存器)、ODR(数据输出寄存器),这里再讲一下位带操作。

  • 点亮LED顺序
  • 开启时钟
  • 设置GPIO口推挽输出
  • 输出高低电平控制LED亮灭

注意:本人使用开发板LED为PB5和PE5。

一、位操作

首先看一段代码:

//这里我们开启GPIOB和GPIOE的时钟
RCC_APB2ENR=1<<3;		//GPIOB
RCC_APB2ENR=1<<6;		//GPIOE

仔细看一下,确定我们真的都打开了两个时钟吗?
并没有,我们只开启了GPIOE的时钟,什么原因,我们分析一下:

RCC_APB2ENR=1<<3;	//GPIOB
//1<<3,得到1000	这句代码结束,这个寄存器里存放的值是:
//0000 0000 0000 0000 0000 0000 0000 1000
RCC_APB2ENR=1<<6;	//GPIOE
//1<<6,得到1000000 这句代码结束,这个寄存器里存放的值是:
//0000 0000 0000 0000 0000 0000 0100 0000

可以看到,因为赋值运算符“=”,之前的赋值被刷新,所以只开启了GPIOE的时钟。
所以,我们用到了C语言中的按位与和按位或:

  • 按位与:& 有0则0
  • 按位或: | 有1则1
    可以看到,如果想给某位置0,而不影响其他位,则按位与;想给某位置1,而不影响其他位,则按位或。
    我们重新按位与赋值,得到以下正确代码:
RCC_APB2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
RCC_APB2ENR|=1<<6;			//APB2-GPIOE外设时钟使能

二、位设置/清除寄存器实现流水灯

//--------------APB2使能时钟寄存器------------------------
#define RCC_APB2ENR	*((unsigned volatile int*)0x40021018)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRL	*((unsigned volatile int*)0x40010C00)
#define	GPIOB_BSRR	*((unsigned volatile int*)0x40010C10)
#define GPIOB_BRR	*((unsigned volatile int*)0x40010C14)
//----------------GPIOE配置寄存器 ------------------------
#define GPIOE_CRL	*((unsigned volatile int*)0x40011800)
#define	GPIOE_BSRR	*((unsigned volatile int*)0x40011810)
#define GPIOE_BRR	*((unsigned volatile int*)0x40011814)
//-------------------简单的延时函数-----------------------
void delay(unsigned long xx)
{
	while(xx--);	//用自减的方式粗略延时
}
//------------------------主函数--------------------------
int main()
{
	RCC_APB2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
	RCC_APB2ENR|=1<<6;			//APB2-GPIOE外设时钟使能
	//这两行代码可以合为 RCC_APB2ENR|=1<<3|1<<6;
	GPIOB_CRL&=0xFF0FFFFF;		//设置位 清零	
	GPIOB_CRL|=0x00200000;		//PB5推挽输出
	GPIOB_BSRR|=1<<5;			//设置初始灯为灭
	
	GPIOE_CRL&=0xFF0FFFFF;		//设置位 清零
	GPIOE_CRL|=0x00200000;   	//PE5推挽输出
	GPIOE_BSRR|=1<<5;			//设置初始灯为灭	
	
	while(1)
	{
		
		GPIOB_BRR|=1<<5;		//PB5低电平
		delay(1000000);
		GPIOB_BSRR|=1<<5;		//PB5高电平
	 	delay(1000000);
		
		GPIOE_BRR|=1<<5;		//PE5低电平	
		delay(1000000);
		GPIOE_BSRR|=1<<5;		//PE5高电平
		delay(1000000);
	}
}

三、数据输出寄存器实现流水灯

//--------------APB2使能时钟寄存器------------------------
#define RCC_APB2ENR		*((unsigned volatile int*)0x40021018)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRL		*((unsigned volatile int*)0x40010C00)
#define	GPIOB_ODR		*((unsigned volatile int*)0x40010C0C)
//----------------GPIOE配置寄存器 ------------------------
#define GPIOE_CRL		*((unsigned volatile int*)0x40011800)
#define	GPIOE_ODR		*((unsigned volatile int*)0x4001180C)
//-------------------简单的延时函数-----------------------
void delay(unsigned long xx)
{
	while(xx--);	//用自减的方式粗略延时
}
//------------------------主函数--------------------------
int main()
{
	RCC_APB2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
	RCC_APB2ENR|=1<<6;			//APB2-GPIOE外设时钟使能
	//这两行代码可以合为 RCC_APB2ENR|=1<<3|1<<6;
	GPIOB_CRL&=0xFF0FFFFF;		//设置位 清零	
	GPIOB_CRL|=0x00200000;		//PB5推挽输出
	GPIOB_ODR|=1<<5;			//设置初始灯为灭
	
	GPIOE_CRL&=0xFF0FFFFF;		//设置位 清零
	GPIOE_CRL|=0x00200000;   	//PE5推挽输出
	GPIOE_ODR|=1<<5;			//设置初始灯为灭	
	
	while(1)
	{
		
		GPIOB_ODR&=~(1<<5);		//PB5低电平,因为是置0,所以用按位与
		delay(1000000);
		GPIOB_ODR|=1<<5;		//PB5高电平
	 	delay(1000000);
		
		GPIOE_ODR&=~(1<<5);		//PE5低电平	
		delay(1000000);
		GPIOE_ODR|=1<<5;		//PE5高电平
		delay(1000000);
	}
}
	

四、位带操作实现流水灯

先说说什么是位带操作
我们知道,STM32中寄存器有32位,而把每一位(bit)膨胀成一个32位的字,通过访问这些32位字,达到操作寄存器中的一个位,这就是位带操作。
在这里插入图片描述
通俗点来讲,就是给寄存器每一个位,设置了一个地址,而通过这个地址就可以直接操作这个位。
位带地址计算方式:
SRAM 位带区的某个比特,记它所在别名区的地址为A,位序号为n:
AliasAddr=0x22000000+ (A‐0x20000000)x32 + nx4
片上外设位带区的某个比特,记它所在字节的地址为 A,位序号为 n,则该比特
AliasAddr=0x42000000+ (A‐0x40000000)x32 + nx4

  • 注意:相乘的时候要把32和n*4转化成16进制再做乘法。
//--------------APB2使能时钟寄存器------------------------
#define RCC_APB2ENR		*((unsigned volatile int*)0x40021018)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRL		*((unsigned volatile int*)0x40010C00)
#define	GPIOB_ODR		*((unsigned volatile int*)0x40010C0C)
//----------------GPIOE配置寄存器 ------------------------
#define GPIOE_CRL		*((unsigned volatile int*)0x40011800)
#define	GPIOE_ODR		*((unsigned volatile int*)0x4001180C)	
//----------------------位带定义--------------------------
#define PB5				*((unsigned volatile int*)0x42218194)
#define PE5				*((unsigned volatile int*)0x42230194)
//-------------------简单的延时函数-----------------------
void delay(unsigned long xx)
{
	while(xx--);	//用自减的方式粗略延时
}
//------------------------主函数--------------------------
int main()
{
	RCC_APB2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
	RCC_APB2ENR|=1<<6;			//APB2-GPIOE外设时钟使能
	
	GPIOB_CRL&=0xFF0FFFFF;		//设置位 清零	
	GPIOB_CRL|=0x00200000;		//PB5推挽输出
	GPIOB_ODR|=1<<5;			//设置初始灯为灭
	
	GPIOE_CRL&=0xFF0FFFFF;		//设置位 清零
	GPIOE_CRL|=0x00200000;   	//PE5推挽输出
	GPIOE_ODR|=1<<5;			//设置初始灯为灭	

	while(1)
	{
		PB5=0;					//PB5低电平
		delay(1000000);
		PB5=1;					//PB5高电平
		delay(1000000);
		
		PE5=0;					//PE5低电平
		delay(1000000);
		PE5=1;					//PE5高电平
		delay(1000000);
	}
}

我们知道,数据输出寄存器(ODR)低位既可以清除,也可以设置,所以在进行位带操作点亮流水灯时,计算位带别名地址:
AliasAddr=0x42000000+ (A‐0x40000000)x32 + nx4
这里的 A 就是ODR寄存器的地址。

  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值