習(xí)日志:PWM 驅(qū)動(dòng)直流電機(jī)、DWT 精確延時(shí)、DHT11 單總線通信與 Keil 工程模板)
STM32 學(xué)習(xí)日志PWM 驅(qū)動(dòng)直流電機(jī)、DWT 精確延時(shí)、DHT11 單總線通信與 Keil 工程模板學(xué)習(xí)日期2026 年 8 月 19 日今天主要學(xué)習(xí)了 PWM 控制 TB6612FNG 有刷直流電機(jī)、DWT 微秒延時(shí)、DHT11 溫濕度傳感器的單總線通信以及手動(dòng)建立 Keil 工程模板的方法。一、PWM 基礎(chǔ)知識(shí)PWM 即脈寬調(diào)制通過控制一個(gè)方波中“高電平持續(xù)時(shí)間”的比例來控制平均輸出效果。對(duì)于 STM32 定時(shí)器PWM 頻率 定時(shí)器計(jì)數(shù)時(shí)鐘 / [(PSC 1) × (ARR 1)] 占空比 CCR / (ARR 1) 占空比分辨率 1 / (ARR 1)其中PSC預(yù)分頻器決定定時(shí)器計(jì)數(shù)速度。 ARR自動(dòng)重裝載值決定一個(gè) PWM 周期需要計(jì)多少次。 CCR比較寄存器決定高電平持續(xù)到第幾次計(jì)數(shù)。 CNT定時(shí)器當(dāng)前正在計(jì)數(shù)的值。可以簡單記憶PSC 控制計(jì)數(shù)速度。 ARR 決定 PWM 周期。 CCR 決定高電平時(shí)間也就是占空比。例如PSC 71 ARR 99 定時(shí)器時(shí)鐘 72MHz則PWM 頻率 72MHz / 72 / 100 10kHz如果CCR 20占空比約為20 / 100 20%如果CCR 80占空比約為80 / 100 80%注意代碼中直接寫20、80作為 PWM 速度值的前提是ARR 99。如果 ARR 不是 99CCR 的范圍也必須跟著改變。二、TB6612FNG 電機(jī)驅(qū)動(dòng)模塊STM32 的 GPIO 引腳只能輸出很小的電流不能直接驅(qū)動(dòng)直流電機(jī)。因此需要 TB6612FNG 這種電機(jī)驅(qū)動(dòng)模塊。TB6612FNG 可以理解為電機(jī)的“功率放大器”STM32 負(fù)責(zé)發(fā)出控制命令。 TB6612FNG 負(fù)責(zé)使用 VM 電源給電機(jī)提供實(shí)際電流。本次電機(jī) 1 使用的核心連接如下STM32 引腳TB6612FNG 引腳作用PB0PWMAPWM 調(diào)速信號(hào)PA4AIN1電機(jī)方向控制PA5AIN2電機(jī)方向控制PA7STBY驅(qū)動(dòng)芯片使能KEY1用戶按鍵切換快慢KEY2用戶按鍵切換正反轉(zhuǎn)需要重點(diǎn)記住PWMA控制電機(jī)速度。 AIN1、AIN2控制電機(jī)方向。 STBY控制驅(qū)動(dòng)芯片是否工作。 AO1、AO2連接電機(jī)兩根線。 VM電機(jī)電源。 VCC驅(qū)動(dòng)芯片邏輯電源。 GNDSTM32、模塊、電機(jī)電源必須共地。三、為什么 PWM 極性通常設(shè)置為 HighTB6612FNG 的 PWMA/PWMB 是高電平有效。CubeMX 中設(shè)置CH Polarity High表示PWM 高電平驅(qū)動(dòng)器允許輸出。 PWM 低電平驅(qū)動(dòng)器停止輸出或進(jìn)入制動(dòng)狀態(tài)。這樣邏輯最直觀CCR 越大 → 高電平時(shí)間越長 → 電機(jī)獲得驅(qū)動(dòng)的時(shí)間越長 → 電機(jī)轉(zhuǎn)速越快注意PWM 極性不決定電機(jī)正反轉(zhuǎn)。PWM控制速度。 AIN1/AIN2控制方向。四、TB6612FNG 電機(jī)控制程序1. 先啟動(dòng) PWM再使能電機(jī)驅(qū)動(dòng)芯片在/* USER CODE BEGIN 2 */中加入HAL_TIM_PWM_Start(htim3,TIM_CHANNEL_3);HAL_GPIO_WritePin(STBY_GPIO_Port,STBY_Pin,GPIO_PIN_SET);含義第一句讓 TIM3_CH3 開始從 PB0 輸出 PWM。 第二句把 STBY 拉高讓 TB6612FNG 退出待機(jī)并開始工作。如果 STBY 沒有拉高即使 PWM 和方向引腳都正確電機(jī)也不會(huì)轉(zhuǎn)動(dòng)。2. 編寫電機(jī)方向與速度控制函數(shù)voidMotor_Set(uint8_tdir,uint16_tspeed){__HAL_TIM_SET_COMPARE(htim3,TIM_CHANNEL_3,speed);if(dir0){HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(AIN2_GPIO_Port,AIN2_Pin,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_RESET);HAL_GPIO_WritePin(AIN2_GPIO_Port,AIN2_Pin,GPIO_PIN_SET);}}這個(gè)函數(shù)有兩個(gè)參數(shù)dir方向。 speedPWM 比較值也就是 CCR 值。方向邏輯AIN1AIN2電機(jī)狀態(tài)高低正轉(zhuǎn)低高反轉(zhuǎn)如果實(shí)際電機(jī)方向和程序描述相反通常只是電機(jī)兩根線接反了。可以交換 AO1/AO2 的電機(jī)接線或者交換程序中正反轉(zhuǎn)邏輯。3. 定義速度和狀態(tài)變量#defineSPEED_LOW20#defineSPEED_HIGH80uint8_tspeed_mode0;uint8_tdir_mode0;含義SPEED_LOW低速 PWM 值。 SPEED_HIGH高速 PWM 值。 speed_mode 0低速。 speed_mode 1高速。 dir_mode 0正轉(zhuǎn)。 dir_mode 1反轉(zhuǎn)。4. 按鍵控制速度和方向if(HAL_GPIO_ReadPin(GPIOA,KEY1_Pin)GPIO_PIN_SET){speed_mode!speed_mode;uint16_tspeed(speed_mode0)?SPEED_LOW:SPEED_HIGH;Motor_Set(dir_mode,speed);while(HAL_GPIO_ReadPin(GPIOA,KEY1_Pin)GPIO_PIN_SET);}!speed_mode的作用是切換 0 和 10 變 1。 1 變 0。三目運(yùn)算符(speed_mode0)?SPEED_LOW:SPEED_HIGH;意思是如果當(dāng)前是低速模式speed 取 SPEED_LOW。 否則 speed 取 SPEED_HIGH。while等待松手的作用是防止按住按鍵時(shí)不斷切換狀態(tài)。實(shí)際使用時(shí)還應(yīng)加入約 10ms 到 20ms 的消抖延時(shí)否則機(jī)械按鍵可能一次按下被識(shí)別多次。五、DWT 精確延時(shí)DWT 是 Cortex-M 內(nèi)核自帶的高速計(jì)數(shù)器全稱為Data Watchpoint and Trace 數(shù)據(jù)觀察點(diǎn)與跟蹤單元初學(xué)階段最常用的是DWT_CYCCNTCPU 周期計(jì)數(shù)器。STM32F103 常用主頻為 72MHz1 秒 72,000,000 個(gè)時(shí)鐘周期。 1 微秒 72 個(gè)時(shí)鐘周期。因此 DWT 可以實(shí)現(xiàn)微秒級(jí)延時(shí)DWT_DelayUs(10);DWT_DelayMs(100);DWT_DelayS(1);DWT 初始化的核心邏輯DEMCR|(uint32_t)DEMCR_TRCENA;DWT_CYCCNT0;DWT_CTRL|(uint32_t)DWT_CTRL_CYCCNTENA;含義DEMCR_TRCENA打開內(nèi)核調(diào)試與跟蹤功能。 DWT_CYCCNT 0先將周期計(jì)數(shù)器清零。 DWT_CTRL_CYCCNTENA讓周期計(jì)數(shù)器開始計(jì)數(shù)。DWT 適合DHT11 單總線時(shí)序。 超聲波模塊微秒延時(shí)。 測(cè)量一段程序的運(yùn)行時(shí)間。 精確軟件延時(shí)。注意DWT 延時(shí)屬于忙等待延時(shí)期間 CPU 一直在循環(huán)等待不適合長時(shí)間延時(shí)。短延時(shí)DWT_DelayUs()。 較長延時(shí)HAL_Delay()、定時(shí)器中斷或非阻塞計(jì)時(shí)。六、DHT11 單總線通信流程DHT11 使用一根 DATA 數(shù)據(jù)線進(jìn)行雙向通信。空閑狀態(tài)下DATA 由上拉電阻保持高電平。完整流程1. DHT11 上電后等待至少 1 秒。 2. STM32 將 DATA 配置為輸出。 3. STM32 拉低 DATA 至少 18ms。 4. STM32 釋放 DATA并改為輸入。 5. DHT11 拉低約 83us表示收到請(qǐng)求。 6. DHT11 拉高約 87us表示準(zhǔn)備發(fā)送數(shù)據(jù)。 7. DHT11 發(fā)送 40 位數(shù)據(jù)。 8. STM32 讀取數(shù)據(jù)并校驗(yàn)。DHT11 一次發(fā)送 5 個(gè)字節(jié)濕度整數(shù) 濕度小數(shù) 溫度整數(shù) 溫度小數(shù) 校驗(yàn)和校驗(yàn)和計(jì)算校驗(yàn)和 濕度整數(shù) 濕度小數(shù) 溫度整數(shù) 溫度小數(shù)如果計(jì)算結(jié)果和接收到的第五個(gè)字節(jié)相同說明數(shù)據(jù)正確。DHT11 每一位數(shù)據(jù)的判斷方式先低電平約 50us。 高電平約 26us表示 0。 高電平約 70us表示 1。所以程序會(huì)在高電平開始后約 40us 判斷40us 后仍是高電平這一位是 1。 40us 后已經(jīng)變低這一位是 0。讀取 DHT11 時(shí)等待電平變化必須加入超時(shí)判斷。否則 DHT11 未接好或沒有供電時(shí)程序可能永久卡在while循環(huán)中。七、GPIO 配置結(jié)構(gòu)體的理解DHT11 中經(jīng)常需要讓一個(gè) GPIO 在輸出和輸入之間切換GPIO_InitTypeDef GPIO_InitStruct{0};這句含義是創(chuàng)建一個(gè) GPIO 配置變量。 并把結(jié)構(gòu)體中所有成員先初始化為 0。后續(xù)填寫GPIO_InitStruct.PinDHT11_DATA_Pin;GPIO_InitStruct.Modemode;GPIO_InitStruct.Pullpull;GPIO_InitStruct.SpeedGPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(DHT11_DATA_GPIO_Port,GPIO_InitStruct);可以理解為先準(zhǔn)備一張 GPIO 配置表。 填寫要配置哪個(gè)引腳、輸入還是輸出、是否上拉、輸出速度。 最后調(diào)用 HAL_GPIO_Init()將配置真正寫入 STM32。八、Keil 工程目錄作用手動(dòng)創(chuàng)建 STM32 工程時(shí)常見目錄如下文件夾作用Doc項(xiàng)目文檔、接線圖、模塊說明、readme 等LibrariesCMSIS 核心庫和 HAL 驅(qū)動(dòng)庫ProjectKeil 工程文件、編譯輸出文件等User用戶編寫的 main.c、外設(shè)驅(qū)動(dòng)、BSP 文件等常見 User 文件作用文件作用main.c主函數(shù)入口主要應(yīng)用邏輯main.hmain.c 對(duì)應(yīng)頭文件放函數(shù)聲明和全局定義stm32f1xx_hal_conf.hHAL 外設(shè)模塊開關(guān)配置stm32f1xx_it.c中斷服務(wù)函數(shù)實(shí)現(xiàn)stm32f1xx_it.h中斷函數(shù)聲明system_stm32f1xx.c系統(tǒng)初始化、時(shí)鐘變量更新九、手動(dòng)建立 Keil 工程模板步驟第一步建立 Doc、Libraries、Project、User 四個(gè)文件夾。 第二步復(fù)制 CMSIS、STM32F1xx_HAL_Driver 到 Libraries。 第三步復(fù)制 main.c、main.h、stm32f1xx_hal_conf.h、 stm32f1xx_it.c、stm32f1xx_it.h、system_stm32f1xx.c 到 User。 第四步在 Keil 中新建工程選擇正確的 STM32 芯片。 第五步建立分組 STARTUP、CMSIS、STM32F1xx_HAL_Driver、USER、DOC。 第六步分別把啟動(dòng)文件、system 文件、HAL 源文件、 User 源文件和文檔添加到對(duì)應(yīng)分組。 第七步在魔術(shù)棒中設(shè)置芯片、時(shí)鐘、輸出 HEX、 宏定義、頭文件路徑和下載調(diào)試器。 第八步編寫 main.c編譯驗(yàn)證。常用宏定義USE_HAL_DRIVER STM32F103xB十、新增模塊文件時(shí)的注意事項(xiàng)在已有工程中加入一個(gè)新模塊例如 DHT11、OLED、DWT 或電機(jī)模塊時(shí)1. 在 User 文件夾建立模塊目錄例如 dht11。 2. 放入該模塊的 .c 和 .h 文件。 3. 在 Keil 的 USER 分組中添加對(duì)應(yīng)的 .c 文件。 4. 在使用該模塊的源文件中包含對(duì)應(yīng) .h 文件。 5. 確認(rèn) HAL 配置文件已打開所需外設(shè)模塊。 6. 重新編譯查看錯(cuò)誤和警告。今天還遇到了一個(gè)典型問題MX_USART1_UART_Init();函數(shù)雖然已經(jīng)在串口源文件中實(shí)現(xiàn)但main.c沒有包含串口頭文件導(dǎo)致編譯器提示function MX_USART1_UART_Init declared implicitly解決方法是在main.c加入#includeusart/bsp_usart.h這說明.c文件負(fù)責(zé)實(shí)現(xiàn)函數(shù).h文件負(fù)責(zé)聲明函數(shù)。使用某模塊時(shí)不能只把.c文件加入 Keil還要在需要調(diào)用的位置包含對(duì)應(yīng)的.h文件。十一、今日總結(jié)今天學(xué)習(xí)的內(nèi)容可以概括為PWM 中ARR 決定周期CCR 決定占空比。 TB6612FNG 中PWMA 控制速度 AIN1/AIN2 控制方向 STBY 必須拉高才能工作。 DWT 是內(nèi)核自帶的高速周期計(jì)數(shù)器 適合微秒延時(shí)和程序運(yùn)行時(shí)間測(cè)量。 DHT11 使用單總線通信 主機(jī)低電平至少 18ms 發(fā)起請(qǐng)求 傳感器回應(yīng)后發(fā)送 40 位溫濕度數(shù)據(jù)。 Keil 工程中 模塊的 .c 文件要加入工程 模塊的 .h 文件要在使用處包含。