1)實驗平臺:alientek 阿波羅 STM32F767 開發板2)摘自《STM32F7 開發指南(HAL 庫版)》關注官方微信號公眾號,獲取更多資料:正點原子

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