??? C語言是單片機開發中的必備基礎知識,本文列舉了部分STM32學習中比較常見的一些C語言基礎知識。
?1?位操作???
?下面我們先講解幾種位操作符,然后講解位操作使用技巧。C語言支持以下六種位操作:
???
?下面,重點講解一下位操作在單片機開發中的一些實用技巧。
1.1 在不改變其他位的值的狀況下,對某幾個位進行設值???
?這個場景在單片機開發中經常使用,方法就是我們先對需要設置的位用&操作符進行清零操作,然后用 | 操作符設值。????比如,我要改變GPIOA的狀態,可以先對寄存器的值進行&清零操作:????
然后再與需要設置的值進行|或運算:
1.2 移位操作提高代碼的可讀性???
?移位操作在單片機開發中非常重要,下面是delay_init函數的一行代碼:
這個操作就是將CTRL寄存器的第1位(從0開始算起)設置為1,為什么要通過左移而不是直接設置一個固定的值呢?????其實這是為了提高代碼的可讀性以及可重用性。這行代碼可以很直觀明了的知道,是將第1位設置為1。如果寫成:
這個雖然也能實現同樣的效果,但是可讀性稍差,而且修改也比較麻煩。
1.3 ~按位取反操作使用技巧???
?按位取反在設置寄存器的時候經常被使用,常用于清除某一個/某幾個位。下面是delay_us函數的一行代碼:
該代碼可以解讀為:僅設置CTRL寄存器的第0位(最低位)為0,其他位的值保持不變。 ????同樣我們也不使用按位取反,將代碼寫成:
可見,前者的可讀性及可維護性都要比后者好很多。
1.4 ^按位異或操作使用技巧???
?該功能非常適合用于控制某個位翻轉,常見的應用場景就是控制LED閃爍,如下:
執行一次該代碼,就會使PB5的輸出狀態翻轉一次,如果我們的LED接在PB5上,就可以看到LED閃爍了。 ?
2?define宏定義??
??define是C語言中的預處理命令,它用于宏定義(定義的是常量),可以提高源代碼的可讀性,為編程提供方便。常見的格式:
??
??“標識符”為所定義的宏名。“字符串”可以是常數、表達式、格式串等。例如:
????
定義標識符HSE_VALUE的值為8000000,數字后的U表示unsigned的意思。至于define宏定義的其他一些知識,比如宏定義帶參數,這里就不多講解了。
?3?ifdef條件編譯???
?單片機程序開發過程中,經常會遇到一種情況,當滿足某條件時對一組語句進行編譯,而當條件不滿足時則編譯另一組語句。?
???條件編譯命令最常見的形式為:
?它的作用是:當標識符已經被定義過(一般是用#define命令定義),則對程序段1進行編譯,否則編譯程序段2。 ????
其中#else部分也可以沒有,即:
條件編譯在HAL庫里面是用得很多,在stm32mp1xx_hal_conf.h這個頭文件中經常會看到這樣的語句:
如果沒有定義HSE_VALUE這個宏,則定義HSE_VALUE宏,并且HSE_VALUE的值為24000000U。條件編譯也是C語言的基礎知識吧。 ????
這里提一下,24000000U中的U表示無符號整型,常見的,UL表示無符號長整型,F表示浮點型。????
這里加了U以后,系統編譯時就不進行類型檢查,直接以U的形式把值賦給某個對應的內存,如果超出定義變量的范圍,則截取。
?4?extern變量申明???
?C語言中extern可以置于變量或者函數前,以表示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。???
?這里面要注意,對于extern申明變量可以多次,但定義只有一次。在我們的代碼中你會看到看到這樣的語句:
這個語句是申明g_usart_rx_sta變量在其他文件中已經定義了,在這里要使用到。??
??所以,你肯定可以找到在某個地方有變量定義的語句:
extern的使用比較簡單,但是也會經常用到,需要掌握。
?5?typedef類型別名???
?typedef用于為現有類型創建一個新的名字,或稱為類型別名,用來簡化變量的定義。typedef在HAL庫用得最多的就是定義結構體的類型別名和枚舉類型了。
定義了一個結構體GPIO,這樣我們定義結構體變量的方式為:
?但這樣很繁瑣,HAL庫中有很多這樣的結構體變量需要定義。????這里我們可以為結體定義一個別名GPIO_TypeDef,這樣我們就可以在其他地方通過別名GPIO_TypeDef來定義結構體變量了,方法如下:
Typedef為結構體定義一個別名GPIO_TypeDef,這樣我們可以通過GPIO_TypeDef來定義結構體變量:
這里的GPIO_TypeDef就跟struct _GPIO是等同的作用了,但是GPIO_TypeDef使用起來方便很多。 ?
?1?位操作???
?下面我們先講解幾種位操作符,然后講解位操作使用技巧。C語言支持以下六種位操作:
???
?下面,重點講解一下位操作在單片機開發中的一些實用技巧。
1.1 在不改變其他位的值的狀況下,對某幾個位進行設值???
?這個場景在單片機開發中經常使用,方法就是我們先對需要設置的位用&操作符進行清零操作,然后用 | 操作符設值。????比如,我要改變GPIOA的狀態,可以先對寄存器的值進行&清零操作:????
然后再與需要設置的值進行|或運算:
1.2 移位操作提高代碼的可讀性???
?移位操作在單片機開發中非常重要,下面是delay_init函數的一行代碼:
- ?
CTRL?|=?1?<1;
????這個操作就是將CTRL寄存器的第1位(從0開始算起)設置為1,為什么要通過左移而不是直接設置一個固定的值呢?????其實這是為了提高代碼的可讀性以及可重用性。這行代碼可以很直觀明了的知道,是將第1位設置為1。如果寫成:
- ?
CTRL?|=?0X0002;
????這個雖然也能實現同樣的效果,但是可讀性稍差,而且修改也比較麻煩。
1.3 ~按位取反操作使用技巧???
?按位取反在設置寄存器的時候經常被使用,常用于清除某一個/某幾個位。下面是delay_us函數的一行代碼:
- ?
SysTick->CTRL?&=?~(1?<0)?;????/*?關閉SYSTICK?*/
????該代碼可以解讀為:僅設置CTRL寄存器的第0位(最低位)為0,其他位的值保持不變。 ????同樣我們也不使用按位取反,將代碼寫成:
- ?
SysTick->CTRL?&=?0XFFFFFFFE;????????/*?關閉SYSTICK?*/
????可見,前者的可讀性及可維護性都要比后者好很多。
1.4 ^按位異或操作使用技巧???
?該功能非常適合用于控制某個位翻轉,常見的應用場景就是控制LED閃爍,如下:
- ?
ODR?^=?1?<5;
????執行一次該代碼,就會使PB5的輸出狀態翻轉一次,如果我們的LED接在PB5上,就可以看到LED閃爍了。 ?
2?define宏定義??
??define是C語言中的預處理命令,它用于宏定義(定義的是常量),可以提高源代碼的可讀性,為編程提供方便。常見的格式:
??
??“標識符”為所定義的宏名。“字符串”可以是常數、表達式、格式串等。例如:
????
定義標識符HSE_VALUE的值為8000000,數字后的U表示unsigned的意思。至于define宏定義的其他一些知識,比如宏定義帶參數,這里就不多講解了。
?3?ifdef條件編譯???
?單片機程序開發過程中,經常會遇到一種情況,當滿足某條件時對一組語句進行編譯,而當條件不滿足時則編譯另一組語句。?
???條件編譯命令最常見的形式為:
- ?
????它的作用是:當標識符已經被定義過(一般是用#define命令定義),則對程序段1進行編譯,否則編譯程序段2。 ????
其中#else部分也可以沒有,即:
- ?
??
????條件編譯在HAL庫里面是用得很多,在stm32mp1xx_hal_conf.h這個頭文件中經常會看到這樣的語句:
- ?
????如果沒有定義HSE_VALUE這個宏,則定義HSE_VALUE宏,并且HSE_VALUE的值為24000000U。條件編譯也是C語言的基礎知識吧。 ????
這里提一下,24000000U中的U表示無符號整型,常見的,UL表示無符號長整型,F表示浮點型。????
這里加了U以后,系統編譯時就不進行類型檢查,直接以U的形式把值賦給某個對應的內存,如果超出定義變量的范圍,則截取。
?4?extern變量申明???
?C語言中extern可以置于變量或者函數前,以表示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。???
?這里面要注意,對于extern申明變量可以多次,但定義只有一次。在我們的代碼中你會看到看到這樣的語句:
- ?
extern?uint16_t?g_usart_rx_sta;
????這個語句是申明g_usart_rx_sta變量在其他文件中已經定義了,在這里要使用到。??
??所以,你肯定可以找到在某個地方有變量定義的語句:
- ?
?uint16_t?g_usart_rx_sta;
????extern的使用比較簡單,但是也會經常用到,需要掌握。
?5?typedef類型別名???
?typedef用于為現有類型創建一個新的名字,或稱為類型別名,用來簡化變量的定義。typedef在HAL庫用得最多的就是定義結構體的類型別名和枚舉類型了。
- ?
struct?_GPIO????{????????__IO?uint32_t?CRL;????????__IO?uint32_t?CRH;????????…????};
????定義了一個結構體GPIO,這樣我們定義結構體變量的方式為:
- ?
struct??_GPIO??gpiox;???????/*?定義結構體變量gpiox?*/
????但這樣很繁瑣,HAL庫中有很多這樣的結構體變量需要定義。????這里我們可以為結體定義一個別名GPIO_TypeDef,這樣我們就可以在其他地方通過別名GPIO_TypeDef來定義結構體變量了,方法如下:
- ?
typedef?struct????{????????????__IO?uint32_t?CRL;????????????__IO?uint32_t?CRH;????????????…????}?GPIO_TypeDef;
????Typedef為結構體定義一個別名GPIO_TypeDef,這樣我們可以通過GPIO_TypeDef來定義結構體變量:
- ?
GPIO_TypeDef gpiox;
????這里的GPIO_TypeDef就跟struct _GPIO是等同的作用了,但是GPIO_TypeDef使用起來方便很多。 ?
審核編輯:湯梓紅
?
?
評論
查看更多