C語言的使用技巧--在IO操作中的移位和快速配置

在WB32F103(ARM cortex m3內核,96Mhz)的gpio初始化中有一段代碼,充分的結合了硬件特征并使用C語言的技巧來快速的配置對應的GPIO的功能,堪稱經典和楷模,代碼異常簡潔,執行速度快,配置任意IO方便快捷。
我們先來看看這一段源代碼:

void GPIO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, uint32_t PinConfig)
{uint32_t tmp = PinConfig;/* Check the parameters */assert_param(IS_GPIO_ALL_PERIPH(GPIOx));assert_param(IS_GPIO_PIN(GPIO_Pin));GPIOx->CFGMSK = ~GPIO_Pin;GPIOx->MODER = ((tmp >> 28) & 0x3) * 0x55555555U;GPIOx->OTYPER = ((tmp >> 24) & 0x1) * 0xFFFFFFFFU;GPIOx->OSPEEDR = ((tmp >> 20) & 0x3) * 0x55555555U;GPIOx->PUPDR = ((tmp >> 16) & 0x3) * 0x55555555U;tmp = (tmp & 0xF) * 0x11111111U;GPIOx->AFRL = tmp;GPIOx->AFRH = tmp;
}

相應的調用方式如下:

GPIO_Init(GPIOB, GPIO_Pin_8|GPIO_Pin_9, GPIO_MODE_IN | GPIO_PUPD_UP|GPIO_SPEED_LOW);

一條簡單的函數調用代碼,就可以完成對同一組IO的多個相同功能IO的同時配置,代碼不可謂不經典和優雅,高效易讀易懂。
但是很多人在閱讀GPIO_Init函數的源代碼的時候,對里面的一些運算操作確感覺到懵懵懂懂,理解不了,搞不明白。下面我們結合該硬件的特征講解和說明一下(注意:不同的MCU的硬件寄存器會有不同的操作方式,需要緊密結合你使用的MCU的硬件寄存器的特征來理解),可以起到舉一反三和觸類旁通的作用:
要完成這個“一步到位”的操作,首先要對相關的宏進行合理的有技巧的定義,看看相關IO操作的宏定義如下:

/** @defgroup GPIO_MODE_define* @{*/
#define GPIO_MODE_IN          0x00000000U     /*!< Input mode */
#define GPIO_MODE_OUT         0x10000000U     /*!< Output mode */
#define GPIO_MODE_AF          0x20000000U     /*!< Alternate function mode */
#define GPIO_MODE_ANA         0x30000000U     /*!< Analog mode */
/*** @}*//** @defgroup GPIO_OTYPE_define* @{*/
#define GPIO_OTYPE_PP         0x00000000U     /*!< Output push-pull */
#define GPIO_OTYPE_OD         0x01000000U     /*!< Output open-drain */
/*** @}*//** @defgroup GPIO_SPEED_define* @{*/
#define GPIO_SPEED_LOW        0x00100000U     /*!< Low speed */
#define GPIO_SPEED_HIGH       0x00000000U     /*!< High speed */
/*** @}*//** @defgroup GPIO_PUPD_define* @{*/
#define GPIO_PUPD_NOPULL      0x00000000U     /*!< No pull resistor */
#define GPIO_PUPD_UP          0x00010000U     /*!< Pull up resistor enabled */
#define GPIO_PUPD_DOWN        0x00020000U     /*!< Pull down resistor enabled */
/*** @}*//** @defgroup GPIO_Pin_sources * @{*/
#define GPIO_PinSource0            ((uint8_t)0x00)
#define GPIO_PinSource1            ((uint8_t)0x01)
#define GPIO_PinSource2            ((uint8_t)0x02)
#define GPIO_PinSource3            ((uint8_t)0x03)
#define GPIO_PinSource4            ((uint8_t)0x04)
#define GPIO_PinSource5            ((uint8_t)0x05)
#define GPIO_PinSource6            ((uint8_t)0x06)
#define GPIO_PinSource7            ((uint8_t)0x07)
#define GPIO_PinSource8            ((uint8_t)0x08)
#define GPIO_PinSource9            ((uint8_t)0x09)
#define GPIO_PinSource10           ((uint8_t)0x0A)
#define GPIO_PinSource11           ((uint8_t)0x0B)
#define GPIO_PinSource12           ((uint8_t)0x0C)
#define GPIO_PinSource13           ((uint8_t)0x0D)
#define GPIO_PinSource14           ((uint8_t)0x0E)
#define GPIO_PinSource15           ((uint8_t)0x0F)
/*** @}*/

宏里面分別定義了GPIO的pin引腳序號(GPIO_PinSource0-15),輸入輸入模式配置(輸入,輸出,特殊功能,模擬),輸出類型(推挽,開漏),IO速度(高速,低速),輸入電阻配置(無上下拉,上拉,下拉)。他們的定義bit位置是經過精心的安排和計算的(比如不同的功能定義占用的bit位置不重疊,方便進行移位運算,和對應的寄存器的操作有一一的對應關系),以便于后續代碼設計和簡化代碼的操作。好了,準備好這些原材料后,我們具體看看代碼的實現過程:

  GPIOx->CFGMSK = ~GPIO_Pin;

這第一行代碼,非常關鍵,要明白它的作用,要對應的查看mcu的規格書,我們發現該mcu有一個操作gpio配置寄存器的特殊功能,其說明如下:
在這里插入圖片描述
以上說明再翻譯一下:這一行代碼的作用,就是允許后續寫其他IO配置寄存器的時候,只對本次要配置的gpio的對應bit進行寫操作,不影響無需配置的其他bit(后面代碼解釋再說明)。
對應的寄存器定義如下:
在這里插入圖片描述好了,接下來看看第二行代碼的作用和操作技巧:

  GPIOx->MODER = ((tmp >> 28) & 0x3) * 0x55555555U;

在這里插入圖片描述
在這里插入圖片描述

該代碼操作的對象是端口模式寄存器,對應的寄存器功能如上圖(用一個32bit的寄存器來表示16個io的模式配置,每一個io的模式配置位占2bit,并且按照順序排列).

#define GPIO_MODE_IN          0x00000000U     /*!< Input mode */
#define GPIO_MODE_OUT         0x10000000U     /*!< Output mode */
#define GPIO_MODE_AF          0x20000000U     /*!< Alternate function mode */
#define GPIO_MODE_ANA         0x30000000U     /*!< Analog mode */

結合前面模式定義的宏來理解:
(tmp >> 28) & 0x3-— 該代碼的作用就是取到傳入的模式配置數據(模式配置定義在最高4個bit,所以先右移位28bit,然后與3,取出來其值),其結果可能的數據為:0,1,2,3,剛好和寄存器的2個bit的4種組合對應:
00 --對應輸入模式
01–對應輸出模式
10–復用功能模式
11–模擬模式
比較讓人疑惑或者難以理解就是后續這個乘以0x55555555U,要理解這個作用,我們把代碼換一種寫法來看看:

GPIOx->MODER = 0x55555555U  * ((tmp >> 28) & 0x3);

0x55555555U用二進制來看看是什么樣子:0101 0101 0101 0101 0101 0101 0101 0101
我們結合寄存器的每兩個bit表示一個gpio的模式配置來看看,也就是對應于每一個gpio的配置位初始值為01,如果把這個01和前面的模式值(((tmp >> 28) & 0x3))進行運算,我們發現得到如下結果:
0101 0101 0101 0101 0101 0101 0101 0101: 和0相乘,結果為0000 0000 0000 0000 0000 0000 0000 0000 ,所有GPIO的配置bit為輸入模式(00)
0101 0101 0101 0101 0101 0101 0101 0101: 和1相乘,結果不變,所有GPIO的配置bit為輸出模式(01)
0101 0101 0101 0101 0101 0101 0101 0101: 和2相乘,相當于左移1位,結果為:1010 1010 1010 1010 1010 1010 1010 1010 所有GPIO的配置bit為復用功能模式(10)
0101 0101 0101 0101 0101 0101 0101 0101: 和3相乘,結果為:1111 1111 1111 1111 1111 1111 1111 1111 ,所有GPIO的配置bit為模擬模式(11)
這真是一個非常高效和簡潔,優雅的設計技巧(包括硬件和軟件)。
關鍵點來了,這個值的修改是對所有16個gpio進行同時操作的,如果我只是設置某一個gpio,會不會影響到其他gpio的配置呢?答案是肯定不會。
回到前面我們看看有一個關鍵寄存器GPIOx->CFGMSK,英文全稱應該是config mask,中文翻譯為配置輔助寄存器,直譯為配置屏蔽寄存器可能更容易理解一些。
GPIOx->CFGMSK = ~GPIO_Pin;通過前面這一條代碼的操作,屏蔽了不需要操作的gpio配置位(也就是說關閉了對無關gpio的bit寫的作用),比如你本次只是操作gpio0,這條代碼就會把對gpio1-15的操作屏蔽,以后寫其他配置寄存器(比如前面的MODER寄存器),就只有gpio0對應的bit起作用,其他bit不會影響原來的值。
接下來的其他幾條語句的作用類似,參考規格書就可以分析和看明白。
這再一次說明了一個道理,嵌入式開發,軟件和硬件要充分結合,才能設計高效的代碼。
根據硬件特征,設計合理和簡潔的操作代碼,也算是一種算法–一種針對硬件進行操作優化的算法。

文章為原創,歡迎轉載,請注明出處

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39275.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39275.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39275.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【深度學習所有損失函數】在 NumPy、TensorFlow 和 PyTorch 中實現(2/2)

一、說明 在本文中&#xff0c;討論了深度學習中使用的所有常見損失函數&#xff0c;并在NumPy&#xff0c;PyTorch和TensorFlow中實現了它們。 (二-五)見 六、稀疏分類交叉熵損失 稀疏分類交叉熵損失類似于分類交叉熵損失&#xff0c;但在真實標簽作為整數而不是獨熱編碼提…

Python pycparser(c文件解析)模塊使用教程

文章目錄 安裝 pycparser 模塊模塊開發者網址獲取抽象語法樹1. 需要導入的模塊2. 獲取 不關注預處理相關 c語言文件的抽象語法樹ast3. 獲取 預處理后的c語言文件的抽象語法樹ast 語法樹組成1. 數據類型定義 Typedef2. 類型聲明 TypeDecl3. 標識符類型 IdentifierType4. 變量聲明…

語聚AI公測發布,大語言模型時代下新的生產力工具

語聚AI 公測發布 距離語聚AI內測上線已經過去近1個月。 這期間&#xff0c;我們共邀請了近百位資深用戶與行業專家加入語聚AI產品體驗。通過大家的熱情參與積極反饋&#xff0c;我們不斷優化并完善了語聚AI的功能與使用體驗。 經過研發團隊不懈的努力&#xff0c;今天語聚AI終…

【Leetcode】88.合并兩個有序數組

一、題目 1、題目描述 給你兩個按 非遞減順序 排列的整數數組 nums1 和 nums2,另有兩個整數 m 和 n ,分別表示 nums1 和 nums2 中的元素數目。 請你 合并 nums2 到 nums1 中,使合并后的數組同樣按 非遞減順序 排列。 注意:最終,合并后數組不應由函數返回,而是存儲在數…

梅賽德斯-奔馳將成為首家集成ChatGPT的汽車制造商

ChatGPT的受歡迎程度毋庸置疑。OpenAI這個基于人工智能的工具&#xff0c;每天能夠吸引無數用戶使用&#xff0c;已成為當下很受歡迎的技術熱點。因此&#xff0c;有許多公司都在想方設法利用ChatGPT來提高產品吸引力&#xff0c;賣點以及性能。在汽車領域&#xff0c;梅賽德斯…

代碼隨想錄算法訓練營第59天|動態規劃part16|583. 兩個字符串的刪除操作、72. 編輯距離、編輯距離總結篇

代碼隨想錄算法訓練營第59天&#xff5c;動態規劃part16&#xff5c;583. 兩個字符串的刪除操作、72. 編輯距離、編輯距離總結篇 583. 兩個字符串的刪除操作 583. 兩個字符串的刪除操作 思路&#xff1a; 思路見代碼 代碼&#xff1a; python class Solution(object):de…

[國產MCU]-BL602開發實例-I2C與總線設備地址掃描

I2C與總線設備掃描 文章目錄 I2C與總線設備掃描1、I2C介紹2、I2C驅動API介紹3、I2C使用實例I2C (Inter-Intergrated Circuit)是一種串行通訊總線,使用多主從架構,用來連接低速外圍裝置。 每個器件都有一個唯一的地址識別,并且都可以作為一個發送器或接收器。每個連接到總線的…

node-sass是什么

一、Sass&#xff08;Syntactically Awesome Style Sheets&#xff09; 是一種CSS預處理器&#xff0c;它擴展了CSS的功能并提供了更強大的樣式表語言。Sass允許開發人員使用變量、嵌套規則、混合&#xff08;Mixins&#xff09;、繼承等高級功能來編寫更簡潔、可維護的樣式代…

2023年國賽數學建模思路 - 案例:FPTree-頻繁模式樹算法

文章目錄 算法介紹FP樹表示法構建FP樹實現代碼 建模資料 ## 賽題思路 &#xff08;賽題出來以后第一時間在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介紹 FP-Tree算法全稱是FrequentPattern Tree算法&#xff0c;就是頻繁模式樹算法&#xff0c…

QT-Mysql數據庫圖形化接口

QT sql mysqloper.h qsqlrelationaltablemodelview.h /************************************************************************* 接口描述&#xff1a;Mysql數據庫圖形化接口 擬制&#xff1a; 接口版本&#xff1a;V1.0 時間&#xff1a;20230727 說明&#xff1a;支…

基于VUE3+Layui從頭搭建通用后臺管理系統(前端篇)九:自定義組件封裝下

一、本章內容 續上一張,本章實現一些自定義組件的封裝,包括文件上傳組件封裝、級聯選擇組件封裝、富文本組件封裝等。 1. 詳細課程地址: 待發布 2. 源碼下載地址: 待發布 二、界面預覽 三、開發視頻 基于VUE3+Layui從頭搭建通用后臺管

【軟件工程】內聚

概念 是指一個模塊內部個成分之間相互關聯程度的度量。也就是說&#xff0c;凝聚是對模塊內各處理動作組合強度的一種度量。很顯然&#xff0c;一個模塊的內聚越大越好。 偶然凝聚 一個模塊內的各處理元素之間沒有任何聯系&#xff0c;只是偶然地被湊到一起。這種模塊也稱為…

mov轉mp4格式怎么轉?

mov轉mp4格式怎么轉&#xff1f;眾所周知&#xff0c;MOV視頻格式是由蘋果公司推出的常用的視頻格式&#xff0c;能夠在蘋果軟件及設備上使用。但是&#xff0c;如果將其應用于其他軟件和設備上的話&#xff0c;可能會遇到文件無法正常播放的情況。在這個時候&#xff0c;我們需…

Linux MQTT智能家居項目(LED界面的布局設置)

文章目錄 前言一、LED界面布局準備工作二、LED界面布局三、邏輯實現總結 前言 上篇文章我們完成了主界面的布局設置那么這篇文章我們就來完成各個界面的布局設置吧。 一、LED界面布局準備工作 首先添加LED燈光控制的圖標。 將選擇好的LED圖標添加進來&#xff1a; 圖標可以…

drawio導出矢量圖

1.選中要導出的圖 2.導出為pdf 3.用adobe打開pdf&#xff0c;另存為eps

華為認證含金量如何

華為認證是指通過華為技術有限公司官方認證考試所獲得的認證資格。華為認證主要分為三個級別&#xff1a;華為認證工程師&#xff08;HCIE&#xff09;、華為認證專家&#xff08;HCNP&#xff09;和華為認證技術專家&#xff08;HCNA&#xff09;&#xff0c;每個級別都有不同…

在測試環境進行sqlserver鎖表測試

將某表設置X鎖1分鐘&#xff1a; begin tran select top 1 * from tableName with (tablockx) waitfor delay 00:01:00 commit tran 查詢當前被鎖的表&#xff1a; --查詢鎖表的事務ID&#xff0c;被鎖表名&#xff0c;鎖模式&#xff0c;客戶端主機名&#xff0c;客戶端程序…

你真的了解數據結構與算法嗎?

數據結構與算法&#xff0c;是理論和實踐必須緊密結合的一門學科&#xff0c;有關數據結構和算法同類的課程或書籍&#xff0c;有些只是名為“數據結構”&#xff0c;而非“數據結構與算法”&#xff0c;它們在內容上并無很大區別。 實際上&#xff0c;數據結構和算法&#xf…

【華為認證 Datacom 練習題(有答案喲)】

1&#xff08;單選題&#xff09;下列配置默認路由的命令中&#xff0c;正確的是&#xff08;&#xff09;。 A、 B、 C、 D、 正確答案A 2&#xff08;單選題&#xff09;UDP是面向無連接的&#xff0c;必須使用&#xff08;&#xff09;來提供傳輸的可靠性。 A、網絡層…

深入源碼分析kubernetes informer機制(零)簡單了解informer

[閱讀指南] 基于kubernetes 1.27 stage版本 為了方便閱讀&#xff0c;后續所有代碼均省略了錯誤處理及與關注邏輯無關的部分。 文章目錄 關于client-goInformer是什么為什么需要informerInformer工作流程后續分析計劃 關于client-go client-go是kubernetes節點與服務端進行資源…