色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

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

3天內不再提示

詳解Cortex-M位帶操作

嵌入式大雜燴 ? 來源:嵌入式大雜燴 ? 作者:嵌入式大雜燴 ? 2023-04-27 15:03 ? 次閱讀

我相信很多朋友在學習單片機之前都學習過51單片機,假設在51單片機的P1.1的IO口上掛了一個LED,那么你單獨對LED的操作就是P1.1 = 0或P1.1 = 1,這樣你就可以單獨的對P1端的第一個IO口進行上下拉操作,然而對于STM32,是沒有這種操作的,那么為了像51單片機一樣能夠單獨的對某個端的某一個IO單獨操作,就引入了__位帶操作__,簡而言之,就是為了去單獨操作STM32里面PA的第1個IO口,所以才有了位帶這樣的操作機制。

1 什么是位帶操作

在講解位帶操作之前,首先要搞清楚什么是位帶操作。我們知道,32位的處理器的32位地址總線提供了4G的地址空間,幾乎所有的嵌入式產品是足夠用的。 Cortex-M就利用了額外的空間實現了稱為位帶(Bit-Banding)操作的硬件屬性,該技術使用地址空間的兩個不同區域來指向同一物理地址 。在主位帶區域,每個地址對應一個字節的數據,在“位帶別名”區域中,每個地址對應同一個數據的一個位。

如下圖所示。在CM3的寄存器映射圖中有1MB的 bit band區,這里被稱為位帶區,與之對應的是32MB的bit band別名區,這里被稱為位帶別名區。

C:\\Users\\BruceOu\\Desktop\\20191203162525194.png

STM32的位帶別名區會把位帶區中的每一位膨脹成一個32位的字,所以相應的別名區的內存也會是位帶區的32倍。從上圖可以看出,位帶操作同時支持SRAM和片上外設,支持位帶操作的兩個內存區域范圍如下:

SRAM區:0x20000000 ~ 0x200FFFFF,最低1M的范圍;

片上外設區: 0x40000000 ~ 0x400FFFFF,最低1M的范圍;

位帶操作就是把位帶區中一個地址的8個位分別映射到位帶別名區的8個地址(LSB有效,即最低位有效),通過操作相應地址的方式實現操作某個位。以SRAM為例,位帶區和位帶別名區的映射如下圖所示:

C:\\Users\\BruceOu\\Desktop\\005xOcwJzy7gW7umetS44.png

位帶區里每個地址的每1位膨脹為別名區里一個32位的字(32位處理器中,1字=4字節),例如:0x20000000的第0位對應0x22000000,第1位對應0x22000004等。

2 位帶操作的計算公式

既然位帶操作屬于Cortex-M內核的一部分,那么在Cortex-M官方手冊也是給出了相應的計算公式的,其通用公式如下:

別名區地址 = 別名區起始地址 + (位字節地址偏移量 * 8 + n) * 4

其中,8表示一個字節有8位,4表示膨脹了4個字節,因此位帶區和位帶別名區也就是32倍的關系。

兩個區的計算公式為:

SRAM區:AliasAddr = 0x22000000 + (A - 0x20000000) * 32 + n * 4

片上外設區:AliasAddr = 0x42000000 + (A - 0x40000000)* 32 + n * 4

其中,AliasAddr是別名區的地址,A是位帶區的地址,n是該端口的上的某一位。

接下來就是對這個地址進行操作了,寫1,該位輸出1,寫0,就輸出0。

3 位帶操作代碼實現

這里STM32F1為例,根據STM32的《RM0008 Reference manual》手冊,其GPIO的地址映射如下:

1682578697232m0vpen34s4

GPIOx_ODR 寄存器如下:

1682578697691w447n9kidh

每個寄存器32位,占4個地址,在訪問或修改某個寄存器時,是從首地址開始的,邏輯運算則是直接可涵蓋到32bit,offset 為 0x0C。GPIOA 的首地址為0x40010800,因此GPIOx_ODR 寄存器的地址為0x4001080C。則所有的GPIO映射如下:

//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08

上述只是位帶區的地址,根據位帶操作的計算公式,則操作位帶別名區的地址方法如下:

//IO口操作宏定義
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

以上代碼的第一句是轉換的關鍵,當然相對的前面的計算公式做了優化,也就是將SRAM和片上外設合并在一起。addr & 0XF0000000 得到SRAM和片上外設的首地址,然后加0x2000000表示位帶別名區相對位帶區的偏移量,(addr &0xFFFFF)<<5)和(bitnum<<2)就是前面“*32”和“*4”,只是換成了移位操作,因為移位操作相對乘法運算速度更快。

好了,接下來使用位帶操作來寫一個GPIO流水燈,同時使用庫函數來做比較。

【main.c】

/* Includes ------------------------------------------------------------------*/
#include "stm32f1_bsp_led.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/*簡單延時函數*/
void Delay(uint32_t xms); 

/* Private functions ---------------------------------------------------------*/

/**
  * @brief     主函數
  * @param     None
  * @retval    
  */
int main(void)
{
    /* LED 初始化 */
    LED_GPIO_Config();
 
    while (1)
    {
#if 0
        GPIO_SetBits(GPIOB,GPIO_Pin_0);  // 亮
        Delay(0xfFfff);
        GPIO_ResetBits(GPIOB,GPIO_Pin_0);  // 滅

        GPIO_SetBits(GPIOG,GPIO_Pin_6);  // 亮
        Delay(0xfFfff);
        GPIO_ResetBits(GPIOG,GPIO_Pin_6);  // 滅

        GPIO_SetBits(GPIOG,GPIO_Pin_7);  // 亮
        Delay(0xffFff);
        GPIO_ResetBits(GPIOG,GPIO_Pin_7);  // 滅
#else
        PBout(0) = 1;  // 亮
        Delay(0xfFfff);
        PBout(0) = 0;  // 滅

        PGout(6) = 1;  // 亮
        Delay(0xfFfff);
        PGout(6) = 0;  // 滅

        PGout(7) = 1;  // 亮
        Delay(0xffFff);
        PGout(7) = 0;  // 滅
#endif

    }
}

/**
  * @brief  延時函數
  * @param  
            xms 延時長度
  * @retval None
  */
void Delay( uint32_t xms)
{
    //for(; nCount != 0; nCount--);(方法一)
    while(xms--);//(方法二)
}

【stm32f1_bsp_led.c】

/* Includes ------------------------------------------------------------------*/
#include "stm32f1_bsp_led.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
  * @brief  初始化LED的GPIO
  * @param  None
  * @retval None
  */
void LED_GPIO_Config(void)
{
    /*定義一個GPIO_InitTypeDef類型的結構體*/
    GPIO_InitTypeDef GPIO_InitStructure;

    /*開啟LED的外設時鐘*/
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE); 

    /*設置IO口*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //設置引腳模式為通用推挽輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //設置引腳速率為50MHz 

    /*調用庫函數,初始化GPIOB0*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  //選擇要控制的GPIOB引腳
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;/*選擇要控制的引腳*/
    GPIO_Init(GPIOG, &GPIO_InitStructure);

    /* 開啟所有led燈*/
    GPIO_SetBits(GPIOB, GPIO_Pin_0);
    GPIO_SetBits(GPIOG, GPIO_Pin_6|GPIO_Pin_7);	 
}

【stm32f1_bsp_led.h】

#ifndef __STM32F1_BSP_LED_H__
#define __STM32F1_BSP_LED_H__

#ifdef __cplusplus
 extern "C" {
#endif 

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
//位帶操作,實現51類似的GPIO控制功能
//具體實現思想,參考<
//IO口操作宏定義
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
 
//IO口操作,只對單一的IO口!
//確保n的值小于16!
#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 ON  1
#define OFF 0

/* 帶參宏,可以像內聯函數一樣使用 */
#define LED1(a)    if (a)    \\
                    GPIO_SetBits(GPIOB,GPIO_Pin_0);\\
                    else        \\
                    GPIO_ResetBits(GPIOB,GPIO_Pin_0)

#define LED2(a)    if (a)    \\
                    GPIO_SetBits(GPIOG,GPIO_Pin_6);\\
                    else        \\
                    GPIO_ResetBits(GPIOG,GPIO_Pin_6)

#define LED3(a)    if (a)    \\
                    GPIO_SetBits(GPIOG,GPIO_Pin_7);\\
                    else        \\
                    GPIO_ResetBits(GPIOG,GPIO_Pin_7)


/* 直接操作寄存器的方法控制IO */
#define    digitalHi(p,i)            {p->BSRR=i;}            //設置為高電平        
#define    digitalLo(p,i)            {p->BRR=i;}             //輸出低電平
#define    digitalToggle(p,i)        {p->ODR ^=i;}           //輸出反轉狀態


/* 定義控制IO的宏 */
#define LED1_TOGGLE        digitalToggle(GPIOB,GPIO_Pin_0)
#define LED1_ON            digitalHi(GPIOB,GPIO_Pin_0)
#define LED1_OFF           digitalLo(GPIOB,GPIO_Pin_0)

#define LED2_TOGGLE        digitalToggle(GPIOC,GPIO_Pin_4)
#define LED2_ON            digitalHi(GPIOG,GPIO_Pin_6)
#define LED2_OFF           digitalLo(GPIOG,GPIO_Pin_6)

#define LED3_TOGGLE        digitalToggle(GPIOC,GPIO_Pin_3)
#define LED3_ON            digitalHi(GPIOG,GPIO_Pin_7)
#define LED3_OFF           digitalLo(GPIOG,GPIO_Pin_7)
/* Exported functions ------------------------------------------------------- */
void LED_GPIO_Config(void);

#ifdef cplusplus
}
#endif

#endif /* __STM32F1_BSP_LED_H__ */

不管使用哪種方式,其實驗現象都是一樣的,但是使用位帶操作更方便些,操作者步驟更少,下面舉例說明。

實例:欲設置地址 0x2000_0000 中的比特 2,則使用普通操作和位帶操作的設置過程如下圖所示:

1682578698223h4kgh19i0c

普通操作和位帶操作的匯編對比代碼如下:

1682578698688e3821chru8

位帶讀操作相對簡單,普通操作和位帶操作的設置過程如下圖所示:

1682578699026zwj46gliu8

普通操作和位帶操作的匯編對比代碼如下:

1682578699376yo56mm73ev

可以看出位帶操作的步驟更少,相對普通操作更簡潔。

而且位帶操作屬于原子操作,在多任務系統中,位帶操作可以解決共享資源中的紊亂危象,關于該部分內容可以參看《Cortex-M3權威指南》。

__總的來說,位帶的主要優點__是數據的一個單獨位可以通過一條指令來讀或者寫,而不需要操作一些利的寄存器。例如,一條從位帶別名區域地址進行讀操作的LDR指令會將值0或者1加1載入寄存器。類似的,一條STR指令在向位帶別名區的地址寫入時,只是修改主區域中數據的一位。當然修改需要由硬件來執行讀寫操作,但是只有一條指令(STR)被取指并執行。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 處理器
    +關注

    關注

    68

    文章

    19259

    瀏覽量

    229651
  • 單片機
    +關注

    關注

    6035

    文章

    44554

    瀏覽量

    634634
  • 嵌入式
    +關注

    關注

    5082

    文章

    19104

    瀏覽量

    304804
  • 寄存器
    +關注

    關注

    31

    文章

    5336

    瀏覽量

    120230
  • Cortex-M
    +關注

    關注

    2

    文章

    229

    瀏覽量

    29752
收藏 人收藏

    評論

    相關推薦

    Cortex-M 系列處理特點和區別詳解

    3ARM Cortex-M4“8/16 ”應用“16/32 ”應用“32 /DSC”應用低成本和簡單性性能效率有效的數字信號控制Cortex-
    發表于 01-14 10:13

    帶操作原理詳解+LED實驗的相關資料分享

    【嵌入式系統】帶操作原理詳解+LED實驗解讀1、帶操作的實質
    發表于 12-16 07:06

    帶操作的相關資料推薦

    注意:本文中關于STM32的帶操作原理只適用于Cortex-M3和Cortex-M4(F)內核處理器,Cortex-M系列的其他內核處理器
    發表于 02-07 09:24

    關于Cortex-M 調試應用的介紹

    Cortex-M 調試應用
    的頭像 發表于 07-10 00:56 ?2618次閱讀

    快速理解STM32帶操作原理

    作者:strongerHuang 說到位帶操作,可能很多人比較陌生,但說到控制IO,你肯定不會陌生。有的項目為了最大效率控制IO,使用帶操作。下面就來簡單說說未帶操作的內容。 一、初
    的頭像 發表于 09-03 15:40 ?4722次閱讀
    快速理解STM32<b class='flag-5'>位</b><b class='flag-5'>帶操作</b>原理

    Cortex-M跑Linux操作系統能行嗎

    單片機、Cortex-M、Linux它們和嵌入式有什么區別? 跑 Linux 操作系統需要什么處理器?ARM9、ARM11? Cortex-M比ARM9更新,為什么不能跑Linux? 相信很多小伙伴
    的頭像 發表于 07-19 09:35 ?1932次閱讀
    <b class='flag-5'>Cortex-M</b>跑Linux<b class='flag-5'>操作</b>系統能行嗎

    Cortex-M可以跑Linux操作系統嗎?

    Cortex-M可以跑Linux操作系統嗎?
    發表于 12-01 11:36 ?2次下載
    <b class='flag-5'>Cortex-M</b>可以跑Linux<b class='flag-5'>操作</b>系統嗎?

    CortexM3內核學習筆記(二):帶操作

    概念Cortex-M3的存儲器系統支持所謂的“帶”(bit‐band)操作。通過它,實現了對單一比特的原子操作
    發表于 12-01 12:51 ?6次下載
    <b class='flag-5'>Cortex</b>‐<b class='flag-5'>M</b>3內核學習筆記(二):<b class='flag-5'>位</b><b class='flag-5'>帶操作</b>

    STM32學習筆記:帶操作(Bit_band Operations)

    注意:本文中關于STM32的帶操作原理只適用于Cortex-M3和Cortex-M4(F)內核處理器,Cortex-M系列的其他內核處理器
    發表于 12-04 12:36 ?0次下載
    STM32學習筆記:<b class='flag-5'>位</b><b class='flag-5'>帶操作</b>(Bit_band Operations)

    初識“帶操作

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

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

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

    STM32F407入門開發: 帶操作

    STM32F407的帶操作可以實現類似51單片機中寄存器的操作方法,操作GPIO口代碼簡潔方便。 關于段的
    的頭像 發表于 05-28 13:50 ?3657次閱讀
    STM32F407入門開發: <b class='flag-5'>位</b><b class='flag-5'>帶操作</b>

    Cortex-M 內核中斷/異常系統、中斷優先級/嵌套 詳解

    Cortex-M 內核中斷/異常系統、中斷優先級/嵌套 詳解
    的頭像 發表于 09-27 15:29 ?2177次閱讀
    <b class='flag-5'>Cortex-M</b> 內核中斷/異常系統、中斷優先級/嵌套 <b class='flag-5'>詳解</b>

    Cortex-M帶操作的原理

    Cortex-M帶操作的原理
    的頭像 發表于 10-24 15:27 ?881次閱讀
    <b class='flag-5'>Cortex-M</b><b class='flag-5'>位</b><b class='flag-5'>帶操作</b>的原理

    stm32帶操作有什么用

    STM32帶操作是一種在ARM Cortex-M微控制器中使用的特殊技術,它允許同時處理多個位,并且可以提高代碼效率和性能。在這篇文章中,我將詳細介紹STM32
    的頭像 發表于 12-22 16:02 ?1386次閱讀
    主站蜘蛛池模板: 女bbbbxxx孕妇| 亚洲欧美国产综合在线一区| 在线播放成人无码日| 国产精品搬运| 欧美激情久久久久久久大片| 亚洲电影第1页| 国产AV99激情久久无码天堂| 嗯 用力啊 嗯 c我 啊哈老师| 亚洲精品天堂自在久久77| 国产99久久九九精品无码不卡| 毛片手机在线观看| 艳妇臀荡乳欲伦岳TXT下载| 国产精品内射久久久久欢欢| 情欲.美女高潮| 97 sese| 精品综合久久久久久8888| 小SAO货叫大声点妓女| 超碰久久国产vs| 美女丝袜夹b| 永久免费毛片| 湖南张丽大战黑人hd视频| 我的年轻漂亮继坶三级| 成人免费毛片观看| 欧美多毛的大隂道| 99re 这里只有精品| 久久亚洲精品AV成人无码| 一个人在线观看免费视频| 娇小亚裔被两个黑人| 亚洲AV无码国产精品色午夜情| 国产成a人片在线观看视频99| 日本高清不卡码无码v亚洲| ae58老司机福利| 美女张开腿露尿口给男人亲| 中文字幕 人妻熟女| 九九热在线视频精品店| 亚洲色大成网站www久久九九| 国产在线伊人| 亚洲第一伊人| 簧片在线观看| 伊人久久电影院| 久久免费国产视频|