0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

STM32為什么需要位帶操作呢?

冬至配餃子 ? 來源:小李的創(chuàng)客實驗室 ? 作者:初出茅廬的小李 ? 2023-07-08 15:16 ? 次閱讀

為什么需要位帶操作?

因為編程需要操作某個bit位來達到我們想要的功能,比如點燈需要操作GPIOA->ODR

的某個bit假設是第2bit,寫1就可以讓GPIO輸出一個高電平。

GPIOA- >ODR |= 1< 2;

這樣寫其實有三個隱含的操作:

//1.讀取ODR寄存器的值到內(nèi)存//2.改寫第2bit的值//3.再把改寫后的值寫進ODR寄存器

這樣的缺點:效率低

位帶操作就是為了解決這個問題,前提是硬件支持這么做。

位操作就是可以單獨的對一個比特位讀和寫,這個在 51 單片機中非常常見。51 單片機中通過關鍵字 sbit 來實現(xiàn)位定義,STM32沒有這樣的關鍵字,而是通過訪問位帶別名區(qū)來實現(xiàn),例如

sbit LED P1^2
LED = 1;//輸出高電平
LED = 0;//輸出低電平

這樣的優(yōu)點:效率高

什么是位帶別名區(qū)?

STM32本身不支持位操作,它發(fā)明了一種位帶操作來讓32的某些資源支持位操作。

這兩個區(qū)域一個是 SRAM 區(qū)的最低 1MB 空間,令一個是外設區(qū)最低 1MB 空間。

這兩個 1MB 的空間除了可以像正常的 RAM 一樣操作外,他們還有自己的 位帶別名區(qū) ,位帶別名區(qū)把這 1MB 的空間的 每一個位膨脹成一個 32 位的字 ,當訪問位帶別名區(qū)的這些字時,就可以達到訪問位帶區(qū)某個比特位的目的。

位帶別名區(qū)就是就是就是本來位的區(qū)域,變成了字的區(qū)域。

這里有個形象的解釋:

打個形象的比方,以某個村,就張村把,該村有3戶人家分別為A,B,C,我想給張村的A送禮,但是明文規(guī)定,不能給具體的個送禮,但是可以給村委會送禮,那我該怎么辦呢,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位寄存器來影響某個位的操作叫做位帶操作。

什么是位帶區(qū)?

我們可以看到下面圖中有兩個位帶區(qū),分別是SRAM區(qū)里的0x20000000-0x200FFFFF地址段和片內(nèi)外設區(qū)里的0x40000000-0x400FFFFF地址段(圖中標號①處),它們的地址空間大小都是1M字節(jié),在SRAM段內(nèi)外設地址段內(nèi)的這1M大小的空間就是位帶區(qū),說白了就是支持位帶操作的區(qū)域就是位帶區(qū)。

圖片

位帶區(qū)跟位帶別名區(qū)有怎樣的關系?

從上面映射圖上可以看到,SRAM區(qū)里的0x22000000-0x23FFFFFF地址段和外設區(qū)里0x42000000-0x43FFFFFF地址段都是位帶別名區(qū),兩個別名區(qū)空間大小都是32MB。那么,這32MB的位帶別名區(qū)地址空間是怎么與1MB的位帶區(qū)地址空間對應起來的呢?

答案:地址映射

那么問題來了?將1M字節(jié)里面的每一個bit映射到32M字節(jié)里面去,那么怎么映射呢?

首先明確一些概念:

1字節(jié)= 8bit
1字  = 4字節(jié) = 32bit

看圖

圖片

將1bit映射到1個字空間(擴大了32倍)

映射前的1個字節(jié) = 映射后的8個字(擴大了32倍 8 * 4 = 32字節(jié))

那么就得出以下結論:

映射前的1個字節(jié) = 映射后的32個字節(jié)

映射前的1M字節(jié) = 映射后的32M字節(jié)

圖片

0x40000000地址處的1個bit變成了0x42000010地址處的32個bit

為什么要將1bit空間要映射到一個字空間里去呢?我映射到1字節(jié)或者2字節(jié)的地址空間不行嗎?我只能說,STM32是一個32位的機器,內(nèi)核按字尋址的話尋址速度是最快的,所以別問這么多為什么,如果問了,答案就是為了速度。就好比你買個電腦用一個小箱子裝著但是順豐快遞發(fā)貨走的是集裝箱,理論上來說裝到集裝箱里空運是最快的,要不然沒辦法上飛機啊......各位想想好像是這么個道理哈

位帶操作該怎么用?

我們已經(jīng)知道了位帶區(qū)就是支持位操作的地址段,位帶別名區(qū)就是位帶區(qū)的地址映射,操作位帶別名區(qū)就等價于操作位帶區(qū),并且我們知道了大致的映射過程,那么在STM32實際使用中又是怎么應用的呢?

在《Cortex M3權威指南》中,前人已經(jīng)整理出了位帶別名區(qū)與位帶區(qū)地址對應關系的表達式,使用的時候只要套用公式就可以,如下圖

圖片

將兩個公式合并一下就得到:

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

式中A為位帶區(qū)地址,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))

// 把位帶別名區(qū)地址轉換成指針
#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所有片內(nèi)外設的寄存器進行封裝(FSMC除外)

如圖

圖片

使用注意事項

  • 使用上面封裝好的位帶操作之前,要先對IO端口進行配置,否則操作結果不可預期。
  • PAout(n)作為左值使用,PAin(n)作為右值使用。(跟51單片機一樣,我想你是懂51的)
  • 最后,使用的過程中要注意一點,強制地址轉換的時候一定要使用volatile關鍵字進行修飾,否則這個操作可能會被編譯器優(yōu)化掉

使用例子

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溫度傳感器示例-位帶控制實現(xiàn)時序

#include "ds18b20.h"
/*
函數(shù)功能: 硬件初始化--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;
}

/*
函數(shù)功能: 發(fā)送復位脈沖檢測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;
}

/*
函數(shù)功能: DS18B20寫一個字節(jié)數(shù)據(jù)
*/
void DS18B20_WriteOnebyte(u8 cmd)
{
   u8 i;
   DS18B20_OUT_MODE(); //輸出模式
   for(i=0;i< 8;i++)
  {
       if(cmd&0x01) //發(fā)送1
      {
           DS18B20_OUT=0;
           delay_us(15);
           DS18B20_OUT=1;
           delay_us(45);
           DS18B20_OUT=1;
           delay_us(2);
      }
       else //發(fā)送0
      {
           DS18B20_OUT=0;
           delay_us(15);
           DS18B20_OUT=0;
           delay_us(45);
           DS18B20_OUT=1;
           delay_us(2);
      }
       cmd >>=1;
  }
}

/*
函數(shù)功能: DS18B20讀一個字節(jié)數(shù)據(jù)
*/
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;
}

/*
函數(shù)功能: 讀取一次DS18B20的溫度數(shù)據(jù)
返回值: 讀取的溫度數(shù)據(jù)高低位
*/
u16 DS18B20_ReadTemp(void)
{
  u16 temp;
  u8 t_L,t_H;
  if(DS18B20_Check())return 1;
  DS18B20_WriteOnebyte(0xCC); //跳躍 ROM 指令 --不驗證身份
  DS18B20_WriteOnebyte(0x44); //發(fā)送溫度轉換指令
   
  if(DS18B20_Check())return 2;
  DS18B20_WriteOnebyte(0xCC); //跳躍 ROM 指令 --不驗證身份
  DS18B20_WriteOnebyte(0xBE); //讀取RAM里的數(shù)據(jù)
 
  //讀取溫度
  t_L=DS18B20_ReadOnebyte(); //低字節(jié)
  t_H=DS18B20_ReadOnebyte(); //高字節(jié)
  temp=t_H< 8|t_L;
  return temp;
}
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • STM32
    +關注

    關注

    2264

    文章

    10858

    瀏覽量

    354395
  • 51單片機
    +關注

    關注

    273

    文章

    5697

    瀏覽量

    123035
  • GPIO
    +關注

    關注

    16

    文章

    1189

    瀏覽量

    51847
  • SRAM存儲器
    +關注

    關注

    0

    文章

    88

    瀏覽量

    13261
  • FSMC模塊
    +關注

    關注

    0

    文章

    9

    瀏覽量

    1916
收藏 人收藏

    評論

    相關推薦

    STM32為什么需要位帶操作?

    STM32
    YS YYDS
    發(fā)布于 :2023年05月12日 21:20:53

    帶操作STM32芯片的特點有哪些

    帶操作STM32芯片除了通用的寄存器訪問,還有一個比較有意思的位帶操作。這個位帶的意思,就是每個比特(bit)位膨脹成一個32位的字(word),當訪問這些字的時候就達到了訪問“位”的目的,這就
    發(fā)表于 12-09 06:42

    STM32帶操作的方法

    前言這篇文章主要用來講解STM32中的位帶操作,學習過51單片機的應改了解,在控制51單片機IO引腳時,只需要向某一個IO口賦值就可以實現(xiàn),對應IO口的輸出高或地。那么STM32可以不
    發(fā)表于 01-17 06:27

    怎樣去使用stm32的位帶操作

    怎樣去使用stm32的位帶操作?stm32支持位帶操作的兩個內(nèi)存區(qū)的范圍分別是多少?
    發(fā)表于 02-25 07:14

    為什么ch32vxx會不支持stm32帶操作

    為什么ch32vxx會不支持stm32帶操作?怎樣去解決ch32vxx不支持stm32帶操作的問題
    發(fā)表于 02-28 07:18

    快速理解STM32帶操作原理

    Bit-banding簡稱位帶,有人也叫位段。支持位帶操作后,可以使用普通的加載/存儲指令來對單一的比特進行讀寫。很多朋友是從學習51單片機過來的,都知道P1.1這個引腳可以單獨控制,我們操作的這個引腳就是一個Bit位。我們都知道在S
    的頭像 發(fā)表于 09-03 15:40 ?4653次閱讀
    快速理解<b class='flag-5'>STM32</b>位<b class='flag-5'>帶操作</b>原理

    MCU_STM32的位帶操作 -- bit banding

    帶操作STM32芯片除了通用的寄存器訪問,還有一個比較有意思的位帶操作。這個位帶的意思,就是每個比特(bit)位膨脹成一個32位的字(word),當訪問這些字的時候就達到了訪問“位”的目的,這就
    發(fā)表于 11-26 15:21 ?6次下載
    MCU_<b class='flag-5'>STM32</b>的位<b class='flag-5'>帶操作</b> -- bit banding

    STM32帶操作

    帶操作:將一個位重新定義一個字的位帶別名來操作位帶區(qū)一個位對應位帶別名區(qū)的四個字節(jié)針對stm32f10的代碼實現(xiàn):頭文件:#ifndef __SYS_H_#define __SYS_H_
    發(fā)表于 11-26 18:06 ?12次下載
    <b class='flag-5'>STM32</b>位<b class='flag-5'>帶操作</b>

    STM32單片機---位帶操作

    STM32單片機---位帶操作一、位帶操作二、寄存器地址與別名地址轉換技巧三、位帶操作LED燈示例一、位帶操作一、位
    發(fā)表于 11-29 14:51 ?2次下載
    <b class='flag-5'>STM32</b>單片機---位<b class='flag-5'>帶操作</b>

    STM32帶操作

    前言這兩天閑著沒事干,想寫點東西給小伙伴看看,覺得就講STM32帶操作,大家在閱讀別人在寫STM32標準庫里,比如PBout(9)=1,詳細查看之后,又發(fā)現(xiàn)了某個頭文件,如下,你會發(fā)現(xiàn),啥也看不懂
    發(fā)表于 12-04 15:21 ?7次下載
    <b class='flag-5'>STM32</b> 位<b class='flag-5'>帶操作</b>

    初識“位帶操作

    目錄初識“位帶操作”什么是“位帶操作”?STM32的“位帶操作”為何會出現(xiàn)?STM32“位帶操作
    發(fā)表于 01-12 17:18 ?0次下載
    初識“位<b class='flag-5'>帶操作</b>”

    STM32帶操作-詳解-計算過程

    前言這篇文章主要用來講解STM32中的位帶操作,學習過51單片機的應改了解,在控制51單片機IO引腳時,只需要向某一個IO口賦值就可以實現(xiàn),對應IO口的輸出高或地。那么STM32可以不
    發(fā)表于 01-17 10:43 ?5次下載
    <b class='flag-5'>STM32</b>位<b class='flag-5'>帶操作</b>-詳解-計算過程

    八、STM32帶操作

    一、位帶區(qū)與位帶別名區(qū)(一)位帶介紹1、位帶操作在學習51單片機時就已經(jīng)使用過位操作,比如使用sbit對單片機IO口的定義,但是STM32中并沒有這類關鍵字,而是通過訪問位帶別名區(qū)來實現(xiàn),即通過將
    發(fā)表于 01-18 11:12 ?8次下載
    八、<b class='flag-5'>STM32</b>位<b class='flag-5'>帶操作</b>

    stm32帶操作有什么用

    STM32帶操作是一種在ARM Cortex-M微控制器中使用的特殊技術,它允許同時處理多個位,并且可以提高代碼效率和性能。在這篇文章中,我將詳細介紹STM32帶操作的原理、用途以
    的頭像 發(fā)表于 12-22 16:02 ?1250次閱讀

    STM32開發(fā)中的位運算以及位帶操作

    STM32開發(fā)中的位運算以及位帶操作? 位運算是計算機中常用的一種操作方式,特別適用于對數(shù)據(jù)的單個或多個位進行操作。在STM32開發(fā)中,位運
    的頭像 發(fā)表于 02-02 14:38 ?1458次閱讀