STM32F030系列實現仿位帶操作


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 }
led.c
 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
led.h
 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;
mytype.h

如果你想進行更多的位操作,只需多定義幾次就行了,很容易的。到這里就差不多結束了,希望能夠幫到大家,有什么問題可以聯系我,或在下面留言。

總結

做技術也很不容易,希望我們大家一起堅持下去!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM