STM32F103C8T6單片機開發:簡單說說單片機的外部GPIO中斷(標準庫)

目錄

前言

如何使用STM32F1系列的標準庫完成外部中斷的抽象

初始化我們的GPIO為輸入的一個模式

初識GPIO復用,開啟GPIO的復用功能時鐘

GPIO_EXTILineConfig和EXTI_Init配置外部中斷參數

插入一個小知識——如何正確的配置結構體?

初始化中斷:NVIC注冊

處理中斷回調函數

本篇的實驗代碼


前言

在之前,我們就詳細的討論過了最簡單的GPIO設備的驅動和使用,但是如你所見,這些調用都是同步,即我們程序主動的去設置和讀取GPIO的狀態。這很正常——我們只有在想要主動出擊的時候,比如說點亮LED電平,查詢按鈕的狀態的時候,我們主動的申請和讀取結果。這是很正常的事情。

但是仍然有一些場景,我們實際上并不是這樣的,甚至完全與此相反。舉個例子,一些事情我們更希望是外部來告知我們發生了。舉個例子,一個經典的場景就是——我們希望在按鍵嗯下的時候,我們來做一部分事情,比如說后面我們切換LCD界面啊等等的事情。按照我們之前的方法,那就是:

while(1)
{if(isKeyPressed()){do_things();}else{do_other_things();}...
}

這個的問題看起來不大,但是實際上你仔細思考一下,問題很大。在我們上一個小節中,我們做一個改動,您可以嘗試一下:

while(1)
{if(isKeyPressed()){reverse_gpio_status(&led0);}else{system_clock_delay_ms(1000);    // delay for 1 seconds}...
}

你會發現一個致命的問題,整個系統罷工了,為什么,我們仔細看看這個代碼——我們在一瞬間檢查完Key沒有按下之后,整個系統直接掛起來1秒鐘,如果我們之后寫更加龐大的項目的時候,問題還能更加尖銳(比如說使用ESP8266等待網絡請求,那可就是一個等待就是好幾秒),這下你的客戶如果發了瘋的按按鈕就會發現根本不起作用的時候,你的好日子也就到頭了。那咋辦?

仔細想下,我們的邏輯是不是有點問題——分明是我們想要“按下按鈕的時候,我們才會翻轉LED的電平。”,整個程序的邏輯是——詢問一下按鈕按下了嘛?有則處理按鈕按下的邏輯,沒有處理其他程序。這不對的。

問題就在于,我們需要把邏輯顛倒過來!一個事件發生了,我們需要如何處理這個事情。而不是反復的詢問這個事情有沒有發生。因此,我們就需要一個機制,這個機制,就是外部中斷,外部中斷按照一種通知的方式,通知我們的單片機一個抽象的事件發生了。

一個光傳感器察覺到了一個光亮度的變化的時候,光傳感器發送了一個電平脈沖。與其一直輪循電平的變化,不如直接讓我們的GPIO處于一個中斷的前端,讓接受到電平變化的引腳申請我們的單片機觸發一個中斷,極其短暫的打斷我們的程序執行,跳轉道一個新的處理流處理我們的電平變化的事件。比如說,光暗了,我們需要開燈,這個時候,我們處理光傳感器發過來的“光暗下去了”的信號,處理的程序就是打開一個LED燈。你看,簡單吧!

因此,按照這種方式,我們就可以擺脫我們的輪循式的詢問,轉向語義更好的“中斷事件通知了”。

關于中斷的本質和STM32的中斷,我們放到旋轉編碼器和光傳感器的之后作為一個硬核原理篇章,仔細的介紹其根本原理,這里,我們仍然只是保留到會用即可的水平。

下面,我們就來聊一聊,標準庫是如何使用外部中斷來完成我們的事情的。

如何使用STM32F1系列的標準庫完成外部中斷的抽象

標準庫已經給我們提供好了腳手架了,只需要我們按照步驟進行配置即可。簡單的說,就是做這些事情

  • 初始化我們的GPIO為輸入的一個模式

  • 初始化GPIO外部中斷的資源寄存器

  • 使能我們的外部中斷

這需要我們一步步來看。

初始化我們的GPIO為輸入的一個模式

為什么是輸入模式呢?你想想,我們接受外部的中斷是不是需要從外界獲取信息?是的,這就是我們的GPIO中斷模式本質上需要以輸入模式進行配置的原因,如果你還看筆者的HAL庫教程,你就會知道,HAL庫直接封裝好了體系,將輸入模式中的一部分特化出來了外部中斷,合并了我們標準庫的配置,標準庫比較原始,因此我們需要做的事情就是將這些步驟正確的組合起來。

關于輸入模式的配置,這里我們不再重復談論了,實在乏味。我們馬上進入下一個階段,配置外部中斷的GPIO引腳資源。

初識GPIO復用,開啟GPIO的復用功能時鐘

關于復用的底層原理,一并合并到我們的GPIO中斷中討論,這里,你只需要知道的是:GPIO現在具備了片上外設和外部設備的溝通能力,在這里,就是我們的中斷控制器現在可以監控我們的外部設備的電平變化了,我們這一步就是做這個事情。

開啟GPIO的AF時鐘

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

開啟這個時鐘,我們的片上外設復用就變得可用,下一步,就是提交我們要監察的外部中斷是如何的。

GPIO_EXTILineConfig和EXTI_Init配置外部中斷參數

使用的函數是GPIO_EXTILineConfig來注冊EXTI中斷控制子系統,連接GPIO和外部中斷線,進而再EXTI_Init函數來初始化。

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

EXTI_InitTypeDef里,提供的就是我們寫的參數了

typedef struct
{uint32_t EXTI_Line; ? ? ? ? ? ? ? /*!< Specifies the EXTI lines to be enabled or disabled.This parameter can be any combination of @ref EXTI_Lines */EXTIMode_TypeDef EXTI_Mode; ? ? ? /*!< Specifies the mode for the EXTI lines.This parameter can be a value of @ref EXTIMode_TypeDef */
?// 筆者注釋:之前的版本中這里的注釋類型寫錯了EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.This parameter can be a value of @ref EXTITrigger_TypeDef */
?FunctionalState EXTI_LineCmd; ? ? /*!< Specifies the new state of the selected EXTI lines.This parameter can be set either to ENABLE or DISABLE */ 
}EXTI_InitTypeDef;
插入一個小知識——如何正確的配置結構體?

我發現,大部分嵌入式的人,不太會查看手冊/查看注釋來進行學習。你看,我們的標準庫實際上已經把事情說的很明白了,我們這里實際上需要填寫的參數

第一個蠶食,人家的說法是任意組合的EXTI_Line,我們一看,嗯很,沒有具體說明,一個最快速的辦法就是看注釋,EXTI_LINE的一個組合,這也就說明了,直接全局搜索一個EXTI_LINE,這里的EXTI_Mode對應的就是對應的GPIO_Pin所在的EXTI線,舉個例子,GPIO_Pin_0對應的就是EXTI_Line0,GPIO_Pin_1對應的就是EXTI_Line1,類推!

#define EXTI_Line0 ? ? ? ((uint32_t)0x00001)  /*!< External interrupt line 0 */
#define EXTI_Line1 ? ? ? ((uint32_t)0x00002)  /*!< External interrupt line 1 */
#define EXTI_Line2 ? ? ? ((uint32_t)0x00004)  /*!< External interrupt line 2 */
#define EXTI_Line3 ? ? ? ((uint32_t)0x00008)  /*!< External interrupt line 3 */
#define EXTI_Line4 ? ? ? ((uint32_t)0x00010)  /*!< External interrupt line 4 */
#define EXTI_Line5 ? ? ? ((uint32_t)0x00020)  /*!< External interrupt line 5 */
#define EXTI_Line6 ? ? ? ((uint32_t)0x00040)  /*!< External interrupt line 6 */
#define EXTI_Line7 ? ? ? ((uint32_t)0x00080)  /*!< External interrupt line 7 */
#define EXTI_Line8 ? ? ? ((uint32_t)0x00100)  /*!< External interrupt line 8 */
#define EXTI_Line9 ? ? ? ((uint32_t)0x00200)  /*!< External interrupt line 9 */
#define EXTI_Line10 ? ?  ((uint32_t)0x00400)  /*!< External interrupt line 10 */
#define EXTI_Line11 ? ?  ((uint32_t)0x00800)  /*!< External interrupt line 11 */
#define EXTI_Line12 ? ?  ((uint32_t)0x01000)  /*!< External interrupt line 12 */
#define EXTI_Line13 ? ?  ((uint32_t)0x02000)  /*!< External interrupt line 13 */
#define EXTI_Line14 ? ?  ((uint32_t)0x04000)  /*!< External interrupt line 14 */
#define EXTI_Line15 ? ?  ((uint32_t)0x08000)  /*!< External interrupt line 15 */

EXTI_Mode是一個enum枚舉類型

typedef enum
{EXTI_Mode_Interrupt = 0x00,EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;

這里有兩個參數,我們理解和學習的是中斷,很顯然是EXTI_Mode_Interrupt,但是這里筆者還是要簡單的說明一下這兩個參數有什么區別。

特性EXTI_Mode_Interrupt (中斷模式)EXTI_Mode_Event (事件模式)
是否觸發中斷?? (調用我們處理的回調函數)? (僅硬件事件)
是否喚醒 CPU???
CPU 參與需要 CPU 處理中斷無需 CPU 干預
典型應用緊急任務(如按鍵、故障檢測)硬件觸發(如 DMA、ADC)

EXTI_Mode_Event不是我們這里可以管的,因此,等到我們談論到了DMA,ADC等概念的時候,我們會回來學習事件模式的。

EXTITrigger_TypeDef說明我們如何觸發這個中斷。

typedef enum
{EXTI_Trigger_Rising = 0x08,EXTI_Trigger_Falling = 0x0C, ?EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;

嗯,上升?下降?上升下降?這些是什么東西啊?別急,我們來仔細思考一下。一個很自然的疑問,舉個例子:我們如何認為外部的GPIO中斷道來了呢?你可能會說這是我關心的嘛?你需要關心!,這需要你仔細閱讀器件的手冊,舉一個例子,筆者的光傳感器,查看手冊后,可以做出這樣的一個時序圖。

我們看到,這個器件的特性是閾值觸發類型的傳感器,也就是掃描過一次閾值,就會觸發一次信號。可以看到,我們觸發一次,我們的光傳感器就會快速向外發射一個低電平,因此,我們只需要檢測低電平,就能捕捉到我們的光強變化事件了!

仔細看看下面的這個圖,思考一下上面的三個枚舉,你認為,這三個枚舉,想要監控的是哪些電平變化呢?答案如下!

現在我們很清楚了,對于我們的器件,如果你的手冊上說:光強變化給一個下降觸發的時候,你就需要配置成Falling了。

剩下的Cmd實際上就是Enable,沒啥好說的。這就是使能的意思。

很好,我們申請GPIO中斷的第一個子步驟,是注冊GPIO的中斷Pin線,使用的函數是

/*** @brief  Selects the GPIO pin used as EXTI Line.* @param  GPIO_PortSource: selects the GPIO port to be used as source for EXTI lines.* ? This parameter can be GPIO_PortSourceGPIOx where x can be (A..G).* @param  GPIO_PinSource: specifies the EXTI line to be configured.* ? This parameter can be GPIO_PinSourcex where x can be (0..15).* @retval None*/void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)

比如說,我們想要給GPIOB注冊中斷,辦法就是提供:GPIO_PortSourceGPIOB和GPIO_PinSource0。這個時候,使用這個函數的后,我們的GPIO_Pin就能根我們的外部中斷線連接起來,下一步只需要把中斷使能起來,就可以使用外部中斷來監控事件了。

初始化中斷:NVIC注冊

NVIC是啥呢?NVIC的全稱是Nested vectored interrupt controller,即嵌套向量中斷控制器。關于通用的中斷,我們需要做的理解就是跟上面說的一致——是事件(中斷也可以來自內部,但對于最核心的CPU而言,你認為他們都是外部好像毛病也不大,總而言之,就是一些事件需要觸發我們的處理器暫停手下的活來處理通知的中斷事件,這里的嵌套你可以認為中斷是可以嵌套的)

如果感覺中斷不好理解,筆者在手搓操作系統的博客中有打過這樣的比喻:

現實生活中,我們可能往往會被任何啥東西打斷,比如說,此時此刻我正在寫一個闡述中斷的博客,我的一個好朋友突然給我發QQ,向我抱怨該死的爬蟲實在是太慢了,需要優化一下并發的結構。這個時候,我就跑去回應他,剛準備打幾個字,我的母上大人著急的找我問點事情,我又拋下對這個哥們的回復,沖過去回復我母上大人的問題,之后再回復我兄弟的抱怨,最后回來,繼續抓耳撓腮的寫下這段話。

從0開始的操作系統手搓教程11:為底層添磚加瓦:中斷_intr指令-CSDN博客

Charliechen114514/CCOperateSystem: A Tutorial Trying teach you how to make an os in modern tools with gcc, nasm and bochs!

理解好中斷之后,我們繼續說明我們的配置。STM32對我們的中斷管理非常的細致,將中斷分組,其描述優先級的位實際上非常了兩個比較大的部分。如下所示

  • 搶占優先級(Preemption Priority):決定中斷是否可以打斷正在執行的其他中斷

  • 子優先級(Sub-Priority):當多個掛起中斷具有相同搶占優先級時,決定它們的執行順序

換而言之,回復我母上大人的消息的搶占優先級是1,回復我兄弟的抱怨搶占優先級是2的時候,我顯然會先處理母上大人的事情,這個時候,盡管我正在回復我的兄弟,我也會讓他靠邊站!,這就是搶占優先級的效果。

那子優先級呢?答案是當我們沒有發生中斷的時候,或者說發生的是同級優先級的中斷的時候(可以認為是特殊的“沒有中斷發生”),我們決定先處理誰。舉個例子,我跟好朋友A和好朋友B中,我更喜歡跟A相處的時候,我優先回復A的消息,完事了之后回復B的消息。因此,在存在多個中斷配置的時候,我們需要牢記好,誰會霸道的優先發生,這樣,在處理中斷的時候,就會理清楚基本的思路。

來,舉個例子,我們的Systicks更新中斷總是放到優先級的最后,這個時候,如果你在高優先級的中斷中,調用了跟Systicks相關的系統中斷,你知道會發生什么的。那就是你希望Systicks更新,好衡量給定的毫秒數,但是你又在高優先級中斷中,沒辦法執行Systicks更新的回調函數,導致Systicks又不更新,這個事情在HAL中就會非常明顯,HAL庫就是一個強依賴軟件更新的時鐘基礎系統,因此,這個時候調用基于這個時鐘的延時,都會導致矛盾:希望Systicks中斷正常工作觸發軟件定時器更新,但是你處于高優先級中斷中無法打斷此時此刻的中斷去更新軟件定時器。導致系統直接死機。

配置上,對于STM32F103C8T6而言,就是這樣了。

分組搶占優先級位數子優先級位數搶占優先級范圍子優先級范圍
Group00位4位00-15
Group11位3位0-10-7
Group22位2位0-30-3
Group33位1位0-70-1
Group44位0位0-150

這個中斷,就對應了如下的結構體的NVIC_IRQChannelPreemptionPriority參數和NVIC_IRQChannelSubPriority參數,我們依次對應了搶占優先級和子優先級:

typedef struct
{uint8_t NVIC_IRQChannel; ? ? ? ? ? ? ? ? ?  /*!< Specifies the IRQ channel to be enabled or disabled.This parameter can be a value of @ref IRQn_Type (For the complete STM32 Devices IRQ Channels list, pleaserefer to stm32f10x.h file) */
?uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channelspecified in NVIC_IRQChannel. This parameter can be a valuebetween 0 and 15 as described in the table @ref NVIC_Priority_Table */
?uint8_t NVIC_IRQChannelSubPriority; ? ? ? ? /*!< Specifies the subpriority level for the IRQ channel specifiedin NVIC_IRQChannel. This parameter can be a valuebetween 0 and 15 as described in the table @ref NVIC_Priority_Table */
?FunctionalState NVIC_IRQChannelCmd; ? ? ? ? /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannelwill be enabled or disabled. This parameter can be set either to ENABLE or DISABLE */ ? 
} NVIC_InitTypeDef;

剩下的NVIC_IRQChannelCmd我們直接填寫enable即可。那NVIC_IRQChannel說明什么呢?答案是——標識使能哪一個中斷。中斷也是需要使能的,牢記一句話——我們的一切電子設備幾乎都是依靠時鐘才能進行節拍有序的工作,沒有基準時鐘,所有的器件沒有了節拍,自然也就黯淡失色

隨后,我們把這個結構體提交給NVIC_Init函數即可。

處理中斷回調函數

剩下的發生的事情,那就是交給單片機了,他經過一系列復雜的機制,終于到達了ST工程師們預設的中斷,對于PB0(舉個例子),這個IRQ_Handler是誰呢?EXTI0_IRQHandler。是隨便起的名字嘛?不是。請看匯編——

g_pfnVectors:
?.word _estack.word Reset_Handler.......word EXTI0_IRQHandler.word EXTI1_IRQHandler.word EXTI2_IRQHandler.word EXTI3_IRQHandler.word EXTI4_IRQHandler.......word EXTI9_5_IRQHandler.......word EXTI15_10_IRQHandler......

在這一串代碼里,我們很快找到了幾個我們看起來很熟悉的東西,對的,就是這些,你會發現這些就是著名的IRQHandler,也就是中斷處理回調函數,對于應用層次,你只需要理解的是發生了這個中斷,就會觸發回調(回調回調,回來調用)函數,來執行你的處理邏輯。

PB0對應的外部中斷線是EXTI0線,也就是EXTI0_IRQn,觸發的函數回調就是EXTI0_IRQHandler。因此,我們只需要在EXTI0_IRQHandler寫上我們的處理邏輯即可。

需要注意的是,中斷處理結束后,需要清理標置位,這個東西相當于我們的中斷是否發生的標志,只有標志為——處理結束,沒有發生了之后,我們才會理回新的中斷!

本篇的實驗代碼

出于篇幅原因,這里不再粘貼代碼,地址在這里:

BetterATK/103c8t6/standard/3_GPIO_EXIT at STM32F1 · Charliechen114514/BetterATKhttps://github.com/Charliechen114514/BetterATK/tree/STM32F1/103c8t6/standard/3_GPIO_EXIT

這里需要注意的是——我們引入了一個新的器件就是OLED器件,關于OLED器件的編程屬于額外的內容,你需要做的就是將SCL接到PB8,SDA接到PB9,筆者寫的CCGraphic框架會自動的在你的OLED屏上展示內容。具體的原理需要等待你掌握IIC協議和SPI協議之后,我們才能開始真正的修改內部的代碼。

現象如下

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

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

相關文章

【自然語言處理】深度學習中文本分類實現

文本分類是NLP中最基礎也是應用最廣泛的任務之一&#xff0c;從無用的郵件過濾到情感分析&#xff0c;從新聞分類到智能客服&#xff0c;都離不開高效準確的文本分類技術。本文將帶您全面了解文本分類的技術演進&#xff0c;從傳統機器學習到深度學習&#xff0c;手把手實現一套…

Java Lambda與方法引用:函數式編程的顛覆性實踐

在Java 8引入Lambda表達式和方法引用后&#xff0c;函數式編程范式徹底改變了Java開發者的編碼習慣。本文將通過實戰案例和深度性能分析&#xff0c;揭示如何在新項目中優雅運用這些特性&#xff0c;同時提供傳統代碼與函數式代碼的對比優化方案。 文章目錄 一、Lambda表達式&a…

劍指offer經典題目(三)

目錄 動態規劃入門 二進制運算 鏈表相關 動態規劃入門 題目1&#xff1a;一只青蛙一次可以跳上1級臺階&#xff0c;也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法&#xff08;先后次序不同算 不同的結果&#xff09;。OJ地址 簡單圖示如下。 題目分析&#…

【每日隨筆】叢林法則 ( 弱肉強食 | 適者生存 | 資源有限稀缺 | 沒有道德約束 | 自發性與無序性 | 叢林法則映射 - 資源分配 與 社會分層 )

文章目錄 一、叢林法則1、弱肉強食2、適者生存3、資源有限稀缺4、沒有道德約束5、自發性與無序性6、叢林法則映射 - 資源分配 與 社會分層 一、叢林法則 叢林法則 是 在 資源有限 的環境中 , 競爭 是生存的基礎 , 弱肉強食 , 適者生存 , 且過程 不受道德約束 ; 叢林法則 在 自…

【含文檔+PPT+源碼】基于小程序的智能停車管理系統設計與開發

項目視頻介紹&#xff1a; 畢業作品基于小程序的智能停車管理系統設計與開發 課程簡介&#xff1a; 本課程演示的是一款基于小程序的智能停車管理系統設計與開發&#xff0c;主要針對計算機相關專業的正在做畢設的學生與需要項目實戰練習的 Java 學習者。 1.包含&#xff1a;…

Navicat連接遠程PostGreSQL失敗

問題描述 使用本地Navicat連接Windows遠程服務器上部署的PostGreSQL數據庫時,出現以下錯誤: 解決方案 出現以上報錯信息,是因為PostGreSQL數據庫服務尚未設置允許客戶端建立遠程連接。可做如下配置, 1. 找到PostGreSQL數據庫安裝目錄下的data子文件夾,重點關注:postgres…

【Linux】jumpserver開源堡壘機部署

JumpServer 安裝部署指南 本文檔詳細記錄了 JumpServer 安裝部署的過程、核心腳本功能說明以及后續管理使用提示&#xff0c;方便運維人員快速查閱和二次安裝。 1. 前提條件 操作系統要求&#xff1a; 僅支持 Linux 系統&#xff0c;不支持 Darwin&#xff08;macOS&#xff0…

餐飲廚房開源監控安全系統的智能革命

面對日益嚴格的合規要求和消費者對衛生的信任危機&#xff0c;傳統人工監督已力不從心&#xff1a;衛生死角難發現、違規操作難追溯、安全隱患防不勝防。如何讓后廚更透明、更安全、更可信&#xff1f;餐飲廚房視頻安全系統橫空出世&#xff01;這套系統融合實時監控與AI技術&a…

HashMap為什么擴容為原來2倍呢?

1、減少哈希碰撞 核心原因&#xff1a;HashMap的所有設計都依賴于數組長度為2的冪次方這一前提。 索引計算使用 &#xff08;n-1)&hash &#xff0c;其中 n 是數組長度當 n 是 2 的冪次方時&#xff0c;n-1 的二進制形式是全 1&#xff08;例如&#xff0c;15——>111…

debian系統中文輸入法失效解決

在 Debian 9.6 上無法切換中文輸入法的問題通常與輸入法框架&#xff08;如 Fcitx 或 IBus&#xff09;的配置或依賴缺失有關。以下是詳細的解決步驟&#xff1a; 1. 安裝中文語言包 確保系統已安裝中文語言支持&#xff1a; sudo apt update sudo apt install locales sudo…

3DGS之光柵化

光柵化&#xff08;Rasterization&#xff09;是計算機圖形學中將連續的幾何圖形&#xff08;如三角形、直線等&#xff09;轉換為離散像素的過程&#xff0c;最終在屏幕上形成圖像。 一、光柵化的核心比喻 像畫家在畫布上作畫 假設你是一個畫家&#xff0c;要把一個3D立方體畫…

學習51單片機Day02---實驗:點亮一個LED燈

目錄 1.先看原理圖 2.思考一下&#xff08;sbit的使用&#xff09;&#xff1a; 3.給0是要讓這個LED亮&#xff08;LED端口設置為低電平&#xff09; 4.完成的代碼 1.先看原理圖 比如我們要讓LED3亮起來&#xff0c;對應的是P2^2。 2.思考一下&#xff08;sbit的使用&…

Redis與Lua原子操作深度解析及案例分析

一、Redis原子操作概述 Redis作為高性能的鍵值存儲系統&#xff0c;其原子性操作是保證數據一致性的核心機制。在Redis中&#xff0c;原子性指的是一個操作要么完全執行&#xff0c;要么完全不執行&#xff0c;不會出現部分執行的情況。 Redis原子性的實現原理 單線程模型&a…

深入理解 GLOG_minloglevel 與 GLOG_v:原理與使用示例

文章目錄 深入理解 GLOG_minloglevel 與 GLOG_v&#xff1a;原理與使用示例1. GLOG_minloglevel&#xff1a;最低日志等級控制2. GLOG_v&#xff1a;控制 VLOG() 的詳細輸出等級3. GLOG_minloglevel 與 GLOG_v 的優先級關系4. 使用示例4.1 基礎示例&#xff1a;不同日志等級4.2…

Cline Memory Bank 結構化文檔持久化 AI 上下文詳解

&#x1f3ae; 什么是 Cline Memory Bank&#xff1f; Memory Bank 是一個結構化文檔系統&#xff0c;允許 Cline 在會話之間保持上下文。它能讓 Cline 從無狀態的助手轉變為持久記憶的開發伙伴&#xff0c;隨著時間推移有效地“記住”項目細節。 &#x1f5e1;? 關鍵優勢 上…

【JavaScript】面向對象與設計模式

個人主頁&#xff1a;Guiat 歸屬專欄&#xff1a;HTML CSS JavaScript 文章目錄 1. JavaScript 中的面向對象編程1.1 對象基礎1.2 構造函數1.3 原型和原型鏈1.4 ES6 類1.5 繼承1.6 封裝 2. 創建型設計模式2.1 工廠模式2.2 單例模式2.3 建造者模式2.4 原型模式 3. 結構型設計模式…

網絡安全防護技術

邊界安全防護——防火墻 控制&#xff1a;在網絡連接點上建立一個安全控制點&#xff0c;對進出數據進行限制隔離&#xff1a;將需要保護的網絡與不可信任網絡進行隔離&#xff0c;隱藏信息并進行安全防護記錄&#xff1a;對進出數據進行檢查&#xff0c;記錄相關信息 防火墻…

Spring MVC 視圖解析器(JSP、Thymeleaf、Freemarker、 JSON/HTML、Bean)詳解

Spring MVC 視圖解析器詳解 1. 視圖解析器概述 視圖解析器&#xff08;ViewResolver&#xff09;是 Spring MVC 的核心組件&#xff0c;負責將控制器返回的視圖名稱&#xff08;如 success&#xff09;轉換為具體的 View 對象&#xff08;如 Thymeleaf 模板或 JSP 文件&#x…

# 爬蟲技術的實現

手把手教你網絡爬蟲&#xff1a;從入門到實踐 一、網絡爬蟲簡介 網絡爬蟲&#xff08;Web Crawler&#xff09;是一種自動化獲取互聯網數據的程序&#xff0c;廣泛應用于搜索引擎、數據分析、市場調研等領域。通過模擬瀏覽器行為&#xff0c;爬蟲可以高效地從網頁中提取結構化…

【HarmonyOS 5】鴻蒙中@State的原理詳解

一、State在鴻蒙中是做什么的&#xff1f; State 是 HarmonyOS ArkTS 框架中用于管理組件狀態的核心裝飾器&#xff0c;其核心作用是實現數據驅動 UI 的響應式編程模式。通過將變量標記為 State&#xff0c;開發者可以確保當狀態值發生變化時&#xff0c;依賴該狀態的 UI 組件…