目录

1 定义

2 位带操作

2.1 范围

2.2 位带操作

2.3代码实现

3 位段的优点

4 STM32的3种不同GPIO驱动

4.1 库函数版

4.2 寄存器版

4.3 位段版


1 定义

首先需要明确下,位段,位带和别名区这三个名词

位段:STM32用户参考手册使用的名字

位带:CortexM3参考手册使用的

别名区:地址总线上用来位访问地址区域,

所以说,位段和位带是一个意思,是不同手册的不同叫法。

由上述的名词解释得知,位带功能并不是STM32独有的,是CortexM3的功能(CortexM4也有这样的功能)。MCS51有位操作,以一位(bit)为数据对象的操作,MCS51可以简单的将P1口的第2位独立操作:P1.2=0;P1.2=1 ;这样就把P1口的第三个脚(bit2)置0置1。而STM32的位段、位带别名区最重要的就为了实现这样的功能。

2 位带操作

2.1 范围

位带是有范围的,并不是CortexM3全部地址空间都支持的。在 CM3中,有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

支持位带操作的两个内存区的范围是:

0x2000_0000‐0x200F_FFFF(SRAM 区中最低1MB区域)

0x4000_0000‐0x400F_FFFF(片上外设区中的最低 1MB)

2.2 位带操作

对 SRAM 位带区的某个比特,记该比特所在字节的地址为A,位序号为 n (0<=n<=7),则它在别名区的地址为:

AliasAddr = 0x22000000 + ((A‐0x20000000)*8+n)*4 =0x22000000 + (A‐0x20000000)*32 + n*4

对于片上外设位带区的某个比特,记该比特所在字节的地址为A,位序号为 n (0<=n<=7),则该比特在别名区的地址为:

AliasAddr = 0x42000000 + ((A‐0x40000000)*8+n)*4 = 0x42000000 + (A‐0x40000000)*32 + n*4

上式中,“*4”表示一个字为 4 个字节,“*8”表示一个字节中有 8 个比特。

2.3代码实现

把“位带地址+位序号”转换别名地址宏为:

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000 + ((addr &0xFF FFF)<<5) + (bitnum<<2))

把该地址转换成一个指针:

#define MEM_ADDR(addr, bitnum) *((volatile unsigned long *)((addr & 0xF0000000)+0x2000000 + ((addr &0xFF FFF)<<5) + (bitnum<<2)))

其中

addr的取值范围:

0x2000_0000‐0x200F_FFFF

0x4000_0000‐0x400F_FFFF

注意:addr取值要32位对齐

bitnum的取值范围:

0-31

解析:

(addr & 0xf0000000) + 0x02000000:

区分SRAM还是外设,如果是外设,结果为4,再加0x2000000就等于0x4200000,0x42000000就是外设别名位带区。如果是SRAM,结果为2,再加上0x2000000就等于0x22000000,0x22000000就是SRAM别名位带区。

addr & 0x00ffffff:

屏蔽了最高2位,相当于减去0x20000000或者0x40000000。因为位带区的有效范围是1M,即0x100000,这样子就做到了低6位有效。

<< 5:

等价于乘以32

<< 2:

等价于乘以4

特别提醒:

当你使用位带功能时,要访问的变量必须用 volatile 来定义。因为 C 编译器并不知道同一个比特可以有两个地址。所以就要通过 volatile,使得编译器每次都如实地把新数值写入存储器,而不再会出于优化的考虑。

3 位段的优点

最容易想到的就是通过 GPIO 的管脚来单独控制每盏 LED 的点亮与熄灭。另一方面,也对操作串行接口器件提供了很大的方便(典型如 74HC165,CD4094)。位带操作可以把代码缩小, 速度更快,效率更高,更安全。总之位带操作对于硬件 I/O 密集型的底层程序最有用处了

位带操作还能用来化简跳转的判断。

当跳转依据是某个位时,以前必须这样做

1、读取整个寄存器

2、掩蔽不需要的位

3、比较并跳转

使用位带操作后

1、从未带别名区读取状态位

2、比较并跳转

当然,对于写入操作也从4步精简到3步

4 STM32的3种不同GPIO驱动

4.1 库函数版

最常用的版本,使用ST标准外设库

void  Led_Key_Init(void)
{  GPIO_InitTypeDef GPIO_Init_s;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);//使能时钟  GPIO_Init_s.GPIO_Pin = GPIO_Pin_4;GPIO_Init_s.GPIO_Mode = GPIO_Mode_OUT;GPIO_Init_s.GPIO_OType = GPIO_OType_PP;GPIO_Init_s.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init_s.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOE, &GPIO_Init_s);
}

开源代码:

https://github.com/strongercjd/STM32F207VCT6/tree/master/06-GPIO-Input-Output

4.2 寄存器版

寄存器版其实就是把ST标准外设库拷贝出来,可以简化一些操作


/********使用寄存器---start********/
//TP---PA15#define TP_PORT      GPIOA
#define TP_PIN      GPIO_Pin_15#define TP_OUT()   TP_PORT->CRH&=0X0FFFFFFF; TP_PORT->CRH|=0X50000000;   //!IO输出
#define TP_IN()    TP_PORT->CRH&=0X0FFFFFFF; TP_PORT->CRH|=0X40000000;   //!IO输入
#define TP_READ()  (((TP_PORT->IDR)>>15)?1:0)   //!<IO读入#define TP_SET()    TP_PORT->BSRR = TP_PIN;//写1
#define TP_CLR()    TP_PORT->BRR  = TP_PIN;//写0#define TP_DATA_SET()   TP_SET() //IO写1
#define TP_DATA_CLR()   TP_CLR() //IO写0
#define TP_DATA_OUT()   TP_OUT() //将IO设为输出
#define TP_DATA_IN()    TP_IN()  //将IO设为输入
#define TP_DATA_READ()  TP_READ()//读取IO的电平/********使用寄存器---end********/

开源代码:

https://github.com/strongercjd/STM32F207VCT6/tree/master/06-GPIO-Input-Output

4.3 位段版

基于位段的,操作方便

/*LED配置---PE4*/
#define  GPIO_IDR_OFFSET  (GPIOE_BASE+0x10 - PERIPH_BASE)
#define  GPIO_ODR_OFFSET  (GPIOE_BASE+0x14 - PERIPH_BASE)#define  GPIO_BitNumber   4
#define  GPIO_OUT_BB     (PERIPH_BB_BASE + (GPIO_ODR_OFFSET * 32) + (GPIO_BitNumber * 4))
#define  GPIO_OUT_DATA           *(__IO uint32_t *)GPIO_OUT_BB#define  GPIO_IN_BB     (PERIPH_BB_BASE + (GPIO_IDR_OFFSET * 32) + (GPIO_BitNumber * 4))
#define  GPIO_IN_DATA           *(__IO uint32_t *)GPIO_IN_BB#define  GPIO_DIR_REG     *(__IO uint32_t *)(GPIOE_BASE+0X00)#define PE4_SET()  GPIO_OUT_DATA = 1   //!< IO写1
#define PE4_CLR()  GPIO_OUT_DATA = 0   //!< IO写0
#define PE4_OUT()  GPIO_DIR_REG = (((GPIO_DIR_REG) & 0xFFFFFCFF) | 0x00000100)  //!IO输出
#define PE4_IN()   GPIO_DIR_REG = ((GPIO_DIR_REG) & 0xFFFFFCFF)  //!IO输入
#define PE4_READ() GPIO_IN_DATA   //!<IO读入

将上述整理为通用GPIO驱动代码

*GPIO配置---PXX*/
#define GPIO_SET(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x14 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4))) = 1   //!< IO写1
#define GPIO_CLR(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x14 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4))) = 0   //!< IO写0#define GPIO_OUT(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)(GPIOx_BASE+0X00)  &= ~(GPIO_MODER_MODER0 << (GPIO_BitNumber * 2));\*(__IO uint32_t *)(GPIOx_BASE+0X00) |= ((GPIO_Mode_OUT) << (GPIO_BitNumber * 2)); //!IO输出#define GPIO_IN(GPIOx_BASE,GPIO_BitNumber)    *(__IO uint32_t *)(GPIOx_BASE+0X00)  &= ~(GPIO_MODER_MODER0 << (GPIO_BitNumber * 2));\*(__IO uint32_t *)(GPIOx_BASE+0X00) |= ((GPIO_Mode_IN) << (GPIO_BitNumber * 2));  //!IO输入#define GPIO_READ(GPIOx_BASE,GPIO_BitNumber)  *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x10 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4)))   //!<IO读入

调用方法

GPIOx_BASE:GPIOE_BASE基地址,而不是GPIOE

GPIO_BitNumber:4,而不是GPIO_Pin_4

GPIO_OUT(GPIOE_BASE,4);

开源代码:

https://github.com/strongercjd/STM32F207VCT6/tree/master/08-GPIO-bit-band

点击查看本文所在的专辑,STM32F207教程

关注公众号,第一时间收到文章更新。评论区不能及时看到,需要交流可以到公众号沟通

STM32 位段详解相关推荐

  1. stm32位操作详解

    stm32位操作详解 STM32位操作原理 思想:把一个比特分成32位,每位都分配一个地址,这样就有32个地址,通过地址直接访问. 位操作基础 位运算 位运算的运算分量只能是整型或字符型数据,位运算把 ...

  2. STM32 定时器详解

    STM32 定时器详解 吃了一个猛亏,自己理解花了大半天时间,结果一看代码发现巨简单 算了,把自己理解的放上来吧 目录 STM32 定时器详解 前言 一.定时器种类和区分 二.时钟源 三.计数过程 3 ...

  3. STM32 DAC详解

    目录 01.DAC简介 02.DAC转换 03.功能说明 04.DAC输出电压 05.代码配置 上一篇介绍了<STM32ADC详解>,既然有模拟转数字的ADC模块,那么就必然有数字转模拟的 ...

  4. STM32 SPI详解

    目录 1.SPI简介 2.SPI特点 2.1.SPI控制方式 2.2.SPI传输方式 2.3.SPI数据交换 2.4.SPI传输模式 3.工作机制 3.1.相关缩写 3.2.CPOL极性 3.3.CP ...

  5. STM32 GPIO 详解

    0. 实验平台 基于STM32F407ZG 1. GPIO 简介 1.1 简介 GPIO全称:General Purpose Input Output,即通用输入输出端口,一般用来采集外部器件的信息或 ...

  6. STM32 ADC详解

    目录 01.ADC简介 02.STM32的ADC外设 03.STM32ADC框图讲解 04.触发源 05.转换周期 06.数据寄存器 07.中断 08.电压转换 09.电路图设计 10.代码设计 01 ...

  7. STM32 串口详解

    目录 01.USART的特点 02.USART简介 2.1.数据传输模型 2.2.帧结构 2.3.波特率 03.STM32的USART 04.代码配置 01.USART的特点 USART是通用异步收发 ...

  8. STM32 SDIO详解

    目录 01.SDIO简介 02.SDIO特点 03.SDIO时钟 04.SDIO的命令与响应 05.SDIO块数据传输 06.代码 1.SDIO简介 SDIO,全称:Secure Digital In ...

  9. STM32 中断详解

    目录 1 EXTI控制器 2 NVIC控制器 3 code 中断,在单片机中占有非常重要的地位.代码默认地从上向下执行,遇到条件或者其他语句,会按照指定的地方跳转.而在单片机执行代码的过程中,难免会有 ...

最新文章

  1. vue动态发布到线上_vue在线动态切换主题色方案
  2. C语言一个双向链表的实现
  3. Apache,php,mysql整合安装包 for Windows 2000/xp/2003
  4. Windows平台Android开发环境搭建几个注意点
  5. 029_html样式
  6. ==与equals,String的equals()方法
  7. 文件到集合改进版【应用】
  8. Elasticsearch聚合深入详解——对比Mysql实现
  9. 蓝桥杯 123 二分+打表
  10. python迭代器使用_Python迭代器的用法
  11. vue 执行函数this_vue回调函数中this无效
  12. spring boot2 kafka
  13. react native进一步学习(NavigatorIOS 学习)
  14. [译]Windows 服务用户帐户
  15. java汉字的编码_Java中文编码小结
  16. java 随机数的判断
  17. ee er_61对词根相同后缀分别是er和ee的单词要这样区别记忆它们
  18. Codeigniter 4基础教程(1)-- Wamp+CodeIgniter 4以及helloworld
  19. 京东程序员压力太大在网页植入骂人代码?网友:。。。
  20. staircase nim 经典组合游戏

热门文章

  1. 我的世界服务器物品解绑定,我的世界更方便控制VIP物品 灵魂绑定插件分享
  2. 弹出数组第一个元素 php,弹出第一个元素数组以在php codeigniter中插入批处理
  3. java 抛出空指针_java - Java ServerSocket抛出空指针异常 - 堆栈内存溢出
  4. java map 更新_更新Map键的值java
  5. html5触摸事件判断滑动方向,H5触摸事件中如何判断用户滑动方向
  6. Lora模块一对一和一对多
  7. matplotlib绘制极坐标图像
  8. numpy pandas 查找在一个区间中的值
  9. python获得距离指定(x,y)固定距离的全部坐标点
  10. 华为p20支持手机云闪付吗_余承东:明年华为智能手机全面支持鸿蒙系统