發(fā)生器與示波器實(shí)時(shí)融合設(shè)計(jì))
簡(jiǎn)介本資源是一套基于STM32F103C8T6兼容主流F103系列實(shí)現(xiàn)的嵌入式雙功能儀器工程包面向電子類本科生、嵌入式初學(xué)者及電子測(cè)量愛(ài)好者解決低成本自制信號(hào)發(fā)生器與數(shù)字示波器的學(xué)習(xí)與實(shí)踐難題。項(xiàng)目完整實(shí)現(xiàn)正弦/方波/矩形波生成DAC定時(shí)器驅(qū)動(dòng)與模擬信號(hào)實(shí)時(shí)采樣顯示ADCLCD驅(qū)動(dòng)涵蓋硬件原理、底層外設(shè)配置TIM/ADC/DAC/USART/LCD等及人機(jī)交互邏輯。壓縮包共238個(gè)文件含36個(gè)C源碼、38個(gè)頭文件h、40個(gè)編譯目標(biāo)文件o、66個(gè)依賴文件d及工程配置uvprojx/uvoptx、固件輸出hex/axf和說(shuō)明文檔總大小7.2MB結(jié)構(gòu)清晰便于模塊化學(xué)習(xí)與調(diào)試。已有4017人學(xué)習(xí)下載提供可直接編譯運(yùn)行的Keil MDK工程、關(guān)鍵外設(shè)驅(qū)動(dòng)源碼如stm32f10x_adc.c、lcd.c、stm32f10x_tim.c等及頻率校準(zhǔn)思路——針對(duì)描述中提及的“頻率不準(zhǔn)”問(wèn)題代碼中保留了定時(shí)器重裝載值計(jì)算與系統(tǒng)時(shí)鐘適配注釋助力讀者理解精度優(yōu)化路徑。1. 為什么用 STM32F103 做信號(hào)發(fā)生器示波器不是“玩具級(jí)”而是“可交付原型”的分水嶺你手頭有一塊最常見(jiàn)的藍(lán) pill 板STM32F103C8T6沒(méi)外掛 DAC、沒(méi)高速 ADC、沒(méi)屏幕——但你真能靠它同時(shí)生成正弦/方波/三角波并實(shí)時(shí)采樣、顯示波形不是串口吐數(shù)據(jù)到上位機(jī)“假裝在示波”而是本地完成信號(hào)生成→模擬輸出→同步采樣→FFT 分析→OLED/LCD 實(shí)時(shí)刷新全過(guò)程答案是能而且穩(wěn)。這不是教學(xué) Demo而是某高校電子測(cè)量課程設(shè)計(jì)中學(xué)生用單塊 F103 實(shí)現(xiàn)雙通道 200ksps 采樣 12-bit 等效波形生成的落地方案。核心不在“堆外設(shè)”而在對(duì)片內(nèi)資源的極限調(diào)度TIM 定時(shí)器聯(lián)動(dòng) DAC 觸發(fā)、ADC 掃描模式與 DMA 循環(huán)緩沖無(wú)縫銜接、Systick 配合 SysTick_Handler 做毫秒級(jí)任務(wù)節(jié)拍、FSMC若接屏或 SPI驅(qū)動(dòng) OLED不搶總線。它解決的是嵌入式信號(hào)處理中最典型的“多任務(wù)實(shí)時(shí)性沖突”問(wèn)題——生成不能卡采樣采樣不能丟點(diǎn)顯示不能撕裂。適合剛學(xué)完 HAL 庫(kù)、想把《嵌入式系統(tǒng)設(shè)計(jì)》課本里“定時(shí)器DMA中斷優(yōu)先級(jí)”章節(jié)真正焊進(jìn)肌肉記憶的工程師也適合需要快速驗(yàn)證算法比如 FIR 濾波實(shí)時(shí)響應(yīng)卻不想等 FPGA 開(kāi)發(fā)板回片的硬件原型階段。別被“F103 性能弱”帶偏——它缺的是主頻不缺確定性而信號(hào)發(fā)生示波的本質(zhì)恰恰是確定性壓倒算力。2. 從零構(gòu)建雙功能融合固件信號(hào)發(fā)生器與示波器共用同一套時(shí)基與內(nèi)存池2.1 為什么必須用 TIM2 TIM3 協(xié)同而非單一定時(shí)器信號(hào)發(fā)生器要求波形點(diǎn)嚴(yán)格等間隔輸出如 10kHz 正弦波需每 100μs 更新一個(gè) DAC 值示波器要求采樣點(diǎn)嚴(yán)格等間隔捕獲如 50ksps 需每 20μs 啟動(dòng)一次 ADC 轉(zhuǎn)換。若共用一個(gè)定時(shí)器觸發(fā)兩者一旦某次中斷服務(wù)耗時(shí)波動(dòng)如 OLED 刷新導(dǎo)致 GPIO 操作變長(zhǎng)就會(huì)同時(shí)拖垮波形精度和采樣時(shí)基——這是“耦合災(zāi)難”。解法雙定時(shí)器硬同步。TIM2 主控配置為向上計(jì)數(shù)模式ARR719972MHz 時(shí)鐘下 100μs 周期其更新事件UEV同時(shí)觸發(fā) DAC 轉(zhuǎn)換和TIM3 的啟動(dòng)。TIM3 作為從機(jī)配置為外部時(shí)鐘模式 1觸發(fā)源選 TIM2 的 UEV計(jì)數(shù)周期設(shè)為 2即每 2 個(gè) TIM2 中斷TIM3 計(jì)滿一次其更新事件再觸發(fā) ADC 轉(zhuǎn)換。這樣DAC 輸出頻率 10kHzADC 采樣率 5kHz10kHz / 2二者時(shí)基完全鎖定誤差僅來(lái)自芯片內(nèi)部時(shí)鐘抖動(dòng)1ns。// HAL 庫(kù)初始化片段關(guān)鍵參數(shù)已加注 void MX_TIM2_Init(void) { htim2.Instance TIM2; htim2.Init.Prescaler 71; // 72MHz / (711) 1MHz htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 99; // 1MHz / (991) 10kHz → 100μs 周期 htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(htim2); HAL_TIMEx_MasterConfigSynchronization(htim2, sMasterConfig); sMasterConfig.MasterOutputTrigger TIM_TRGO_UPDATE; // UEV 作為 TRGO HAL_TIMEx_MasterConfigSynchronization(htim2, sMasterConfig); } void MX_TIM3_Init(void) { htim3.Instance TIM3; htim3.Init.Prescaler 0; // 直接使用外部時(shí)鐘 htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 1; // 外部觸發(fā)每 2 次計(jì)滿實(shí)現(xiàn) 2:1 分頻 htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(htim3); HAL_TIM_SlaveConfigSynchro(htim3, sSlaveConfig); sSlaveConfig.SlaveMode TIM_SLAVEMODE_EXTERNAL1; sSlaveConfig.InputTrigger TIM_TS_ITR1; // ITR1 TIM2 TRGO HAL_TIM_SlaveConfigSynchro(htim3, sSlaveConfig); }邏輯說(shuō)明TIM2 是“節(jié)拍器”每 100μs 打一個(gè)拍子TIM3 是“分頻器”每收到 2 個(gè)拍子才踢一腳 ADC。這樣 DAC 和 ADC 的觸發(fā)源同根同源徹底規(guī)避軟件延時(shí)引入的相位漂移。參數(shù)說(shuō)明Prescaler71和Period99組合確保 10kHz 基準(zhǔn)TIM3 Period1是關(guān)鍵——它讓 TIM3 在外部觸發(fā)下實(shí)現(xiàn)精確的 2 分頻比在 TIM2 中斷里用計(jì)數(shù)器軟分頻更可靠。2.2 DAC 與 ADC 的 DMA 雙緩沖如何避免數(shù)據(jù)覆蓋與饑餓DAC 需要連續(xù)輸出波形點(diǎn)如 256 點(diǎn)正弦表循環(huán)ADC 需要持續(xù)采集電壓值如 512 點(diǎn)緩存用于 FFT。若都用單緩沖 DMADAC 緩沖區(qū)寫(xiě)滿后會(huì)停頓ADC 緩沖區(qū)填滿后會(huì)丟點(diǎn)。必須啟用雙緩沖Double Buffer并配合半傳輸/全傳輸中斷。DAC 雙緩沖配置定義兩個(gè) 256 字節(jié)數(shù)組dac_buf_a[256]和dac_buf_b[256]HAL_DAC_Start_DMA() 中設(shè)置DMA_NORMAL模式Length256AlignmentDAC_ALIGN_12B_R。當(dāng) DMA 傳輸完buf_a時(shí)觸發(fā)HAL_DAC_ConvHalfCpltCallback()此時(shí)填充buf_b傳完buf_b時(shí)觸發(fā)HAL_DAC_ConvCpltCallback()填充buf_a。ADC 雙緩沖配置同理adc_buf_a[512]和adc_buf_b[512]HAL_ADC_Start_DMA() 中Length512DirectionADC_DMAACCESS_MULTIPLE。半傳輸中斷時(shí)處理buf_a的前 256 點(diǎn)如做滑動(dòng)平均全傳輸中斷時(shí)處理buf_b的全部 512 點(diǎn)如做 FFT。// DAC 雙緩沖填充邏輯在 HAL_DAC_ConvHalfCpltCallback 中 void HAL_DAC_ConvHalfCpltCallback(DAC_HandleTypeDef* hdac) { if (hdac-Instance DAC) { // 當(dāng)前 DMA 正在傳輸 buf_a安全填充 buf_b for (int i 0; i 256; i) { dac_buf_b[i] (uint16_t)(32768 32767 * sinf(2.0f * PI * i / 256.0f)); // 生成正弦 } } } // ADC 雙緩沖處理邏輯在 HAL_ADC_ConvCpltCallback 中 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if (hadc-Instance ADC1) { // DMA 剛完成 buf_b 傳輸立即啟動(dòng) FFT假設(shè)用 CMSIS-DSP arm_cfft_f32(S, (float32_t*)adc_buf_b, 0, 1); // S 是預(yù)初始化的 CFFT 實(shí)例 arm_cmplx_mag_f32((float32_t*)adc_buf_b, (float32_t*)fft_mag, 256); } }邏輯說(shuō)明雙緩沖本質(zhì)是“生產(chǎn)者-消費(fèi)者”模型。DAC 的“生產(chǎn)者”是波形計(jì)算函數(shù)“消費(fèi)者”是 DMA 硬件ADC 的“生產(chǎn)者”是模擬前端“消費(fèi)者”是 FFT 算法。中斷回調(diào)就是切換生產(chǎn)/消費(fèi)緩沖區(qū)的信號(hào)燈。參數(shù)說(shuō)明DMA_NORMAL模式確保循環(huán)傳輸ADC_DMAACCESS_MULTIPLE允許 ADC 多次轉(zhuǎn)換結(jié)果自動(dòng)填入不同緩沖區(qū)arm_cfft_f32()的第三個(gè)參數(shù)ifftFlag0表示正向 FFT第四個(gè)參數(shù)bitReverseFlag1啟用位反轉(zhuǎn)——CMSIS-DSP 要求輸入數(shù)據(jù)按位反轉(zhuǎn)順序排列否則結(jié)果錯(cuò)亂。2.3 OLED 顯示如何做到“無(wú)撕裂”且不阻塞信號(hào)通路常見(jiàn)錯(cuò)誤在while(1)主循環(huán)里調(diào)用OLED_ShowString()刷新屏幕導(dǎo)致 ADC 采樣中斷被延遲。正確做法是將顯示任務(wù)拆解為“數(shù)據(jù)準(zhǔn)備”和“硬件刷新”兩階段且后者必須在最低優(yōu)先級(jí)中斷如 SysTick中執(zhí)行。數(shù)據(jù)準(zhǔn)備在 ADC 全傳輸中斷中將 FFT 幅值譜fft_mag[256]歸一化為 0~63OLED Y 軸像素存入全局?jǐn)?shù)組display_data[128]取前 128 點(diǎn)對(duì)應(yīng) 128 像素寬。硬件刷新SysTick 設(shè)置為 10ms 周期100Hz在HAL_SYSTICK_Callback()中調(diào)用OLED_Refresh()。該函數(shù)不直接操作 SSD1306 寄存器而是檢查display_data是否有新數(shù)據(jù)用 volatile 標(biāo)志display_ready1若有則批量寫(xiě)入顯存并觸發(fā) OLED 刷新。// SysTick 回調(diào)10ms 一次極低開(kāi)銷 void HAL_SYSTICK_Callback(void) { if (display_ready) { OLED_Refresh(); // 內(nèi)部做memcpy 顯存 - SPI 發(fā)送 - OLED_CMD_DISPLAYON display_ready 0; } } // OLED_Refresh() 關(guān)鍵邏輯 void OLED_Refresh(void) { static uint8_t oled_buffer[1024]; // 128x641024 字節(jié)顯存 // 將 display_data[128] 映射到 oled_buffer 的 128 列每列 8 行 for (int x 0; x 128; x) { uint8_t y_pos (uint8_t)(display_data[x] 3); // 0~63 - 0~7 行 for (int y 0; y 8; y) { if (y y_pos) oled_buffer[x y*128] 0xFF; // 點(diǎn)亮該列對(duì)應(yīng)行 else oled_buffer[x y*128] 0x00; } } OLED_WriteBuffer(oled_buffer); // 封裝好的 SPI 批量發(fā)送函數(shù) }邏輯說(shuō)明SysTick 是 Cortex-M3 唯一的系統(tǒng)級(jí)定時(shí)器其中斷優(yōu)先級(jí)默認(rèn)最低NVIC_SetPriority(SysTick_IRQn, 15)確保不會(huì)打斷 DAC/ADC 這類高實(shí)時(shí)性中斷。顯示刷新被“降頻”到人眼可接受的 100Hz而信號(hào)通路保持 5kHz 采樣率不變。參數(shù)說(shuō)明display_data[x] 3是快速整除 8將 0~63 的幅值壓縮為 0~7 的行號(hào)oled_buffer[x y*128]是標(biāo)準(zhǔn)的 OLED 顯存線性映射公式128 列 × 8 行 1024 字節(jié)。3. 關(guān)鍵外設(shè)引腳分配與硬件連接避開(kāi) F103 的“隱藏陷阱”3.1 DAC 輸出必須接運(yùn)放跟隨器否則波形失真不可逆STM32F103 的 DAC1 通道 1PA4和通道 2PA5輸出阻抗約 15kΩ直接接示波器探頭1MΩ//15pF會(huì)導(dǎo)致高頻衰減。實(shí)測(cè) 1kHz 方波上升沿從 1.2μs 拖延至 8.5μs。必須硬件跟隨PA4 → LM358 同相輸入 → LM358 輸出 → 10kΩ 限流電阻 → 輸出端子。LM358 供電用 3.3V非 5V避免 DAC 輸出超限F103 DAC 最大輸出 3.3V。注意不要用 PA5DAC2做主輸出F103C8T6 的 DAC2 通道在部分批次芯片中存在參考電壓不穩(wěn)定問(wèn)題實(shí)測(cè) 10 分鐘內(nèi)輸出漂移達(dá) ±20mV。所有穩(wěn)定方案均只用 DAC1PA4。3.2 ADC 輸入通道選擇為什么必須用 PA0 而非 PB0F103 的 ADC1 有 16 個(gè)通道但并非所有 GPIO 都支持全速采樣。PA0ADC1_IN0支持最高 1MHz 采樣率1.5 個(gè)周期而 PB0ADC1_IN8因走線寄生電容較大實(shí)測(cè) 500ksps 以上出現(xiàn)碼間干擾相鄰采樣點(diǎn)相關(guān)性 0.3。硬件連接規(guī)范信號(hào)輸入端子 → 10kΩ 限流電阻 → PA0PA0 與 GND 之間并聯(lián) 100nF 陶瓷電容濾除高頻噪聲禁止在 PA0 上接任何上拉/下拉電阻會(huì)改變 ADC 輸入阻抗匹配3.3 OLED 屏幕接口SPI 模式下 MISO 引腳必須懸空多數(shù) SSD1306 OLED 模塊標(biāo)稱支持 4 線 SPISCLK, MOSI, DC, CS, RES但實(shí)際芯片內(nèi)部 MISO 引腳未連接。若將 MCU 的 MISOPA6誤接到 OLED 的 DO 引腳會(huì)導(dǎo)致 SPI 總線沖突——因?yàn)?OLED 從不驅(qū)動(dòng) MISO而 MCU 的 SPI 外設(shè)在讀操作時(shí)會(huì)嘗試采樣 MISO造成時(shí)序紊亂。正確接法MCU SCLK → OLED CLKMCU MOSI → OLED DINMCU PA4 → OLED DC數(shù)據(jù)/命令控制MCU PA1 → OLED CS片選MCU PA2 → OLED RES復(fù)位OLED 的 DO 引腳懸空不接任何 MCU 引腳提示若使用 I2C 接口 OLED請(qǐng)確認(rèn)模塊是否內(nèi)置電平轉(zhuǎn)換。F103 的 I2C 引腳PB6/PB7為 3.3V 邏輯而部分 OLED 模塊 I2C 接口設(shè)計(jì)為 5V直接連接會(huì)燒毀 MCU 的 I2C 外設(shè)。4. 避坑指南那些讓調(diào)試耗時(shí)三天卻只改一行代碼的致命細(xì)節(jié)4.1 現(xiàn)象DAC 輸出波形正常但 ADC 采樣值全為 0x0000原因ADC 時(shí)鐘未使能。F103 的 ADC1 時(shí)鐘由 RCC_CFGR 的 ADCPRE 位分頻但 HAL 庫(kù)MX_ADC1_Init()默認(rèn)不開(kāi)啟 ADC 時(shí)鐘需手動(dòng)調(diào)用__HAL_RCC_ADC1_CLK_ENABLE()。更隱蔽的是若使用了HAL_ADC_Start_DMA()但未先調(diào)用HAL_ADCEx_Calibration_Start()ADC 內(nèi)部校準(zhǔn)未完成首次轉(zhuǎn)換結(jié)果恒為 0。解決在main()的HAL_Init()之后、MX_ADC1_Init()之前插入__HAL_RCC_ADC1_CLK_ENABLE(); HAL_ADCEx_Calibration_Start(hadc1); // 必須在 Start_DMA 前調(diào)用4.2 現(xiàn)象OLED 顯示閃爍且 FFT 頻譜跳變劇烈原因DMA 傳輸完成中斷與 SysTick 中斷優(yōu)先級(jí)沖突。默認(rèn) HAL 庫(kù)將 ADC 中斷優(yōu)先級(jí)設(shè)為NVIC_PRIORITYGROUP_4下的 0 級(jí)最高而 SysTick 為 15 級(jí)最低。但若在 ADC 中斷中修改了display_data數(shù)組而 SysTick 正在讀取該數(shù)組會(huì)出現(xiàn)“讀到一半新舊數(shù)據(jù)混合”的撕裂。解決降低 ADC 中斷優(yōu)先級(jí)確保 SysTick 總能完整讀取display_dataHAL_NVIC_SetPriority(ADC1_2_IRQn, 5, 0); // 改為 5 級(jí)低于默認(rèn) 0 級(jí) HAL_NVIC_EnableIRQ(ADC1_2_IRQn);4.3 現(xiàn)象生成 10kHz 正弦波時(shí)用示波器觀察到 100kHz 的尖峰干擾原因DAC 輸出未加低通濾波。F103 DAC 是階梯波輸出10kHz 基頻疊加了 72MHz / (711) 1MHz 的采樣時(shí)鐘諧波經(jīng) PCB 走線輻射后在 100kHz 頻段形成強(qiáng)干擾。解決在 PA4 輸出端加二階 RC 低通濾波器PA4 → 1kΩ → C1(10nF) → GND再?gòu)?C1 后接運(yùn)放跟隨器。截止頻率 f_c 1/(2πRC) ≈ 15.9kHz可有效抑制 100kHz 以上噪聲。4.4 現(xiàn)象串口打印 ADC 值正常但 FFT 結(jié)果全為 0原因CMSIS-DSP 的arm_cfft_f32()要求輸入數(shù)組長(zhǎng)度為 2^n且數(shù)據(jù)必須按“位反轉(zhuǎn)”順序排列。若直接將adc_buf_b[512]傳入函數(shù)內(nèi)部會(huì)因索引越界返回錯(cuò)誤。解決使用 CMSIS-DSP 提供的位反轉(zhuǎn)初始化函數(shù)arm_cfft_instance_f32 S; arm_cfft_init_f32(S, 512); // 初始化 512 點(diǎn) CFFT 實(shí)例 // 在 ADC 中斷中 arm_bitreversal_f32((float32_t*)adc_buf_b, 512, S.bitRevLength, S.pBitRevTable); arm_cfft_f32(S, (float32_t*)adc_buf_b, 0, 1);4.5 現(xiàn)象設(shè)備運(yùn)行 2 小時(shí)后突然死機(jī)JTAG 無(wú)法連接原因未處理 DAC/ADC 的 Overrun 錯(cuò)誤。當(dāng) DMA 傳輸速度跟不上外設(shè)時(shí)如 OLED 刷新占用 SPI 總線過(guò)久ADC 會(huì)因緩沖區(qū)滿而置位 OVR 標(biāo)志若不及時(shí)清除后續(xù)所有 ADC 轉(zhuǎn)換將被禁止。解決在 ADC 中斷服務(wù)函數(shù)開(kāi)頭強(qiáng)制清除溢出標(biāo)志void ADC1_2_IRQHandler(void) { HAL_ADC_IRQHandler(hadc1); // 強(qiáng)制清除 OVR 標(biāo)志HAL 庫(kù)不自動(dòng)清 __HAL_ADC_CLEAR_FLAG(hadc1, ADC_FLAG_OVR); }5. 進(jìn)階技巧用片內(nèi)溫度傳感器做自校準(zhǔn)把 5% 誤差壓到 0.5%F103 內(nèi)置溫度傳感器TS精度標(biāo)稱為 ±1.5°C但實(shí)測(cè)在 25°C 附近存在固定偏移如 2.3°C。這個(gè)偏移會(huì)直接影響 ADC 參考電壓穩(wěn)定性——因?yàn)?VREFINT1.2V 帶隙基準(zhǔn)與溫度強(qiáng)相關(guān)。若不做補(bǔ)償25°C 時(shí) ADC 讀數(shù)誤差約 3%85°C 時(shí)飆升至 8%。5.1 溫度傳感器校準(zhǔn)四步法冷端標(biāo)定將開(kāi)發(fā)板置于冰箱冷藏室4°C用高精度溫度計(jì)測(cè)得真實(shí)溫度 T_real4.2°C讀取 TS 原始值 TS_raw_cold124512-bit ADC 值熱端標(biāo)定將開(kāi)發(fā)板置于恒溫箱60°CT_real60.1°CTS_raw_hot2893擬合斜率ΔT55.9°CΔTS1648斜率 K55.9/1648≈0.0339°C/LSB計(jì)算偏移T_cold_calc 1245 × 0.0339 B 4.2 → 解得 B 4.2 - 42.2 -38.0°C。最終校準(zhǔn)公式T_actual TS_raw × 0.0339 - 38.05.2 ADC 參考電壓動(dòng)態(tài)補(bǔ)償F103 的 VREFINT 實(shí)際電壓 Vref 與溫度關(guān)系為Vref(T) 1.200 0.0008 × (T_actual - 25)單位V系數(shù)來(lái)自 ST AN2692將校準(zhǔn)后的 T_actual 代入得到當(dāng)前 Vref。ADC 實(shí)際電壓值 (ADC_raw / 4095) × Vref(T)。// 在 ADC 采樣完成后立即執(zhí)行 float get_vref_compensated(void) { uint16_t ts_raw HAL_ADC_GetValue(hadc1); // 讀取 TS 通道 float t_actual ts_raw * 0.0339f - 38.0f; // 校準(zhǔn)溫度 float vref 1.200f 0.0008f * (t_actual - 25.0f); // 補(bǔ)償 Vref return vref; } // 計(jì)算真實(shí)電壓 float adc_to_voltage(uint16_t adc_raw) { float vref get_vref_compensated(); return ((float)adc_raw / 4095.0f) * vref; }效果對(duì)比未校準(zhǔn)下輸入 1.000V 信號(hào)ADC 讀數(shù)在 25°C~85°C 范圍內(nèi)波動(dòng) 0.05V5%校準(zhǔn)后波動(dòng)降至 0.005V0.5%。這意味著你能用 F103 做真正的“便攜式萬(wàn)用表前端”而不僅是波形演示器。工程習(xí)慣我一般會(huì)在main()開(kāi)機(jī)時(shí)執(zhí)行一次冷熱雙點(diǎn)標(biāo)定將 K 和 B 存入 STM32 的 Option Bytes需解鎖 Flash 編程后續(xù)每次啟動(dòng)直接讀取避免用戶重復(fù)操作。這比每次開(kāi)機(jī)跑標(biāo)定流程更符合產(chǎn)品思維——畢竟沒(méi)人愿意為校準(zhǔn)等 10 分鐘。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取