stm32f767中文手册_ALIENTEK 阿波罗 STM32F767 开发板资料连载第五章 SYSTEM 文件夹
1)實(shí)驗(yàn)平臺(tái):alientek 阿波羅 STM32F767 開發(fā)板2)摘自《STM32F7 開發(fā)指南(HAL 庫(kù)版)》關(guān)注官方微信號(hào)公眾號(hào),獲取更多資料:正點(diǎn)原子
第五章 SYSTEM 文件夾介紹
第三章,我們介紹了如何在 MDK5 下建立 STM32F7 工程。在這個(gè)新建的工程之中,我們
用到了一個(gè) SYSTEM 文件夾里面的代碼,此文件夾里面的代碼由 ALIENTEK 提供,是
STM32F7xx 系列的底層核心驅(qū)動(dòng)函數(shù),可以用在 STM32F7xx 系列的各個(gè)型號(hào)上面,方便大家
快速構(gòu)建自己的工程。
SYSTEM 文件夾下包含了 delay、sys、usart 等三個(gè)文件夾。分別包含了 delay.c、sys.c、usart.c
及其頭文件。通過(guò)這 3 個(gè) c 文件,可以快速的給任何一款 STM32F7 構(gòu)建最基本的框架。使用
起來(lái)是很方便的。
本章,我們將向大家介紹這些代碼,通過(guò)這章的學(xué)習(xí),大家將了解到這些代碼的由來(lái),也
希望大家可以靈活使用 SYSTEM 文件夾提供的函數(shù),來(lái)快速構(gòu)建工程,并實(shí)際應(yīng)用到自己的項(xiàng)
目中去。
本章包括如下 3 個(gè)小結(jié):
5.1,delay 文件夾代碼介紹;
5.2,sys 文件夾代碼介紹;
5.3,usart 文件夾代碼介紹;
5.1 delay 文件夾代碼介紹
delay 文件夾內(nèi)包含了 delay.c 和 delay.h 兩個(gè)文件,這兩個(gè)文件用來(lái)實(shí)現(xiàn)系統(tǒng)的延時(shí)功能,
其中包含 7 個(gè)函數(shù):
void delay_osschedlock(void);
void delay_osschedunlock(void);
void delay_ostimedly(u32 ticks);
void SysTick_Handler(void);
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);
前面 4 個(gè)函數(shù),僅在支持操作系統(tǒng)(OS)的時(shí)候,需要用到,而后面 3 個(gè)函數(shù),則不論是
否支持 OS 都需要用到。
在介紹這些函數(shù)之前,我們先了解一下編程思想:CM4 內(nèi)核的處理和 CM3 一樣,內(nèi)部都
包含了一個(gè) SysTick 定時(shí)器,SysTick 是一個(gè) 24 位的倒計(jì)數(shù)定時(shí)器,當(dāng)計(jì)到 0 時(shí),將從 RELOAD
寄存器中自動(dòng)重裝載定時(shí)初值。只要不把它在 SysTick 控制及狀態(tài)寄存器中的使能位清除,就
永不停息。SysTick 在《STM32F7 中文參考手冊(cè)》里面基本沒(méi)有介紹,其詳細(xì)介紹,請(qǐng)參閱
《STM32F7 編程手冊(cè)》第 211 頁(yè),4.4 節(jié)。我們就是利用 STM32 的內(nèi)部 SysTick 來(lái)實(shí)現(xiàn)延時(shí)的,
這樣既不占用中斷,也不占用系統(tǒng)定時(shí)器。
這里我們將介紹的是 ALIENTEK 提供的最新版本的延時(shí)函數(shù),該版本的延時(shí)函數(shù)支持在任
意操作系統(tǒng)(OS)下面使用,它可以和操作系統(tǒng)共用 SysTick 定時(shí)器。
這里,我們以 UCOSII 為例,介紹如何實(shí)現(xiàn)操作系統(tǒng)和我們的 delay 函數(shù)共用 SysTick 定時(shí)
器。首先,我們簡(jiǎn)單介紹下 UCOSII 的時(shí)鐘:ucos 運(yùn)行需要一個(gè)系統(tǒng)時(shí)鐘節(jié)拍(類似 “心跳”),
而這個(gè)節(jié)拍是固定的(由 OS_TICKS_PER_SEC 宏定義設(shè)置),比如要求 5ms 一次(即可設(shè)置:
OS_TICKS_PER_SEC=200),在 STM32 上面,一般是由 SysTick 來(lái)提供這個(gè)節(jié)拍,也就是 SysTick
要設(shè)置為 5ms 中斷一次,為 ucos 提供時(shí)鐘節(jié)拍,而且這個(gè)時(shí)鐘一般是不能被打斷的(否則就不
準(zhǔn)了)
因?yàn)樵?ucos 下 systick 不能再被隨意更改,如果我們還想利用 systick 來(lái)做 delay_us 或者
delay_ms 的延時(shí),就必須想點(diǎn)辦法了,這里我們利用的是時(shí)鐘摘取法。以 delay_us 為例,比如
delay_us(50),在剛進(jìn)入 delay_us 的時(shí)候先計(jì)算好這段延時(shí)需要等待的 systick 計(jì)數(shù)次數(shù),這里
為 50*216(假設(shè)系統(tǒng)時(shí)鐘為 216Mhz,因?yàn)槲覀冊(cè)O(shè)置 systick 的頻率為系統(tǒng)時(shí)鐘頻率,那么 systick
每增加 1,就是 1/216us),然后我們就一直統(tǒng)計(jì) systick 的計(jì)數(shù)變化,直到這個(gè)值變化了 50*216,
一旦檢測(cè)到變化達(dá)到或者超過(guò)這個(gè)值,就說(shuō)明延時(shí) 50us 時(shí)間到了。這樣,我們只是抓取 SysTick
計(jì)數(shù)器的變化,并不需要修改 SysTick 的任何狀態(tài),完全不影響 SysTick 作為 UCOS 時(shí)鐘節(jié)拍
的功能,這就是實(shí)現(xiàn) delay 和操作系統(tǒng)共用 SysTick 定時(shí)器的原理。
下面我們開始介紹這幾個(gè)函數(shù)。
5.1.1 操作系統(tǒng)支持宏定義及相關(guān)函數(shù)
當(dāng)需要 delay_ms 和 delay_us 支持操作系統(tǒng)(OS)的時(shí)候,我們需要用到 3 個(gè)宏定義和 4
個(gè)函數(shù),宏定義及函數(shù)代碼如下:
//本例程僅作 UCOSII 和 UCOSIII 的支持,其他 OS,請(qǐng)自行參考著移植
//支持 UCOSII
#ifdef OS_CRITICAL_METHOD
//OS_CRITICAL_METHOD 定義了,說(shuō)明要支持 UCOSII
#define delay_osrunning
OSRunning
//OS 是否運(yùn)行標(biāo)記,0,不運(yùn)行;1,在運(yùn)行
#define delay_ostickspersec OS_TICKS_PER_SEC //OS 時(shí)鐘節(jié)拍,即每秒調(diào)度次數(shù)
#define delay_osintnesting OSIntNesting
//中斷嵌套級(jí)別,即中斷嵌套次數(shù)
#endif
//支持 UCOSIII
#ifdef CPU_CFG_CRITICAL_METHOD
//CPU_CFG_CRITICAL_METHOD 定義了,說(shuō)明要支持 UCOSIII
#define delay_osrunning
OSRunning
//OS 是否運(yùn)行標(biāo)記,0,不運(yùn)行;1,在運(yùn)行
#define delay_ostickspersec OSCfg_TickRate_Hz
//OS 時(shí)鐘節(jié)拍,即每秒調(diào)度次數(shù)
#define delay_osintnesting OSIntNestingCtr
//中斷嵌套級(jí)別,即中斷嵌套次數(shù)
#endif
//us 級(jí)延時(shí)時(shí),關(guān)閉任務(wù)調(diào)度(防止打斷 us 級(jí)延遲)
void delay_osschedlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD //使用 UCOSIII
OS_ERR err;
OSSchedLock(&err);
//UCOSIII 的方式,禁止調(diào)度,防止打斷 us 延時(shí)
#else
//否則 UCOSII
OSSchedLock();
//UCOSII 的方式,禁止調(diào)度,防止打斷 us 延時(shí)
#endif
}
//us 級(jí)延時(shí)時(shí),恢復(fù)任務(wù)調(diào)度
void delay_osschedunlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD //使用 UCOSIII
OS_ERR err;
OSSchedUnlock(&err);
//UCOSIII 的方式,恢復(fù)調(diào)度
#else
//否則 UCOSII
OSSchedUnlock();
//UCOSII 的方式,恢復(fù)調(diào)度
#endif
}
//調(diào)用 OS 自帶的延時(shí)函數(shù)延時(shí)
//ticks:延時(shí)的節(jié)拍數(shù)
void delay_ostimedly(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD //使用 UCOSIII 時(shí)
OS_ERR err;
OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);//UCOSIII 延時(shí)采用周期模式
#else
OSTimeDly(ticks);
//UCOSII 延時(shí)
#endif
}
//systick 中斷服務(wù)函數(shù),使用 ucos 時(shí)用到
void SysTick_Handler(void)
{
if(delay_osrunning==1)
//OS 開始跑了,才執(zhí)行正常的調(diào)度處理
{
OSIntEnter();
//進(jìn)入中斷
OSTimeTick();
//調(diào)用 ucos 的時(shí)鐘服務(wù)程序
OSIntExit();
//觸發(fā)任務(wù)切換軟中斷
}
}
以上代碼,僅支持 UCOSII 和 UCOSIII,不過(guò),對(duì)于其他 OS 的支持,也只需要對(duì)以上代
碼進(jìn)行簡(jiǎn)單修改即可實(shí)現(xiàn)。
支持 OS 需要用到的三個(gè)宏定義(以 UCOSII 為例)即:
#define delay_osrunning
OSRunning
//OS 是否運(yùn)行標(biāo)記,0,不運(yùn)行;1,在運(yùn)行
#define delay_ostickspersec OS_TICKS_PER_SEC //OS 時(shí)鐘節(jié)拍,即每秒調(diào)度次數(shù)
#define delay_osintnesting OSIntNesting
//中斷嵌套級(jí)別,即中斷嵌套次數(shù)
宏定義:delay_osrunning,用于標(biāo)記 OS 是否正在運(yùn)行,當(dāng) OS 已經(jīng)開始運(yùn)行時(shí),該宏定義
值為 1,當(dāng) OS 還未運(yùn)行時(shí),該宏定義值為 0。
宏定義:delay_ ostickspersec,用于表示 OS 的時(shí)鐘節(jié)拍,即 OS 每秒鐘任務(wù)調(diào)度次數(shù)。
宏定義:delay_ osintnesting,用于表示 OS 中斷嵌套級(jí)別,即中斷嵌套次數(shù),每進(jìn)入一個(gè)
中斷,該值加 1,每退出一個(gè)中斷,該值減 1。
支持 OS 需要用到的 4 個(gè)函數(shù),即:
函數(shù):delay_osschedlock,用于 delay_us 延時(shí),作用是禁止 OS 進(jìn)行調(diào)度,以防打斷 us 級(jí)
延時(shí),導(dǎo)致延時(shí)時(shí)間不準(zhǔn)。
函數(shù):delay_osschedunlock,同樣用于 delay_us 延時(shí),作用是在延時(shí)結(jié)束后恢復(fù) OS 的調(diào)度,
繼續(xù)正常的 OS 任務(wù)調(diào)度。
函數(shù):delay_ostimedly,則是調(diào)用 OS 自帶的延時(shí)函數(shù),實(shí)現(xiàn)延時(shí)。該函數(shù)的參數(shù)為時(shí)鐘節(jié)
拍數(shù)。
函數(shù):SysTick_Handler,則是 systick 的中斷服務(wù)函數(shù),該函數(shù)為 OS 提供時(shí)鐘節(jié)拍,同時(shí)
可以引起任務(wù)調(diào)度。
以上就是 delay_ms 和 delay_us 支持操作系統(tǒng)時(shí),需要實(shí)現(xiàn)的 3 個(gè)宏定義和 4 個(gè)函數(shù)。
5.1.2 delay_init 函數(shù)
該函數(shù)用來(lái)初始化 2 個(gè)重要參數(shù):fac_us 以及 fac_ms;同時(shí)把 SysTick 的時(shí)鐘源選擇為外
部時(shí)鐘,如果需要支持操作系統(tǒng)(OS),只需要在 sys.h 里面,設(shè)置 SYSTEM_SUPPORT_OS 宏
的值為 1 即可,然后,該函數(shù)會(huì)根據(jù) delay_ostickspersec 宏的設(shè)置,來(lái)配置 SysTick 的中斷時(shí)間,
并開啟 SysTick 中斷。具體代碼如下:
//初始化延遲函數(shù)
//當(dāng)使用 OS 的時(shí)候,此函數(shù)會(huì)初始化 OS 的時(shí)鐘節(jié)拍
//SYSTICK 的時(shí)鐘固定為 HCLK
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS
//如果需要支持 OS.
u32 reload;
#endif
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
//SysTick 頻率為 HCLK
fac_us=SYSCLK;
//不論是否使用 OS,fac_us 都需要使用
#if SYSTEM_SUPPORT_OS
//如果需要支持 OS.
reload=SYSCLK;
//每秒鐘的計(jì)數(shù)次數(shù) 單位為 K
reload*=1000000/delay_ostickspersec; //根據(jù) delay_ostickspersec 設(shè)定溢出時(shí)間
//reload 為 24 位寄存器,最大值:16777216,在 180M 下,約合 0.745s 左右
fac_ms=1000/delay_ostickspersec;
//代表 OS 可以延時(shí)的最少單位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//開啟 SYSTICK 中斷
SysTick->LOAD=reload;
//每 1/OS_TICKS_PER_SEC 秒中斷一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //開啟 SYSTICK
#else
#endif
}
可以看到,delay_init 函數(shù)使用了條件編譯,來(lái)選擇不同的初始化過(guò)程,如果不使用 OS 的
時(shí)候,只是設(shè)置一下 SysTick 的時(shí)鐘源以及確定 fac_us 值。而如果使用 OS 的時(shí)候,則會(huì)進(jìn)行
一些不同的配置,這里的條件編譯是根據(jù)SYSTEM_SUPPORT_OS這個(gè)宏來(lái)確定的,該宏在sys.h
里面定義。
SysTick 是 MDK 定義了的一個(gè)結(jié)構(gòu)體(在 core_m4.h 里面),里面包含 CTRL、LOAD、VAL、
CALIB 等 4 個(gè)寄存器,
SysTick->CTRL 的各位定義如圖 5.1.2.1 所示:
圖 5.1.2.1 SysTick->CTRL 寄存器各位定義
SysTick-> LOAD 的定義如圖 5.1.2.2 所示:
圖 5.1.2.2 SysTick->LOAD 寄存器各位定義
SysTick-> VAL 的定義如圖 5.1.2.3 所示:
圖 5.1.2.3 SysTick->VAL 寄存器各位定義
SysTick-> CALIB 不常用,在這里我們也用不到,故不介紹了。
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);這句代碼把 SysTick 的時(shí)鐘選擇為
內(nèi)核時(shí)鐘,這里需要注意的是:SysTick 的時(shí)鐘源自 HCLK,假設(shè)我們外部晶振為 25M,然后
倍頻到 216MHZ,那么 SysTick 的時(shí)鐘即為 216Mhz,也就是 SysTick 的計(jì)數(shù)器 VAL 每減 1,就
代表時(shí)間過(guò)了 1/216us。所以 fac_us=SYSCLK;這句話就是計(jì)算在 SYSCLK 時(shí)鐘頻率下延時(shí) 1us
需要多少個(gè) SysTick 時(shí)鐘周期。
在不使用 OS 的時(shí)候:fac_us,為 us 延時(shí)的基數(shù),也就是延時(shí) 1us,Systick 定時(shí)器需要走
過(guò)的時(shí)鐘周期數(shù)。 當(dāng)使用 OS 的時(shí)候,fac_us,還是 us 延時(shí)的基數(shù),不過(guò)這個(gè)值不會(huì)被寫到
SysTick->LOAD 寄存器來(lái)實(shí)現(xiàn)延時(shí),而是通過(guò)時(shí)鐘摘取的辦法實(shí)現(xiàn)的(前面已經(jīng)介紹了)。而
fac_ms 則代表 ucos 自帶的延時(shí)函數(shù)所能實(shí)現(xiàn)的最小延時(shí)時(shí)間(如 delay_ostickspersec=200,那
么 fac_ms 就是 5ms)。
5.1.3 delay_us 函數(shù)
該函數(shù)用來(lái)延時(shí)指定的 us,其參數(shù) nus 為要延時(shí)的微秒數(shù)。該函數(shù)有使用 OS 和不使用 OS
兩個(gè)版本,這里我們首先介紹不使用 OS 的時(shí)候,實(shí)現(xiàn)函數(shù)如下:
//延時(shí) nus
//nus 為要延時(shí)的 us 數(shù).
//nus:0~204522252(最大值即 2^32/fac_us@fac_us=21)
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD;
//LOAD 的值
ticks=nus*fac_us;
//需要的節(jié)拍數(shù)
told=SysTick->VAL;
//剛進(jìn)入時(shí)的計(jì)數(shù)器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;
//時(shí)間超過(guò)/等于要延遲的時(shí)間,則退出.
}
};
}
這里就正是利用了我們前面提到的時(shí)鐘摘取法,ticks 是延時(shí) nus 需要等待的 SysTick 計(jì)數(shù)
次數(shù)(也就是延時(shí)時(shí)間),told 用于記錄最近一次的 SysTick->VAL 值,然后 tnow 則是當(dāng)前的
SysTick->VAL 值,通過(guò)他們的對(duì)比累加,實(shí)現(xiàn) SysTick 計(jì)數(shù)次數(shù)的統(tǒng)計(jì),統(tǒng)計(jì)值存放在 tcnt 里
面,然后通過(guò)對(duì)比 tcnt 和 ticks,來(lái)判斷延時(shí)是否到達(dá),從而達(dá)到不修改 SysTick 實(shí)現(xiàn) nus 的延
時(shí)。對(duì)于使用 OS 的時(shí)候,delay_us 的實(shí)現(xiàn)函數(shù)和不使用 OS 的時(shí)候方法類似,都是使用的時(shí)鐘
摘取法,只不過(guò)使用 delay_osschedlock 和 delay_osschedunlock 兩個(gè)函數(shù),用于調(diào)度上鎖和解鎖,
這是為了防止 OS 在 delay_us 的時(shí)候打斷延時(shí),可能導(dǎo)致的延時(shí)不準(zhǔn),所以我們利用這兩個(gè)函
數(shù)來(lái)實(shí)現(xiàn)免打斷,從而保證延時(shí)精度。
5.1.4 delay_ms 函數(shù)
該函數(shù)是用來(lái)延時(shí)指定的 ms 的,其參數(shù) nms 為要延時(shí)的毫秒數(shù)。該函數(shù)有使用 OS 和不
使用 OS 兩個(gè)版本,這里我們分別介紹,首先是不使用 OS 的時(shí)候,實(shí)現(xiàn)函數(shù)如下:
//延時(shí) nms
//nms:要延時(shí)的 ms 數(shù)
void delay_ms(u16 nms)
{
u32 i;
for(i=0;i< nms;i++) delay_us(1000);
}
該函數(shù)其實(shí)就是多次調(diào)用前面所講的 delay_us 函數(shù),來(lái)實(shí)現(xiàn)毫秒級(jí)延時(shí)的。
再來(lái)看看使用 OS 的時(shí)候,delay_ms 的實(shí)現(xiàn)函數(shù)如下:
//延時(shí) nms
//nms:要延時(shí)的 ms 數(shù)
//nms:0~65535
void delay_ms(u16 nms)
{
if(delay_osrunning&&delay_osintnesting==0)//如果 OS 已經(jīng)在跑了,且不是在中斷里面
{
if(nms>=fac_ms)
//延時(shí)的時(shí)間大于 OS 的最少時(shí)間周期
{
delay_ostimedly(nms/fac_ms);
//OS 延時(shí)
}
nms%=fac_ms;
//OS 已經(jīng)無(wú)法提供這么小的延時(shí)了,采用普通方式延時(shí)
}
delay_us((u32)(nms*1000)); //普通方式延時(shí)
}
該函數(shù)中,delay_osrunning 是 OS 正在運(yùn)行的標(biāo)志,delay_osintnesting 則是 OS 中斷嵌套次
數(shù),必須 delay_osrunning 為真,且 delay_osintnesting 為 0 的時(shí)候,才可以調(diào)用 OS 自帶的延時(shí)
函數(shù)進(jìn)行延時(shí)(可以進(jìn)行任務(wù)調(diào)度),delay_ostimedly 函數(shù)就是利用 OS 自帶的延時(shí)函數(shù),實(shí)現(xiàn)
任 務(wù)級(jí) 延時(shí) 的, 其參數(shù) 代表 延時(shí) 的時(shí) 鐘節(jié)拍 數(shù)( 假設(shè) delay_ostickspersec=200 ,那 么
delay_ostimedly (1),就代表延時(shí) 5ms)。
當(dāng) OS 還未運(yùn)行的時(shí)候,我們的 delay_ms 就是直接由 delay_us 實(shí)現(xiàn)的,OS 下的 delay_us
可以實(shí)現(xiàn)很長(zhǎng)的延時(shí)(達(dá)到 204 秒)而不溢出!,所以放心的使用 delay_us 來(lái)實(shí)現(xiàn) delay_ms,
不過(guò)由于 delay_us 的時(shí)候,任務(wù)調(diào)度被上鎖了,所以還是建議不要用 delay_us 來(lái)延時(shí)很長(zhǎng)的時(shí)
間,否則影響整個(gè)系統(tǒng)的性能。
當(dāng) OS 運(yùn)行的時(shí)候,我們的 delay_ms 函數(shù)將先判斷延時(shí)時(shí)長(zhǎng)是否大于等于 1 個(gè) OS 時(shí)鐘節(jié)
拍(fac_ms),當(dāng)大于這個(gè)值的時(shí)候,我們就通過(guò)調(diào)用 OS 的延時(shí)函數(shù)來(lái)實(shí)現(xiàn)(此時(shí)任務(wù)可以調(diào)
度),不足 1 個(gè)時(shí)鐘節(jié)拍的時(shí)候,直接調(diào)用 delay_us 函數(shù)實(shí)現(xiàn)(此時(shí)任務(wù)無(wú)法調(diào)度)。
5.1.5 HAL 庫(kù)延時(shí)函數(shù) HAL_Delay 解析
前面我們講解了 ALIENTEK 提供的使用 Systick 實(shí)現(xiàn)延時(shí)相關(guān)函數(shù)。實(shí)際上,HAL 庫(kù)有提
供延時(shí)函數(shù),只不過(guò)它只能實(shí)現(xiàn)簡(jiǎn)單的毫秒級(jí)別延時(shí),沒(méi)有實(shí)現(xiàn) us 級(jí)別延時(shí)。下面我們列出
HAL 庫(kù)實(shí)現(xiàn)延時(shí)相關(guān)的函數(shù)。首先是功能配置函數(shù):
//調(diào)用 HAL_SYSTICK_Config 函數(shù)配置每隔 1ms 中斷一次:文件 stm32f7xx_hal.c 中定義
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/*配置 1ms 中斷一次*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0);
return HAL_OK;
}
//HAL 庫(kù)的 SYSTICK 配置函數(shù):文件 stm32f7xx_hal_context.c 中定義
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
return SysTick_Config(TicksNumb);
}
//內(nèi)核的 Systick 配置函數(shù),配置每隔 ticks 個(gè) systick 周期中斷一次
//文件 core_cm4.h 中
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
...//此處省略函數(shù)定義
}
上面三個(gè)函數(shù),實(shí)際上開放給 HAL 調(diào)用的主要是 HAL_InitTick 函數(shù),該函數(shù)在 HAL 庫(kù)初
始化函數(shù) HAL_Init 中會(huì)被調(diào)用。該函數(shù)通過(guò)間接調(diào)用 SysTick_Config 函數(shù)配置 Systick 定時(shí)器
每隔 1ms 中斷一次,永不停歇。
接下來(lái)我們來(lái)看看延時(shí)的邏輯控制代碼:
//Systick 中斷服務(wù)函數(shù):文件 stm32f7xx_it.c 中
void SysTick_Handler(void)
{
HAL_IncTick();
}
//下面代碼均在文件 stm32f7xx_hal.c 中
static __IO uint32_t uwTick; //定義計(jì)數(shù)全局變量
__weak void HAL_IncTick(void) //全局變量 uwTick 遞增
{
uwTick++;
}
__weak uint32_t HAL_GetTick(void) //獲取全局變量 uwTick 的值
{
return uwTick;
}
//開放的 HAL 延時(shí)函數(shù),延時(shí) Delay 毫秒
__weak void HAL_Delay(__IO uint32_t Delay)
{
uint32_t tickstart = 0;
tickstart = HAL_GetTick();
while((HAL_GetTick() - tickstart) < Delay)
{
}
}
HAL 庫(kù)實(shí)現(xiàn)延時(shí)功能非常簡(jiǎn)單,首先定義了一個(gè) 32 位全局變量 uwTick,在 Systick 中斷
服務(wù)函數(shù) SysTick_Handler 中通過(guò)調(diào)用 HAL_IncTick 實(shí)現(xiàn) uwTick 值不斷增加,也就是每隔 1ms
增加 1。而 HAL_Delay 函數(shù)在進(jìn)入函數(shù)之后先記錄當(dāng)前 uwTick 的值,然后不斷在循環(huán)中讀取
uwTick 當(dāng)前值,進(jìn)行減運(yùn)算,得出的就是延時(shí)的毫秒數(shù),整個(gè)邏輯非常簡(jiǎn)單也非常清晰。
但是,HAL庫(kù)的延時(shí)函數(shù)有一個(gè)局限性,在中斷服務(wù)函數(shù)中使用 HAL_Delay會(huì)引起混亂,
因?yàn)樗峭ㄟ^(guò)中斷方式實(shí)現(xiàn),而 Systick 的中斷優(yōu)先級(jí)是最低的,所以在中斷中運(yùn)行 HAL_Delay
會(huì)導(dǎo)致延時(shí)出現(xiàn)嚴(yán)重誤差。所以一般情況下,推薦大家使用 ALIENTEK 提供的延時(shí)函數(shù)庫(kù)。
5.2 sys 文件夾代碼介紹
sys 文件夾內(nèi)包含了 sys.c 和 sys.h 兩個(gè)文件。在 sys.h 里面除了函數(shù)申明外主要是定義了一
些常用數(shù)據(jù)類型短關(guān)鍵字。sys.c 里面除了定義時(shí)鐘系統(tǒng)配置函數(shù) Stm32_Clock_Init 外主要是一
些匯編函數(shù)以及 Cache 相關(guān)操作函數(shù),對(duì)于函數(shù) Stm32_Clock_Init 的講解請(qǐng)參考本手冊(cè) 4.3 小
節(jié) STM32F7 時(shí)鐘系統(tǒng)章節(jié)內(nèi)容。接下來(lái)我們看看 STM32F7 的 Cache 使能函數(shù)。
5.2.1 Cache 使能函數(shù)
STM32F7 自帶了指令 Cache(I Cache)和數(shù)據(jù) Cache(D Cache),使用 I/D Cache 可以緩存
指令/數(shù)據(jù),提高 CPU 訪問(wèn)指令/數(shù)據(jù)的速度,從而大大提高 MCU 的性能。不過(guò),MCU 在復(fù)位
后,I/D Cache 默認(rèn)都是關(guān)閉的,為了提高性能,我們需要開啟 I/D Cache,在 sys.c 里面,我們
提供了如下函數(shù):
//使能 STM32F7 的 L1-Cache,同時(shí)開啟 D cache 的強(qiáng)制透寫
void Cache_Enable(void)
{
SCB_EnableICache(); //使能 I-Cache,函數(shù)在 core_cm7.h 里面定義
SCB_EnableDCache(); //使能 D-Cache,函數(shù)在 core_cm7.h 里面定義
SCB->CACR|=1<<2; //強(qiáng)制 D-Cache 透寫,如不開啟,實(shí)際使用中可能遇到各種問(wèn)題
}
該函數(shù),通過(guò)調(diào)用 SCB_EnableICache 和 SCB_EnableDCache 這兩個(gè)函數(shù)來(lái)使能 I Cache 和
D Cache。不過(guò),在使能 D Cache 之后,SRAM 里面的數(shù)據(jù)有可能會(huì)被緩存在 Cache 里面,此
時(shí)如果有 DMA 之類的外設(shè)訪問(wèn)這個(gè) SRAM 里面的數(shù)據(jù),就有可能和 Cache 里面數(shù)據(jù)不同步,
導(dǎo)致數(shù)據(jù)出錯(cuò),為了防止這種問(wèn)題,保證數(shù)據(jù)的一致性,我們?cè)O(shè)置了 D Cache 的強(qiáng)制透寫功能
(Write Through),這樣 CPU 每次操作 Cache 里面的數(shù)據(jù),同時(shí)也會(huì)更新到 SRAM 里面,保證
D Cache 和 SRAM 里面數(shù)據(jù)一致。關(guān)于 Cache 的詳細(xì)介紹,請(qǐng)參考《STM32F7 Cache Oveview》
和《Level 1 cache on STM32F7 Series》(見光盤:8,STM32 參考資料 文件夾)。
這里 SCB_EnableICache 和 SCB_EnableDCache 這兩個(gè)函數(shù),是在 core_cm7.h 里面定義的,
我們直接調(diào)用即可,另外,core_cm7.h 里面還提供了以下五個(gè)常用函數(shù):
1,SCB_DisableICache 函數(shù),用于關(guān)閉 I Cache。
2,SCB_DisableDCache 函數(shù),用于關(guān)閉 D Cache。
3,SCB_InvalidateDCache 函數(shù),用于丟棄 D Cache 當(dāng)前數(shù)據(jù),重新從 SRAM 獲取數(shù)據(jù)。
4,SCB_CleanDCache 函數(shù),用于將 D Cache 數(shù)據(jù)回寫到 SRAM 里面,同步數(shù)據(jù)。
5,SCB_CleanInvalidateDCache 函數(shù),用于回寫數(shù)據(jù)到 SRAM,并重新獲取 D Cache 數(shù)據(jù)。
在 Cache_Enable 函數(shù)里面,我們直接開啟了 D Cache 的透寫模式,這樣帶來(lái)的好處就是可
以保證D Cache 和SRAM里面數(shù)據(jù)的一致性,壞處就是會(huì)損失一定的性能(每次都要回寫數(shù)據(jù)),
如果大家想自己控制 D Cache 數(shù)據(jù)的回寫,以獲得最佳性能,則可以關(guān)閉 D Cache 透寫模式,
并在適當(dāng)?shù)臅r(shí)候,調(diào)用 SCB_CleanDCache、SCB_InvalidateDCache 和 SCB_CleanInvalidateDCache
等函數(shù),這對(duì)程序員的要求非常高,程序員必須清楚什么時(shí)候該回寫,什么時(shí)候該更新 D Cache!
如果能力不夠,還是建議開啟 D Cache 的透寫,以免引起各種莫名其妙的問(wèn)題。
5.3 usart 文件夾介紹
該文件夾下面有 usart.c 和 usarts.h 兩個(gè)文件。串口相關(guān)知識(shí),我們將在第九章講解串
口實(shí)驗(yàn)的時(shí)候給大家詳細(xì)講解。本節(jié)我們只給大家講解比較獨(dú)立的 printf 函數(shù)支持相關(guān)的
知識(shí)。
5.3.1 printf 函數(shù)支持
printf 函數(shù)支持的代碼在 usart.c 文件的最上方,在我們初始化和使能串口 1 之后,然
后把這段代碼加入到工程,便可以通過(guò) printf 函數(shù)向串口 1 發(fā)送我們需要的內(nèi)容,方便開
發(fā)過(guò)程中查看代碼執(zhí)行情況以及一些變量值。這段代碼如果要修改一般也只是用來(lái)改變
printf 函數(shù)針對(duì)的串口號(hào),大多情況我們都不需要修改。
代碼內(nèi)容如下:
//加入以下代碼,支持 printf 函數(shù),而不需要選擇 use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//標(biāo)準(zhǔn)庫(kù)需要的支持函數(shù)
struct __FILE
{
int handle;
};
FILE __stdout;
//定義_sys_exit()以避免使用半主機(jī)模式
_sys_exit(int x)
{
x = x;
}
//重定義 fputc 函數(shù)
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循環(huán)發(fā)送,直到發(fā)送完畢
USART1->DR = (u8) ch;
return ch;
}
#endif
總結(jié)
以上是生活随笔為你收集整理的stm32f767中文手册_ALIENTEK 阿波罗 STM32F767 开发板资料连载第五章 SYSTEM 文件夹的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: javaInt占几个字节
- 下一篇: jsbridge与通信模型