板入門指南:從環(huán)境搭建到第一個程序)
STM32F103的開發(fā)板到手了接下來該干什么板子剛拆封那會兒我盯著那塊藍(lán)色PCB看了半天——STM32F103C8T672MHz主頻64KB Flash20KB SRAM板載了ST-Link和USB接口還引出了所有GPIO。說實話這玩意兒比當(dāng)年我用的51單片機(jī)強(qiáng)太多了但隨之而來的問題是從哪兒下手網(wǎng)上教程鋪天蓋地有講寄存器的有講標(biāo)準(zhǔn)庫的有講HAL的還有直接上RTOS的看多了反而不知道該聽誰的。這篇文章就是寫給剛拿到STM32F103開發(fā)板、想系統(tǒng)入門但不想被各種教程繞暈的人。我會從板子到手后的第一件事開始把環(huán)境搭建、工程創(chuàng)建、第一個程序燒錄、常見問題排查這條完整鏈路講清楚中間穿插我自己踩過的坑和總結(jié)出來的經(jīng)驗。不管你是電子專業(yè)的學(xué)生還是轉(zhuǎn)行做嵌入式的開發(fā)者跟著這條路徑走至少能少走兩三個月的彎路。1. 拿到板子先別急著寫代碼這幾件事決定了后面順不順1.1 確認(rèn)板子型號和核心芯片的具體參數(shù)STM32F103這個系列下面有非常多型號C8T6、RCT6、ZET6、VET6等等封裝不同、引腳數(shù)不同、Flash和RAM大小也不同。我手里這塊是C8T6LQFP48封裝64KB Flash、20KB SRAM屬于入門級里最經(jīng)典的配置。為什么要先確認(rèn)這個因為后面你選芯片包、配工程、寫鏈接腳本的時候都要跟具體型號對應(yīng)。比如你拿的是RCT6256KB Flash、48KB SRAM卻按C8T6的配置去建工程編譯可能沒問題但燒錄后跑起來會出各種莫名其妙的問題。板子上一般會印型號如果沒有看主控芯片表面的絲印。另外注意板子的晶振頻率——大部分F103板子用的是8MHz外部晶振經(jīng)過PLL倍頻到72MHz作為系統(tǒng)時鐘。也有少數(shù)板子用12MHz晶振這個在配置時鐘樹的時候要特別留意配錯了串口波特率會全亂。1.2 檢查板載的調(diào)試器和供電方式現(xiàn)在市面上很多F103開發(fā)板都板載了ST-Link或者DAP-Link調(diào)試器直接用一根USB線就能完成供電、下載、調(diào)試三件事。但有些便宜的板子只留了SWD排針需要你外接調(diào)試器。拿到板子后先確認(rèn)這一點因為它決定了你后面用什么方式燒錄程序。如果板載了調(diào)試器插上USB后設(shè)備管理器里應(yīng)該能看到對應(yīng)的調(diào)試器設(shè)備。如果沒看到可能需要裝驅(qū)動。ST-Link的驅(qū)動在ST官網(wǎng)可以下載DAP-Link的話Win10以上一般免驅(qū)。這里有個細(xì)節(jié)有些板子的USB接口只負(fù)責(zé)供電調(diào)試接口是另外一個Type-C或者M(jìn)icro-USB口別插錯了。注意插上USB后如果板子上的電源燈不亮先換一根USB線試試。很多便宜的USB線只有充電功能沒有數(shù)據(jù)線芯這個坑我見過太多次了。1.3 準(zhǔn)備好必要的軟件工具鏈STM32開發(fā)常用的IDE有Keil MDK、IAR EWARM、STM32CubeIDE這幾種。Keil在國內(nèi)用得最多資料也最全但需要單獨安裝芯片包Pack。STM32CubeIDE是ST官方推出的免費(fèi)IDE基于Eclipse集成了CubeMX配置工具適合不想折騰License的開發(fā)者。VS Code加PlatformIO或者EIDE插件也是一條路適合習(xí)慣輕量級編輯器的朋友。我的建議是入門階段用Keil MDK或者STM32CubeIDE先把流程跑通別一上來就折騰VS Code的各種插件配置。等你對編譯、鏈接、燒錄這些環(huán)節(jié)有了直觀感受之后再換工具也不遲。芯片包的安裝后面會詳細(xì)講這里先有個概念就行。2. 開發(fā)環(huán)境搭建從零到編譯通過的完整路徑2.1 Keil MDK的安裝與芯片包配置Keil MDK的安裝本身沒什么難度一路Next就行。關(guān)鍵是裝完之后要安裝STM32F1系列的Device Family Pack。這個包不在Keil安裝包里需要單獨下載。打開Keil后點擊Pack Installer圖標(biāo)在左側(cè)找到STMicroelectronics展開后找到STM32F1 Series點擊對應(yīng)的Pack進(jìn)行安裝。如果網(wǎng)絡(luò)環(huán)境不好導(dǎo)致在線安裝失敗也可以去Keil官網(wǎng)手動下載Pack文件然后雙擊安裝。Pack文件的后綴是.pack安裝路徑一般會自動識別Keil的安裝目錄。裝完之后新建工程時才能在芯片列表里找到STM32F103C8這類型號。這里有個常見問題有些人裝完P(guān)ack后新建工程還是找不到芯片原因通常是Pack版本和Keil版本不匹配。比如Keil MDK 5.36配STM32F1的Pack 2.3.0一般沒問題但如果Keil版本太老比如5.14可能需要用舊版的Pack。遇到這種情況要么升級Keil要么降級Pack沒有別的辦法。2.2 使用STM32CubeMX生成初始化代碼STM32CubeMX是ST官方出的圖形化配置工具可以幫你生成引腳配置、時鐘樹、外設(shè)初始化的代碼。對于新手來說它能大幅降低入門門檻因為你不需要一上來就對著參考手冊寫寄存器。具體流程是這樣的新建工程選擇芯片型號STM32F103C8Tx然后配置時鐘源。在RCC里把HSE設(shè)為Crystal/Ceramic Resonator然后在Clock Configuration里把HCLK拉到72MHz。接著配置調(diào)試接口在SYS里把Debug設(shè)為Serial Wire這樣SWD接口才能正常工作。最后配置你要用的外設(shè)比如GPIO、USART、TIM等。生成代碼的時候注意選對工具鏈。如果你用Keil就選MDK-ARM如果用STM32CubeIDE就選STM32CubeIDE。生成的代碼會包含main.c、stm32f1xx_hal_msp.c、stm32f1xx_it.c等文件以及HAL庫的驅(qū)動文件。提示CubeMX生成的代碼里用戶代碼要寫在/* USER CODE BEGIN/和/USER CODE END */之間否則下次重新生成代碼時會被覆蓋掉。這個規(guī)則一定要記住我見過太多人因為把代碼寫錯位置重新生成后全部丟失的。2.3 標(biāo)準(zhǔn)庫工程模板的手動搭建雖然HAL庫現(xiàn)在是主流但很多老教程和項目還在用標(biāo)準(zhǔn)庫Standard Peripheral Library。標(biāo)準(zhǔn)庫的好處是代碼量小、執(zhí)行效率高、貼近寄存器操作適合想深入理解底層的人。但ST已經(jīng)不再維護(hù)標(biāo)準(zhǔn)庫了新建工程需要手動拷貝庫文件。手動搭建標(biāo)準(zhǔn)庫工程的步驟大致是新建工程目錄在里面創(chuàng)建Startup、Library、User、Output等文件夾。把啟動文件startup_stm32f10x_md.s、系統(tǒng)文件system_stm32f10x.c、內(nèi)核文件core_cm3.c/h以及STM32F10x_StdPeriph_Driver里的外設(shè)驅(qū)動文件拷貝到對應(yīng)目錄。然后在Keil里新建工程添加這些文件配置頭文件包含路徑最后在Options for Target里設(shè)置好Output和Listing路徑。這個過程比較繁瑣但做一遍之后你對STM32工程的組成結(jié)構(gòu)會有非常清晰的認(rèn)識。網(wǎng)上有很多現(xiàn)成的標(biāo)準(zhǔn)庫工程模板可以下載但建議至少自己手動搭一次知道每個文件是干什么用的。3. 第一個程序從點亮LED到串口輸出3.1 GPIO輸出實驗點亮板載LED點亮LED是嵌入式開發(fā)的Hello World。F103的GPIO配置流程是使能GPIO時鐘、配置GPIO模式、設(shè)置輸出電平。用HAL庫的話代碼大概長這樣// 使能GPIOC時鐘 __HAL_RCC_GPIOC_CLK_ENABLE(); // 配置PC13為推挽輸出 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_13; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, GPIO_InitStruct); // 在主循環(huán)里翻轉(zhuǎn)電平 while (1) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(500); }這里有個細(xì)節(jié)很多F103最小系統(tǒng)板的板載LED接在PC13上而且是低電平點亮。也就是說你寫HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET)的時候LED是亮的寫SET的時候是滅的。這個跟硬件電路有關(guān)拿到板子后最好用萬用表測一下LED的正負(fù)極方向或者直接看原理圖。3.2 串口通信實驗printf重定向串口是調(diào)試過程中最重要的工具之一。F103一般用USART1引腳是PA9TX和PA10RX。配置好波特率常用115200、數(shù)據(jù)位、停止位、校驗位之后就可以收發(fā)數(shù)據(jù)了。為了能用printf輸出調(diào)試信息需要重定向fputc函數(shù)int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, HAL_MAX_DELAY); return ch; }然后在Keil的Options for Target里勾選Use MicroLIB否則printf可能無法正常工作。這個坑非常經(jīng)典很多人寫了重定向代碼但串口沒輸出就是因為沒勾MicroLIB。串口調(diào)試助手可以用SSCOM、XCOM或者PuTTY。注意波特率、數(shù)據(jù)位、停止位、校驗位要和代碼里配置的一致否則收到的全是亂碼。3.3 定時器實驗精準(zhǔn)延時與PWM輸出HAL_Delay()函數(shù)用的是SysTick定時器精度一般而且在中斷里調(diào)用會有問題。如果需要更精準(zhǔn)的延時或者PWM輸出就要用通用定時器TIM2~TIM5。配置定時器的時候要算好預(yù)分頻系數(shù)PSC和自動重裝載值A(chǔ)RR。比如系統(tǒng)時鐘72MHz想要1ms的定時周期可以設(shè)PSC71ARR999這樣定時器時鐘就是72MHz/(711)1MHz計數(shù)1000次就是1ms。PWM輸出的配置稍微復(fù)雜一點需要設(shè)置PWM模式、占空比、極性等。用CubeMX配置的話選好定時器和通道設(shè)置好PSC和ARR然后在代碼里用__HAL_TIM_SET_COMPARE()宏來動態(tài)調(diào)整占空比。4. 燒錄與調(diào)試程序怎么進(jìn)到芯片里4.1 ST-Link燒錄的接線與配置SWD接口需要四根線VCC、GND、SWDIO、SWCLK。有些板子還引出了SWO和NRST但最基本的四根線就能完成燒錄和調(diào)試。接線的時候注意VCC的電壓F103是3.3V供電別接5V。在Keil里配置ST-Link調(diào)試器Options for Target - Debug - 選擇ST-Link Debugger - Settings - 確認(rèn)Port是SW能識別到芯片ID。然后在Flash Download選項卡里確認(rèn)編程算法是STM32F10x Medium-density Flash對應(yīng)64KB/128KB的型號。如果識別不到芯片先檢查接線再檢查板子是否上電最后檢查SWD引腳是否被程序禁用了。有些程序會把SWD引腳配置成普通GPIO導(dǎo)致下次無法燒錄。遇到這種情況需要把BOOT0拉高讓芯片從系統(tǒng)存儲器啟動然后重新燒錄。4.2 常見燒錄失敗原因排查燒錄失敗是新手最常遇到的問題原因五花八門。我整理了一個排查表現(xiàn)象可能原因解決辦法找不到芯片接線錯誤或板子沒供電檢查SWD四根線確認(rèn)電源燈亮找不到芯片SWD引腳被禁用BOOT0拉高重新上電后再燒錄燒錄報錯Flash Download failed編程算法選錯確認(rèn)芯片型號對應(yīng)的Flash算法燒錄成功但不運(yùn)行復(fù)位電路問題檢查NRST引腳嘗試手動復(fù)位燒錄成功但不運(yùn)行時鐘配置錯誤檢查HSE晶振是否起振還有一個經(jīng)典問題VS Code里編譯成功但怎么也燒錄不進(jìn)開發(fā)板。這種情況通常是燒錄工具配置不對或者OpenOCD的配置文件跟芯片不匹配。建議先用Keil或者STM32CubeProgrammer確認(rèn)能正常燒錄再回頭調(diào)VS Code的配置。4.3 使用STM32CubeProgrammer進(jìn)行獨立燒錄STM32CubeProgrammer是ST官方的燒錄工具支持ST-Link、UART、USB、OTA等多種方式。它的好處是不依賴IDE可以單獨用來燒錄hex或者bin文件。批量生產(chǎn)的時候特別有用。用ST-Link連接后選擇對應(yīng)的端口加載hex文件點擊Download就行。如果要燒錄bin文件需要手動指定起始地址一般是0x08000000。燒錄完成后可以點擊Verify校驗確保數(shù)據(jù)寫入正確。5. 入門之后往哪走幾個值得深入的方向5.1 中斷系統(tǒng)與NVIC配置STM32的中斷系統(tǒng)比51復(fù)雜得多有優(yōu)先級分組、搶占優(yōu)先級、響應(yīng)優(yōu)先級等概念。F103的NVIC支持16個優(yōu)先級通過NVIC_PriorityGroupConfig()來設(shè)置分組。分組決定了搶占優(yōu)先級和響應(yīng)優(yōu)先級各占幾位。舉個例子如果你設(shè)置分組為NVIC_PriorityGroup_2那么搶占優(yōu)先級和響應(yīng)優(yōu)先級各占2位取值范圍都是0~3。搶占優(yōu)先級高的可以打斷搶占優(yōu)先級低的中斷相同搶占優(yōu)先級下響應(yīng)優(yōu)先級高的先執(zhí)行但不能打斷。外部中斷EXTI的配置流程是使能GPIO和AFIO時鐘、配置GPIO為輸入模式、配置EXTI觸發(fā)方式、配置NVIC、編寫中斷服務(wù)函數(shù)。按鍵中斷是最典型的應(yīng)用場景。5.2 定時器輸入捕獲與超聲波測距超聲波測距模塊HC-SR04是很多STM32項目的標(biāo)配。原理很簡單Trig引腳發(fā)一個10us的高電平脈沖模塊發(fā)出超聲波Echo引腳變高收到回波后變低。Echo高電平的持續(xù)時間就是超聲波往返的時間除以2再乘以聲速就是距離。用定時器的輸入捕獲功能可以精準(zhǔn)測量Echo引腳的高電平時間。配置TIM的某個通道為輸入捕獲模式上升沿觸發(fā)在中斷里記錄CNT值然后切換為下降沿觸發(fā)再次記錄CNT值兩次之差就是高電平時間。這里有個經(jīng)驗超聲波測距的精度受溫度影響比較大聲速在0度時是331m/s每升高1度增加0.6m/s。如果要做高精度測量需要加溫度補(bǔ)償。另外測量周期不要太短HC-SR04建議至少60ms一次否則會有回波干擾。5.3 USB虛擬串口與設(shè)備開發(fā)F103的USB外設(shè)是Full Speed的可以做成虛擬串口CDC、HID設(shè)備、大容量存儲等。用CubeMX配置USB Device的時候選擇Communication Device Class生成代碼后安裝ST的VCP驅(qū)動電腦上就會多出一個串口。USB虛擬串口的好處是不需要額外的USB轉(zhuǎn)串口芯片一根線就能搞定通信。但要注意F103的USB和CAN共用引腳不能同時使用。另外USB的時鐘必須精確配置為48MHz否則枚舉會失敗。5.4 基于標(biāo)準(zhǔn)庫和HAL庫的取舍標(biāo)準(zhǔn)庫和HAL庫各有優(yōu)劣。標(biāo)準(zhǔn)庫代碼精簡、執(zhí)行效率高、寄存器操作直觀適合資源緊張或者需要精細(xì)控制的場景。HAL庫抽象程度高、跨系列移植方便、配合CubeMX開發(fā)效率高但代碼體積大、執(zhí)行效率相對低。我的建議是入門階段用HAL庫CubeMX快速建立信心和整體認(rèn)知。等你有了一定基礎(chǔ)再回頭研究標(biāo)準(zhǔn)庫或者直接操作寄存器理解底層原理。實際項目中兩者可以混用比如用CubeMX生成初始化代碼然后關(guān)鍵部分用寄存器操作優(yōu)化性能。6. 那些教程不會告訴你的實操經(jīng)驗6.1 關(guān)于延時函數(shù)卡死的問題HAL_Delay()卡死是新手經(jīng)常遇到的現(xiàn)象。原因通常是SysTick中斷優(yōu)先級配置不當(dāng)或者在中斷服務(wù)函數(shù)里調(diào)用了HAL_Delay()。SysTick的中斷優(yōu)先級默認(rèn)是最低的如果某個高優(yōu)先級中斷一直在執(zhí)行SysTick中斷就無法觸發(fā)HAL_Delay()就會一直等待。解決辦法是不要在中斷里調(diào)用HAL_Delay()如果確實需要延時用簡單的循環(huán)延時或者硬件定時器。另外檢查一下NVIC的優(yōu)先級分組確保SysTick的優(yōu)先級沒有被意外設(shè)得太低。6.2 關(guān)于JTAG和SWD引腳復(fù)用F103的PA13、PA14、PA15、PB3、PB4這幾個引腳默認(rèn)是JTAG功能。如果你想把它們當(dāng)普通GPIO用需要禁用JTAG只保留SWD。用標(biāo)準(zhǔn)庫的話調(diào)用GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE)就行。用HAL庫的話在CubeMX的SYS配置里把Debug設(shè)為Serial Wire生成的代碼會自動處理。這個坑很隱蔽因為引腳看起來能用但輸出電平不對。我當(dāng)初調(diào)一個SPI屏幕PB3和PB4死活不出信號查了半天才發(fā)現(xiàn)是JTAG沒禁用。6.3 關(guān)于電源和復(fù)位電路的細(xì)節(jié)有些便宜的F103板子為了省成本復(fù)位電路只放了一個電容沒有上拉電阻。這種板子上電后可能不復(fù)位或者復(fù)位不可靠。表現(xiàn)就是程序有時候跑有時候不跑或者燒錄后要手動按復(fù)位鍵才運(yùn)行。如果遇到這種情況可以在NRST引腳和VCC之間加一個10K的上拉電阻再并一個100nF的電容到地。這是ST官方推薦的標(biāo)準(zhǔn)復(fù)位電路。另外BOOT0引腳最好也加一個10K下拉電阻避免懸空導(dǎo)致啟動模式不確定。6.4 關(guān)于芯片包安裝失敗的替代方案Keil的Pack Installer有時候會因為網(wǎng)絡(luò)問題下載失敗。除了手動下載Pack文件之外還有一個辦法是用離線Pack。在Keil的Pack Installer里選擇File - Import然后選擇下載好的.pack文件。如果連官網(wǎng)都打不開可以去一些國內(nèi)的電子論壇找網(wǎng)友分享的Pack文件注意核對版本號和文件完整性。STM32CubeIDE的話芯片包是集成在安裝包里的一般不需要單獨安裝。但如果你需要更新到最新版的HAL庫可以在Help - Manage Embedded Software Packages里在線更新。6.5 關(guān)于開發(fā)板掛載Ubuntu后的權(quán)限問題有些朋友喜歡在Ubuntu下用OpenOCD或者STM32CubeProgrammer燒錄。插上ST-Link后可能會提示權(quán)限不足這是因為普通用戶沒有USB設(shè)備的訪問權(quán)限。解決辦法是添加udev規(guī)則sudo nano /etc/udev/rules.d/99-stlink.rules在里面寫入SUBSYSTEMusb, ATTR{idVendor}0483, ATTR{idProduct}3748, MODE0666然后重新加載udev規(guī)則sudo udevadm control --reload-rules sudo udevadm trigger不同版本的ST-Link的idProduct可能不同可以用lsusb命令查看。這個配置做一次就行以后插上就能直接用。板子到手只是開始真正有意思的是后面把一個個外設(shè)調(diào)通、把一個個項目做出來的過程。F103雖然老但資料全、生態(tài)好、價格便宜作為入門第一塊STM32開發(fā)板再合適不過。我到現(xiàn)在還會用F103做一些小項目它的性能和資源對于大多數(shù)控制類應(yīng)用來說完全夠用。先把GPIO、串口、定時器、中斷這幾個基礎(chǔ)外設(shè)玩熟后面學(xué)RTOS、文件系統(tǒng)、網(wǎng)絡(luò)協(xié)議棧都會順很多。遇到問題別慌先查手冊、再查論壇、最后動手驗證嵌入式開發(fā)就是這樣一步步積累起來的。