驗(yàn)任務(wù) 2:標(biāo)準(zhǔn)外設(shè)庫(kù)流水燈 + Keil 邏輯分析儀測(cè)波形)
STM32 實(shí)驗(yàn)任務(wù) 2標(biāo)準(zhǔn)外設(shè)庫(kù)流水燈 Keil 邏輯分析儀測(cè)波形任務(wù)要求在實(shí)驗(yàn) 1寄存器方式流水燈的基礎(chǔ)上改用STM32 標(biāo)準(zhǔn)外設(shè)庫(kù)方式使用 GPIOx 端口控制幾個(gè) LED 輪流閃爍間隔 1 秒。要求寫出工程項(xiàng)目創(chuàng)建、添加標(biāo)準(zhǔn)外設(shè)庫(kù)文件.c/.h的詳細(xì)過(guò)程用 Keil 軟件仿真邏輯分析儀觀察 GPIO 輸出波形分析時(shí)序是否正確、LED 閃爍周期實(shí)際為多少。問(wèn) 1標(biāo)準(zhǔn)外設(shè)庫(kù)工程怎么建1.1 準(zhǔn)備工作下載標(biāo)準(zhǔn)外設(shè)庫(kù)STM32F1 的標(biāo)準(zhǔn)外設(shè)庫(kù)STM32F10x_StdPeriph_Driver從官方渠道下載后解壓里面Libraries目錄有兩大塊CMSIS芯片內(nèi)核相關(guān)core、設(shè)備頭文件 stm32f10x.h、啟動(dòng)文件STM32F10x_StdPeriph_Driver外設(shè)驅(qū)動(dòng)gpio、rcc、flash、usart 等每個(gè)外設(shè)一個(gè) .c .h注意本項(xiàng)目只用到了 GPIO、RCC、FLASH 三個(gè)外設(shè)所以只把這三個(gè)驅(qū)動(dòng)文件復(fù)制進(jìn)工程其他外設(shè)不添加工程更干凈。1.2 創(chuàng)建工程文件夾我在D:\Project\STM32Project\下創(chuàng)建工程目錄stm32-led-stdlib/ ├── main.c ← 用戶主程序 ├── stm32f10x\_conf.h ← 外設(shè)配置頭文件聲明啟用哪些外設(shè) └── StdPeriph/ ← 標(biāo)準(zhǔn)外設(shè)庫(kù)驅(qū)動(dòng)文件 ├── stm32f10x\_gpio.c / .h ├── stm32f10x\_rcc.c / .h └── stm32f10x\_flash.c / .h文件夾的作用StdPeriph/專門放標(biāo)準(zhǔn)外設(shè)庫(kù)的驅(qū)動(dòng)源碼和用戶代碼分開(kāi)結(jié)構(gòu)清晰stm32f10x_conf.h是標(biāo)準(zhǔn)庫(kù)的 “總開(kāi)關(guān)”它 include 哪些外設(shè)頭文件工程就能用哪些外設(shè)。1.3 新建 Keil 工程Keil →Project → New uVision Project→ 定位到stm32-led-stdlib文件夾工程名main→ 保存選擇芯片STM32F103C8彈出 Manage Run-Time Environment 時(shí)勾選CMSIS → CORE提供內(nèi)核頭文件 core_cm3.hDevice → Startup提供啟動(dòng)文件和系統(tǒng)時(shí)鐘初始化文件點(diǎn) OK。1.4 添加源文件左側(cè)Source Group 1上右鍵 →Add Existing Files to Group...添加 4 個(gè)文件main.cStdPeriph\stm32f10x_gpio.cStdPeriph\stm32f10x_rcc.cStdPeriph\stm32f10x_flash.c1.5 配置宏和頭文件路徑重點(diǎn)點(diǎn)魔術(shù)棒Options for Target→C/C頁(yè)配置項(xiàng)值說(shuō)明DefineUSE_STDPERIPH_DRIVER讓 stm32f10x.h 自動(dòng)包含 stm32f10x_conf.hInclude Paths.\;.\StdPeriph告訴編譯器去哪找頭文件Language Cc99CMSIS 6.x 內(nèi)核頭文件要求1.6 踩坑記錄編譯報(bào)錯(cuò)排查坑 1宏名寫錯(cuò) → 20 個(gè)錯(cuò)誤第一次編譯報(bào)了一堆GPIO_InitTypeDef、GPIO_Pin_0未定義的錯(cuò)誤。原因是很多網(wǎng)絡(luò)教程寫的是USE_STDPERIPH_DEVICE但我這臺(tái) KeilSTM32F1xx_DFP 2.4.0 包里的 stm32f10x.h 實(shí)際要求的是\#ifdef USE\_STDPERIPH\_DRIVER #x20; #include stm32f10x\_conf.h \#endif宏名必須改成USE_STDPERIPH_DRIVER標(biāo)準(zhǔn)庫(kù)的所有定義才能被包含進(jìn)來(lái)。坑 2conf.h 引用不存在的頭文件宏改對(duì)后又報(bào)stm32f10x_adc.h file not found。原因官方模板stm32f10x_conf.h默認(rèn) include 了全部外設(shè)頭文件adc、can、dac……而我工程里只添加了 gpio/rcc/flash 三個(gè)驅(qū)動(dòng)。解決把 conf.h 精簡(jiǎn)只保留用到的三個(gè)\#include stm32f10x\_flash.h \#include stm32f10x\_gpio.h \#include stm32f10x\_rcc.h編譯通過(guò)0 Error。1.7 流水燈代碼標(biāo)準(zhǔn)外設(shè)庫(kù)版\#include stm32f10x.h void delay\_ms(unsigned int ms) // 軟件延時(shí) { #x20; volatile unsigned int i, j; #x20; for (i 0; i ms; i) #x20; for (j 0; j 5000; j) {} } int main(void) { #x20; GPIO\_InitTypeDef GPIO\_InitStructure; #x20; // 1. 使能 GPIOA/B/C 時(shí)鐘 #x20; RCC\_APB2PeriphClockCmd(RCC\_APB2Periph\_GPIOA | RCC\_APB2Periph\_GPIOB | #x20; RCC\_APB2Periph\_GPIOC, ENABLE); #x20; // 2. PA0 紅燈 #x20; GPIO\_InitStructure.GPIO\_Pin GPIO\_Pin\_0; #x20; GPIO\_InitStructure.GPIO\_Mode GPIO\_Mode\_Out\_PP; #x20; GPIO\_InitStructure.GPIO\_Speed GPIO\_Speed\_50MHz; #x20; GPIO\_Init(GPIOA, \GPIO\_InitStructure); #x20; // 3. PB0 綠燈復(fù)用同一結(jié)構(gòu)體 #x20; GPIO\_InitStructure.GPIO\_Pin GPIO\_Pin\_0; #x20; GPIO\_Init(GPIOB, \GPIO\_InitStructure); #x20; // 4. PC14 藍(lán)燈PC13/14/15 低速引腳2MHz #x20; GPIO\_InitStructure.GPIO\_Pin GPIO\_Pin\_14; #x20; GPIO\_InitStructure.GPIO\_Speed GPIO\_Speed\_2MHz; #x20; GPIO\_Init(GPIOC, \GPIO\_InitStructure); #x20; // 5. PC13 板載燈低電平點(diǎn)亮 #x20; GPIO\_InitStructure.GPIO\_Pin GPIO\_Pin\_13; #x20; GPIO\_Init(GPIOC, \GPIO\_InitStructure); #x20; // 6. 初始全滅 #x20; GPIO\_ResetBits(GPIOA, GPIO\_Pin\_0); #x20; GPIO\_ResetBits(GPIOB, GPIO\_Pin\_0); #x20; GPIO\_ResetBits(GPIOC, GPIO\_Pin\_14); #x20; GPIO\_SetBits(GPIOC, GPIO\_Pin\_13); #x20; while (1) { #x20; GPIO\_SetBits(GPIOA, GPIO\_Pin\_0); delay\_ms(1000); #x20; GPIO\_ResetBits(GPIOA, GPIO\_Pin\_0); #x20; GPIO\_SetBits(GPIOB, GPIO\_Pin\_0); delay\_ms(1000); #x20; GPIO\_ResetBits(GPIOB, GPIO\_Pin\_0); #x20; GPIO\_SetBits(GPIOC, GPIO\_Pin\_14); delay\_ms(1000); #x20; GPIO\_ResetBits(GPIOC, GPIO\_Pin\_14); #x20; GPIO\_ResetBits(GPIOC, GPIO\_Pin\_13); delay\_ms(1000); #x20; GPIO\_SetBits(GPIOC, GPIO\_Pin\_13); #x20; } }相比實(shí)驗(yàn) 1 的寄存器操作標(biāo)準(zhǔn)外設(shè)庫(kù)把所有寄存器操作封裝成了函數(shù)GPIO_Init、GPIO_SetBits可讀性明顯更好。問(wèn) 2軟件延時(shí)的 “1 秒” 到底準(zhǔn)不準(zhǔn)2.1 思路LED 亮 / 滅靠delay_ms(1000)軟件循環(huán)延時(shí)。它到底是精確 1 秒還是存在誤差沒(méi)有示波器的情況下可以用Keil 軟件仿真的邏輯分析儀直接觀察 GPIO 波形、測(cè)量時(shí)間。2.2 仿真配置步驟Target 頁(yè)設(shè)置晶振Xtal (MHz) 設(shè)為8.0STM32F103C8T6 外部晶振 8MHz。這一步很重要仿真時(shí)間計(jì)算依賴它。我的 Keil 工程 Xtal 顯示undefined改不動(dòng)直接修改工程文件main.uvprojx里的CLOCK(12000000)為CLOCK(8000000)解決Debug 頁(yè)勾選Use SimulatorRun to main()Dialog DLL 填DARMSTM.DLLParameter 填-pSTM32F103C8點(diǎn)CtrlF5進(jìn)入仿真菜單View → Analysis Windows → Logic Analyzer打開(kāi)邏輯分析儀菜單View → Symbols Window找到GPIOA_ODR拖入邏輯分析儀窗口點(diǎn)邏輯分析儀里的SetupDisplay Type 選Bit按位顯示And Mask 填0x00000001只看 bit0 PA0點(diǎn) Close按F5全速運(yùn)行觀察波形。2.3 波形分析PA0 是紅燈的控制腳波形如下綠色方波從波形可以清楚看到高電平段紅燈亮0.879936s ≈0.88s低電平段紅燈滅其他三燈輪流亮約 3 秒完整周期兩個(gè)上升沿之間3.519741s驗(yàn)證自洽性3.519741 ÷ 4 0.879935 ≈ 0.879936正好等于單燈點(diǎn)亮?xí)r間—— 說(shuō)明四盞燈確實(shí)是輪流各亮約 0.88s程序邏輯和波形完全對(duì)應(yīng)。2.4 結(jié)論軟件延時(shí)準(zhǔn)確嗎以下是波形圖圖片數(shù)據(jù)記錄結(jié)論不準(zhǔn)確。實(shí)測(cè)數(shù)據(jù)如下項(xiàng)目理論值實(shí)測(cè)值誤差單燈點(diǎn)亮?xí)r間1.000 s0.879936 s約 12%PA0 完整周期4.000 s3.519741 s約 12%誤差原因分析軟件延時(shí)本質(zhì)是 “數(shù)指令周期”delay_ms里 5000 這個(gè)參數(shù)是按經(jīng)驗(yàn)估的循環(huán)次數(shù)而每條循環(huán)指令實(shí)際占幾個(gè)時(shí)鐘周期、volatile 變量訪問(wèn)的內(nèi)存開(kāi)銷都和估算有偏差編譯優(yōu)化等級(jí)-O0也影響循環(huán)開(kāi)銷所以軟件延時(shí)的 “1 秒” 只能做到 “大約 1 秒”誤差在 10% 左右完全正常。如果想精確到 1 秒應(yīng)該改用硬件定時(shí)器SysTick 系統(tǒng)節(jié)拍定時(shí)器或 TIM 通用定時(shí)器實(shí)驗(yàn)任務(wù) 3 的 HAL 庫(kù)方案正是用 SysTick 實(shí)現(xiàn)精確延時(shí)可以對(duì)比??偨Y(jié)這個(gè)實(shí)驗(yàn)把 STM32 的開(kāi)發(fā)方式從 “寄存器直接操作” 推進(jìn)到了 “標(biāo)準(zhǔn)外設(shè)庫(kù)函數(shù)調(diào)用”工程從建文件夾、復(fù)制庫(kù)文件、配置宏、到解決兩個(gè)編譯坑宏名、conf.h完整走了一遍標(biāo)準(zhǔn)外設(shè)庫(kù)工程的搭建流程。更重要的是通過(guò) Keil 邏輯分析儀把 “軟件延時(shí)到底準(zhǔn)不準(zhǔn)” 這個(gè)看不見(jiàn)的問(wèn)題變成了看得見(jiàn)的波形和數(shù)據(jù)實(shí)測(cè) 0.88s 與理論 1s 的 12% 誤差讓我真正理解了為什么嵌入式開(kāi)發(fā)里講究 “能用定時(shí)器就別用軟件延時(shí)”。