主頁 >  其他 > 了解庫開發,我們從STM32標準庫開發學起

了解庫開發,我們從STM32標準庫開發學起

2021-01-12 14:01:00 其他

摘要:從STM32新建工程、編譯下載程式出發,讓新手由淺入深,盡享STM32標準庫開發的樂趣,

自從CubeMX等影像配置軟體的出現,同學們往往點幾下滑鼠就解決了單片機的配置問題,對于追求開發速度的業務場景下,使用快速配置軟體是合理的,高效的,但對于學生的學習場景下,更為重要的是知其然并知其所以然,

以下是學習(包括但不限于)嵌入式的三個重要內容,

1、學會如何參考官方的手冊和官方的代碼來獨立寫自己的程式,

2、積累常用代碼段,知道哪里的問題需要哪些代碼處理,

3、跟隨大佬步伐,一步一個腳印,

  • 首先:我們都知道編程時一般查的是《參考手冊》,而進行芯片選型或需要芯片資料時,查閱的是《資料手冊》,此外市面上所有關于STM32的書籍都是立足于前二者(+Cortex內核手冊)進行編著,
  • 其次:要分清什么是內核外設與內核之外的外設,為了便于區分,按照網上的一種說法,將“內核之外的外設”以“處理器外設”代替,
  • 再者:如今很少使用標準庫了,都是HAL庫,但作為高校目前教學方式,

我們將以STM32f10xxx為例對標準庫開發進行概覽,

一、STM32 系統結構

STM32f10xxx 系統結構

內核IP

從結構框圖上看,Cortex-M3 內部有若 干個總線介面,以使 CM3 能同時取址和訪內(訪問記憶體),它們是: 指令存盤區總線(兩條)、系統總線、私有外設總線,有兩條代碼存盤區總線負責對代 碼存盤區(即 FLASH 外設)的訪問,分別是 I-Code 總線和 D-Code 總線,

I-Code 用于取指,D-Code 用于查表等操作,它們按最佳執行速度進行優化,

系統總線(System)用于訪問記憶體和外設,覆寫的區域包括 SRAM,片上外設,片外 RAM,片外擴展設備,以及系統級存盤區的部分空間,

私有外設總線負責一部分私有外設的訪問,主要就是訪問除錯組件,它們也在系統級 存盤區,

還有一個 MDA 總線,從字面上看,DMA 是 data memory access 的意思,是一種連接內核和外設的橋梁,它可以訪問外設、記憶體,傳輸不受 CPU 的控制,并且是雙向通信,簡而言之,這個家伙就是一個速度很快的且不受老大控制的資料搬運工,

處理器外設(內核之外的外設)

從結構框圖上看,STM32 的外設有 串口、定時器、IO 口、FSMC、SDIO、SPI、I2C 等,這些外設按 照速度的不同,分別掛載到 AHB、APB2、APB1 這三條總線上,

二、暫存器

什么是暫存器?暫存器是內置于各個 IP 外設中,是一種用于配置外設功能的存盤器,并且有想對應的地址,一切庫的封裝始于映射,

是不是“又臭又長”,如果進行暫存器開發,就需要懟地址以及對暫存器進行位元組賦值,不僅效率低而且容易出錯,

來,開個玩笑,

你也許聽說過“國際 C 語言亂碼大賽(IOCCC)”下面這個例子就是網上廣為流傳的 一個經典作品:

#include <stdio.h>

main(t,_,a)char *a;{return!0<t?t<3?main(-79,-13,a+main(-87,1-_,
main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?
main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?main(_,t,
"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#\
;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l \
q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# \
){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' \
iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c \
;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# \
}'+}##(!!/")
:t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1)
:0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,
"!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m.vpbks,fxntdCeghiry"),a+1);}

庫的存在就是為了解決這類問題,將代碼語意化,語意化思想不僅僅是嵌入式有的,前端代碼也在追求語意特性,

三、萬物始于點燈

(1)內核庫檔案分析

  • cor_cm3.h

這個頭檔案實作了:1、內核結構體暫存器定義 2、內核暫存器記憶體映射 3、記憶體寄存 器位定義,跟處理器相關的頭檔案 stm32f10x.h 實作的功能一樣,一個是針對內核的暫存器,一個是針對內核之外,即處理器的暫存器,

  • misc.h

內核應用函式庫頭檔案,對應 stm32f10x_xxx.h

  • misc.c

內核應用函式庫檔案,對應 stm32f10x_xxx.c,在 CM3 這個內核里面還有一些功能組 件,如 NVIC、SCB、ITM、MPU、CoreDebug,CM3 帶有非常豐富的功能組件,但是芯片 廠商在設計 MCU 的時候有一些并不是非要不可的,是可裁剪的,比如 MPU、ITM 等在 STM32 里面就沒有,其中 NVIC 在每一個 CM3 內核的單片機中都會有,但都會被裁剪,只能是 CM3 NVIC 的一個子集,在 NVIC 里面還有一個 SysTick,是一個系統定時器,可以提 供時基,一般為作業系統定時器所用, misc.h 和 mics.c 這兩個檔案提供了操作這些組件的函式,并可以在 CM3 內核單片機 直接移植,

(2)處理器外設庫檔案分析

  • startup_stm32f10x_hd.s

這個是由匯編撰寫的啟動檔案,是 STM32 上電啟動的第一個程式,啟動檔案主要實作 了

  1. 初始化堆疊指標 SP;
  2. 設定 PC 指標=Reset_Handler ;
  3. 設定向量表的地址,并 初始化向量表,向量表里面放的是 STM32 所有中斷函式的入口地址
  4. 呼叫庫函式 SystemInit,把系統時鐘配置成 72M,SystemInit 在庫檔案 stytem_stm32f10x.c 中定義;
  5. 跳轉到標號_main,最終去到 C 的世界,
  • system_stm32f10x.c

這個檔案的作用是里面實作了各種常用的系統時鐘設定函式,有 72M,56M,48, 36,24,8M,我們使用的是是把系統時鐘設定成 72M,

  • Stm32f10x.h

這個頭檔案非常重要,這個頭檔案實作了:1、處理器外設暫存器 的結構體定義 2、處理器外設的記憶體映射 3、處理器外設暫存器的位定義,

關于 1 和 2 我們在用暫存器點亮 LED 的時候有講解,

其中 3:處理器外設暫存器的位定義,這個非常重要,具體是什么意思?我們知道一個暫存器有很多個位,每個位寫 1 或 者寫 0 的功能都是不一樣的,處理器外設暫存器的位定義就是把外設的每個暫存器的每一 個位寫 1 的 16 進制數定義成一個宏,宏名即用該位的名稱表示,如果我們操作暫存器要開啟某一個功能的話,就不用自己親自去算這個值是多少,可以直接到這個頭檔案里面找,

我們以片上外設 ADC 為例,假設我們要啟動 ADC 開始轉換,根據手冊我們知道是要控制 ADC_CR2 暫存器的位 0:ADON,即往位 0 寫 1,即:

ADC->CR2=0x00000001;

這是 一般的操作方法,現在這個頭檔案里面有關于 ADON 位的位定義:

 #define ADC_CR2_ADON ((uint32_t)0x00000001)

有了這個位定義,我們剛剛的 代碼就變成了:

ADC->CR2=ADC_CR2_ADON
  • stm32f10x_xxx.h

外設 xxx 應用函式庫頭檔案,這里面主要定義了實作外設某一功能 的結構體,比如通用定時器有很多功能,有定時功能,有輸出比較功能,有輸入捕捉功 能,而通用定時器有非常多的暫存器要實作某一個功能,比如定時功能,我們根本不知道 具體要操作哪些暫存器,這個頭檔案就為我們打包好了要實作某一個功能的暫存器,是以機構體的形式定義的,比如通用定時器要實作一個定時的功能,我們只需要初始化 TIM_TimeBaseInitTypeDef 這個結構體里面的成員即可,里面的成員就是定時所需要 操作的暫存器, 有了這個頭檔案,我們就知道要實作某個功能需要操作哪些暫存器,然后 再回手冊中精度這些暫存器的說明即可,

  • stm32f10x_xxx.c

stm32f10x_xxx.c:外設 xxx 應用函式庫,這里面寫好了操作 xxx 外設的所有常用的函 數,我們使用庫編程的時候,使用的最多的就是這里的函式,

(3)SystemInit

工程中新建main.c ,

在此檔案中撰寫main函式后直接編譯會報錯:

Undefined symbol SystemInit (referred from startup_stm32f10x_hd.o).

錯誤提示說SystemInit 沒有定義,從分析啟動檔案startup_stm32f10x_hd.s時我們知道,

1 ;Reset handler
2 Reset_Handler PROC
3 EXPORT Reset_Handler [WEAK]
4 IMPORT __main
5 ;IMPORT SystemInit
6 ;LDR R0, =SystemInit
7 BLX R0
8 LDR R0, =__main
9 BX R0
10 ENDP
匯編中;分號是注釋的意思

第五行第六行代碼Reset_Handler 呼叫了SystemInit該函式用來初始化系統時鐘,而該函式是在庫檔案system_stm32f10x.c 中實作的,我們重新寫一個這樣的函式也可以,把功能完整實作一遍,但是為了簡單起見,我們在main 檔案里面定義一個SystemInit 空函式,為的是騙過編譯器,把這個錯誤去掉,關于配置系統時鐘之后會出文章RCC 時鐘樹詳細介紹,主要配置時鐘控制暫存器(RCC_CR)和時鐘配置暫存器(RCC_CFGR)這兩個暫存器,但最好是直接使用CubeMX直接生成,因為它的配置程序有些冗長,

如果我們用的是庫,那么有個庫函式SystemInit,會幫我們把系統時鐘設定成72M,

現在我們沒有使用庫,那現在時鐘是多少?答案是8M,當外部HSE 沒有開啟或者出現故障的時候,系統時鐘由內部低速時鐘LSI 提供,現在我們是沒有開啟HSE,所以系統默認的時鐘是LSI=8M,

(4)庫封裝層級

如圖,達到第四層級便是我們所熟知的韌體庫或HAL庫的效果,當然庫的撰寫還需要考慮許多問題,不止于這些內容,我們需要的是了解庫封裝的大概程序,

將庫封裝等級分為四級來介紹是為了有層次感,就像打怪升級一樣,進行認知理解的升級,

我們都知道,操作GPIO輸出分三大步:

時鐘控制:

STM32 外設很多,為了降低功耗,每個外設都對應著一個時鐘,在系統復位的時候這些時鐘都是被關閉的,如果想要外設作業,必須把相應的時鐘打開,

STM32 的所有外設的時鐘由一個專門的外設來管理,叫RCC(reset and clockcontrol),RCC 在STM32 參考手冊的第六章,

STM32 的外設因為速率的不同,分別掛載到三條總系上:AHB、APB2、APB1,AHB為高速總線,APB2 次之,APB1 再次之,所以的IO 口都掛載到APB2 總線上,屬于高速外設,

模式配置:

這個由埠配置暫存器來控制,埠配置暫存器分為高低兩個,每4bit 控制一個IO 口,所以埠配置低暫存器:CRL 控制這IO 口的低8 位,埠配置高暫存器:CRH控制這IO 口的高8bit,在4 位一組的控制位中,CNFy[1:0] 用來控制埠的輸入輸出,MODEy[1:0]用來控制輸出模式的速率,又稱驅動電路的回應速度,注意此處速率與程式無關,具體內容見文章:【嵌入式】GPIO引腳速度、翻轉速度、輸出速度區別輸入有4種模式,輸出有4種模式,我們在控制LED 的時候選擇通用推挽輸出,

輸出速率有三種模式:2M、10M、50M,這里我們選擇2M,

電平控制:

STM32 的IO 口比較復雜,如果要輸出1 和0,則要通過控制:埠輸出資料暫存器ODR 來實作,ODR 是:Output data register 的簡寫,在STM32 里面,其暫存器的命名名稱都是英文的簡寫,很容易記住,從手冊上我們知道ODR 是一個32 位的暫存器,低16位有效,高16 位保留,低16 位對應著IO0~IO16,只要往相應的位置寫入0 或者1 就可以輸出低或者高電平,

第一層級:基地址宏定義

時鐘控制:

在STM32 中,每個外設都有一個起始地址,叫做外設基地址,外設的暫存器就以這個基地址為標準按照順序排列,且每個暫存器32位,(后面作為結構體里面的成員正好記憶體對齊),查表看到時鐘由APB2 外設時鐘使能暫存器(RCC_APB2ENR)來控制,其中PB 埠的時鐘由該暫存器的位3 寫1 使能,我們可以通過基地址+偏移量0x18,算出RCC_APB2ENR 的地址為:0x40021018,那么使能PB 口的時鐘代碼則如下所示:

 #define RCC_APB2ENR *(volatile unsigned long *)0x40021018

 // 開啟埠B 時鐘
 RCC_APB2ENR |= 1<<3;

模式配置:

同RCC_APB2ENR 一樣,GPIOB 的起始地址是:0X4001 0C00,我們也可以算出GPIO_CRL 的地址為:0x40010C00,那么設定PB0 為通用推挽輸出,輸出速率為2M 的代碼則如下所示:

同上,從手冊中我們看到ODR 暫存器的地址偏移是:0CH,可以算出GPIOB_ODR 暫存器的地址是:0X4001 0C00 + 0X0C = 0X4001 0C0C,現在我們就可以定義GPIOB_ODR 這個暫存器了,代碼如下:

#define GPIOB_ODR *(volatile unsigned long *)0x40010C0C

//PB0 輸出低電平
 GPIOB_ODR = 0<<0;

第一層級:基地址宏定義完成用STM32 控制一個LED 的完整代碼:

1 #define RCC_APB2ENR *(volatile unsigned long *)0x40021018
2 #define GPIOB_CRL *(volatile unsigned long *)0x40010C00
3 #define GPIOB_ODR *(volatile unsigned long *)0x40010C0C
45
int main(void)
6 {
7 // 開啟埠B 的時鐘
8 RCC_APB2ENR |= 1<<3;
9
10 // 配置PB0 為通用推挽輸出模式,速率為2M
11 GPIOB_CRL = (2<<0) | (0<<2);
12
13 // PB0 輸出低電平,點亮LED
14 GPIOB_ODR = 0<<0;
15 }
16
17 void SystemInit(void)
18 {
19 }

第二層級:基地址宏定義+結構體封裝

外設暫存器結構體封裝

上面我們在操作暫存器的時候,操作的是暫存器的絕對地址,如果每個暫存器都這樣操作,那將非常麻煩,我們考慮到外設暫存器的地址都是基于外設基地址的偏移地址,都是在外設基地址上逐個連續遞增的,每個暫存器占 32 個或者 16 個位元組,這種方式跟結構體里面的成員類似,所以我們可以定義一種外設結構體,結構體的地址等于外設的基地址,結構體的成員等于暫存器,成員的排列順序跟暫存器的順序一樣,這樣我們操作暫存器的時候就不用每次都找到絕對地址,只要知道外設的基地址就可以操作外設的全部暫存器,即操作結構體的成員即可,

下面我們先定義一個 GPIO 暫存器結構體,結構體里面的成員是 GPIO 的暫存器,成員的順序按照暫存器的偏移地址從低到高排列,成員型別跟暫存器型別一樣,(struct用法參考【C語言】(2):關鍵字的詳細介紹)

1 typedef struct {
2 volatile uint32_t CRL;
3 volatile uint32_t CRH;
4 volatile uint32_t IDR;
5 volatile uint32_t ODR;
6 volatile uint32_t BSRR;
7 volatile uint32_t BRR;
8 volatile uint32_t LCKR;
9 } GPIO_TypeDef;

在《STM32 中文參考手冊》8.2 暫存器描述章節,我們可以找到結構體里面的7 個暫存器描述,在點亮LED 的時候我們只用了CRL 和ODR 這兩個暫存器,至于其他暫存器的功能大家可以自行看手冊了解,

在GPIO 結構體里面我們用了兩個資料型別,一個是uint32_t,表示無符號的32 位整型,因為GPIO 的暫存器都是32 位的,這個型別宣告在標準頭檔案stdint.h 里面使用typedef對unsigned int重命名,我們在程式上只要包含這個頭檔案即可,

另外一個是volatile(volatile用法參考【C語言】(2):關鍵字的詳細介紹),作用就是告訴編譯器這里的變數會變化不因優化而省略此指令,必須每次都直接讀寫其值,這樣就能確保每次讀或者寫暫存器都真正執行到位,

外設封裝

STM32F1 系列的GPIO 埠分A~G,即GPIOA、GPIOB,,,,,,GPIOG,每個埠都含有GPIO_TypeDef 結構體里面的暫存器,我們可以根據手冊各個埠的基地址把GPIO 的各個埠定義成一個GPIO_TypeDef 型別指標,然后我們就可以根據埠名(實際上現在是結構體指標了)來操作各個埠的暫存器,代碼實作如下:

1 #define GPIOA ((GPIO_TypeDef *) 0X4001 0800)
2 #define GPIOB ((GPIO_TypeDef *) 0X4001 0C00)
3 #define GPIOC ((GPIO_TypeDef *) 0X4001 1000)
4 #define GPIOD ((GPIO_TypeDef *) 0X4001 1400)
5 #define GPIOE ((GPIO_TypeDef *) 0X4001 1800)
6 #define GPIOF ((GPIO_TypeDef *) 0X4001 1C00)
7 #define GPIOG ((GPIO_TypeDef *) 0X4001 2000)

外設記憶體映射

講到基地址的時候我們再引人一個知識點:Cortex-M3 存盤器系統,這個知識點在《Cortex-M3 權威指南》第5 章里面講到,CM3 的地址空間是4GB,如下圖所示:

我們這里要講的是片上外設,就是我們所說的暫存器的根據地,其大小總共有512MB,512MB 是其極限空間,并不是每個單片機都用得完,實際上各個MCU 廠商都只是用了一部分而已,STM32F1 系列用到了:0x4000 0000 ~0x5003 FFFF,現在我們說的STM32 的暫存器就是位于這個區域

  • APB1、APB2、AHB 總線基地址

現在我們說的STM32 的暫存器就是位于這個區域,這里面ST 設計了三條總線:AHB、APB2 和APB1,其中AHB 和APB2 是高速總線,APB1 是低速總線,不同的外設根據速度不同分別掛載到這三條總線上,從下往上依次是:APB1、APB2、AHB,每個總線對應的地址分別是:APB1:0x40000000,APB2:0x4001 0000,AHB:0x4001 8000,

這三條總線的基地址我們是從《STM32 中文參考手冊》2.3 小節—存盤器映像得到的:APB1 的基地址是TIM2 定時器的起始地址,APB2 的基地址是AFIO 的起始地址,AHB 的基地址是SDIO 的起始地址,其中APB1 地址又叫做外設基地址,是所有外設的基地址,叫做PERIPH_BASE,

現在我們把這三條總線地址用宏定義出來,以后我們在定義其他外設基地址的時候,只需要在這三條總線的基址上加上偏移地址即可,代碼如下:

1 #define PERIPH_BASE ((uint32_t)0x40000000)
2 #define APB1PERIPH_BASE PERIPH_BASE
3 #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
4 #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
  • GPIO 埠基地址

因為GPIO 掛載到APB2 總線上,那么現在我們就可以根據APB2 的基址算出各個GPIO 埠的基地址,用宏定義實作代碼如下:

1 #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
2 #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
3 #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
4 #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
5 #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
6 #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
7 #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)

第二層級:基地址宏定義+結構體封裝完成用STM32 控制一個LED 的完整代碼:

1 #include <stdint.h>
2 #define __IO volatile
3
4typedef struct {
5 __IO uint32_t CRL;
6 __IO uint32_t CRH;
7 __IO uint32_t IDR;
8 __IO uint32_t ODR;
9 __IO uint32_t BSRR;
10 __IO uint32_t BRR;
11 __IO uint32_t LCKR;
12 } GPIO_TypeDef;
13
14 typedef struct {
15 __IO uint32_t CR;
16 __IO uint32_t CFGR;
17 __IO uint32_t CIR;
18 __IO uint32_t APB2RSTR;
19 __IO uint32_t APB1RSTR;
20 __IO uint32_t AHBENR;
21 __IO uint32_t APB2ENR;
22 __IO uint32_t APB1ENR;
23 __IO uint32_t BDCR;
24 __IO uint32_t CSR;
25 } RCC_TypeDef;
26
27 #define PERIPH_BASE ((uint32_t)0x40000000)
28
29 #define APB1PERIPH_BASE PERIPH_BASE
30 #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
31 #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
32
33 #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
34 #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
35 #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
36 #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
37 #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
38 #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
39 #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)
40 #define RCC_BASE (AHBPERIPH_BASE + 0x1000)
41
42 #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
43 #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
44 #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
45 #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
46 #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
47 #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
48 #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
49 #define RCC ((RCC_TypeDef *) RCC_BASE)
50
51
52 #define RCC_APB2ENR *(volatile unsigned long *)0x40021018
53 #define GPIOB_CRL *(volatile unsigned long *)0x40010C00
54 #define GPIOB_ODR *(volatile unsigned long *)0x40010C0C
55
56 int main(void)
57 {
58 // 開啟埠B 的時鐘
59 RCC->APB2ENR |= 1<<3;
60
61 // 配置PB0 為通用推挽輸出模式,速率為2M
62 GPIOB->CRL = (2<<0) | (0<<2);
63
64 // PB0 輸出低電平,點亮LED
65 GPIOB->ODR = 0<<0;
66
67 }
68
69 void SystemInit(void)
70 {
71 }

第二層級變化:

①、定義一個外設(GPIO)暫存器結構體,結構體的成員包含該外設的所有暫存器,成員的排列順序跟暫存器偏移地址一樣,成員的資料型別跟暫存器的一樣,

②外設記憶體映射,即把地址跟外設建立起一一對應的關系,

③外設宣告,即把外設的名字定義成一個外設暫存器結構體型別的指標,

④通過結構體操作暫存器,實作點亮LED,

第三層級:基地址宏定義+結構體封裝+“位封裝”(每一位的對應位元組封裝)

上面我們在控制GPIO 輸出內容的時候控制的是ODR(Output data register)暫存器,ODR 是一個16 位的暫存器,必須以字的形式控制其實我們還可以控制BSRR 和BRR 這兩個暫存器來控制IO 的電平,下面我們簡單介紹下BRR 暫存器的功能,BSRR 自行看手冊研究,

位清除暫存器BRR 只能實作位清0 操作,是一個32 位暫存器,低16 位有效,寫0 沒影響,寫1 清0,現在我們要使PB0 輸出低電平,點亮LED,則只要往BRR 的BR0 位寫1 即可,其他位為0,代碼如下:

1 GPIOB->BRR = 0X0001;

這時PB0 就輸出了低電平,LED 就被點亮了,

如果要PB2 輸出低電平,則是:

1 GPIOB->BRR = 0X0004;

如果要PB3/4/5/6,,,,,,這些IO 輸出低電平呢?道理是一樣的,只要往BRR 的相應位置賦不同的值即可,因為BRR 是一個16 位的暫存器,位數比較多,賦值的時候容易出錯,而且從賦值的16 進制數字我們很難清楚的知道控制的是哪個IO,這時,我們是否可以把BRR 的每個位置1 都用宏定義來實作,如GPIO_Pin_0 就表示0X0001,GPIO_Pin_2 就表示0X0004,只要我們定義一次,以后都可以使用,而且還見名知意,“位封裝”(每一位的對應位元組封裝) 代碼如下:

1 #define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
2 #define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
3 #define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
4 #define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
5 #define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
6 #define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
7 #define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
8 #define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
9 #define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
10 #define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
11 #define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
12 #define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
13 #define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
14 #define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
15 #define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
16 #define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
17 #define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */

這時PB0 就輸出了低電平的代碼就變成了:

1 GPIOB->BRR = GPIO_Pin_0;

(如果同時讓PB0/PB15輸出低電平,用或運算,代碼:

1 GPIOB->BRR = GPIO_Pin_0|GPIO_Pin_15;

為了不使main 函式看起來冗余,上述庫封裝 的代碼不應該放在main 里面,因為其是跟GPIO 相關的,我們可以把這些宏放在一個單獨的頭檔案里面,

在工程目錄下新建stm32f10x_gpio.h,把封裝代碼放里面,然后把這個檔案添加到工程里面,這時我們只需要在main.c 里面包含這個頭檔案即可,

第四層級:基地址宏定義+結構體封裝+“位封裝”+函式封裝

我們點亮LED 的時候,控制的是PB0 這個IO,如果LED 接到的是其他IO,我們就需要把GPIOB 修改成其他的埠,其實這樣修改起來也很快很方便,但是為了提高程式的可讀性和可移植性,我們是否可以撰寫一個專門的函式用來復位GPIO 的某個位,這個函式有兩個形參,一個是GPIOX(X=A...G),另外一個是GPIO_Pin(0...15),函式的主體則是根據形參GPIOX 和GPIO_Pin 來控制BRR 暫存器,代碼如下:

1 void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
2 {
3 GPIOx->BRR = GPIO_Pin;
4 }

這時,PB0 輸出低電平,點亮LED 的代碼就變成了:

1 GPIO_ResetBits(GPIOB,GPIO_Pin_0);

同理, 我們可以控制BSRR 這個暫存器來實作關閉LED,代碼如下:

1 // GPIO 埠置位函式
2 void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
3 {
4 GPIOx->BSRR = GPIO_Pin;
5 }

這時,PB0 輸出高電平,關閉LED 的代碼就變成了:

1 GPIO_SetBits(GPIOB,GPIO_Pin_0);

同樣,因為這個函式是控制GPIO 的函式,我們可以新建一個專門的檔案來放跟gpio有關的函式,

在工程目錄下新建stm32f10x_gpio.c,把GPIO 相關的函式放里面,這時我們是否發現剛剛新建了一個頭檔案stm32f10x_gpio.h,這兩個檔案存放的都是跟外設GPIO 相關的,C 檔案里面的函式會用到h 頭檔案里面的定義,這兩個檔案是相輔相成的,故我們在stm32f10x_gpio.c 檔案中也包含stm32f10x_gpio.h 這個頭檔案,別忘了把stm32f10x.h 這個頭檔案也包含進去,因為有關暫存器的所有定義都在這個頭檔案里面,

如果我們寫其他外設的函式,我們也應該跟GPIO 一樣,新建兩個檔案專門來存函式,比如RCC 這個外設我們可以新建stm32f10x_rcc.c 和stm32f10x_rcc.h,其他外依葫蘆畫瓢即可,

(5)實體撰寫

以上,是對庫封住程序的概述,下面我們正在地使用庫函式撰寫LED程式

①管理庫的頭檔案

當我們開始呼叫庫函式寫代碼的時候,有些庫我們不需要,在編譯的時候可以不編譯,可以通過一個總的頭檔案stm32f10x_conf.h 來控制,該頭檔案主要代碼如下:

1 //#include "stm32f10x_adc.h"
2 //#include "stm32f10x_bkp.h"
3 //#include "stm32f10x_can.h"
4 //#include "stm32f10x_cec.h"
5 //#include "stm32f10x_crc.h"
6 //#include "stm32f10x_dac.h"
7 //#include "stm32f10x_dbgmcu.h"
8 //#include "stm32f10x_dma.h"
9 //#include "stm32f10x_exti.h"
10 //#include "stm32f10x_flash.h"
11 //#include "stm32f10x_fsmc.h"
12 #include "stm32f10x_gpio.h"
13 //#include "stm32f10x_i2c.h"
14 //#include "stm32f10x_iwdg.h"
15 //#include "stm32f10x_pwr.h"
16 #include "stm32f10x_rcc.h"
17 //#include "stm32f10x_rtc.h"
18 //#include "stm32f10x_sdio.h"
19 //#include "stm32f10x_spi.h"
20 //#include "stm32f10x_tim.h"
21 //#include "stm32f10x_usart.h"
22 //#include "stm32f10x_wwdg.h"
23 //#include "misc.h"

這里面包含了全部外設的頭檔案,點亮一個LED 我們只需要RCC 和GPIO 這兩個外設的庫函式即可,其中RCC 控制的是時鐘,GPIO 控制的具體的IO 口,所以其他外設庫函式的頭檔案我們注釋掉,當我們需要的時候就把相應頭檔案的注釋去掉即可,

stm32f10x_conf.h 這個頭檔案在stm32f10x.h 這個頭檔案的最后面被包含,在第8296行:

1 #ifdef USE_STDPERIPH_DRIVER
2 #include "stm32f10x_conf.h"
3 #endif

代碼的意思是,如果定義了USE_STDPERIPH_DRIVER 這個宏的話,就包含stm32f10x_conf.h 這個頭檔案,我們在新建工程的時候,在魔術棒選項卡C/C++中,我們定義了USE_STDPERIPH_DRIVER 這個宏,所以stm32f10x_conf.h 這個頭檔案就被stm32f10x.h 包含了,我們在寫程式的時候只需要呼叫一個頭檔案:stm32f10x.h 即可,(預處理指令詳細內容會在【C語言】的文章中提到)

②撰寫LED 初始化函式

經過暫存器點亮LED 的操作,我們知道操作一個GPIO 輸出的編程要點大概如下:

1、開啟GPIO 的埠時鐘

2、選擇要具體控制的IO 口,即pin

3、選擇IO 口輸出的速率,即speed

4、選擇IO 口輸出的模式,即mode

5、輸出高/低電平

STM32 的時鐘功能非常豐富,配置靈活,為了降低功耗,每個外設的時鐘都可以獨自的關閉和開啟,STM32 中跟時鐘有關的功能都由RCC 這個外設控制,RCC 中有三個暫存器控制著所以外設時鐘的開啟和關閉:RCC_APHENR、RCC_APB2ENR 和RCC_APB1ENR,AHB、APB2 和APB1 代表著三條總線,所有的外設都是掛載到這三條總線上,GPIO 屬于高速的外設,掛載到APB2 總線上,所以其時鐘有RCC_APB2ENR 控制,

GPIO 時鐘控制

韌體庫函式:RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE)函式的

原型為:

1 void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph,
                                FunctionalState NewState)
2 {
3 /* Check the parameters */
4 assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
5 assert_param(IS_FUNCTIONAL_STATE(NewState));
6 if (NewState != DISABLE) {
7 RCC->APB2ENR |= RCC_APB2Periph;
8 } else {
9 RCC->APB2ENR &= ~RCC_APB2Periph;
10 }
11 }

當程式編譯一次之后,把游標定位到函式/變數/宏定義處,按鍵盤的F12 或滑鼠右鍵的Go to definition of,就可以找到原型,韌體庫的底層操作的就是RCC 外設的APB2ENR這個暫存器,宏RCC_APB2Periph_GPIOB 的原型是:0x00000008,即(1<<3),還原成存器操作就是:RCC->APB2ENR |= 1<<<3,相比韌體庫操作,暫存器操作的代碼可讀性就很差,只有才查閱暫存器配置才知道具體代碼的功能,而韌體庫操作恰好相反,見名知意,

GPIO 埠配置

GPIO 的pin,速度,模式,都由GPIO 的埠配置暫存器來控制,其中IO0~IO7 由埠配置低暫存器CRL 控制,IO8~IO15 由埠配置高暫存器CRH 配置,韌體庫把埠配置的pin,速度和模式封裝成一個結構體:

1 typedef struct {
2 uint16_t GPIO_Pin;
3 GPIOSpeed_TypeDef GPIO_Speed;
4 GPIOMode_TypeDef GPIO_Mode;
5 } GPIO_InitTypeDef;

pin 可以是GPIO_Pin_0~GPIO_Pin_15 或者是GPIO_Pin_All,這些都是庫預先定義好的宏,speed 也被封裝成一個結構體:

1 typedef enum {
2 GPIO_Speed_10MHz = 1,
3 GPIO_Speed_2MHz,
4 GPIO_Speed_50MHz
5 } GPIOSpeed_TypeDef;

速度可以是10M,2M 或者50M,這個由埠配置暫存器的MODE 位控制,速度是針對IO 口輸出的時候而言,在輸入的時候可以不用設定,mode 也被封裝成一個結構體:

1 typedef enum {
2 GPIO_Mode_AIN = 0x0, // 模擬輸入
3 GPIO_Mode_IN_FLOATING = 0x04, // 浮空輸入(復位后的狀態)
4 GPIO_Mode_IPD = 0x28, // 下拉輸入
5 GPIO_Mode_IPU = 0x48, // 上拉輸入
6 GPIO_Mode_Out_OD = 0x14, // 通用開漏輸出
7 GPIO_Mode_Out_PP = 0x10, // 通用推挽輸出
8 GPIO_Mode_AF_OD = 0x1C, // 復用開漏輸出
9 GPIO_Mode_AF_PP = 0x18 // 復用推挽輸出
10 } GPIOMode_TypeDef;

IO 口的模式有8 種,輸入輸出各4 種,由埠配置暫存器的CNF 配置,平時用的最多的就是通用推挽輸出,可以輸出高低電平,驅動能力大,一般用于接數字器件,至于剩下的七種模式的用法和電路原理,我們在后面的GPIO 章節再詳細講解,

最終用韌體庫實作就變成這樣:

1 // 定義一個GPIO_InitTypeDef 型別的結構體
2 GPIO_InitTypeDef GPIO_InitStructure;
3
4// 選擇要控制的IO 口
5 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
6
7// 設定引腳為推挽輸出
8 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
9
10 // 設定引腳速率為50MHz
11 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
12
13 /*呼叫庫函式,初始化GPIOB0*/
14 GPIO_Init(GPIOB, &GPIO_InitStructure);

倘若同一埠下不同引腳有不同的模式配置,每次對每個引腳配置完成后都要呼叫GPIO初始化函式,代碼如下:

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 ;                      
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;                  //上拉輸入
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;                     
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;               //推挽輸出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure); 

GPIO 輸出控制

GPIO 輸出控制,可以通過埠資料輸出暫存器ODR、埠位設定/清除暫存器BSRR和埠位清除暫存器BRR 這三個來控制,埠輸出暫存器ODR 是一個32 位的暫存器,低16 位有效,對應著IO0~IO15,只能以字的形式操作,一般使用暫存器操作,

// PB0 輸出高電平,點亮LED
 GPIOB->ODR = 1<<0;

埠位清除暫存器BRR 是一個32 位的暫存器,低十六位有效,對應著IO0~IO15,只能以字的形式操作,可以單獨對某一個位操作,寫1 清0,

// PB0 輸出低電平,點亮LED
 GPIO_ResetBits(GPIOB, GPIO_Pin_0);

BSRR 是一個32 位的暫存器,低16 位用于置位,寫1 有效,高16 位用于復位,寫1有效,相當于BRR 暫存器,高16 位我們一般不用,而是操作BRR 這個暫存器,所以BSRR 這個暫存器一般用來置位操作,

// PB0 輸出高電平,熄滅LED
 GPIO_SetBits(GPIOB, GPIO_Pin_0);

綜上:韌體庫LED GPIO 初始化函式

1 void LED_GPIO_Config(void)
2 {
3 // 定義一個GPIO_InitTypeDef 型別的結構體
4 GPIO_InitTypeDef GPIO_InitStructure;
5
6// 開啟GPIOB 的時鐘
7 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
8
9// 選擇要控制的IO 口
10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
11
12 // 設定引腳為推挽輸出
13 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
14
15 // 設定引腳速率為50MHz
16 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
17
18 /*呼叫庫函式,初始化GPIOB0*/
19 GPIO_Init(GPIOB, &GPIO_InitStructure);
20
21 // 關閉LED
22 GPIO_SetBits(GPIOB, GPIO_Pin_0);
23 }

主函式

1 #include "stm32f10x.h"
2
3
void SOFT_Delay(__IO uint32_t nCount);
4 void LED_GPIO_Config(void);
5
6int main(void)
7 {
8 // 程式來到main 函式之前,啟動檔案:statup_stm32f10x_hd.s 已經呼叫
9 // SystemInit()函式把系統時鐘初始化成72MHZ
10 // SystemInit()在system_stm32f10x.c 中定義
11 // 如果用戶想修改系統時鐘,可自行撰寫程式修改
12
13 LED_GPIO_Config();
14
15 while ( 1 ) {
16 // 點亮LED
17 GPIO_ResetBits(GPIOB, GPIO_Pin_0);
18 Time_Delay(0x0FFFFF);
19
20 // 熄滅LED
21 GPIO_SetBits(GPIOB, GPIO_Pin_0);
22 Time_Delay(0x0FFFFF);
23 }
24 }
25// 簡陋的軟體延時函式
26 void Time_Delay(volatile uint32_t Count)
27 {
28 for (; Count != 0; Count--);
29 }

注意void Time_Delay(volatile uint32_t Count)只是一個簡陋的軟體延時函式,如果小伙伴們有興趣可以看一看MultiTimer,它是一個軟體定時器擴展模塊,可無限擴展所需的定時器任務,取代傳統的標志位判斷方式, 更優雅更便捷地管理程式的時間觸發時序,

本文分享自華為云社區《【嵌入式】層層遞進,了解庫開發》,原文作者:LongYorke,

點擊關注,第一時間了解華為云新鮮技術~

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/247755.html

標籤:其他

上一篇:進制

下一篇:tomcat

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more