0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM32为什么需要位带操作呢?

冬至配饺子 来源:小李的创客实验室 作者:初出茅庐的小李 2023-07-08 15:16 次阅读

为什么需要位带操作?

因为编程需要操作某个bit位来达到我们想要的功能,比如点灯需要操作GPIOA->ODR

的某个bit假设是第2bit,写1就可以让GPIO输出一个高电平。

GPIOA- >ODR |= 1< < 2;

这样写其实有三个隐含的操作:

//1.读取ODR寄存器的值到内存//2.改写第2bit的值//3.再把改写后的值写进ODR寄存器

这样的缺点:效率低

位带操作就是为了解决这个问题,前提是硬件支持这么做。

位操作就是可以单独的对一个比特位读和写,这个在 51 单片机中非常常见。51 单片机中通过关键字 sbit 来实现位定义,STM32没有这样的关键字,而是通过访问位带别名区来实现,例如

sbit LED P1^2
LED = 1;//输出高电平
LED = 0;//输出低电平

这样的优点:效率高

什么是位带别名区?

STM32本身不支持位操作,它发明了一种位带操作来让32的某些资源支持位操作。

这两个区域一个是 SRAM 区的最低 1MB 空间,令一个是外设区最低 1MB 空间。

这两个 1MB 的空间除了可以像正常的 RAM 一样操作外,他们还有自己的 位带别名区 ,位带别名区把这 1MB 的空间的 每一个位膨胀成一个 32 位的字 ,当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。

位带别名区就是就是就是本来位的区域,变成了字的区域。

这里有个形象的解释:

打个形象的比方,以某个村,就张村把,该村有3户人家分别为A,B,C,我想给张村的A送礼,但是明文规定,不能给具体的个送礼,但是可以给村委会送礼,那我该怎么办呢,OK,即日起,A不叫A了,改名叫做村委会1,B和C分别改叫做村委会2和村委会3,哦了,可以给A送礼了,虽然我送礼的对象是村委会1,听起来好像比个人级别高一点,但是最终收到礼物的还是个人A。

同理,STM32不允许对某个端的某一个IO口进行操作,也就是PA.1 = 0或者PA.1 = 1这样的操作是非法的,好了,那我就给PA.1起个别名,将原来PA.1的位地址扩展成一个32位的 字地址 ,对32位的地址进行操作,这个是STM32允许的,肯定是可以的,STM32对所有的寄存器配置,都是对某个32位地址的操作,因此说白了,操作一个32位寄存器来影响某个位的操作叫做位带操作。

什么是位带区?

我们可以看到下面图中有两个位带区,分别是SRAM区里的0x20000000-0x200FFFFF地址段和片内外设区里的0x40000000-0x400FFFFF地址段(图中标号①处),它们的地址空间大小都是1M字节,在SRAM段内外设地址段内的这1M大小的空间就是位带区,说白了就是支持位带操作的区域就是位带区。

图片

位带区跟位带别名区有怎样的关系?

从上面映射图上可以看到,SRAM区里的0x22000000-0x23FFFFFF地址段和外设区里0x42000000-0x43FFFFFF地址段都是位带别名区,两个别名区空间大小都是32MB。那么,这32MB的位带别名区地址空间是怎么与1MB的位带区地址空间对应起来的呢?

答案:地址映射

那么问题来了?将1M字节里面的每一个bit映射到32M字节里面去,那么怎么映射呢?

首先明确一些概念:

1字节= 8bit
1字  = 4字节 = 32bit

看图

图片

将1bit映射到1个字空间(扩大了32倍)

映射前的1个字节 = 映射后的8个字(扩大了32倍 8 * 4 = 32字节)

那么就得出以下结论:

映射前的1个字节 = 映射后的32个字节

映射前的1M字节 = 映射后的32M字节

图片

0x40000000地址处的1个bit变成了0x42000010地址处的32个bit

为什么要将1bit空间要映射到一个字空间里去呢?我映射到1字节或者2字节的地址空间不行吗?我只能说,STM32是一个32位的机器,内核按字寻址的话寻址速度是最快的,所以别问这么多为什么,如果问了,答案就是为了速度。就好比你买个电脑用一个小箱子装着但是顺丰快递发货走的是集装箱,理论上来说装到集装箱里空运是最快的,要不然没办法上飞机啊......各位想想好像是这么个道理哈

位带操作该怎么用?

我们已经知道了位带区就是支持位操作的地址段,位带别名区就是位带区的地址映射,操作位带别名区就等价于操作位带区,并且我们知道了大致的映射过程,那么在STM32实际使用中又是怎么应用的呢?

在《Cortex M3权威指南》中,前人已经整理出了位带别名区与位带区地址对应关系的表达式,使用的时候只要套用公式就可以,如下图

图片

将两个公式合并一下就得到:

AliasAddr = ((A & 0xF0000000)+0x02000000+((A &0x00FFFFFF)<<5)+(n<<2))

式中A为位带区地址,n为位序号

<<5 <<2又是什么鬼

2进制左移5位就相当于乘以2^5次方 就是扩大32倍的意思 为什么不写成*32 问就是效率 <<2同理扩大4倍

使用以下开源代码即可完成映射

// 把“位带地址+位序号”转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x000FFFFF)< < 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 GPIOA_ODR_Addr   (GPIOA_BASE+20)
#define GPIOB_ODR_Addr   (GPIOB_BASE+20)  
#define GPIOC_ODR_Addr   (GPIOC_BASE+20)  
#define GPIOD_ODR_Addr   (GPIOD_BASE+20)
#define GPIOE_ODR_Addr   (GPIOE_BASE+20)
#define GPIOF_ODR_Addr   (GPIOF_BASE+20)      
#define GPIOG_ODR_Addr   (GPIOG_BASE+20)
#define GPIOH_ODR_Addr   (GPIOH_BASE+20)      
#define GPIOI_ODR_Addr   (GPIOI_BASE+20)
#define GPIOJ_ODR_Addr   (GPIOJ_BASE+20)      
#define GPIOK_ODR_Addr   (GPIOK_BASE+20)

#define GPIOA_IDR_Addr   (GPIOA_BASE+16)  
#define GPIOB_IDR_Addr   (GPIOB_BASE+16)  
#define GPIOC_IDR_Addr   (GPIOC_BASE+16)  
#define GPIOD_IDR_Addr   (GPIOD_BASE+16)  
#define GPIOE_IDR_Addr   (GPIOE_BASE+16)    
#define GPIOF_IDR_Addr   (GPIOF_BASE+16)    
#define GPIOG_IDR_Addr   (GPIOG_BASE+16)  
#define GPIOH_IDR_Addr   (GPIOH_BASE+16)
#define GPIOI_IDR_Addr   (GPIOI_BASE+16)
#define GPIOJ_IDR_Addr   (GPIOJ_BASE+16)
#define GPIOK_IDR_Addr   (GPIOK_BASE+16)


// 单独操作 GPIO的某一个IO口,n(0,1,2...16),n表示具体是哪一个IO口
#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) //输入

#define PJout(n)   BIT_ADDR(GPIOJ_ODR_Addr,n) //输出  
#define PJin(n)   BIT_ADDR(GPIOJ_IDR_Addr,n) //输入  

#define PKout(n)   BIT_ADDR(GPIOK_ODR_Addr,n) //输出  
#define PKin(n)   BIT_ADDR(GPIOK_IDR_Addr,n) //输入

理论上我们不仅可以使用公式对所有GPIO端口进行封装,我们也可以对STM32所有片内外设的寄存器进行封装(FSMC除外)

如图

图片

使用注意事项

  • 使用上面封装好的位带操作之前,要先对IO端口进行配置,否则操作结果不可预期。
  • PAout(n)作为左值使用,PAin(n)作为右值使用。(跟51单片机一样,我想你是懂51的)
  • 最后,使用的过程中要注意一点,强制地址转换的时候一定要使用volatile关键字进行修饰,否则这个操作可能会被编译器优化掉

使用例子

Led.h 增加位带操作代码

#define LED0 PFout(9)
#define LED1 PFout(10)
#define BEEP PFout(8)

Key.h增加位带操作代码

#define KEY0 PEin(4)
#define KEY1 PEin(3)
#define KEY2 PEin(2)
#define KEY_UP PAin(0)

main.c示例代码

#include "stm32f4xx.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include "bit_band.h"
int main(void)
{
uint8_t i,key;
LED_Init();
KEY_Init();
USART1_Init(115200);
while(1)
{
key=ScanKeyVal(0);
if(key)
{
i=!i;
LED0=!LED0;
LED1=!LED1;
}
}
}

三、DS18B20温度传感器示例-位带控制实现时序

#include "ds18b20.h"
/*
函数功能: 硬件初始化--IO配置
硬件连接: PB15
*/
void DS18B20_Init(void)
{
   /*1. 开时钟*/
   RCC- >APB2ENR|=1< < 3; //PB
   /*2. 配置GPIO口模式*/
   GPIOB- >CRH&=0x0FFFFFFF;
   GPIOB- >CRH|=0x30000000;
   /*3. 上拉*/
   GPIOB- >ODR|=1< < 15;
}

/*
函数功能: 发送复位脉冲检测DS18B20硬件--建立通信过程
返 回 值: 0表示成功 1表示失败  
*/
u8 DS18B20_Check(void)
{
   u8 i;
   DS18B20_OUT_MODE(); //配置IO口为输出模式
   DS18B20_OUT=0;      //拉低
   delay_us(580);      
   DS18B20_OUT=1;      //拉高
   
   DS18B20_IN_MODE();  //配置IO口为输入模式
   for(i=0;i< 100;i++)
  {
       if(DS18B20_IN==0)break;
       delay_us(1);
  }
   if(i==100)return 1;
   
   for(i=0;i< 250;i++)
  {
      if(DS18B20_IN)break;
      delay_us(1);
  }
   if(i==250)return 1;
   return 0;
}

/*
函数功能: DS18B20写一个字节数据
*/
void DS18B20_WriteOnebyte(u8 cmd)
{
   u8 i;
   DS18B20_OUT_MODE(); //输出模式
   for(i=0;i< 8;i++)
  {
       if(cmd&0x01) //发送1
      {
           DS18B20_OUT=0;
           delay_us(15);
           DS18B20_OUT=1;
           delay_us(45);
           DS18B20_OUT=1;
           delay_us(2);
      }
       else //发送0
      {
           DS18B20_OUT=0;
           delay_us(15);
           DS18B20_OUT=0;
           delay_us(45);
           DS18B20_OUT=1;
           delay_us(2);
      }
       cmd >>=1;
  }
}

/*
函数功能: DS18B20读一个字节数据
*/
u8 DS18B20_ReadOnebyte(void)
{
   u8 i;
   u8 data=0;
   for(i=0;i< 8;i++)
  {
       DS18B20_OUT_MODE(); //输出模式
       DS18B20_OUT=0;
       delay_us(2);
       DS18B20_IN_MODE();
       delay_us(8);
       data >>=1; //右移1位
       if(DS18B20_IN)data|=0x80;
       delay_us(50);
       DS18B20_OUT=1;
       delay_us(2);
  }
   return data;
}

/*
函数功能: 读取一次DS18B20的温度数据
返回值: 读取的温度数据高低位
*/
u16 DS18B20_ReadTemp(void)
{
  u16 temp;
  u8 t_L,t_H;
  if(DS18B20_Check())return 1;
  DS18B20_WriteOnebyte(0xCC); //跳跃 ROM 指令 --不验证身份
  DS18B20_WriteOnebyte(0x44); //发送温度转换指令
   
  if(DS18B20_Check())return 2;
  DS18B20_WriteOnebyte(0xCC); //跳跃 ROM 指令 --不验证身份
  DS18B20_WriteOnebyte(0xBE); //读取RAM里的数据
 
  //读取温度
  t_L=DS18B20_ReadOnebyte(); //低字节
  t_H=DS18B20_ReadOnebyte(); //高字节
  temp=t_H< < 8|t_L;
  return temp;
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • STM32
    +关注

    关注

    2265

    文章

    10870

    浏览量

    354716
  • 51单片机
    +关注

    关注

    273

    文章

    5697

    浏览量

    123134
  • GPIO
    +关注

    关注

    16

    文章

    1196

    浏览量

    51897
  • SRAM存储器
    +关注

    关注

    0

    文章

    88

    浏览量

    13268
  • FSMC模块
    +关注

    关注

    0

    文章

    9

    浏览量

    1919
收藏 人收藏

    评论

    相关推荐

    STM32为什么需要位带操作

    STM32
    YS YYDS
    发布于 :2023年05月12日 21:20:53

    带操作STM32芯片的特点有哪些

    带操作STM32芯片除了通用的寄存器访问,还有一个比较有意思的位带操作。这个位带的意思,就是每个比特(bit)位膨胀成一个32位的字(word),当访问这些字的时候就达到了访问“位”的目的,这就
    发表于 12-09 06:42

    STM32带操作的方法

    前言这篇文章主要用来讲解STM32中的位带操作,学习过51单片机的应改了解,在控制51单片机IO引脚时,只需要向某一个IO口赋值就可以实现,对应IO口的输出高或地。那么STM32可以不
    发表于 01-17 06:27

    怎样去使用stm32的位带操作

    怎样去使用stm32的位带操作stm32支持位带操作的两个内存区的范围分别是多少
    发表于 02-25 07:14

    为什么ch32vxx会不支持stm32带操作

    为什么ch32vxx会不支持stm32带操作?怎样去解决ch32vxx不支持stm32带操作的问题
    发表于 02-28 07:18

    快速理解STM32带操作原理

    Bit-banding简称位带,有人也叫位段。支持位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。很多朋友是从学习51单片机过来的,都知道P1.1这个引脚可以单独控制,我们操作的这个引脚就是一个Bit位。我们都知道在S
    的头像 发表于 09-03 15:40 4662次阅读
    快速理解<b class='flag-5'>STM32</b>位<b class='flag-5'>带操作</b>原理

    MCU_STM32的位带操作 -- bit banding

    带操作STM32芯片除了通用的寄存器访问,还有一个比较有意思的位带操作。这个位带的意思,就是每个比特(bit)位膨胀成一个32位的字(word),当访问这些字的时候就达到了访问“位”的目的,这就
    发表于 11-26 15:21 6次下载
    MCU_<b class='flag-5'>STM32</b>的位<b class='flag-5'>带操作</b> -- bit banding

    STM32带操作

    带操作:将一个位重新定义一个字的位带别名来操作位带区一个位对应位带别名区的四个字节针对stm32f10的代码实现:头文件:#ifndef __SYS_H_#define __SYS_H_
    发表于 11-26 18:06 12次下载
    <b class='flag-5'>STM32</b>位<b class='flag-5'>带操作</b>

    STM32单片机---位带操作

    STM32单片机---位带操作一、位带操作二、寄存器地址与别名地址转换技巧三、位带操作LED灯示例一、位带操作一、位
    发表于 11-29 14:51 2次下载
    <b class='flag-5'>STM32</b>单片机---位<b class='flag-5'>带操作</b>

    STM32带操作

    前言这两天闲着没事干,想写点东西给小伙伴看看,觉得就讲STM32带操作,大家在阅读别人在写STM32标准库里,比如PBout(9)=1,详细查看之后,又发现了某个头文件,如下,你会发现,啥也看不懂
    发表于 12-04 15:21 7次下载
    <b class='flag-5'>STM32</b> 位<b class='flag-5'>带操作</b>

    初识“位带操作

    目录初识“位带操作”什么是“位带操作”?STM32的“位带操作”为何会出现?STM32“位带操作
    发表于 01-12 17:18 0次下载
    初识“位<b class='flag-5'>带操作</b>”

    STM32带操作-详解-计算过程

    前言这篇文章主要用来讲解STM32中的位带操作,学习过51单片机的应改了解,在控制51单片机IO引脚时,只需要向某一个IO口赋值就可以实现,对应IO口的输出高或地。那么STM32可以不
    发表于 01-17 10:43 5次下载
    <b class='flag-5'>STM32</b>位<b class='flag-5'>带操作</b>-详解-计算过程

    八、STM32带操作

    一、位带区与位带别名区(一)位带介绍1、位带操作在学习51单片机时就已经使用过位操作,比如使用sbit对单片机IO口的定义,但是STM32中并没有这类关键字,而是通过访问位带别名区来实现,即通过将
    发表于 01-18 11:12 8次下载
    八、<b class='flag-5'>STM32</b>位<b class='flag-5'>带操作</b>

    stm32带操作有什么用

    STM32带操作是一种在ARM Cortex-M微控制器中使用的特殊技术,它允许同时处理多个位,并且可以提高代码效率和性能。在这篇文章中,我将详细介绍STM32带操作的原理、用途以
    的头像 发表于 12-22 16:02 1279次阅读

    STM32开发中的位运算以及位带操作

    STM32开发中的位运算以及位带操作  位运算是计算机中常用的一种操作方式,特别适用于对数据的单个或多个位进行操作。在STM32开发中,位运
    的头像 发表于 02-02 14:38 1485次阅读