GPIO的使用--存储系统与位带操作理解

目录

存储系统与位带操作

1、对GPIO的操作函数

2、计算机对地址的管理

3、板子地址

4、什么是位带操作

5、位带地址好处


存储系统与位带操作

1、对GPIO的操作函数

//方案一
GPIO_WriteBit(GPIOF,GPIO_Pin_9,0);

//方案二
GPIO_Write(GPIOF,0x0000);

//方案三
GPIOF->ODR = 0x0000;//寄存器操作
	
//方案四
*((volatile uint32_t*)(0X40021414))=0x0000;

//方案五
*((volatile uint32_t*)(0x42000000 + 0x21414*32 + 9*4)) =1;			

//方案六
PFout(9)=0;	

其中

GPIO_Write(GPIOF,0x0600);

GPIO_Write()本质上是对寄存器的操控:即

GPIOF ->ODR = 0x0600;

而寄存器本质上是一块存贮空间,可以理解上面代码是对于一块地址写入 0x0600.

*((volatile uint32_t*)(0X40021414)) = 0x0600;

但是对于STM32f407系列的板子,还有一种写法:

*((volatile uint32_t*)(0x42000000 + 0x21414*32 + 9*4)) = 1;
*((volatile uint32_t*)(0x42000000 + 0x21414*32 + 10*4)) = 1;

上面这个地址能实现对于LED的控制,是硬件工程师做了特殊处理,该解决方案 叫做“位带操作”。要想理解这个中方案,需要对计算机地址访问有着深刻理解。

2、计算机对地址的管理

从C语言角度理解内存:拿到地址,找到地址中的数据并返回

从计算组成理解的存储结构示意图

假设cpu中有3根地址线,那么它进过译码器后组合出来(扩展)的结果表示8根线(再多的都表示不了),进而控制8个内存单元。每一个内存单元即就是一个字符型(char)。

内存申请和释放的最小单元, 实际申请的是地址资源,而一个地址刚好对应一个字符存储空间。因此字符空间 (char)是内存分配的最小单元。【bool型实际不存在,它是变相的char】

内存不可以无限扩展,受制于机器位数,管理不了那么多的内存单元。32位机器 最多管理2^32个地址单元。64位机器可以管理2^64个地址单元。 

3、板子地址

为什么 ((volatile uint32_t)(0X40021414)) = 0x0600,是可以控制LED灯?

 首先我们机器是32位的地址线。这些地址线提供了2的32次方的地址。这些地址 我们机器没有用完,有剩余的。我们机器上的用在了这些地方:程序存储器, 数据存储器,IO端口和寄存器

存储器:内存。

IO端口:端口地址。网络通信里面要选端口。USB插口。CPU使用IO来通信。

寄存器:这个和内存使用起来很像,但是具体物理实现和计算机所处位置不同。

stm32给与GPIOF组的寄存器分配的地址如下

从地址:0X4002 1400 - 0X4002 17FF.总共有0x3FF+1的空间给与了GPIOF组使用。我们之前所有的操纵函数本质上都是操作了F组的寄存器的地址。给与这个地址写入0或者1。而寄存器的数据决定了引脚上的电压为高/低

操作函数GPIO_WriteBit();也是在操作寄存器,给寄存器写入0或1,完成灯的控制。

 GPIOF组的地址如下

地址跳转步骤

GPIOF组地址为:GPIO_BASE = 0x4000 0000 + 0x0002 0000 + 0x1400 = 0x4002 1400

输出寄存器:ODR寄存器,偏移地址 = 0x14。

GPIOF->ODR = GPIOF_BASE +0x14 = 0x40021414 ,给与这根地址线-->0x40021414,写数据。相当于配置ODR寄存器,相当于调用 GPIO的操控函数。

这几个位对应的是:

 PF15,PF14,PF13,PF12,PF11,PF10,PF9,PF8,PF7,PF6,PF5,PF4,PF3,PF2,PF1,PF0,每一个位对应F组16个引脚的每一个,故此数据是 0XFFFF--0X0000。

4、什么是位带操作

了解更多点击这里-->位带(位段)操作<--

我的通俗理解:一块内存可以用两个地址来访问,一个是内存地址,一个是硬件工程师在寄存器内接的,该地址与原来PF9的输出寄存器的地址(GPIOF->ODR = 0x40021414)地位平等。

上图:红色线路是原有的地址,属于这个寄存器的,因此可以访问整个寄存器。 绿色线路是位带区域,硬件接上去的,这个地址仅仅属于这个寄存器的一个特定的位。

那么这块区域的操作有两种路线:

红色路线1:GPIOF->ODR = 0XFFFF FFFF。我们计算好第9的数据位的数据,赋 值给ODR寄存器。 

绿色路线2:获取单独分给ODR的第9根的数据位的地址,并给与这根地址赋值 1/0.

位带操作可以让我们更好地理解底层存储,但也知识理解一下就好了,现有一个简化位带操作的封装函数,BitBand.h,可以简化书写

#ifndef __BITBAND_H__
#define __BITBAND_H__

#define BITBAND(addr,bitnum)       0x42000000+ (addr & 0xFFFFF)*32 + bitnum *4
#define MEM_ADDR(addr)             *((volatile uint32_t*)(addr))
#define BIT_ADDR(addr,bitnum)      MEM_ADDR(BITBAND(addr,bitnum))

#define GPIOA_IDR_ADDR             (GPIOA_BASE + 0x10)
#define GPIOA_ODR_ADDR             (GPIOA_BASE + 0x14)

#define GPIOB_IDR_ADDR             (GPIOB_BASE + 0x10)
#define GPIOB_ODR_ADDR             (GPIOB_BASE + 0x14)

#define GPIOC_IDR_ADDR             (GPIOC_BASE + 0x10)
#define GPIOC_ODR_ADDR             (GPIOC_BASE + 0x14)

#define GPIOD_IDR_ADDR             (GPIOD_BASE + 0x10)
#define GPIOD_ODR_ADDR             (GPIOD_BASE + 0x14)

#define GPIOE_IDR_ADDR             (GPIOE_BASE + 0x10)
#define GPIOE_ODR_ADDR             (GPIOE_BASE + 0x14)

#define GPIOF_IDR_ADDR             (GPIOF_BASE + 0x10)
#define GPIOF_ODR_ADDR             (GPIOF_BASE + 0x14)

#define GPIOG_IDR_ADDR             (GPIOG_BASE + 0x10)
#define GPIOG_ODR_ADDR             (GPIOG_BASE + 0x14)

#define GPIOH_IDR_ADDR             (GPIOH_BASE + 0x10)
#define GPIOH_ODR_ADDR             (GPIOH_BASE + 0x14)

#define GPIOI_IDR_ADDR             (GPIOI_BASE + 0x10)
#define GPIOI_ODR_ADDR             (GPIOI_BASE + 0x14)


#define PAout(n)       BIT_ADDR(GPIOA_ODR_ADDR,n)  
#define PAin(n)        BIT_ADDR(GPIOA_IDR_ADDR,n) 

#define PBout(n)       BIT_ADDR(GPIOB_ODR_ADDR,n)  
#define PBin(n)        BIT_ADDR(GPIOB_IDR_ADDR,n)

#define PCout(n)       BIT_ADDR(GPIOC_ODR_ADDR,n)  
#define PCin(n)        BIT_ADDR(GPIOC_IDR_ADDR,n)

#define PDout(n)       BIT_ADDR(GPIOD_ODR_ADDR,n)  
#define PDin(n)        BIT_ADDR(GPIOD_IDR_ADDR,n)

#define PEout(n)       BIT_ADDR(GPIOE_ODR_ADDR,n)  
#define PEin(n)        BIT_ADDR(GPIOE_IDR_ADDR,n)

#define PFout(n)       BIT_ADDR(GPIOF_ODR_ADDR,n)  
#define PFin(n)        BIT_ADDR(GPIOF_IDR_ADDR,n)

#define PGout(n)       BIT_ADDR(GPIOG_ODR_ADDR,n)  
#define PGin(n)        BIT_ADDR(GPIOG_IDR_ADDR,n)

#define PHout(n)       BIT_ADDR(GPIOH_ODR_ADDR,n)  
#define PHin(n)        BIT_ADDR(GPIOH_IDR_ADDR,n)

#define PIout(n)       BIT_ADDR(GPIOI_ODR_ADDR,n)  
#define PIin(n)        BIT_ADDR(GPIOI_IDR_ADDR,n)

#endif

封装前

 封装后

5、位带地址好处

1.提升效率:比特位的读写,操作效率提升。

2.代码简洁:仅仅对于某一个位进行控制,无需考虑取值判断。(0x0200) 硬件把GPIO寄存器映射为位带区域,可以提升IO操控和通信的速度。

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值