STM32 嵌入式學習入門(0) C語言基礎複習

2021-08-15 11:37:36 字數 4057 閱讀 5570

摘要

主要介紹了嵌入式程式設計中幾個常用,但軟體程式設計中用得不是很多的c語言知識。包括位操作、條件編譯、結構體和結構體指標、typedef宣告型別、以及extern變數宣告、static關鍵字等內容。

本文並沒有將相關c語言知識點介紹地很詳細,畢竟這麼多知識點要想掌握絕對不是看幾篇文件就能掌握的。因此博主建議,如果上述的c語言知識掌握得還不是很好的話,找一本c語言的書好好研究研究。尤其是結構體和結構體指標、還有函式的知識(本文沒提到),一定要很熟練。

要想學習stm32,c語言的基礎是必須的。除了最基本的c語言的語法,如迴圈、判斷、陣列、結構體、函式、指標這些軟體程式設計常用的知識外,還包括位操作、條件編譯、結構體指標、typedef宣告型別、以及extern變數宣告、static關鍵字等常用內容。這裡結合實際**分析一下這些知識點,如果想完整系統地了解這些c語言知識,大家可以翻翻c語言教材,比如《c primer plus》(第六版)這本書,尤其對於位操作的知識講得很詳細。

位操作簡單說就是指對基本型別變數可以在位級別進行操作。下面先看幾種位操作符:

&按位與~取反

|按位或

<<

左移^ 

按位異或 

>>右移

掌握了這六種操作否的用法,c語言的位操作就差不多了。這六種操作符的解釋如下:

1. & 按位與: 如果兩個相應的二進位制位都為1,則該位的結果值為1,否則為0。//   1&1 = 1   1&0 = 0   0&1 = 0   0&0 = 0

2.|按位或:兩個相應的二進位制位中只要有乙個為1,該位的結果值為1。//   1|1 = 1   0|1 = 1   1|0 = 1   0|0 = 0 

3.^按位異或: 若參加運算的兩個二進位制位值相同則為0,否則為1。//   1^1 = 0   0^1 = 1   1^0 = 1   0^0 = 0

4.!取反:  對乙個二進位制數按位取反,即將0變1,將1變0。//    1! = 0    0! = 1

5.《左移:用來將乙個數的各二進位制位全部左移n位,右補0。//   00001100 << 2 = 00110000

6.>>右移:將乙個數的各二進位制位右移n位,移到右端的低位被捨棄,對於無符號數,高位補0。//   00001100 >> 2 = 00000011

下面介紹一些用暫存器開發stm32時候實用的位操作技巧:

1)不改變其他位的值的狀況下,對某幾個位進行設值。

這個場景微控制器開發中經常使用,方法就是先對需要設定的位用 & 操作符進行清零操作,然後用 | 操作符設值。比如我要改變gpioa的狀態,可以先對暫存器的值進行 & 清零操作

然後再與需要設定的值進行 | (或運算)。

gpioa->crl&=0xffffff0f; //將第 4-7 位清 0

gpioa->crl|=0x00000040; //設定相應位的值,不改變其他位的值 (將crl暫存器第7位設定為1)

2)取反操作使用技巧

sr 暫存器的每一位都代表乙個狀態,某個時刻我們希望去設定某一位的值為 0,同時其他位都保留為 1,簡單的作法是直接給暫存器設定乙個值:

timx->sr=0xfff7;

這樣的做法設定第3位為0,但是這樣的作法同樣不好看,並且可讀性很差。看看庫函式**中怎樣使用的:

timx->sr = (uint16_t)~tim_flag;

而 tim_flag 是通過巨集定義定義的值:

#define tim_flag_update ((uint16_t)0x0001)

#define tim_flag_cc1 ((uint16_t)0x0002)

看這個應該很容易明白,可以直接從巨集定義中看出 tim_flag_update 就是設定的第 0 位了,可讀性非常強。

注:在stm32的開發中,更多的時間可能會直接使用官方的庫函式,庫函式實際上是將複雜的暫存器封裝了一下。使用庫函式可以避免複雜的位操作,使**更具有可讀性,但同樣的專案,使用庫函式其**量可能會比直接通過操作暫存器寫出來的工程的**量稍微多一點,執行效率可能會稍微低一點,當然這只是一點點…………

學習stm32的時候要從暫存器上去理解原理,理解實現過程,但是如果真的需要做乙個嵌入式專案,可能用庫函式去開發比較方便,效率更好一點,這是博主自己的感受和觀點。

微控制器程式開發過程中,經常會遇到一種情況, 當滿足某條件時對一組語句進行編譯,而當條件不滿足時則編譯另一組語句。 條件編譯命令最常見的形式為:

#ifdef 識別符號

程式段 1

#else

程式段 2

#endif

它的作用是:當識別符號已經被定義過(一般是用#define 命令定義),則對程式段 1 進行編譯,否則編譯程式段 2。 其中#else 部分也可以沒有,即:

#ifdef

程式段 1

#endif

這個條件編譯在 mdk 裡面是用得很多的,在 stm32f10x.h 這個標頭檔案中經常會看到這樣的語句:

#ifdef stm32f10x_hd

大容量晶元需要的一些變數定義

#end

而 stm32f10x_hd 則是我們通過#define 來定義的。

條件編譯理解起來也不是很困難,可以模擬於c語言中的 if-else 語句去理解。條件編譯在stm32的開發中還是比較常用的。自己寫**寫 .h 檔案的時候開頭會用到。此外就是要能看懂庫函式裡面的條件編譯了。

結構體是c語言中的基礎知識,同時結構體和結構指標也是stm32開發中非常重要的東西,尤其在使用庫函式的時候,庫函式中很多函式的入口引數中都有結構體指標,所以如果我們要呼叫這種函式,就先在主調函式中宣告乙個結構體變數,然後對這個結構體變數的各個成員賦值,最後再呼叫相關函式,呼叫的時候看清楚函式原型,入口引數是結構體型別還是結構體指標,不要搞錯了。這裡再多說兩句,這裡的結構體每個成員可以賦的值往往都是通過列舉或者巨集定義確定好的,不能自己亂寫,而應該去查詢巨集定義部分的**,選定需要的那個列舉字面值作為結構體相關成員的值。

關於結構體和結構體指標的例子可以看gpio的初始化,這裡就不再多說了:stm32 gpio的介紹

如果學過資料結構,相信對typedef也不陌生。用typedef的乙個好處就是使**的可讀性更高,寫**也更方便。typedef 在**中用得最多的就是定義結構體的型別別名和列舉型別了。

struct _gpio

;

定義了乙個結構體 gpio,這樣我們定義變數的方式為:

struct _gpio gpioa;//定義結構體變數 gpioa
但是這樣很繁瑣, mdk 中有很多這樣的結構體變數需要定義。這裡我們可以為結體定義乙個別名 gpio_typedef,這樣我們就可以在其他地方通過別名 gpio_typedef 來定義結構體變數了。方法如下:

typedef struct

gpio_typedef;

typedef 為結構體定義乙個別名 gpio_typedef,這樣我們可以通過 gpio_typedef 來定義結構體變數:

gpio_typedef gpioa,gpiob;
這裡的 gpio_typedef 就跟 struct _gpio 是等同的作用了。 這樣是不是方便很多? 

除了用在結構體上,typedef型別別名也大量用在int、short等這種變數上, 所以寫stm32**的時候幾乎就不會出現類似於定義int型變數這樣的語句,全部用 u8、u16這樣的量代替了,比如u16代表的就是乙個無符號的16位整型資料(這乙個描述可能有一點偏差)。

c 語言中 extern 可以置於變數或者函式前,以表示變數或者函式的定義在別的檔案中,提示編譯器遇到此變數和函式時在其他模組中尋找其定義。這裡面要注意,對於 extern 申明變數可以多次,但定義只有一次。在我們的**中你會看到看到這樣的語句:

extern u16 usart_rx_sta;
這個語句是申明 usart_rx_sta 變數在其他檔案中已經定義了,在這裡要使用到。所以,你肯定可以找到在某個地方有變數定義的語句:

u16 usart_rx_sta;

嗯,extern關鍵字,說實話,博主自己寫**確實沒用過。so……

STM32嵌入式開發學習筆記(一)

stm32cubemx是st官方推出的一種stm32設定和初始化c 的生成器。它可以直觀的選擇stm32微控制器 配置微控制器 自動處理引腳衝突 動態設定確定時鐘樹 動態確定引數設定的外圍和中介軟體模式和初始化。cubemx生成的 可以在keil iar gcc等程式設計軟體上面使用。簡單理解,就是...

嵌入式之STM32系列筆記

一 微控制器程式構架設計 用微控制器開發專案,當 開發到一定的程度後,由於涉及到的源程式檔案與標頭檔案眾多,若在前期為程式設計好架構,則在一定程度上便於程式的維護與管理,更有利於理清思路,加快專案的開發。通常將程式架構設定為四層,如下圖 其中middleware可以沒有,如果沒有外部擴充套件件,如外...

Pre藍橋杯嵌入式 STM32 學習索引

寫在前面 0 專欄鏈結 1 unkown device 2 移植lcd程式 建立工程 lcd程式分析 3 如何直接使用lcd例程來作為賽場使用的工程 學習目錄 1 led之點燈儀式 2 keyboard 主迴圈掃瞄 外部中斷 3 buzzer之將jtag功能相關引腳對映為普通io afio的使用 4...