天天看點

STM32F10X 位帶操作

在STM32單片機的學習中,有一個最大的特點就是你所編寫的STM32的程式操作,基本上都是總線的操作,比如GPIOB->ODR = 0x0001(或者直接等于1),這樣的操作必須要考慮整個寄存器的資料,但其實如果學過51單片機的人都知道,我們除了對于總線操作外,我們還是經常會對單獨的位進行操作,比如

#include <reg52.h>
sbit led1 = P0^0;
led1 = 1 ;
           

我想學過51的人對于上面的代碼再熟悉不過了,這就是我們對單獨的一個位進行的操作,那難道在STM32裡面,就沒辦法這樣操作了嗎,答案是否定的,這就要引出位帶操作這個概念了。

首先什麼是位帶操作,按照個人的了解,就是把每個bit,膨脹(映射)到32位的存儲器上去,這樣開發者就可以對每一位的資料編寫。

那為什麼要引入位帶操作呢,首先一點就是操作比較的簡單,我們隻要考慮一位的資料就可以了,不用考慮總線上所有的資料變化。

還有一點,也是很重要的一點(但可能有點難了解,要是我說的不清楚還請見諒)。就是使用位帶操作的時候,在硬體層面是不允許中斷打斷的,這就是我們所說的“原子”特性,我們都知道在我們寫的程式中會有很多很多的中斷,這樣也就會存在一個問題,那就是如果我們的變量還沒有發生變化,就被中斷打斷,去執行了中斷的程式,那麼變量的值就會和我們想要的值有了出入,然後我們等代碼再回到原程式運作的時候,這時候我們又會因為上次的指派沒有成功而導緻重新的指派,這樣我們的變量的值就徹底和我們預料的不一樣了。

STM32F10X 位帶操作

(https://blog.csdn.net/Emmy_kanly/article/details/80865318/這張圖檔我是取自于這個部落客,如果涉及版權,我會及時的删除這篇部落格。)

從這幅圖我們可以看到(左邊是沒有使用位帶操作,右邊使用了bit-band),我們可以很清楚的看到,在使用了位帶操作的程式裡面,在彙編的層面,是把指派塊作為了一個整體,中斷隻能從指派前和指派後進行,在指派的過程中是沒法進行的,這樣就確定了整個程式的可靠性和穩定性。

那我們要怎麼進行位帶操作呢,首先我們要了解位帶操作的原理。

首先位帶操作不是所有地方都可以用,一般隻能用在SARM區和外設寄存器區。SARM的位帶區是從0X22000000開始的,而外設的位帶區是從0X42000000開始的,由于前面所提到的位帶區就是在我們的寄存器膨脹為32位,這樣就可以一一的進行操作,根據這個原理,我們就可以寫出公式,完成位帶區與位帶别名區的一個轉化。

PERIPHERAL_Addr = 0x4200_0000 + (A - 0x4000_0000)32 + n4

SARM_Addr = 0x2200_0000 + (A - 0x4000_0000)32 + n4

其中A 為寄存器的位址,n代碼位号。

*這裡稍微的說明一下為什麼位帶别名區可以膨脹為位帶區的32位,這是因為在位帶區的一位,相當于是位帶别名區的4位元組。

再根據上面的兩算式,進行一下統一,就可以得到一個統一的位帶區與位帶别名區的一個轉化。

(addr & 0xF000_0000) + 0x2000_0000 + ((addr & 0x00FF_FFFF) << 5)+(bitnum << 2)

這個公式比較難了解的就是((addr & 0x00FF_FFFF) << 5)這塊,首先我們看addr & 0x00FF_FFFF,這就是因為在前面(addr & 0xF000_0000) + 0x2000_0000 中,已經完成了前兩位的操作,是以我們隻看後面的6位,不看前面的2位,是以是&0x00FF_FFFF,之是以要再左移5位,是因為2^5 = 32 , 是以是對應了前面的*32。後面的左移兩位也就是同理了。

下面我就以GPIO->ODR寄存器為例,來代碼實作一下位帶操作。

//bit_band.h
#ifndef BIT_BAND_H
#define BIT_BAND_H
#include "stm32f10x.h"

#define GPIOB_ODR_Addr  GPIOB_BASE + 0X0C
#define LED_OUT(bitnum)  (GPIOB_ODR_Addr & 0xF000_0000) + 0x2000_0000 + ((GPIOB_ODR_Addr & 0x00FF_FFFF) << 5)+(bitnum << 2)

void LED_OUTPUT();

#endif /*BIT_BAND_H*/
           
//bit_band.c
void LED_OUTPUT(void)
{
	LED_OUT(0) = 0;
}
           
//main函數
int main(void)
{
	LED_OUTPUT();	
}
           

這樣我們就可以用位帶操作來點亮一個led燈,使我們的程式更加的可靠。

繼續閱讀