1. 程式人生 > 其它 >STM32的位段操作,基於STM32F103xxx多方法點亮LED燈

STM32的位段操作,基於STM32F103xxx多方法點亮LED燈

技術標籤:c語言嵌入式微控制器stm32

首先我把位段操作的步驟說一下:

(直接cp手冊上的原文)

下面的對映公式給出了別名區中的每個字是如何對應位帶區的相應位的:
bit_word_addr = bit_band_base + (byte_offset×32) + (bit_number×4)
其中:
bit_word_addr是別名儲存器區中字的地址,它對映到某個目標位。
bit_band_base是別名區的起始地址。
byte_offset是包含目標位的位元組在位段裡的序號
bit_number是目標位所在位置(0-31)
例子:
下面的例子說明如何對映別名區中SRAM地址為0x20000300的位元組中的位2:

0x22006008 = 0x22000000 + (0x300×32) + (2×4).
對0x22006008地址的寫操作與對SRAM中地址0x20000300位元組的位2執行讀-改-寫操作有著相同的效果

我們需要知道位段操作同樣適用於外設的對映地址

外設起始地址為 0x40000000;

我們以stm32f103RC6晶片為例:

我買的該板子pd2引腳連著led燈,因此需要知道的一些關鍵資訊包括如下

//GPIOD的對映地址

#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE) //0x4001 1400

//GPIO的暫存器的定義如下,其中__IO就是volatile關鍵字,對於GPIOD而言,我們直接將GPIOD包含的暫存器的地址標上,方便檢視

typedef struct
{
__IO uint32_t CRL; //0x40011400
__IO uint32_t CRH; //0x40011404
__IO uint32_t IDR; //0x40011408
__IO uint32_t ODR; //0x4001140c
__IO uint32_t BSRR; //0x40011410
__IO uint32_t BRR; //0x40011414
__IO uint32_t LCKR; //0x40011418
} GPIO_TypeDef;

如何點亮led燈?

我們可以對 GPIOD的 ODR 暫存器或者BSSR暫存器進行操作;

1、對ODR暫存器的 bit2(0~31)寫0,則 PD2輸出低電平,對ODR暫存器的 bit2

(0~31)寫1,則 PD2輸出高電平

2、對BSSR的 bit2(0~31)寫1,PD2輸出高電平,對BSSR的 bit(2+16)(0~31)寫1,PD2輸出低電平, //16 代表引腳為 0~15 ,16個引腳 。對BSSR暫存器寫0 ,不產生任何動作

因此我們可以用以下的方法來控制LED燈的亮滅。

1、用位段操作來控制ODR或者 BSSR暫存器

2、在直接的外設地址暫存器中去寫資料

因此我們可以按照以下方法

①第一種是對BSSR進行位段操作,我們看之前的 BSSR的地址為0x40011410,則位段操作地址(對PD2引腳,因此是 bit2)為 0x4200 0000 + 0x11410*32 + 2*4 引腳輸出高電平

引腳需要輸出低電平時 則位段操作為(對BSSR暫存器的18bit進行操作) 地址為0x4200 0000 + 11412*32 +18*4

②對BSSR暫存器的對映地址直接寫資料 BSSR的地址為 0x40011410,則我們在 bit2寫1 ,操作為*((volatile uint32_t *)0x40011410) = 0x04; 給PD2輸出高電平

或者bit18寫1, *((volatile uint32_t *)0x40011410) = 0x040000; 給PD2輸出低電平

③ 當然我們可以直接對 GPIOD的BSSR暫存器進行操作 GPIOD->BSRR = 4;//輸出高電平

GPIOD->BSRR = (uint32_t)4<<16; //輸出低電平 也是完全ok的

④我們對 ODR暫存器進行位段操作 ODR的對映地址為 0x4001140c ,位段操作 地址為0x4200 0000 + 0x1140c*32 + 2*4

⑤直接對ODR暫存器進行寫資料

⑥呼叫HAL庫

程式碼如下,只起到參考作用

#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))   

int main()
{
//....
//..一些GPIO初始化
while(1)
{
    HAL_Delay(1000);

 switch(way)
    {
      case 1:
	    //TODO:1   對BSSR暫存器進行位段操作
	    BIT_ADDR(0x40011410,2) = 1;     //巨集定義展開為  *((volatile unsigned long*)0x42228208) = 1;
	    HAL_Delay(1000);
	    BIT_ADDR(0x40011410,18) = 1;
	    break;
      case 2:
	    //TODO:2 對BSSR暫存器的對映地址直接寫數
	    *((volatile uint32_t *)0x40011410) = 0x04;
	    HAL_Delay(1000);
	    *((volatile uint32_t *)0x40011410) = 0x040000;
	    break;
      case 3:
	    //TODO:3 對BSSR暫存器的操作
	    GPIOD->BSRR = 4;
	    HAL_Delay(1000);
	    GPIOD->BSRR = (uint32_t)4<<16;
	    break;
      case 4:
	    //TODO:4  對ODR暫存器進行位段操作
	    BIT_ADDR((GPIOD_BASE+12),2) = 1;
	    HAL_Delay(1000);
	    BIT_ADDR((GPIOD_BASE+12),2) = 0;
	    break;
      case 5:
	    //TODO:5 對ODR暫存器的對映地址直接寫數
	    *((volatile uint32_t *)0x4001140c) = 0x04;
	    HAL_Delay(1000);
	    *((volatile uint32_t *)0x4001140c) = 0x00;
	    break;
      case 6:
	    //TODO:6  呼叫HAL庫函式
	    HAL_GPIO_WritePin(GPIOD, LED1_Pin, 0);
	    HAL_Delay(1000);
	    HAL_GPIO_WritePin(GPIOD, LED1_Pin, 1);
	    break;
      default:break;

    }
}
}

功能就是led閃爍。用的IDE是 STM32CUBE ide 講真的確非常好用!!!(