0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

STM32管理程序的時間觸發(fā)時序

科技綠洲 ? 來源:華為云社區(qū) ? 作者:華為云社區(qū) ? 2023-06-22 10:52 ? 次閱讀

自從CubeMX等圖像配置軟件的出現(xiàn),同學們往往點幾下鼠標就解決了單片機的配置問題。對于追求開發(fā)速度的業(yè)務場景下,使用快速配置軟件是合理的,高效的,但對于學生的學習場景下,更為重要的是知其然并知其所以然。

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

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

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

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

首先:我們都知道編程時一般查的是 《參考手冊》 ,而進行芯片選型或需要芯片數(shù)據(jù)時,查閱的是**《數(shù)據(jù)手冊》**。此外市面上所有關于STM32的書籍都是立足于前二者(+ Cortex內(nèi)核手冊 )進行編著。

其次:要分清什么是內(nèi)核外設與內(nèi)核之外的外設,為了便于區(qū)分,按照網(wǎng)上的一種說法,將“內(nèi)核之外的外設”以“處理器外設”代替。再者:如今很少使用標準庫了,都是HAL庫,但作為高校目前教學方式,

我們將以STM32f10xxx為例對標準庫開發(fā)進行概覽。

一、STM32 系統(tǒng)結構

圖片

STM32f10xxx 系統(tǒng)結構

內(nèi)核IP

從結構框圖上看,Cortex-M3 內(nèi)部有若 干個總線接口,以使 CM3 能同時取址和訪內(nèi)(訪問內(nèi)存),它們是: 指令存儲區(qū)總線(兩條)、系統(tǒng)總線、私有外設總線。 有兩條代碼存儲區(qū)總線負責對代碼存儲區(qū)(即 FLASH 外設)的訪問,分別是** I-Code 總線**和 D-Code 總線

I-Code 用于取指,D-Code 用于查表等操作,它們按最佳執(zhí)行速度進行優(yōu)化。

**系統(tǒng)總線(System)**用于訪問內(nèi)存和外設,覆蓋的區(qū)域包括 SRAM,片上外設,片外 RAM,片外擴展設備,以及系統(tǒng)級存儲區(qū)的部分空間。

私有外設總線負責一部分私有外設的訪問,主要就是訪問調(diào)試組件。它們也在系統(tǒng)級存儲區(qū)。

還有一個 DMA 總線,從字面上看,DMA 是 data memory access 的意思,是一種連接內(nèi)核和外設的橋梁,它可以訪問外設、內(nèi)存,傳輸不受 CPU 的控制,并且是雙向通信。簡而言之,這個家伙就是一個速度很快的且不受老大控制的數(shù)據(jù)搬運工。

處理器外設(內(nèi)核之外的外設)

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

二、寄存器

什么是寄存器?寄存器是內(nèi)置于各個 IP 外設中,是一種用于配置外設功能的存儲器,并且有想對應的地址。一切庫的封裝始于 映射 。

圖片

圖片

是不是“又臭又長”,如果進行寄存器開發(fā),就需要懟地址以及對寄存器進行字節(jié)賦值,不僅效率低而且容易出錯。

來,開個玩笑。

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

#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
"):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,{}:
uwloca-O;m.vpbks,fxntdCeghiry"),a+1);}

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

三、萬物始于點燈

圖片

(1)內(nèi)核庫文件分析

cor_cm3.h

這個頭文件實現(xiàn)了:

1、內(nèi)核結構體寄存器定義。

2、內(nèi)核寄存器內(nèi)存映射。

3、內(nèi)存寄存 器位定義。跟處理器相關的頭文件 stm32f10x.h 實現(xiàn)的功能一樣,一個是針對內(nèi)核的寄存器,一個是針對內(nèi)核之外,即處理器的寄存器。

misc.h

內(nèi)核應用函數(shù)庫頭文件,對應 stm32f10x_xxx.h。

misc.c

內(nèi)核應用函數(shù)庫文件,對應 stm32f10x_xxx.c。在 CM3 這個內(nèi)核里面還有一些功能組 件,如 NVIC、SCB、ITM、MPU、CoreDebug ,CM3 帶有非常豐富的功能組件,但是芯片廠商在設計 MCU 的時候有一些并不是非要不可的,是可裁剪的,比如 MPU、ITM 等在 STM32 里面就沒有。

其中 NVIC 在每一個 CM3 內(nèi)核的單片機中都會有,但都會被裁剪,只能是 CM3 NVIC 的一個子集。在 NVIC 里面還有一個 SysTick,是一個系統(tǒng)定時器,可以提供時基,一般為操作系統(tǒng)定時器所用。misc.h 和 mics.c 這兩個文件提供了操作這些組件的函數(shù),并可以在 CM3 內(nèi)核單片機直接移植。

(2)處理器外設庫文件分析

startup_stm32f10x_hd.s

這個是由匯編編寫的啟動文件,是 STM32 上電啟動的第一個程序,啟動文件主要實現(xiàn)了

  • 初始化堆棧指針 SP;
  • 設置 PC 指針=Reset_Handler ;
  • 設置向量表的地址,并 初始化向量表,向量表里面放的是 STM32 所有中斷函數(shù)的入口地址
  • 調(diào)用庫函數(shù) SystemInit,把系統(tǒng)時鐘配置成 72M,SystemInit 在庫文件 stytem_stm32f10x.c 中定義;
  • 跳轉到標號_main,最終去到 C 的世界。

system_stm32f10x.c

這個文件的作用是里面實現(xiàn)了各種常用的系統(tǒng)時鐘設置函數(shù),有 72M,56M,48, 36,24,8M,我們使用的是是把系統(tǒng)時鐘設置成 72M。

Stm32f10x.h

這個頭文件非常重要,這個頭文件實現(xiàn)了:

1、處理器外設寄存器 的結構體定義。

2、處理器外設的內(nèi)存映射。

3、處理器外設寄存器的位定義。

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

其中 3:處理器外設寄存器的位定義,這個非常重要,具體是什么意思?

我們知道一個寄存器有很多個位,每個位寫 1 或 者寫 0 的功能都是不一樣的,處理器外設寄存器的位定義就是把外設的每個寄存器的每一 個位寫 1 的 16 進制數(shù)定義成一個宏,宏名即用該位的名稱表示,如果我們操作寄存器要開啟某一個功能的話,就不用自己親自去算這個值是多少,可以直接到這個頭文件里面找。

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

ADC- >CR2=0x00000001;

這是 一般的操作方法?,F(xiàn)在這個頭文件里面有關于 ADON 位的位定義:

#define ADC_CR2_ADON ((uint32_t)0x00000001)

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

ADC- >CR2=ADC_CR2_ADON

stm32f10x_xxx.h

外設 xxx 應用函數(shù)庫頭文件,這里面主要定義了實現(xiàn)外設某一功能 的結構體,比如通用定時器有很多功能,有定時功能,有輸出比較功能,有輸入捕捉功 能,而通用定時器有非常多的寄存器要實現(xiàn)某一個功能。

比如定時功能,我們根本不知道 具體要操作哪些寄存器,這個頭文件就為我們打包好了要實現(xiàn)某一個功能的寄存器,是以機構體的形式定義的,比如通用定時器要實現(xiàn)一個定時的功能,我們只需要初始化 TIM_TimeBaseInitTypeDef 這個結構體里面的成員即可,里面的成員就是定時所需要 操作的寄存器。

有了這個頭文件,我們就知道要實現(xiàn)某個功能需要操作哪些寄存器,然后 再回手冊中精度這些寄存器的說明即可。

stm32f10x_xxx.c

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

(3)SystemInit

工程中新建main.c 。

在此文件中編寫main函數(shù)后直接編譯會報錯:

?Undefined symbol SystemInit (referred from startup_stm32f10x_hd.o).

?

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

;Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
;IMPORT SystemInit
;LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

?匯編中;分號是注釋的意思

?

第五行第六行代碼Reset_Handler 調(diào)用了SystemInit該函數(shù)用來初始化系統(tǒng)時鐘,而該函數(shù)是在庫文件system_stm32f10x.c 中實現(xiàn)的。我們重新寫一個這樣的函數(shù)也可以,把功能完整實現(xiàn)一遍,但是為了簡單起見,我們在main 文件里面定義一個SystemInit 空函數(shù),為的是騙過編譯器,把這個錯誤去掉。

關于配置系統(tǒng)時鐘之后會出文章RCC 時鐘樹詳細介紹,主要配置時鐘控制寄存器(RCC_CR)和時鐘配置寄存器(RCC_CFGR)這兩個寄存器,但最好是直接使用CubeMX直接生成,因為它的配置過程有些冗長。

如果我們用的是庫,那么有個庫函數(shù)SystemInit,會幫我們把系統(tǒng)時鐘設置成72M。

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

(4)庫封裝層級

圖片

如圖,達到第四層級便是我們所熟知的固件庫或HAL庫的效果。當然庫的編寫還需要考慮許多問題,不止于這些內(nèi)容。我們需要的是了解庫封裝的大概過程。

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

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

時鐘控制:

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

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]用來控制輸出模式的速率,又稱驅動電路的響應速度,注意此處速率與程序無關,具體內(nèi)容見文章:【嵌入式】GPIO引腳速度、翻轉速度、輸出速度區(qū)別輸入有4種模式,輸出有4種模式,我們在控制LED 的時候選擇通用推挽輸出。

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

電平控制:

STM32 的IO 口比較復雜,如果要輸出1 和0,則要通過控制:端口輸出數(shù)據(jù)寄存器ODR 來實現(xiàn),ODR 是:Output data register 的簡寫,在STM32 里面,其寄存器的命名名稱都是英文的簡寫,很容易記住。

從手冊上我們知道ODR 是一個32 位的寄存器,低16位有效,高16 位保留。低16 位對應著IO0~IO16,只要往相應的位置寫入0 或者1 就可以輸出低或者高電平。

第一層級:基地址宏定義

圖片

時鐘控制:

圖片

在STM32 中,每個外設都有一個起始地址,叫做 外設基地址 ,外設的寄存器就以這個基地址為標準按照順序排列,且每個寄存器32位,(后面作為結構體里面的成員正好內(nèi)存對齊)。

查表看到時鐘由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?,F(xiàn)在我們就可以定義GPIOB_ODR 這個寄存器了,代碼如下:

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

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

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

#define RCC_APB2ENR *(volatile unsigned long *)0x40021018
#define GPIOB_CRL *(volatile unsigned long *)0x40010C00
#define GPIOB_ODR *(volatile unsigned long *)0x40010C0C

int main(void)
{
 // 開啟端口B 的時鐘
 RCC_APB2ENR |= 1< < 3;

 // 配置PB0 為通用推挽輸出模式,速率為2M
 GPIOB_CRL = (2< < 0) | (0< < 2);

 // PB0 輸出低電平,點亮LED
 GPIOB_ODR = 0< < 0;
}

void SystemInit(void)
{
}

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

外設寄存器結構體封裝

上面我們在操作寄存器的時候,操作的是寄存器的絕對地址,如果每個寄存器都這樣操作,那將非常麻煩。我們考慮到外設寄存器的地址都是基于外設基地址的偏移地址,都是在外設基地址上逐個連續(xù)遞增的,每個寄存器占 32 個或者 16 個字節(jié),這種方式跟結構體里面的成員類似。

所以我們可以定義一種外設結構體, 結構體的地址等于外設的基地址,結構體的成員等于寄存器 ,成員的排列順序跟寄存器的順序一樣。這樣我們操作寄存器的時候就不用每次都找到絕對地址,只要知道外設的基地址就可以操作外設的全部寄存器,即操作結構體的成員即可。

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

typedef struct 
{
 volatile uint32_t CRL;
 volatile uint32_t CRH;
 volatile uint32_t IDR;
 volatile uint32_t ODR;
 volatile uint32_t BSRR;
 volatile uint32_t BRR;
 volatile uint32_t LCKR;
} GPIO_TypeDef;

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

在GPIO 結構體里面我們用了兩個數(shù)據(jù)類型,一個是uint32_t,表示無符號的32 位整型,因為GPIO 的寄存器都是32 位的。這個類型聲明在標準頭文件stdint.h 里面使用typedef對unsigned int重命名,我們在程序上只要包含這個頭文件即可。

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

外設封裝

圖片

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

#define GPIOA ((GPIO_TypeDef *) 0X4001 0800)
#define GPIOB ((GPIO_TypeDef *) 0X4001 0C00)
#define GPIOC ((GPIO_TypeDef *) 0X4001 1000)
#define GPIOD ((GPIO_TypeDef *) 0X4001 1400)
#define GPIOE ((GPIO_TypeDef *) 0X4001 1800)
#define GPIOF ((GPIO_TypeDef *) 0X4001 1C00)
#define GPIOG ((GPIO_TypeDef *) 0X4001 2000)

外設內(nèi)存映射

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

圖片

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

APB1、APB2、AHB 總線基地址

現(xiàn)在我們說的STM32 的寄存器就是位于這個區(qū)域,這里面ST 設計了三條總線:AHB、APB2 和APB1,其中AHB 和APB2 是高速總線,APB1 是低速總線。不同的外設根據(jù)速度不同分別掛載到這三條總線上。

從下往上依次是:APB1、APB2、AHB,每個總線對應的地址分別是:APB1:0x40000000,APB2:0x4001 0000,AHB:0x4001 8000。

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

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

#define PERIPH_BASE ((uint32_t)0x40000000)
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)

GPIO 端口基地址

因為GPIO 掛載到APB2 總線上,那么現(xiàn)在我們就可以根據(jù)APB2 的基址算出各個GPIO 端口的基地址,用宏定義實現(xiàn)代碼如下:

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)

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

#include < stdint.h >
#define __IO volatile

typedef struct 
{
 __IO uint32_t CRL;
 __IO uint32_t CRH;
 __IO uint32_t IDR;
 __IO uint32_t ODR;
 __IO uint32_t BSRR;
 __IO uint32_t BRR;
 __IO uint32_t LCKR;
} GPIO_TypeDef;

typedef struct 
{
 __IO uint32_t CR;
 __IO uint32_t CFGR;
 __IO uint32_t CIR;
 __IO uint32_t APB2RSTR;
 __IO uint32_t APB1RSTR;
 __IO uint32_t AHBENR;
 __IO uint32_t APB2ENR;
 __IO uint32_t APB1ENR;
 __IO uint32_t BDCR;
 __IO uint32_t CSR;
} RCC_TypeDef;

#define PERIPH_BASE ((uint32_t)0x40000000)

#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
#define RCC ((RCC_TypeDef *) RCC_BASE)


#define RCC_APB2ENR *(volatile unsigned long *)0x40021018
#define GPIOB_CRL *(volatile unsigned long *)0x40010C00
#define GPIOB_ODR *(volatile unsigned long *)0x40010C0C

int main(void)
{
 // 開啟端口B 的時鐘
 RCC- >APB2ENR |= 1< < 3;

 // 配置PB0 為通用推挽輸出模式,速率為2M
 GPIOB- >CRL = (2< < 0) | (0< < 2);

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

void SystemInit(void)
{
}

第二層級變化:

①、定義一個外設(GPIO)寄存器結構體,結構體的成員包含該外設的所有寄存器,成員的排列順序跟寄存器偏移地址一樣,成員的數(shù)據(jù)類型跟寄存器的一樣。

②外設內(nèi)存映射,即把地址跟外設建立起一一對應的關系。

③外設聲明,即把外設的名字定義成一個外設寄存器結構體類型的指針。

④通過結構體操作寄存器,實現(xiàn)點亮LED。

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

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

圖片

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

GPIOB- >BRR = 0X0001;

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

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

GPIOB- >BRR = 0X0004;

如果要PB3/4/5/6。。。。。。這些IO 輸出低電平呢?

道理是一樣的,只要往BRR 的相應位置賦不同的值即可。因為BRR 是一個16 位的寄存器,位數(shù)比較多,賦值的時候容易出錯,而且從賦值的16 進制數(shù)字我們很難清楚的知道控制的是哪個IO。

這時,我們是否可以把BRR 的每個位置1 都用宏定義來實現(xiàn),如GPIO_Pin_0 就表示0X0001,GPIO_Pin_2 就表示0X0004。只要我們定義一次,以后都可以使用,而且還見名知意?!拔环庋b”(每一位的對應字節(jié)封裝) 代碼如下:

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

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

GPIOB- >BRR = GPIO_Pin_0;

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

GPIOB- >BRR = GPIO_Pin_0|GPIO_Pin_15;

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

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

第四層級:基地址宏定義+結構體封裝+“位封裝”+函數(shù)封裝

我們點亮LED 的時候,控制的是PB0 這個IO,如果LED 接到的是其他IO,我們就需要把GPIOB 修改成其他的端口,其實這樣修改起來也很快很方便。

但是為了提高程序的可讀性和可移植性,我們是否可以編寫一個專門的函數(shù)用來復位GPIO 的某個位,這個函數(shù)有兩個形參,一個是GPIOX(X=A...G),另外一個是GPIO_Pin(0...15),函數(shù)的主體則是根據(jù)形參GPIOX 和GPIO_Pin 來控制BRR 寄存器,代碼如下:

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

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

GPIO_ResetBits(GPIOB,GPIO_Pin_0);

同理, 我們可以控制BSRR 這個寄存器來實現(xiàn)關閉LED,代碼如下:

// GPIO 端口置位函數(shù)
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
 GPIOx- >BSRR = GPIO_Pin;
}

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

GPIO_SetBits(GPIOB,GPIO_Pin_0);

同樣,因為這個函數(shù)是控制GPIO 的函數(shù),我們可以新建一個專門的文件來放跟gpio有關的函數(shù)。

在工程目錄下新建stm32f10x_gpio.c,把GPIO 相關的函數(shù)放里面。這時我們是否發(fā)現(xiàn)剛剛新建了一個頭文件stm32f10x_gpio.h,這兩個文件存放的都是跟外設GPIO 相關的。

C 文件里面的函數(shù)會用到h 頭文件里面的定義,這兩個文件是相輔相成的,故我們在stm32f10x_gpio.c 文件中也包含stm32f10x_gpio.h 這個頭文件。別忘了把stm32f10x.h 這個頭文件也包含進去,因為有關寄存器的所有定義都在這個頭文件里面。

如果我們寫其他外設的函數(shù),我們也應該跟GPIO 一樣,新建兩個文件專門來存函數(shù),比如RCC 這個外設我們可以新建stm32f10x_rcc.c 和stm32f10x_rcc.h。其他外依葫蘆畫瓢即可。

(5)實例編寫

以上,是對庫封住過程的概述,下面我們正在地使用庫函數(shù)編寫LED程序

①管理庫的頭文件

當我們開始調(diào)用庫函數(shù)寫代碼的時候,有些庫我們不需要,在編譯的時候可以不編譯,可以通過一個總的頭文件stm32f10x_conf.h 來控制,該頭文件主要代碼如下:

//#include "stm32f10x_adc.h"
//#include "stm32f10x_bkp.h"
//#include "stm32f10x_can.h"
//#include "stm32f10x_cec.h"
//#include "stm32f10x_crc.h"
//#include "stm32f10x_dac.h"
//#include "stm32f10x_dbgmcu.h"
//#include "stm32f10x_dma.h"
//#include "stm32f10x_exti.h"
//#include "stm32f10x_flash.h"
//#include "stm32f10x_fsmc.h"
#include "stm32f10x_gpio.h"
//#include "stm32f10x_i2c.h"
//#include "stm32f10x_iwdg.h"
//#include "stm32f10x_pwr.h"
#include "stm32f10x_rcc.h"
//#include "stm32f10x_rtc.h"
//#include "stm32f10x_sdio.h"
//#include "stm32f10x_spi.h"
//#include "stm32f10x_tim.h"
//#include "stm32f10x_usart.h"
//#include "stm32f10x_wwdg.h"
//#include "misc.h"

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

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

#ifdef USE_STDPERIPH_DRIVER
#include "stm32f10x_conf.h"
#endif

代碼的意思是,如果定義了USE_STDPERIPH_DRIVER 這個宏的話,就包含stm32f10x_conf.h 這個頭文件。

我們在新建工程的時候,在魔術棒選項卡C/C++中,我們定義了USE_STDPERIPH_DRIVER 這個宏,所以stm32f10x_conf.h 這個頭文件就被stm32f10x.h 包含了,我們在寫程序的時候只需要調(diào)用一個頭文件:stm32f10x.h 即可。(預處理指令詳細內(nèi)容會在【C語言】的文章中提到)

②編寫LED 初始化函數(shù)

經(jīng)過寄存器點亮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 時鐘控制

固件庫函數(shù):RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE)函數(shù)的

原型為:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph,
                              FunctionalState NewState)
{
 /* Check the parameters */
 assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
 assert_param(IS_FUNCTIONAL_STATE(NewState));
 if (NewState != DISABLE) 
 {
  RCC- >APB2ENR |= RCC_APB2Periph;
 } 
 else 
 {
  RCC- >APB2ENR &= ~RCC_APB2Periph;
 }
}

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

GPIO 端口配置

GPIO 的pin,速度,模式,都由GPIO 的端口配置寄存器來控制,其中IO0IO7 由端口配置低寄存器CRL 控制,IO8IO15 由端口配置高寄存器CRH 配置。固件庫把端口配置的pin,速度和模式封裝成一個結構體:

typedef struct 
{
 uint16_t GPIO_Pin;
 GPIOSpeed_TypeDef GPIO_Speed;
 GPIOMode_TypeDef GPIO_Mode;
} GPIO_InitTypeDef;

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

typedef enum 
{
 GPIO_Speed_10MHz = 1,
 GPIO_Speed_2MHz,
 GPIO_Speed_50MHz
} GPIOSpeed_TypeDef;

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

typedef enum 
{
 GPIO_Mode_AIN = 0x0, // 模擬輸入
 GPIO_Mode_IN_FLOATING = 0x04, // 浮空輸入(復位后的狀態(tài))
 GPIO_Mode_IPD = 0x28, // 下拉輸入
 GPIO_Mode_IPU = 0x48, // 上拉輸入
 GPIO_Mode_Out_OD = 0x14, // 通用開漏輸出
 GPIO_Mode_Out_PP = 0x10, // 通用推挽輸出
 GPIO_Mode_AF_OD = 0x1C, // 復用開漏輸出
 GPIO_Mode_AF_PP = 0x18 // 復用推挽輸出
} GPIOMode_TypeDef;

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

最終用固件庫實現(xiàn)就變成這樣:

// 定義一個GPIO_InitTypeDef 類型的結構體
GPIO_InitTypeDef GPIO_InitStructure;

// 選擇要控制的IO 口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

// 設置引腳為推挽輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

// 設置引腳速率為50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

/*調(diào)用庫函數(shù),初始化GPIOB0*/
GPIO_Init(GPIOB, &GPIO_InitStructure);

倘若同一端口下不同引腳有不同的模式配置,每次對每個引腳配置完成后都要調(diào)用GPIO初始化函數(shù),代碼如下:

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 輸出控制,可以通過端口數(shù)據(jù)輸出寄存器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 初始化函數(shù)

void LED_GPIO_Config(void)
{
 // 定義一個GPIO_InitTypeDef 類型的結構體
 GPIO_InitTypeDef GPIO_InitStructure;

 // 開啟GPIOB 的時鐘
 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);

 // 選擇要控制的IO 口
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

 // 設置引腳為推挽輸出
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

 // 設置引腳速率為50MHz
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 /*調(diào)用庫函數(shù),初始化GPIOB0*/
 GPIO_Init(GPIOB, &GPIO_InitStructure);

 // 關閉LED
 GPIO_SetBits(GPIOB, GPIO_Pin_0);
}

主函數(shù)

#include "stm32f10x.h"


void SOFT_Delay(__IO uint32_t nCount);
void LED_GPIO_Config(void);

int main(void)
{
 // 程序來到main 函數(shù)之前,啟動文件:statup_stm32f10x_hd.s 已經(jīng)調(diào)用
 // SystemInit()函數(shù)把系統(tǒng)時鐘初始化成72MHZ
 // SystemInit()在system_stm32f10x.c 中定義
 // 如果用戶想修改系統(tǒng)時鐘,可自行編寫程序修改

 LED_GPIO_Config();

 while ( 1 ) 
 {
  // 點亮LED
  GPIO_ResetBits(GPIOB, GPIO_Pin_0);
  Time_Delay(0x0FFFFF);

  // 熄滅LED
  GPIO_SetBits(GPIOB, GPIO_Pin_0);
  Time_Delay(0x0FFFFF);
 }
}
// 簡陋的軟件延時函數(shù)
void Time_Delay(volatile uint32_t Count)
{
 for (; Count != 0; Count--);
}

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

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • STM32
    +關注

    關注

    2270

    文章

    10900

    瀏覽量

    356042
  • 軟件
    +關注

    關注

    69

    文章

    4944

    瀏覽量

    87500
  • 時序
    +關注

    關注

    5

    文章

    387

    瀏覽量

    37333
收藏 人收藏

    評論

    相關推薦

    面向AWorks框架時間管理程序設計

    實際應用中,時間管理往往是必不可少的。例如:定時完成某件事情、周期性地觸發(fā)某一動作、測量評估程序運行時間等。AWorks提供了一系列時間相關
    的頭像 發(fā)表于 05-30 09:33 ?5030次閱讀
    面向AWorks框架<b class='flag-5'>時間管理程序</b>設計

    單片機處理多個任務程序框架設計

    MultiTimer 是一個軟件定時器擴展模塊,可無限擴展你所需的定時器任務,取代傳統(tǒng)的標志位判斷方式, 更優(yōu)雅更便捷地管理程序時間觸發(fā)時序。
    的頭像 發(fā)表于 09-03 17:52 ?4119次閱讀
    單片機處理多個任務<b class='flag-5'>程序</b>框架設計

    TC38xQ或TC397是否支持管理程序?需要安裝哪個管理程序?

    你好,我們正在做一個項目,需要在 TC387 板上使用管理程序,在此基礎上需要安裝多個 vms(建議安裝一個 os,我想安裝 andriod)。 是否可以在 TC387 或 TC397 硬件上運行
    發(fā)表于 05-20 07:17

    基于labview員工管理程序

    基于labview員工管理程序
    發(fā)表于 04-21 19:33

    制功能的料號管理程序

    哎呀呀,發(fā)帖子發(fā)上癮了,怎么辦!今天給大家分享一種帶復制功能的料號管理程序吧!廢話不多說,上主界面!實際上主界面是一個數(shù)組包簇的,我把簇做成了自定義控件,針對不同的項目只要更改自定義控件就可以了!首先讀取配置文件額其他我懶得截圖了,需要的下面私我把!截圖好煩
    發(fā)表于 11-23 13:21

    管理程序選擇

    親愛的社區(qū),如果需要多個虛擬機(最多8個),每個虛擬機需要2Mb vGPU,我們希望聽到您對虛擬機管理程序選擇問題的看法。我正在對可用的虛擬機管理程序功能與網(wǎng)格配對進行一些探索,并發(fā)現(xiàn)WMware
    發(fā)表于 09-10 17:09

    內(nèi)存管理程序結構

    內(nèi)存管理程序結構內(nèi)存分配方式內(nèi)存管理函數(shù)mallocrealloccallocmemsetfree堆和棧的區(qū)別管理方式不同空間大小不同是否產(chǎn)生碎片增長方向不同分配方式不同分配效率不同程序
    發(fā)表于 12-17 07:15

    ThinkPad卸載電源管理程序會出現(xiàn)哪些問題呢

    ThinkPad安裝電源管理程序后,會在“控制面板\硬件和聲音\電源選項”中產(chǎn)生3個條目, 在任務欄通知區(qū)域的電源圖標右鍵菜單中增加對應的3條。卸載電源管理程序后會出現(xiàn)如下的問題。解決方法:清空
    發(fā)表于 01-03 07:16

    ADAM設備管理程序DevMgr.exe安裝

    【LabVIEW從入門到精通】5.7.1.1 ADAM設備管理程序DevMgr.exe安裝
    發(fā)表于 01-08 15:50 ?0次下載

    板卡設備管理程序DevMgr.exe安裝

    【LabVIEW從入門到精通】6.4.2.2 板卡設備管理程序DevMgr.exe安裝
    發(fā)表于 01-08 15:52 ?0次下載

    Xen開源管理程序為 Zynq Ultrascale+ MPSoC 實現(xiàn)虛擬化

    Xilinx? 選擇聯(lián)盟高級會員 ?DornerWorks? 在 ?Zynq MPSoC? 上支持開源 ?Xen? 管理程序,從而可在 4? 核 ?ARM? Cortex?-A53? 上實現(xiàn)虛擬化
    發(fā)表于 02-09 06:31 ?420次閱讀
    Xen開源<b class='flag-5'>管理程序</b>為 Zynq Ultrascale+ MPSoC 實現(xiàn)虛擬化

    stm32f103寫的菜單管理程序

    stm32f103寫的菜單管理程序,支持無限嵌套程序里有使用說明,12864采用串口驅動
    發(fā)表于 07-27 16:32 ?69次下載

    基于Zynq UltraScale+ MPSoC上運行 Xen 管理程序

    熟悉運行在賽靈思 Zynq UltraScale+ MPSoC 上的 Xen 管理程序。 賽靈思和 DornerWorks 的系統(tǒng)軟件團隊在賽靈思的 Zynq? Ultrascale+? MPSoC
    發(fā)表于 11-16 20:17 ?3489次閱讀
    基于Zynq UltraScale+ MPSoC上運行 Xen <b class='flag-5'>管理程序</b>

    賽靈思 Zynq UltraScale+ MPSoC 上的 Xen 管理程序教程

    通過這篇有趣的教程,熟悉運行在賽靈思 Zynq UltraScale+ MPSoC 上的 Xen 管理程序。 賽靈思和 DornerWorks 的系統(tǒng)軟件團隊在賽靈思的 Zynq
    發(fā)表于 02-01 01:38 ?1332次閱讀

    支持物聯(lián)網(wǎng)方案:Linux基金會打造“ACRN”管理程序

    Linux 基金會已經(jīng)托管 Xen 項目,此項目致力于為嵌入式及車載應用提供管理程序方案。2017年有報道指出,隨著 Amazon Web Services 公司宣布不會將 Xen 作為長期管理程序解決方案以來,Xen 項目便將嵌入式等場景作為主攻方向。
    的頭像 發(fā)表于 03-21 09:46 ?5137次閱讀
    支持物聯(lián)網(wǎng)方案:Linux基金會打造“ACRN”<b class='flag-5'>管理程序</b>