STM32单片机学习笔记(六)-位带操作(一)-尝试操作

本文介绍STM32位带操作原理与应用,通过位带区地址转换公式控制LED灯,实现精确的I/O口操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面:本系列内容均为自学笔记,参考资料为野火指南者开发板资料及芯片参考手册等,使用野火指南者开发板进行学习,该系列内容仅用于记录笔记,不做其他用途,笔记的内容可能会存在不准确或者错误等,如有大佬看到错误内容还望能够评论指正,感谢各位。
本节包括前几节的程序,请参考野火开发板资料,里面由更加清晰的教学,野火B站账号:野火官方B站账号链接

学习目标

1、简单理解位带区域,并尝试使用位带区地址的转化公式来操作单片机点亮LED灯。

电路图

本次使用以下电路图:
图6-1:
在这里插入图片描述

下面的内容均为个人见解,如有不对望指出,谢谢。

一、位带操作

先看以下来自《CM3 权威指南 CnR2》的关于位带区域的一张截图:
图6-2:
在这里插入图片描述
从图中可以看出,片上外设和SRAM均有一个位带别名区,这个区域主要是存放位带区膨胀后的地址的,在实际操作中,通过访问位带别名区可以实现对位带区某一个寄存器的一个位进行控制的操作;
以片上外设为例:正常情况下,我们操作的片上外设地址在0x40000000~0x40100000,这部分地址映射到位带别名区时,一个位将会膨胀为4个字节,假设是寄存器,每个寄存器地址有8个位,所以寄存器的地址将被膨胀4×8倍,再说寄存器本身,每个寄存器中,以GPIO为例,GPIO有16个端口,每个端口都是一个位,所以GPIO的每个端口将被膨胀4倍;
于是可以得到地址的膨胀公式1:AliasAddr= =0x42000000+ (A-0x40000000)×8×4 +n×4,
其中A表示位带区寄存器原本的地址,n表示寄存器的位数,部分寄存器有8位,则0≤n≤7,像GPIO有16位,则0≤n≤15;
相同的,SRAM中位带区为0x20000000~0x20100000,这部分到位带别名区也会按照片上外设那样被膨胀相应的倍数;
于是可以得到地址的膨胀公式2:AliasAddr= =0x22000000+ (A-0x20000000)×8×4 +n×4;
在实际操作时,操作两个公式不如直接操作一个公式便捷,所以我们将两个公式整合成一个公式,公式如下:
AliasAddr=(A&0xF0000000)+0x02000000+((A&0x00FFFFFF)<<5)+(n<<2),
公式中(A&0xF0000000)主要是为了确认是片上外设的还是SRAM的,加上后面的0x02000000,就相当于公式1和公式2中的0x42000000和0x22000000,而这个地址是位带别名区的基地址,A&0x00FFFFFF是为了确认寄存器实际的地址相对于片上外设基地址的偏移,如GPIOB的地址是0X40010C00 - 0x40010FFF,那GPIOB的偏移地址就是0x10C00~0x10FFF,将这个地址左移5位,就相当于将寄存器的地址膨胀32倍,加上前面算出的基地址就是寄存器在位带别名区膨胀后的地址,n左移2位,就相当于寄存器第n位的位数膨胀4倍,如将GPIOB_ODR寄存器的第0位膨胀4倍(PB0膨胀4倍),上述总和就是寄存器的每个位膨胀后的地址;
位带操作就相当于51单片机中sbit的操作,直接控制IO口输入输出,这种操作不会影响同一寄存器中的其他位,在一些项目中位带操作可以起到很大的作用,下面看一下程序:
bsp_led.c文件中的程序如下:

//BSP:board support package 板级支持包,仅适用于野火开发板

#include "bsp_led.h"

void LED_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;		//定义变量,方便赋值
	
	RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK,ENABLE);	//打开APB2时钟,GPIO挂载在APB2
	//GPIO_PIN的部分用或将用到的管脚初始化为一个十六进制数据,原本是三个十六进制
	GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN;						//设置需要用到的管脚,LED_G_GPIO_PIN看.h文件
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;					//设置输出模式为推挽输出
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;					//设置输出速率为50MHz,LED_G_GPIO_CLK看.h文件
	
	GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);					//加上&,方便取值	//初始化GPIO
	//由于默认开灯,所以先关闭红灯和绿灯,防止混色
	//下面这两段程序最好放在初始化函数里,放main主函数灯上电会闪一下
	GPIO_SetBits(GPIOB,LED_G_GPIO_PIN);	//置1
}

bsp_led.h文件中的程序如下:

#ifndef _BSP_LED_H
#define _BSP_LED_H

#include "stm32f10x.h"		//要包含固件库的.h文件

#define LED_G_GPIO_PIN						GPIO_Pin_0		//定义绿灯管脚号
#define LED_G_GPIO_PORT						GPIOB							//定义用到的GPIO
#define LED_G_GPIO_CLK						RCC_APB2Periph_GPIOB	//定义RCC时钟寄存器

void LED_GPIO_Config(void);		//.c文件中的函数声明

#endif	/*_BSP_LED_H*/

上述程序主要是定义一下端口,见相应端口初始化;
main.c文件中的程序如下:

#include "stm32f10x.h"   // 相当于51单片机中的  #include <reg51.h>
#include "bsp_led.h"
//A:地址,n标号
// 外设位带地址AliasAddr= =0x42000000+ (A-0x40000000)*8*4 +n*4
// SRAM位带地址AliasAddr= =0x22000000+ (A-0x20000000)*8*4 +n*4
//总:AliasAddr=(A&0xF0000000)+0x02000000+((A&0x00FFFFFF)<<5)+(n<<2)
//先将外设最高的两位取出,然后通过与0x00FFFFFF相与确定剩下的几位,即偏移地址,
//然后将偏移地址×32(1个位膨胀为4个字节,一个地址表示一个字节,一个字节8个位),
//最后加上相应寄存器具体的位号(0-16(GPIO 16个口)),当然,位号也要膨胀为4个字节
//总之,寄存器地址膨胀32倍,寄存器位数膨胀4倍
#define BITBAND(addr,bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))
//#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))
//将地址转化为指针,方便操作,先定义一下
#define MEM_ADDR(addr)   *((volatile unsigned long  *)(addr))
//把位带别名区正式转化地址为指针
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

// GPIO ODR 和 IDR 寄存器地址映射 
#define GPIOB_ODR_Addr    (GPIOB_BASE+0x0C) //0x40010C0C   
#define GPIOB_IDR_Addr    (GPIOB_BASE+0x08)  //0x40010C08   

// 单独操作 GPIO的某一个IO口,n(0,1,2...16),n表示具体是哪一个IO口
#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出   
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入   

void Delay(uint32_t count)
{
	for(;count != 0;count--);
}

int main(void)
{
	// 来到这里的时候,系统的时钟已经被配置成72M。
	LED_GPIO_Config();	//GPIO初始化

	while(1)
	{
		PBout(0)=1;		//关灯
		Delay(0xFFFFF);
		PBout(0)=0;		//开灯
		Delay(0xFFFFF);
	}
}

main.c文件中的程序主要就是对上面理论部分的定义,先将总的整合后的公式定义出来,然后映射一下需要用到的寄存器的原地址,最后使用公式操作寄存器,当然程序中只操作了输出寄存器,输入寄存器只定义了并没有操作,这个程序最终实现的效果是灯的闪烁。
以上内容仅为个人理解,还存在很多错误及改进之处,后面深入学习后会更新:位带操作(二)。
以上仅作笔记参考,不作教学,感谢观看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值