本節(jié)內(nèi)容介紹
1、HAL庫GPIO在cubemx中的配置及注意事項;
2、HAL庫GPIO操作詳解與結(jié)構(gòu)介紹;
3、rt-thread任務(wù)介紹與創(chuàng)建;
4、利用多任務(wù)點燈,實現(xiàn)rtos多任務(wù)創(chuàng)建于執(zhí)行;
HAL庫GPIO在cubemx中的配置
上節(jié)課程我們介紹了cubemx的界面、時鐘配置以及如何新建工程等,本節(jié)咱們就繼續(xù)進(jìn)行程序員屆的“hello world”-“點燈”。
GPIO選擇與配置
嵌入式軟件工程師拿到板子第一件事要做的一定是熟悉原理圖,你可以不會設(shè)計,但一定要能看懂,軟件工程師調(diào)試代碼,一看原理圖,二才是寫代碼、調(diào)試代碼
先來看看開發(fā)板上的LED是哪個引腳,可以看到PB6、PE3、PD15都是LED控制引腳,采用的是灌電流的方式,低電平燈亮:
接下來,咱們在cubemx對這些IO進(jìn)行配置,小飛哥只選擇了兩個LED燈,名字命名和原理圖保持一致或者是按照實際功能命名
右擊選擇第一個選項,就可以修改label
單擊選中,會有很多功能,MCU的一個引腳是可以復(fù)用為許多功能的,我們根據(jù)自己的需要配置對應(yīng)的模式即可,此處我們控制LED燈,低電平-燈亮,高電平-燈滅,顯然是要配置為輸出模式的,選擇output即可,其他輸出引腳同理
接下來我們來看下GPIO的一些模式配置
對于開發(fā)板上的LED控制引腳,我們可以按照如下配置,初始化輸出高電平,LED不開啟,這樣初始化就可以設(shè)置GPIO輸出電平,設(shè)置為需要的狀態(tài):
GPIO配置比較簡單,就不再啰嗦了
HAL庫GPIO操作詳解與結(jié)構(gòu)介紹
打開生成的代碼,看看上面配置的GPIO初始化內(nèi)容,上面cubemx的配置項可以看到已經(jīng)生成對應(yīng)的代碼了,GPIO的配置順序:使能GPIO時鐘->配置相關(guān)采參數(shù):
voidMX_GPIO_Init(void) { GPIO_InitTypeDefGPIO_InitStruct={0}; /*GPIOPortsClockEnable*/ __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*ConfigureGPIOpinOutputLevel*/ HAL_GPIO_WritePin(Y_LED_GPIO_Port,Y_LED_Pin,GPIO_PIN_RESET); /*ConfigureGPIOpinOutputLevel*/ HAL_GPIO_WritePin(CP_LED_GPIO_Port,CP_LED_Pin,GPIO_PIN_RESET); /*ConfigureGPIOpin:PtPin*/ GPIO_InitStruct.Pin=Y_LED_Pin; GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull=GPIO_NOPULL; GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(Y_LED_GPIO_Port,&GPIO_InitStruct); /*ConfigureGPIOpin:PtPin*/ GPIO_InitStruct.Pin=CP_LED_Pin; GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull=GPIO_NOPULL; GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(CP_LED_GPIO_Port,&GPIO_InitStruct); }
關(guān)于GPIO操作的API:
/*Initializationandde-initializationfunctions*****************************/ voidHAL_GPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_Init); voidHAL_GPIO_DeInit(GPIO_TypeDef*GPIOx,uint32_tGPIO_Pin); /** *@} */ /**@addtogroupGPIO_Exported_Functions_Group2IOoperationfunctions *@{ */ /*IOoperationfunctions*****************************************************/ GPIO_PinStateHAL_GPIO_ReadPin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin); voidHAL_GPIO_WritePin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin,GPIO_PinStatePinState); voidHAL_GPIO_TogglePin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin); HAL_StatusTypeDefHAL_GPIO_LockPin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin); voidHAL_GPIO_EXTI_IRQHandler(uint16_tGPIO_Pin); voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin);
本節(jié)由于是LED操作,我們只需要操作:
voidHAL_GPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_Init); voidHAL_GPIO_WritePin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin,GPIO_PinStatePinState); voidHAL_GPIO_TogglePin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin);
如何使用呢?
參數(shù)GPIO_TypeDef *GPIOx可以是GPIO組的地址: #defineGPIOA((GPIO_TypeDef*)GPIOA_BASE) #defineGPIOB((GPIO_TypeDef*)GPIOB_BASE) #defineGPIOC((GPIO_TypeDef*)GPIOC_BASE) #defineGPIOD((GPIO_TypeDef*)GPIOD_BASE) #defineGPIOE((GPIO_TypeDef*)GPIOE_BASE) #defineGPIOF((GPIO_TypeDef*)GPIOF_BASE) #defineGPIOG((GPIO_TypeDef*)GPIOG_BASE) #defineGPIOH((GPIO_TypeDef*)GPIOH_BASE) #defineGPIOI((GPIO_TypeDef*)GPIOI_BASE)
參數(shù)GPIO_Pin可以是GPIO的引腳號: #defineGPIO_PIN_0((uint16_t)0x0001)/*Pin0selected*/ #defineGPIO_PIN_1((uint16_t)0x0002)/*Pin1selected*/ #defineGPIO_PIN_2((uint16_t)0x0004)/*Pin2selected*/ #defineGPIO_PIN_3((uint16_t)0x0008)/*Pin3selected*/ #defineGPIO_PIN_4((uint16_t)0x0010)/*Pin4selected*/ #defineGPIO_PIN_5((uint16_t)0x0020)/*Pin5selected*/ #defineGPIO_PIN_6((uint16_t)0x0040)/*Pin6selected*/ #defineGPIO_PIN_7((uint16_t)0x0080)/*Pin7selected*/ #defineGPIO_PIN_8((uint16_t)0x0100)/*Pin8selected*/ #defineGPIO_PIN_9((uint16_t)0x0200)/*Pin9selected*/ #defineGPIO_PIN_10((uint16_t)0x0400)/*Pin10selected*/ #defineGPIO_PIN_11((uint16_t)0x0800)/*Pin11selected*/ #defineGPIO_PIN_12((uint16_t)0x1000)/*Pin12selected*/ #defineGPIO_PIN_13((uint16_t)0x2000)/*Pin13selected*/ #defineGPIO_PIN_14((uint16_t)0x4000)/*Pin14selected*/ #defineGPIO_PIN_15((uint16_t)0x8000)/*Pin15selected*/ #defineGPIO_PIN_All((uint16_t)0xFFFF)/*Allpinsselected*/
參數(shù)GPIO_PinState PinState可以是: /** *@briefGPIOBitSETandBitRESETenumeration */ typedefenum { GPIO_PIN_RESET=0U, GPIO_PIN_SET }GPIO_PinState;
輸出低電平:
/*ConfigureGPIOpinOutputLevel*/ HAL_GPIO_WritePin(Y_LED_GPIO_Port,Y_LED_Pin,GPIO_PIN_RESET); /*ConfigureGPIOpinOutputLevel*/ HAL_GPIO_WritePin(CP_LED_GPIO_Port,CP_LED_Pin,GPIO_PIN_RESET);
輸出高電平:
/*ConfigureGPIOpinOutputLevel*/ HAL_GPIO_WritePin(Y_LED_GPIO_Port,Y_LED_Pin,GPIO_PIN_SET); /*ConfigureGPIOpinOutputLevel*/ HAL_GPIO_WritePin(CP_LED_GPIO_Port,CP_LED_Pin,GPIO_PIN_SET);
翻轉(zhuǎn)電平:
HAL_GPIO_TogglePin(Y_LED_GPIO_Port,Y_LED_Pin); HAL_GPIO_TogglePin(CP_LED_GPIO_Port,CP_LED_Pin);
關(guān)于GPIO輸出不外乎這幾個API,掌握如何使用就可以了
rt-thread任務(wù)介紹與創(chuàng)建
主要摘取一些比較關(guān)鍵的信息,也可參見RT-Thread官網(wǎng)介紹
線程管理
嵌入式系統(tǒng)執(zhí)行這樣的任務(wù),系統(tǒng)通過傳感器采集數(shù)據(jù),并通過顯示屏將數(shù)據(jù)顯示出來,在多線程實時系統(tǒng)中,可以將這個任務(wù)分解成兩個子任務(wù),如下圖所示,一個子任務(wù)不間斷地讀取傳感器數(shù)據(jù),并將數(shù)據(jù)寫到共享內(nèi)存中,另外一個子任務(wù)周期性的從共享內(nèi)存中讀取數(shù)據(jù),并將傳感器數(shù)據(jù)輸出到顯示屏上。
在 RT-Thread 中,與上述子任務(wù)對應(yīng)的程序?qū)嶓w就是線程,線程是實現(xiàn)任務(wù)的載體,它是 RT-Thread 中最基本的調(diào)度單位,它描述了一個任務(wù)執(zhí)行的運(yùn)行環(huán)境,也描述了這個任務(wù)所處的優(yōu)先等級,重要的任務(wù)可設(shè)置相對較高的優(yōu)先級,非重要的任務(wù)可以設(shè)置較低的優(yōu)先級,不同的任務(wù)還可以設(shè)置相同的優(yōu)先級,輪流運(yùn)行。
線程狀態(tài)
線程運(yùn)行的過程中,同一時間內(nèi)只允許一個線程在處理器中運(yùn)行,從運(yùn)行的過程上劃分,線程有多種不同的運(yùn)行狀態(tài),如初始狀態(tài)、掛起狀態(tài)、就緒狀態(tài)等。在 RT-Thread 中,線程包含五種狀態(tài),操作系統(tǒng)會自動根據(jù)它運(yùn)行的情況來動態(tài)調(diào)整它的狀態(tài)。RT-Thread 中線程的五種狀態(tài),如下表所示:
狀態(tài) | 描述 |
---|---|
初始狀態(tài) | 當(dāng)線程剛開始創(chuàng)建還沒開始運(yùn)行時就處于初始狀態(tài);在初始狀態(tài)下,線程不參與調(diào)度。此狀態(tài)在 RT-Thread 中的宏定義為 RT_THREAD_INIT |
就緒狀態(tài) | 在就緒狀態(tài)下,線程按照優(yōu)先級排隊,等待被執(zhí)行;一旦當(dāng)前線程運(yùn)行完畢讓出處理器,操作系統(tǒng)會馬上尋找最高優(yōu)先級的就緒態(tài)線程運(yùn)行。此狀態(tài)在 RT-Thread 中的宏定義為 RT_THREAD_READY |
運(yùn)行狀態(tài) | 線程當(dāng)前正在運(yùn)行。在單核系統(tǒng)中,只有 rt_thread_self() 函數(shù)返回的線程處于運(yùn)行狀態(tài);在多核系統(tǒng)中,可能就不止這一個線程處于運(yùn)行狀態(tài)。此狀態(tài)在 RT-Thread 中的宏定義為 RT_THREAD_RUNNING |
掛起狀態(tài) | 也稱阻塞態(tài)。它可能因為資源不可用而掛起等待,或線程主動延時一段時間而掛起。在掛起狀態(tài)下,線程不參與調(diào)度。此狀態(tài)在 RT-Thread 中的宏定義為 RT_THREAD_SUSPEND |
關(guān)閉狀態(tài) | 當(dāng)線程運(yùn)行結(jié)束時將處于關(guān)閉狀態(tài)。關(guān)閉狀態(tài)的線程不參與線程的調(diào)度。此狀態(tài)在 RT-Thread 中的宏定義為 RT_THREAD_CLOSE |
線程狀態(tài)切換
RT-Thread 提供一系列的操作系統(tǒng)調(diào)用接口,使得線程的狀態(tài)在這五個狀態(tài)之間來回切換。幾種狀態(tài)間的轉(zhuǎn)換關(guān)系如下圖所示:
線程優(yōu)先級
RT-Thread 線程的優(yōu)先級是表示線程被調(diào)度的優(yōu)先程度。每個線程都具有優(yōu)先級,線程越重要,賦予的優(yōu)先級就應(yīng)越高,線程被調(diào)度的可能才會越大。
RT-Thread 最大支持 256 個線程優(yōu)先級 (0~255),數(shù)值越小的優(yōu)先級越高,0 為最高優(yōu)先級。在一些資源比較緊張的系統(tǒng)中,可以根據(jù)實際情況選擇只支持 8 個或 32 個優(yōu)先級的系統(tǒng)配置;對于 ARM Cortex-M 系列,普遍采用 32 個優(yōu)先級。最低優(yōu)先級默認(rèn)分配給空閑線程使用,用戶一般不使用。在系統(tǒng)中,當(dāng)有比當(dāng)前線程優(yōu)先級更高的線程就緒時,當(dāng)前線程將立刻被換出,高優(yōu)先級線程搶占處理器運(yùn)行。
時間片
每個線程都有時間片這個參數(shù),但時間片僅對優(yōu)先級相同的就緒態(tài)線程有效。系統(tǒng)對優(yōu)先級相同的就緒態(tài)線程采用時間片輪轉(zhuǎn)的調(diào)度方式進(jìn)行調(diào)度時,時間片起到約束線程單次運(yùn)行時長的作用,其單位是一個系統(tǒng)節(jié)拍(OS Tick),詳見《時鐘管理》章節(jié)。假設(shè)有 2 個優(yōu)先級相同的就緒態(tài)線程 A 與 B,A 線程的時間片設(shè)置為 10,B 線程的時間片設(shè)置為 5,那么當(dāng)系統(tǒng)中不存在比 A 優(yōu)先級高的就緒態(tài)線程時,系統(tǒng)會在 A、B 線程間來回切換執(zhí)行,并且每次對 A 線程執(zhí)行 10 個節(jié)拍的時長,對 B 線程執(zhí)行 5 個節(jié)拍的時長,如下圖。
線程通過調(diào)用函數(shù) rt_thread_create/init() 進(jìn)入到初始狀態(tài)(RT_THREAD_INIT);
初始狀態(tài)的線程通過調(diào)用函數(shù) rt_thread_startup() 進(jìn)入到就緒狀態(tài)(RT_THREAD_READY);
就緒狀態(tài)的線程被調(diào)度器調(diào)度后進(jìn)入運(yùn)行狀態(tài)(RT_THREAD_RUNNING);當(dāng)處于運(yùn)行狀態(tài)的線程調(diào)用 rt_thread_delay(),rt_sem_take(),rt_mutex_take(),rt_mb_recv() 等函數(shù)或者獲取不到資源時,將進(jìn)入到掛起狀態(tài)(RT_THREAD_SUSPEND);
處于掛起狀態(tài)的線程,如果等待超時依然未能獲得資源或由于其他線程釋放了資源,那么它將返回到就緒狀態(tài)。掛起狀態(tài)的線程,如果調(diào)用 rt_thread_delete/detach() 函數(shù),將更改為關(guān)閉狀態(tài)(RT_THREAD_CLOSE);
而運(yùn)行狀態(tài)的線程,如果運(yùn)行結(jié)束,就會在線程的最后部分執(zhí)行 rt_thread_exit() 函數(shù),將狀態(tài)更改為關(guān)閉狀態(tài)。
線程相關(guān)的API
創(chuàng)建和刪除線程
一個線程要成為可執(zhí)行的對象,就必須由操作系統(tǒng)的內(nèi)核來為它創(chuàng)建一個線程。可以通過如下的接口創(chuàng)建一個動態(tài)線程:
rt_thread_trt_thread_create(constchar*name, void(*entry)(void*parameter), void*parameter, rt_uint32_tstack_size, rt_uint8_tpriority, rt_uint32_ttick);
調(diào)用這個函數(shù)時,系統(tǒng)會從動態(tài)堆內(nèi)存中分配一個線程句柄以及按照參數(shù)中指定的棧大小從動態(tài)堆內(nèi)存中分配相應(yīng)的空間。分配出來的??臻g是按照 rtconfig.h 中配置的 RT_ALIGN_SIZE 方式對齊。線程創(chuàng)建 rt_thread_create() 的參數(shù)和返回值見下圖:
對于一些使用 rt_thread_create() 創(chuàng)建出來的線程,當(dāng)不需要使用,或者運(yùn)行出錯時,我們可以使用下面的函數(shù)接口來從系統(tǒng)中把線程完全刪除掉:
rt_err_trt_thread_delete(rt_thread_tthread);

初始化和脫離線程
線程的初始化可以使用下面的函數(shù)接口完成,來初始化靜態(tài)線程對象:
rt_err_trt_thread_init(structrt_thread*thread, constchar*name, void(*entry)(void*parameter),void*parameter, void*stack_start,rt_uint32_tstack_size, rt_uint8_tpriority,rt_uint32_ttick);
靜態(tài)線程的線程句柄(或者說線程控制塊指針)、線程棧由用戶提供。靜態(tài)線程是指線程控制塊、線程運(yùn)行棧一般都設(shè)置為全局變量,在編譯時就被確定、被分配處理,內(nèi)核不負(fù)責(zé)動態(tài)分配內(nèi)存空間。需要注意的是,用戶提供的棧首地址需做系統(tǒng)對齊(例如 ARM 上需要做 4 字節(jié)對齊)。線程初始化接口 rt_thread_init() 的參數(shù)和返回值見下表:
對于用 rt_thread_init() 初始化的線程,使用 rt_thread_detach() 將使線程對象在線程隊列和內(nèi)核對象管理器中被脫離。線程脫離函數(shù)如下:
rt_err_trt_thread_detach(rt_thread_tthread);

啟動線程
創(chuàng)建(初始化)的線程狀態(tài)處于初始狀態(tài),并未進(jìn)入就緒線程的調(diào)度隊列,我們可以在線程初始化 / 創(chuàng)建成功后調(diào)用下面的函數(shù)接口讓該線程進(jìn)入就緒態(tài):
rt_err_trt_thread_startup(rt_thread_tthread);

...還有其他一些線程API,就不再一一贅述了,可以在RT-Thread官網(wǎng)查看
創(chuàng)建任務(wù)
上面對線程的介紹,羅里吧嗦的說了一大堆,接下來一起實戰(zhàn)來看看,如何創(chuàng)建并運(yùn)行任務(wù)
創(chuàng)建任務(wù)實現(xiàn)多任務(wù)點燈
根據(jù)創(chuàng)建任務(wù)的API
rt_thread_trt_thread_create(constchar*name, void(*entry)(void*parameter), void*parameter, rt_uint32_tstack_size, rt_uint8_tpriority, rt_uint32_ttick);
來創(chuàng)建我們的2個任務(wù):
/** ****************************************************************************** *@filert_user_task.c *@brief用戶任務(wù)文件 * ****************************************************************************** *@attention * * Copyright (c) 2022 公眾號:小飛哥玩嵌入式. *Allrightsreserved. * Author:小飛哥 * *****************************************************************************/ /*Includes------------------------------------------------------------------*/ #include"main.h" #include#include"rt_user_task.h" /*Privatetypedef-----------------------------------------------------------*/ /*Privatedefine------------------------------------------------------------*/ #defineTHREAD1_PRIORITY27//線程 #defineTHREAD_STACK_SIZE512//線程棧深度 #defineTHREAD_TIMESLICE5//線程的時間片 #defineTHREAD2_PRIORITY26//線程 /*Privatemacro-------------------------------------------------------------*/ /*Privatevariables---------------------------------------------------------*/ /*Privatefunctionprototypes-----------------------------------------------*/ /*Privateusercode---------------------------------------------------------*/ /** *@functionrt_ledflash_entry *@author:小飛哥玩嵌入式-小飛哥 *@TODO:LED控制線程 *@param: *@return:NULL */ staticvoidrt_led1_flash_entry(void*parameter) { for(;;) { HAL_GPIO_TogglePin(Y_LED_GPIO_Port,Y_LED_Pin); rt_kprintf("LED1TaskisRunning! "); rt_thread_mdelay(500); } } /** *@functionrt_led2flash_entry *@author:小飛哥玩嵌入式-小飛哥 *@TODO:LED控制線程 *@param: *@return:NULL */ staticvoidrt_led2_flash_entry(void*parameter) { for(;;) { HAL_GPIO_TogglePin(CP_LED_GPIO_Port,CP_LED_Pin); rt_kprintf("LED2TaskisRunning! "); rt_thread_mdelay(500); } } /** *@functionrt_user_thread_entry *@author:小飛哥玩嵌入式-小飛哥 *@TODO:創(chuàng)建線程 *@param: *@return:NULL */ intrt_user_thread_entry(void) { staticrt_thread_tresult=RT_NULL; /*創(chuàng)建一個線程,名稱是rt_ledflash,入口是rt_ledflash_entry*/ result=rt_thread_create("rt_led1flash",rt_led1_flash_entry, NULL, THREAD_STACK_SIZE, THREAD1_PRIORITY, THREAD_TIMESLICE); if(result!=RT_NULL)//線程創(chuàng)建成功 { rt_thread_startup(result); } else { rt_kprintf(" rt_led1flashthreadcreatefailed "); } /*創(chuàng)建一個線程,名稱是rt_ledflash,入口是rt_ledflash_entry*/ result=rt_thread_create("rt_led2flash",rt_led2_flash_entry, NULL, THREAD_STACK_SIZE, THREAD2_PRIORITY, THREAD_TIMESLICE); if(result!=RT_NULL)//線程創(chuàng)建成功 { rt_thread_startup(result); } else { rt_kprintf(" rt_led2flashthreadcreatefailed "); } return0; }
上面創(chuàng)建了兩個任務(wù),rt-thread任務(wù)的數(shù)值越小優(yōu)先級是越高的,我們設(shè)置任務(wù)1的優(yōu)先級為27,任務(wù)2的優(yōu)先級為26,按照優(yōu)先級設(shè)置規(guī)則,任務(wù)2的優(yōu)先級是比較高的,接下來我們通過打印的方式(LED就不展示啦)來看看是不是按照我們的優(yōu)先級去執(zhí)行,可以看到,任務(wù)2的優(yōu)先級是比較任務(wù)1高的
然后我們測試下同一個優(yōu)先級情況下會發(fā)生什么,不出意外的話,應(yīng)該是先執(zhí)行先創(chuàng)建的任務(wù),下圖也可以看到,確實如此
至此,本節(jié)教程就完了,講的還是比較粗略的,LED就不展示啦,希望對小伙們有所幫助哈!
審核編輯:湯梓紅
-
led
+關(guān)注
關(guān)注
242文章
23614瀏覽量
669194 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1348瀏覽量
41376 -
HAL
+關(guān)注
關(guān)注
2文章
71瀏覽量
12929 -
CubeMx
+關(guān)注
關(guān)注
0文章
31瀏覽量
1551
原文標(biāo)題:02-rt-thread 任務(wù)創(chuàng)建與HAL庫點燈
文章出處:【微信號:小飛哥玩嵌入式,微信公眾號:小飛哥玩嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
HAL庫GPIO輸入模式在cubemx中的配置
使用STM32 HAL庫進(jìn)行GPIO控制的實例
HAL庫GPIO使用注意事項
講解GPIO的API使用和注意事項
Simulink開發(fā)STM32環(huán)境配置注意事項
MCU的SWD端口復(fù)用為GPIO端口功能的配置方法及注意事項詳細(xì)說明

【STM32】標(biāo)準(zhǔn)庫與HAL庫對照學(xué)習(xí)教程三--使用庫函數(shù)配置GPIO點亮LED燈

00_STM32F4學(xué)習(xí)_HAL庫_GPIO函數(shù)

STM32 HAL庫 CUBEMX配置 ADC采集

AN092GD32MCU GPIO結(jié)構(gòu)與使用注意事項

評論