軟硬件平台
軟件
STM32CubeIDE 1.7.0
Saleae Logic 1.2.18
硬件
STM32F103ZET6
MG995
Saleae Logic Analyer
工程初始化
RCC(系統時鍾)
HSE(外部高速時鍾) -> Crystal/Ceramic Resonator(晶振 / 陶瓷諧振器)
HSE(外部低速時鍾) -> Crystal/Ceramic Resonator(晶振 / 陶瓷諧振器)
HCLK(AHB總線時鍾) -> 72MHz
APB2 Prescaier(APB2總線預分頻器) -> 1(即不分頻)
TIM1(高級控制定時器)
Clock Source(時鍾源) -> Internal Clock(內部時鍾)
Channel1(通道1) -> PWM Generation CH1(PWM模式通道1)
Prescaier(預分頻器) -> 720-1
TIM1所使用的APB2時鍾為72MHz,現期望獲得100KHz(10us)時鍾信號
Counter Period(計數周期) -> 2000-1
時鍾周期為10us,MG995舵機信號周期為20ms
auto-reload preload(自動重裝載) -> Enable(使能)
代碼部分
開啟通道PWM輸出
/* main.c */
/* 開啟定時器 1 通道 1 PWM */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
舵機控制
由於發現不同的舵機的控制信號不一樣,但是大同小異,所以就將定時器通道以及舵機控制信號的一些指標通過define的方式定義,方便移植。
/* servo.c */ #include "servo.h" /* 設置所使用的定時器 */ #define servo_htim htim1 /* 設置所使用的通道 */ #define servo_channel TIM_CHANNEL_1 /* 設置定時器計數值 */ #define servo_plus 2000 /* 設置舵機控制信號周期(ms) */ #define servo_cycle_ms 20.0 /* 設置舵機控制信號最短脈沖寬度(ms) */ #define servo_min_ms 0.5 /* 設置舵機控制信號1ms脈沖寬度變化所對應的角度變化值 */ #define servo_1ms_angle 90.0 void servo_control(int angle) { int value; /* 目標角度為0時需要單獨處理 */ if(angle == 0) { value = ((servo_min_ms / servo_cycle_ms) * servo_plus); } /* 通過計算目標角脈沖寬度來計算所需要的脈沖計數值 */ else { value = (((servo_min_ms + (angle / servo_1ms_angle)) / servo_cycle_ms) * servo_plus); } /* 改變定時器 1 通道 1 的脈沖計數值 */ __HAL_TIM_SET_COMPARE(&servo1_htim,servo1_channel,value); }
/* servo.h */ #ifndef INC_SERVO_H_ #define INC_SERVO_H_ #include "main.h" #include "tim.h" void servo_control(int angle); #endif /* INC_SERVO_H_ */
測試
測試代碼
/* 舵機轉到0°位置 */ servo1_control(0); HAL_Delay(1000); /* 舵機轉到45°位置 */ servo1_control(45); HAL_Delay(1000); /* 舵機轉到90°位置 */ servo1_control(90); HAL_Delay(1000); /* 舵機轉到120°位置 */ servo1_control(135); HAL_Delay(1000); /* 舵機轉到180°位置 */ servo1_control(180); HAL_Delay(1000);
測試結果
舵機轉到0°位置

舵機轉到45°位置

舵機轉到90°位置

舵機轉到135°位置

舵機轉到180°位置

Debug
對於下面這條語句我最初想當然的加了一個 -1 ,后來采集出來的脈沖寬度少了10us,下面對這個問題的。
value = (((servo_min_ms + (angle / servo_1ms_angle)) / servo_cycle_ms) * servo_plus);
上面這條語句是被下面這條語句所使用的。
__HAL_TIM_SET_COMPARE(&servo1_htim,servo1_channel,value);
查詢數據手冊,發現有如下描述:

進一步的,看一下我們的 value 指向到哪里,可以在 stm32f1xx_hal_tim.h 中看到這樣的描述。
如果我沒有理解錯的話,value 值最終被指向Capture Compare Register,那就是手冊中的CCRx了。

再進一步可以在手冊中發現OCXREF信號為高電平有效信號,CCxIF是一個置中斷狀態寄存器中的標志位也是高有效。
分析到現在,很顯然,ARR的值在我們設置的過程中是需要-1的,不然會多計數一個。
而CCRx的值就是表示計數的次數,那么在這里加上-1的話就會少計數一次。
至此,這個bug已經分析完成了,那么在以后執行相關操作的時候這個點就要注意了,不要賦錯值。
