1、閑言
最近開發的時候,用到了STM32F030F4P6型號的單片機,它只有20個引腳,價格非常便宜,但是功能齊全;定時器、外部中斷、串口、IIC、SPI、DMA和WWDG等等,應用盡有,非常適合用來做小設備。可是有個問題是,它是Cortex-M0內核的,不像M3,M4內核一樣,可以支持位帶操作(就是一位一位地操作,像80C51單片機一樣),這就給程序移植或者開發帶來了一點點小麻煩,因此我就利用C語言結構的位段操作,實現了個訪位帶操作,只是在效率可能會稍遜於真正的位帶操作,但是代碼上可以兼容,基本上可以應用於任何一款處理器。希望能夠幫到大家。
2、位帶操作基本知識
關於真正的位帶操作,網上有不少的資料,寫得也很詳細,在這里我只是簡單說一下我的理解。另,不理解真正的位帶操作,也不影響對本文的理解,因本文跟位帶操作沒有任何關系,只是仿仿罷了,不能當真。如果不想了解貨真價實的位帶操作,此節可直接忽略。
如果不使用位帶操作,我們操作一個次數據時,就要動32位(STM32是32位的),做一個不恰當的比喻,這就相當於我們坐在一輛有32節車廂的火車上,但是輛火車只有一個門,如果我們要查看這火車中乘客的信息,或者是乘客想下車,必須從那一個門進出,如下圖1。
圖1 只有1個車門的32節火車
而如果我們有了位帶操作,就相當於,給這輛32節車廂的火車裝上了32個車門,這樣一來,想查看哪個乘客的信息,或都那個乘客要下車,都可以迅速地從指定的車門下車。如下圖2所示。
圖2 有32個車門的32節火車
有了32個門后,速度就快多了,但是硬件成本肯定要起來了,這就是為什么STM32F030系列沒有位帶操作的原因,就是它的成本低。
3、C言語結構體位段操作
此節主要講述C語言結構體的基礎知識,如果有C言高手,請無視此節。在C語言中,對結構體的聲明,有一個位域,它可以控制,此結構體中的成員占幾個位,關於它的使用,有如下代碼:
1 typedef struct _16_Bits_Struct 2 { 3 u16 bit0 : 1;//占一個字節 4 u16 bit1 : 1; 5 u16 bit2 : 1; 6 u16 bit3 : 1; 7 u16 bit4 : 1; 8 u16 bit5 : 1; 9 u16 bit6 : 1; 10 u16 bit7 : 1; 11 u16 bit8 : 1; 12 u16 bit9 : 1; 13 u16 bit10 : 1; 14 u16 bit11 : 2;//占兩個字節 15 u16 bit12 : 3;//占三個字節 16 } _16_Bits_Struct;
上面的_16_Bits_Struct結構體類型共占用2個字節,即16位,但它的13個成員變量所占用的位數不全都一樣,通過“:”后面的數字可決定它占幾位。代碼如下,操作一個此結構體類型的位。
1 _16_Bits_Struct _16_bits; 2 unsigned short _16bits_data; 3 memset(&_16_bits, 0, sizeof(_16_Bits_Struct));//將其內存清0 4 5 _16_bits.bit2 = 1; 6 _16_bits.bit5 = 1; 7 _16_bits.bit8 = 5; 8 9 _16bits_data = *((unsigned short*)(&_16_bits)); 10 11 printf("_16bits_data = %0xH\n", _16bits_data);
其輸出結果為:
從結果中可以看出,在結構體,從bit0~bit12依次是從低位到高位。在上面代碼的第7行,雖然給bit8寫入了5,但是因為它只占一位,所以只取了5(D)=0101(B)的最低位,即為1。因此最終結果為124H,它的內存結構如下圖3所示。
圖3 結構體內存結構圖
4、STM32F030仿位帶操作
有了上面結構體位段操作的基礎后,離實現仿STM32F030的位帶操作就很近了。我打算做一個最簡單的,實現對GPIO的某一個引腳操作,達到亮滅LED的功能。
從STM32F030的參考手冊中,找到GPIO的輸出寄存器ODR,看到它的基本信息如下圖4所示,這個寄存器是可讀可寫的(RW),因此只要作我們給這個寄存器其中的一個位寫入1,那么這個引腳就會輸出1,寫0就輸出0(當然前提條件是你把它配置成輸出模式,並且使能了它的時鍾)。
圖4 GPIO的ODR寄存器結構圖
我是如何對這個寄存器一次只操作一位的呢,且看下面代碼再來解釋。
1 typedef struct _16_Bits_Struct 2 { 3 u16 bit0 : 1; 4 u16 bit1 : 1; 5 u16 bit2 : 1; 6 u16 bit3 : 1; 7 u16 bit4 : 1; 8 u16 bit5 : 1; 9 u16 bit6 : 1; 10 u16 bit7 : 1; 11 u16 bit8 : 1; 12 u16 bit9 : 1; 13 u16 bit10 : 1; 14 u16 bit11 : 1; 15 u16 bit12 : 1; 16 u16 bit13 : 1; 17 u16 bit14 : 1; 18 u16 bit15 : 1; 19 } Bits_16_TypeDef; 20 #define LED_GPIO_CLK RCC_AHBPeriph_GPIOA 21 #define LED_PORT GPIOA 22 #define LED_PIN GPIO_Pin_4 23 //使用結構體的位段操作, 兼容Cortex-M3的位帶操作. 24 #define LED_PORT_OUT ((Bits_16_TypeDef *)(&(LED_PORT->ODR))) 25 #define LED (LED_PORT_OUT->bit4)
我的硬件連接是:LED接GPIOA的4引腳上。1~19行在前面的結構體知識中已經做出了解釋了,20~22只是為了代碼更好移植做的一些宏定義,可不要。24行就比較關鍵了:先取出GPIOA->ODR的地址,然后再將它強制轉化為Bits_16_TypeDef * 類型(注意,是指針類型)。轉化為此類型后,ODR就有位域的特性了,因此就可以對它進行位操作。25行就是將接在PA.4的LED定義為GPIOA->ODR的第4位。
有了這樣的操作后,想要我們的LED亮滅,就很容易了,代碼如下。
1 LED = 0;//LED亮 2 LED = 1;//LED滅
因硬件的連接不同,效果可能是反的。看到這里,是不是覺得操作起來很簡單呢。
完整的代碼如下:

1 /*------------------------------------------------------------------------------ 2 風機監測系統(2017年8月12日12:22:38) 3 功能描述: 4 LED的開關功能,主要用於狀態顯示 5 6 使用資源:GPIOA隨板子不用而變化 7 8 文件說明:無 9 作者:Endless 郵箱:endless@139.com 時間:2017年8月10日21:35:38 10 修改:無 時間: 11 ------------------------------------------------------------------------------*/ 12 #include "led.h" 13 #include "stm32f0xx.h" 14 15 /*----------------------------------------------------------------------------- 16 函數功能:LED初始化 17 函數參數:無 18 函數返回:無 19 函數說明:調用此函數前,需要在LED.h修改宏定義LED引腳 20 作者:Endless 21 -----------------------------------------------------------------------------*/ 22 void LED_Init(void) 23 { 24 GPIO_InitTypeDef GPIO_InitStructure; 25 26 RCC_AHBPeriphClockCmd(LED_GPIO_CLK, ENABLE); 27 28 GPIO_InitStructure.GPIO_Pin = LED_PIN; 29 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; 30 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 31 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 32 GPIO_Init(LED_PORT, &GPIO_InitStructure); 33 }

1 #ifndef __led_H 2 #define __led_H 3 4 #include "stm32f0xx.h" 5 #include "mytype.h" 6 7 #define LED_GPIO_CLK RCC_AHBPeriph_GPIOA 8 #define LED_PORT GPIOA 9 #define LED_PIN GPIO_Pin_4 10 11 //使用結構體的位段操作, 兼容Cortex-M3的位帶操作. 12 #define LED_PORT_OUT ((Bits_16_TypeDef *)(&(LED_PORT->ODR))) 13 #define LED (LED_PORT_OUT->bit4) 14 15 void LED_Init(void); 16 17 #endif

1 #ifndef __MYTYPE_H 2 #define __MYTYPE_H 3 #include "stm32f0xx.h" 4 5 #ifndef BIT 6 #define BIT(x) (1 << (x)) 7 #endif 8 9 #ifndef u8 10 #define u8 uint8_t 11 #endif 12 13 #ifndef u16 14 #define u16 uint16_t 15 #endif 16 17 #ifndef u32 18 #define u32 uint32_t 19 #endif 20 21 #ifndef NULL 22 #define NULL 0 23 #endif 24 25 /*------------------------------------------------------------------------------ 26 用戶自定變量 27 功能描述:使用結構體的位段操作,可以實現位操作 28 作者:Endless 2017年8月13日18:32:37 29 修改:無 時間: 30 ------------------------------------------------------------------------------*/ 31 typedef struct _16_Bits_Struct 32 { 33 u16 bit0 : 1; 34 u16 bit1 : 1; 35 u16 bit2 : 1; 36 u16 bit3 : 1; 37 u16 bit4 : 1; 38 u16 bit5 : 1; 39 u16 bit6 : 1; 40 u16 bit7 : 1; 41 u16 bit8 : 1; 42 u16 bit9 : 1; 43 u16 bit10 : 1; 44 u16 bit11 : 1; 45 u16 bit12 : 1; 46 u16 bit13 : 1; 47 u16 bit14 : 1; 48 u16 bit15 : 1; 49 } Bits_16_TypeDef;
如果你想進行更多的位操作,只需多定義幾次就行了,很容易的。到這里就差不多結束了,希望能夠幫到大家,有什么問題可以聯系我,或在下面留言。
總結
做技術也很不容易,希望我們大家一起堅持下去!!