發(fā)入門(mén):從ARM內(nèi)核到ADC/DMA實(shí)戰(zhàn)應(yīng)用)
1. 從零開(kāi)始為什么是STM32如果你剛開(kāi)始接觸嵌入式開(kāi)發(fā)面對(duì)市面上琳瑯滿(mǎn)目的單片機(jī)可能會(huì)感到無(wú)從下手。51單片機(jī)太簡(jiǎn)單性能捉襟見(jiàn)肘樹(shù)莓派這類(lèi)Linux板卡又太復(fù)雜底層細(xì)節(jié)被操作系統(tǒng)封裝不利于理解硬件本質(zhì)。那么STM32就是一個(gè)絕佳的“中間點(diǎn)”。它基于A(yíng)RM Cortex-M內(nèi)核性能強(qiáng)大、外設(shè)豐富同時(shí)保持了單片機(jī)直接操作寄存器的“純粹感”是連接理論知識(shí)與工業(yè)實(shí)踐的橋梁。我最初選擇STM32是因?yàn)樗鼰o(wú)處不在。從智能家居的溫控器、工業(yè)現(xiàn)場(chǎng)的傳感器采集模塊到消費(fèi)電子中的穿戴設(shè)備STM32的身影隨處可見(jiàn)。這意味著你學(xué)到的技能有廣泛的應(yīng)用場(chǎng)景和就業(yè)前景。更重要的是STM32擁有極其完善的生態(tài)系統(tǒng)官方的STM32CubeMX工具可以圖形化配置引腳和時(shí)鐘HAL庫(kù)和LL庫(kù)簡(jiǎn)化了驅(qū)動(dòng)編寫(xiě)而豐富的社區(qū)資源和成熟的調(diào)試工具如ST-LINK讓學(xué)習(xí)和排錯(cuò)變得相對(duì)容易。學(xué)習(xí)STM32核心是理解其“基礎(chǔ)理論知識(shí)”。這不僅僅是記住幾個(gè)外設(shè)的名字而是要建立起一個(gè)完整的認(rèn)知框架CPU如何通過(guò)總線(xiàn)訪(fǎng)問(wèn)內(nèi)存和外設(shè)時(shí)鐘系統(tǒng)如何為整個(gè)芯片提供“心跳”GPIO、ADC、DMA這些外設(shè)是如何與內(nèi)核協(xié)同工作的只有把這些基礎(chǔ)打牢你才能在未來(lái)面對(duì)更復(fù)雜的項(xiàng)目時(shí)做到游刃有余而不是簡(jiǎn)單地復(fù)制粘貼代碼。接下來(lái)我將帶你深入STM32的世界從最核心的ARM Cortex-M內(nèi)核開(kāi)始一步步拆解其基礎(chǔ)架構(gòu)。2. 核心基石ARM Cortex-M內(nèi)核與STM32芯片架構(gòu)2.1 ARM Cortex-M內(nèi)核高效能的核心引擎STM32的性能之源在于其搭載的ARM Cortex-M系列處理器內(nèi)核。你可以把它理解為一輛汽車(chē)的發(fā)動(dòng)機(jī)。常見(jiàn)的STM32F1系列用的是Cortex-M3F4系列是Cortex-M4而最新的高性能或超低功耗系列則可能采用Cortex-M7或Cortex-M0。不同“發(fā)動(dòng)機(jī)”的“馬力”主頻和“特長(zhǎng)”是否帶浮點(diǎn)運(yùn)算單元FPU是否有DSP指令不同。Cortex-M內(nèi)核采用精簡(jiǎn)指令集RISC這意味著它的指令集數(shù)量少格式規(guī)整執(zhí)行效率高。與個(gè)人電腦上復(fù)雜的x86架構(gòu)相比Cortex-M內(nèi)核結(jié)構(gòu)簡(jiǎn)潔中斷響應(yīng)迅速引入了嵌套向量中斷控制器NVIC非常適合實(shí)時(shí)性要求高的嵌入式場(chǎng)景。理解內(nèi)核首先要明白兩個(gè)關(guān)鍵概念工作模式和特權(quán)等級(jí)。芯片上電后內(nèi)核處于線(xiàn)程模式并且擁有特權(quán)級(jí)可以訪(fǎng)問(wèn)所有資源和執(zhí)行所有指令。當(dāng)我們配置中斷并觸發(fā)后內(nèi)核會(huì)切換到處理器模式來(lái)處理中斷服務(wù)程序。為了系統(tǒng)安全我們可以通過(guò)配置讓一部分代碼比如用戶(hù)任務(wù)運(yùn)行在非特權(quán)級(jí)這部分代碼無(wú)法訪(fǎng)問(wèn)某些關(guān)鍵的系統(tǒng)控制寄存器如NVIC、SysTick。這種機(jī)制在復(fù)雜的RTOS實(shí)時(shí)操作系統(tǒng)應(yīng)用中非常有用。注意對(duì)于初學(xué)者大部分裸機(jī)開(kāi)發(fā)都運(yùn)行在特權(quán)級(jí)的線(xiàn)程模式下。但了解這些概念有助于你日后閱讀更深入的RTOS源碼或芯片手冊(cè)。2.2 STM32芯片整體架構(gòu)總線(xiàn)矩陣與存儲(chǔ)器映射內(nèi)核再?gòu)?qiáng)大也需要與內(nèi)存、外設(shè)通信。STM32內(nèi)部通過(guò)一個(gè)復(fù)雜的“交通網(wǎng)絡(luò)”——總線(xiàn)矩陣來(lái)實(shí)現(xiàn)。主要的總線(xiàn)有I-Bus D-Bus指令總線(xiàn)和數(shù)據(jù)總線(xiàn)內(nèi)核通過(guò)它們從Flash中取指令、從SRAM中讀寫(xiě)數(shù)據(jù)。System Bus系統(tǒng)總線(xiàn)用于訪(fǎng)問(wèn)外設(shè)和部分SRAM。DMA Bus直接存儲(chǔ)器訪(fǎng)問(wèn)總線(xiàn)專(zhuān)供DMA控制器使用可以與內(nèi)核并行工作不占用CPU資源。所有這些總線(xiàn)最終都連接到總線(xiàn)矩陣上它是一個(gè)交叉開(kāi)關(guān)允許多個(gè)主設(shè)備如內(nèi)核、DMA同時(shí)訪(fǎng)問(wèn)不同的從設(shè)備如Flash、SRAM、外設(shè)只要它們的路徑不沖突這大大提升了系統(tǒng)并行處理能力。那么CPU或DMA是如何找到要訪(fǎng)問(wèn)的內(nèi)存或外設(shè)的呢這就引出了存儲(chǔ)器映射的概念。你可以把整個(gè)4GB32位地址總線(xiàn)的尋址空間想象成一個(gè)超級(jí)大的線(xiàn)性數(shù)組STM32廠(chǎng)商已經(jīng)為這個(gè)數(shù)組的每一段“地址區(qū)間”分配了固定的功能。地址范圍功能區(qū)域說(shuō)明0x0000 0000 - 0x1FFF FFFF代碼區(qū)通常映射到內(nèi)部Flash用于存放程序代碼和常量。0x2000 0000 - 0x3FFF FFFFSRAM區(qū)映射到芯片內(nèi)部RAM用于存放變量、堆棧等運(yùn)行時(shí)數(shù)據(jù)。0x4000 0000 - 0x5FFF FFFF外設(shè)區(qū)這是最關(guān)鍵的區(qū)域所有GPIO、USART、ADC、TIM等外設(shè)的寄存器都像一個(gè)個(gè)“開(kāi)關(guān)”或“數(shù)據(jù)箱”整齊地排列在這個(gè)地址空間里。操作外設(shè)本質(zhì)上就是讀寫(xiě)這個(gè)地址區(qū)間內(nèi)的特定寄存器。0x6000 0000 - ...其他可能用于外部存儲(chǔ)器FSMC/FMC、內(nèi)核私有外設(shè)等。例如STM32F1的GPIOA端口的輸出數(shù)據(jù)寄存器ODR的地址可能是0x4001 080C。當(dāng)我們寫(xiě)GPIOA-ODR 0xFFFF;這條C語(yǔ)言語(yǔ)句時(shí)編譯器最終會(huì)生成一條向這個(gè)地址寫(xiě)入數(shù)據(jù)的機(jī)器指令從而控制PA0-PA15所有引腳輸出高電平。理解存儲(chǔ)器映射是理解你寫(xiě)的C代碼如何最終控制硬件動(dòng)作的關(guān)鍵。3. 動(dòng)力之源時(shí)鐘系統(tǒng)與電源管理3.1 時(shí)鐘樹(shù)芯片的“血液循環(huán)系統(tǒng)”STM32的時(shí)鐘系統(tǒng)常被初學(xué)者視為畏途但它其實(shí)是理解芯片穩(wěn)定運(yùn)行的基礎(chǔ)。你可以把時(shí)鐘信號(hào)看作數(shù)字電路的心跳所有邏輯單元的同步動(dòng)作都依賴(lài)于它。STM32的時(shí)鐘源多樣通過(guò)一個(gè)被稱(chēng)為“時(shí)鐘樹(shù)”的復(fù)雜網(wǎng)絡(luò)分發(fā)到各個(gè)部件。時(shí)鐘源主要有四類(lèi)HSI內(nèi)部高速RC振蕩器約8MHz精度較低但上電即用常用于系統(tǒng)啟動(dòng)。HSE外部高速晶振4-26MHz常用8MHz精度高是系統(tǒng)主時(shí)鐘的主要來(lái)源。LSI內(nèi)部低速RC振蕩器約40kHz供獨(dú)立看門(mén)狗IWDG和RTC在停機(jī)/待機(jī)模式下使用。LSE外部低速晶振32.768kHz專(zhuān)為RTC提供精準(zhǔn)時(shí)鐘。這些時(shí)鐘源經(jīng)過(guò)PLL鎖相環(huán)倍頻后可以產(chǎn)生更高的系統(tǒng)主時(shí)鐘SYSCLK。例如外接8MHz的HSE經(jīng)過(guò)PLL 9倍頻就可以得到72MHz的SYSCLKSTM32F103的常見(jiàn)配置。時(shí)鐘分配則是指SYSCLK通過(guò)分頻器預(yù)分頻器產(chǎn)生不同頻率的時(shí)鐘供給不同外設(shè)AHB總線(xiàn)時(shí)鐘HCLK給內(nèi)核、內(nèi)存、DMA使用。APB1總線(xiàn)時(shí)鐘PCLK1給低速外設(shè)如TIM2-7、USART2-3等使用最高36MHzF1系列。APB2總線(xiàn)時(shí)鐘PCLK2給高速外設(shè)如GPIOA-E、ADC1、USART1、TIM1等使用最高72MHz。實(shí)操心得使用STM32CubeMX配置時(shí)鐘樹(shù)是最佳實(shí)踐。圖形化界面清晰展示了信號(hào)流向和分頻系數(shù)并能自動(dòng)計(jì)算最終頻率避免配置錯(cuò)誤導(dǎo)致外設(shè)無(wú)法工作或超頻。務(wù)必養(yǎng)成在程序初始化后先調(diào)用SystemClock_Config()函數(shù)的習(xí)慣。3.2 電源管理平衡性能與功耗STM32提供了多種運(yùn)行模式以適應(yīng)不同的功耗需求運(yùn)行模式所有時(shí)鐘開(kāi)啟全速運(yùn)行。睡眠模式CPU停止但外設(shè)和中斷喚醒源仍在工作任何中斷可喚醒。停機(jī)模式所有時(shí)鐘停止HSI/HSE關(guān)閉僅保留少量寄存器和SRAM內(nèi)容功耗極低。只能通過(guò)外部中斷或RTC鬧鐘喚醒。待機(jī)模式功耗最低幾乎關(guān)閉一切僅需備份域RTC、備份寄存器供電。喚醒后相當(dāng)于系統(tǒng)復(fù)位。選擇策略在電池供電的設(shè)備中合理使用低功耗模式至關(guān)重要。例如一個(gè)無(wú)線(xiàn)傳感器節(jié)點(diǎn)可以在采集并發(fā)送數(shù)據(jù)后立即進(jìn)入停機(jī)模式等待定時(shí)中斷喚醒進(jìn)行下一次采集從而將平均功耗降至微安級(jí)別。4. 數(shù)字世界的基礎(chǔ)GPIO的八種工作模式深度解析GPIO是芯片與外界交互最直接的窗口。STM32的GPIO功能強(qiáng)大其靈活性的核心在于可配置的八種工作模式。理解每種模式的硬件電路原理是正確使用GPIO的前提。4.1 輸入模式讀取外部信號(hào)輸入浮空引腳內(nèi)部既不上拉也不下拉完全由外部電路決定電平。當(dāng)引腳懸空時(shí)電平不確定極易受干擾。僅適用于外部有確定驅(qū)動(dòng)電路如接了下拉電阻的場(chǎng)景。輸入上拉內(nèi)部通過(guò)一個(gè)約40kΩ的電阻連接到VDD。當(dāng)外部無(wú)驅(qū)動(dòng)時(shí)引腳被拉至高電平。常用于連接按鍵按鍵另一端接地按下時(shí)讀到低電平松開(kāi)時(shí)讀到高電平。輸入下拉內(nèi)部通過(guò)電阻連接到VSS。當(dāng)外部無(wú)驅(qū)動(dòng)時(shí)引腳被拉至低電平。應(yīng)用場(chǎng)景與上拉模式相反。模擬輸入信號(hào)直接通往ADC模塊或比較器內(nèi)部施密特觸發(fā)器被禁用數(shù)字讀取功能失效。這是進(jìn)行ADC采樣時(shí)必須配置的模式。4.2 輸出模式驅(qū)動(dòng)外部電路開(kāi)漏輸出輸出級(jí)只有一個(gè)N-MOS管連接到地。當(dāng)輸出0時(shí)MOS管導(dǎo)通引腳被拉低當(dāng)輸出1時(shí)MOS管關(guān)閉引腳呈高阻態(tài)。開(kāi)漏輸出本身無(wú)法輸出高電平必須外接上拉電阻。其優(yōu)點(diǎn)是允許“線(xiàn)與”多個(gè)開(kāi)漏輸出接在一起任一輸出低則總線(xiàn)為低常用于I2C總線(xiàn)等通信場(chǎng)合。推挽輸出輸出級(jí)包含一個(gè)P-MOS管接VDD和一個(gè)N-MOS管接VSS。輸出1時(shí)P-MOS導(dǎo)通輸出高電平輸出0時(shí)N-MOS導(dǎo)通輸出低電平。這是最常用的輸出模式驅(qū)動(dòng)能力強(qiáng)高低電平都有主動(dòng)驅(qū)動(dòng)。4.3 復(fù)用功能模式引腳的第二生命復(fù)用開(kāi)漏輸出與8. 復(fù)用推挽輸出這兩種模式與普通開(kāi)漏/推挽輸出的硬件結(jié)構(gòu)完全一樣區(qū)別在于輸出信號(hào)的來(lái)源。普通模式下輸出信號(hào)來(lái)自輸出數(shù)據(jù)寄存器ODR而復(fù)用模式下輸出信號(hào)來(lái)自片上外設(shè)如USART的TX引腳、SPI的MOSI引腳。當(dāng)你使用串口發(fā)送數(shù)據(jù)時(shí)必須將TX引腳配置為復(fù)用推挽輸出模式。避坑指南驅(qū)動(dòng)LED、繼電器等簡(jiǎn)單負(fù)載用推挽輸出。做I2C通信必須用開(kāi)漏輸出并外接上拉電阻。做ADC采樣必須配置為模擬輸入。使用串口、SPI等外設(shè)的輸入功能時(shí)通常配置為浮空輸入或上拉/下拉輸入根據(jù)協(xié)議需要輸出功能則配置為復(fù)用推挽輸出。5. 模擬世界的橋梁ADC原理與應(yīng)用陷阱5.1 ADC工作原理與關(guān)鍵參數(shù)ADC模數(shù)轉(zhuǎn)換器負(fù)責(zé)將連續(xù)的模擬電壓信號(hào)如傳感器輸出的0-3.3V轉(zhuǎn)換為離散的數(shù)字量如0-4095。STM32內(nèi)置的通常是逐次逼近型ADC。核心參數(shù)理解分辨率通常為12位。表示ADC能將參考電壓范圍劃分為2^124096個(gè)等級(jí)。分辨率越高能區(qū)分的電壓變化越小。參考電壓ADC轉(zhuǎn)換的基準(zhǔn)。STM32通常有VREF和VREF-引腳。VREF-一般接地VREF接一個(gè)干凈的電源如3.3V。參考電壓的穩(wěn)定性直接決定ADC的精度。采樣時(shí)間ADC內(nèi)部采樣保持電容對(duì)輸入信號(hào)充電的時(shí)間。對(duì)于高阻抗的信號(hào)源如用大電阻分壓必須延長(zhǎng)采樣時(shí)間否則電容充電不足轉(zhuǎn)換結(jié)果會(huì)偏小。這就是為什么有些開(kāi)發(fā)者發(fā)現(xiàn)“ADC采樣值偏小”的一個(gè)重要原因。轉(zhuǎn)換時(shí)間 采樣時(shí)間 12.5個(gè)ADC時(shí)鐘周期。ADC時(shí)鐘ADCCLK不能超過(guò)芯片手冊(cè)規(guī)定的最大值如STM32F1為14MHz。5.2 多通道掃描與DMA傳輸單個(gè)ADC模塊可以輪流采樣多個(gè)通道。通過(guò)配置掃描模式ADC會(huì)自動(dòng)按順序轉(zhuǎn)換一組預(yù)設(shè)的通道。如果每個(gè)通道轉(zhuǎn)換完成后都產(chǎn)生中斷讓CPU來(lái)讀取數(shù)據(jù)會(huì)嚴(yán)重消耗CPU資源。此時(shí)DMA直接存儲(chǔ)器訪(fǎng)問(wèn)就派上用場(chǎng)了。你可以配置DMA讓ADC每轉(zhuǎn)換完一個(gè)通道的數(shù)據(jù)就自動(dòng)通過(guò)DMA總線(xiàn)將數(shù)據(jù)搬運(yùn)到指定的SRAM數(shù)組中。整個(gè)過(guò)程無(wú)需CPU干預(yù)極大地提高了效率。對(duì)于需要高速、連續(xù)采樣的應(yīng)用如音頻信號(hào)、振動(dòng)信號(hào)分析ADCDMA是標(biāo)準(zhǔn)配置。一個(gè)典型配置流程在CubeMX中使能ADC設(shè)置分辨率、掃描模式、連續(xù)轉(zhuǎn)換、DMA連續(xù)請(qǐng)求。配置ADC通道的采樣時(shí)間根據(jù)信號(hào)源阻抗計(jì)算。配置DMA模式設(shè)為循環(huán)模式外設(shè)地址是ADC數(shù)據(jù)寄存器內(nèi)存地址是自定義的數(shù)組數(shù)據(jù)寬度為半字16位。啟動(dòng)ADC和DMA。之后目標(biāo)數(shù)組里就會(huì)自動(dòng)被ADC數(shù)據(jù)填滿(mǎn)CPU只需在需要時(shí)去讀取這個(gè)數(shù)組即可。常見(jiàn)問(wèn)題排查如果ADC采樣值跳動(dòng)大首先檢查硬件參考電壓是否穩(wěn)定模擬輸入引腳是否配置為模擬輸入模式輸入信號(hào)是否加了濾波電容軟件上檢查采樣時(shí)間是否足夠可以嘗試增大采樣時(shí)間看是否改善。對(duì)于工頻干擾50Hz可以嘗試在軟件上對(duì)多次采樣結(jié)果取平均。6. 效率倍增器DMA機(jī)制精講6.1 DMA是什么為什么需要它想象一下你CPU正在寫(xiě)一份重要報(bào)告但需要不斷起身去門(mén)口外設(shè)取快遞數(shù)據(jù)每次取快遞都要中斷手頭工作效率極低。DMA就像一個(gè)專(zhuān)職的快遞分揀員你只需要告訴他“把門(mén)口A(yíng)DC的快遞搬到客廳桌子內(nèi)存上搬滿(mǎn)100個(gè)為止。” 之后你就可以專(zhuān)心寫(xiě)報(bào)告分揀員會(huì)默默完成所有搬運(yùn)工作并在完成后通知你一聲。DMA的本質(zhì)是一個(gè)智能的數(shù)據(jù)搬運(yùn)工它可以在外設(shè)如ADC、USART、SPI和內(nèi)存SRAM之間或者內(nèi)存與內(nèi)存之間直接搬運(yùn)數(shù)據(jù)完全繞過(guò)CPU。它的優(yōu)勢(shì)在于解放CPUCPU可以從繁瑣的重復(fù)性數(shù)據(jù)搬運(yùn)中解脫出來(lái)去處理更復(fù)雜的邏輯和運(yùn)算。提高系統(tǒng)吞吐量DMA使用專(zhuān)用總線(xiàn)可以與CPU并行工作尤其適合大數(shù)據(jù)量、高帶寬的應(yīng)用。6.2 DMA控制器結(jié)構(gòu)與工作流程以STM32F1為例它有兩個(gè)DMA控制器DMA1, DMA2每個(gè)控制器有多個(gè)通道Stream。每個(gè)通道可以配置為服務(wù)于一個(gè)特定的外設(shè)請(qǐng)求。核心概念源地址數(shù)據(jù)從哪里來(lái)如 ADC1-DR。目的地址數(shù)據(jù)到哪里去如 uint16_t adc_buffer[100]。數(shù)據(jù)寬度每次傳輸?shù)臄?shù)據(jù)單位字節(jié)、半字、字。傳輸計(jì)數(shù)器一共要傳輸多少次。傳輸模式單次模式傳輸計(jì)數(shù)器減到0后停止需要重新使能才能再次傳輸。循環(huán)模式傳輸計(jì)數(shù)器減到0后自動(dòng)重裝初始值周而復(fù)始。這是ADC連續(xù)采樣的常用模式。指針遞增設(shè)定每次傳輸后源地址和/或目的地址是否自動(dòng)遞增。內(nèi)存到內(nèi)存的拷貝通常都需要遞增外設(shè)寄存器地址通常固定不變。配置DMA的典型步驟使能時(shí)鐘先開(kāi)啟DMA控制器的時(shí)鐘。配置通道參數(shù)選擇通道與外設(shè)綁定設(shè)置傳輸方向外設(shè)到內(nèi)存/內(nèi)存到外設(shè)/內(nèi)存到內(nèi)存設(shè)置數(shù)據(jù)寬度、地址是否遞增。配置傳輸數(shù)量與模式設(shè)置要傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)量選擇循環(huán)或單次模式。使能通道。使能外設(shè)的DMA請(qǐng)求例如使能USART的發(fā)送DMA請(qǐng)求或ADC的DMA請(qǐng)求。避坑指南DMA配置中最常見(jiàn)的錯(cuò)誤是內(nèi)存緩沖區(qū)溢出。在循環(huán)模式下如果CPU讀取數(shù)據(jù)的速度跟不上DMA寫(xiě)入的速度新數(shù)據(jù)就會(huì)覆蓋未讀的舊數(shù)據(jù)。解決方案通常是使用“雙緩沖區(qū)”或“環(huán)形緩沖區(qū)”準(zhǔn)備兩個(gè)一樣大的數(shù)組A和BDMA寫(xiě)滿(mǎn)A后產(chǎn)生中斷在中斷里切換DMA的目的地址到B同時(shí)CPU處理A中的數(shù)據(jù)。如此交替實(shí)現(xiàn)無(wú)鎖通信。7. 開(kāi)發(fā)環(huán)境搭建與調(diào)試實(shí)戰(zhàn)7.1 工具鏈選擇Keil、IAR與VSCodeKeil MDK-ARM國(guó)內(nèi)最主流集成度高調(diào)試方便對(duì)STM32支持極好。但它是商業(yè)軟件正版價(jià)格昂貴。IAR Embedded Workbench另一款商業(yè)神器編譯優(yōu)化效率有時(shí)比Keil更高在行業(yè)中也廣泛應(yīng)用。VSCode ARM GCC免費(fèi)開(kāi)源方案的后起之秀。通過(guò)安裝Cortex-Debug、STM32 for VSCode等插件配合OpenOCD或ST-LINK GDB Server進(jìn)行調(diào)試可以打造一個(gè)高度定制化、現(xiàn)代化的開(kāi)發(fā)環(huán)境。雖然初始配置稍復(fù)雜但一旦配好體驗(yàn)非常流暢深受資深開(kāi)發(fā)者喜愛(ài)。個(gè)人體會(huì)初學(xué)者建議從Keil或STM32CubeIDEST官方基于Eclipse的免費(fèi)IDE開(kāi)始快速上手把精力集中在學(xué)習(xí)STM32本身。當(dāng)你有一定經(jīng)驗(yàn)后可以嘗試搭建VSCode環(huán)境它會(huì)讓你更理解編譯、鏈接、調(diào)試的底層過(guò)程對(duì)成長(zhǎng)更有幫助。7.2 調(diào)試?yán)鱏T-LINK與串口打印硬件調(diào)試器ST-LINK是ST官方的調(diào)試編程器價(jià)格親民功能強(qiáng)大。它通過(guò)SWD接口僅需SWDIO、SWCLK兩根線(xiàn)與芯片連接可以實(shí)現(xiàn)下載程序?qū)⒕幾g好的二進(jìn)制文件燒錄到芯片F(xiàn)lash。在線(xiàn)調(diào)試單步執(zhí)行、設(shè)置斷點(diǎn)、查看/修改變量、查看寄存器、查看外設(shè)狀態(tài)。性能分析一些高級(jí)功能可以查看函數(shù)調(diào)用時(shí)間。軟件調(diào)試法在沒(méi)有調(diào)試器或調(diào)試不方便的場(chǎng)合如產(chǎn)品已封裝串口打印是最簡(jiǎn)單有效的調(diào)試手段。通過(guò)USART外設(shè)將調(diào)試信息變量值、程序狀態(tài)發(fā)送到電腦的串口助手顯示。你甚至可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的命令行交互界面CLI用于動(dòng)態(tài)查詢(xún)或配置設(shè)備參數(shù)。一個(gè)高效的調(diào)試習(xí)慣在項(xiàng)目初期就規(guī)劃好一個(gè)穩(wěn)定的調(diào)試信息輸出框架。例如定義一個(gè)宏DEBUG_PRINTF在開(kāi)發(fā)階段將其映射到串口發(fā)送函數(shù)在發(fā)布階段將其定義為空。這樣既能方便調(diào)試又不會(huì)在最終產(chǎn)品中留下調(diào)試代碼。8. 項(xiàng)目進(jìn)階從理論到綜合實(shí)踐掌握了GPIO、ADC、DMA等獨(dú)立外設(shè)后真正的挑戰(zhàn)在于如何將它們有機(jī)結(jié)合起來(lái)解決實(shí)際問(wèn)題。這里以一個(gè)“基于DMA的ADC多通道高速采樣與實(shí)時(shí)波形顯示”項(xiàng)目為例串聯(lián)核心知識(shí)點(diǎn)。項(xiàng)目目標(biāo)同時(shí)采集兩路模擬信號(hào)如音頻信號(hào)通過(guò)DMA實(shí)時(shí)傳輸?shù)絻?nèi)存再通過(guò)串口以特定協(xié)議發(fā)送給上位機(jī)PC繪制波形圖。系統(tǒng)設(shè)計(jì)時(shí)鐘與引腳配置使用CubeMX配置系統(tǒng)時(shí)鐘為72MHz配置ADC1的通道1和通道2為模擬輸入并使能掃描模式。配置一個(gè)USART用于與PC通信。ADC與DMA配置ADC設(shè)置為12位分辨率掃描模式連續(xù)轉(zhuǎn)換模式。為兩個(gè)通道設(shè)置合適的采樣時(shí)間例如239.5個(gè)周期確保對(duì)信號(hào)源充分采樣。啟用ADC的DMA請(qǐng)求。配置DMA1的通道1方向?yàn)橥庠O(shè)到內(nèi)存外設(shè)地址為ADC數(shù)據(jù)寄存器不遞增內(nèi)存地址為一個(gè)雙緩沖區(qū)遞增數(shù)據(jù)寬度半字模式為循環(huán)模式。雙緩沖區(qū)實(shí)現(xiàn)定義兩個(gè)數(shù)組BufferA[1000]和BufferB[1000]。每個(gè)數(shù)組交替存放通道1和通道2的數(shù)據(jù)即[Ch1, Ch2, Ch1, Ch2...]。初始讓DMA搬運(yùn)數(shù)據(jù)到BufferA。配置DMA傳輸完成中斷半傳輸完成和全傳輸完成中斷。在半傳輸完成中斷DMA搬了500個(gè)數(shù)據(jù)到BufferA前半部分中將DMA的目的地址切換到BufferB并通知主循環(huán)處理BufferA的前半部分?jǐn)?shù)據(jù)。在全傳輸完成中斷DMA搬了500個(gè)數(shù)據(jù)到BufferB中將DMA的目的地址切回BufferA并通知主循環(huán)處理BufferB的數(shù)據(jù)。如此數(shù)據(jù)處理打包、通過(guò)串口發(fā)送和ADC采樣搬運(yùn)在時(shí)間上完全并行互不等待實(shí)現(xiàn)了高效流水線(xiàn)。數(shù)據(jù)協(xié)議與上位機(jī)設(shè)計(jì)一個(gè)簡(jiǎn)單的幀頭數(shù)據(jù)長(zhǎng)度數(shù)據(jù)校驗(yàn)和的串口通信協(xié)議。上位機(jī)可以用Python的PyQtGraph或LabVIEW編寫(xiě)解析數(shù)據(jù)包將兩個(gè)通道的數(shù)據(jù)分離并實(shí)時(shí)繪制成兩條波形。可能遇到的坑與解決HardFault錯(cuò)誤在配置串口DMA時(shí)如果內(nèi)存緩沖區(qū)地址未對(duì)齊比如不是4字節(jié)對(duì)齊或者DMA訪(fǎng)問(wèn)了非法內(nèi)存區(qū)域可能引發(fā)硬件錯(cuò)誤。確保緩沖區(qū)地址是4字節(jié)對(duì)齊的可以使用__attribute__((aligned(4)))修飾數(shù)組。數(shù)據(jù)錯(cuò)位由于兩個(gè)通道的數(shù)據(jù)是交錯(cuò)存儲(chǔ)的在數(shù)據(jù)處理和解包時(shí)索引計(jì)算必須非常小心否則會(huì)導(dǎo)致通道數(shù)據(jù)混淆。清晰的代碼注釋和測(cè)試先發(fā)送固定模式的數(shù)據(jù)至關(guān)重要。通信速率瓶頸ADC采樣率很高時(shí)串口波特率可能成為瓶頸。需要計(jì)算數(shù)據(jù)量2通道 * 1000點(diǎn)/秒 * 2字節(jié)/點(diǎn) 4000字節(jié)/秒。選擇115200波特率約11.5KB/s是足夠的。如果數(shù)據(jù)量更大可以考慮提高波特率或使用USB虛擬串口CDC等更高帶寬的通信方式。通過(guò)這樣一個(gè)綜合項(xiàng)目你將深刻體會(huì)到時(shí)鐘配置是基礎(chǔ)GPIO配置是前提ADC是數(shù)據(jù)來(lái)源DMA是效率保障而串口是數(shù)據(jù)出口。它們環(huán)環(huán)相扣任何一個(gè)環(huán)節(jié)配置不當(dāng)整個(gè)系統(tǒng)都無(wú)法正常工作。這種從理論到系統(tǒng)集成的能力正是嵌入式工程師的核心價(jià)值所在。