5. ünite / 11 · yaklaşık 5 saat
Zamanlayıcılar ve PWM
Ön bölücü (PSC), otomatik yeniden yükleme (ARR), karşılaştırma (CCR); frekans, görev oranı ve çözünürlük; servo ve motor PWM'i.
Bu ünitenin sonunda
- Zamanlayıcı sayacının PSC ve ARR ile nasıl saydığını ve taştığını açıklamak
- İstenen PWM frekansı için PSC ve ARR seçmek; çözünürlük ile frekans arasındaki ödünleşimi göstermek
- CCR ile görev oranını ayarlamak; servo için mikrosaniye çözünürlüklü ayar kurmak
- PWM'i hem HAL hem yazmaçlarla başlatmak
Ders notları
STM32 zamanlayıcıları (TIM1–TIM14) donanımda sayan sayaçlardır; işlemci uğraşmadan kesin zamanlama üretirler. Bir zamanlayıcının üç temel yazmacı vardır:
- PSC (ön bölücü): giriş saatini PSC + 1'e böler.
- ARR (yeniden yükleme): sayaç 0'dan ARR'ye kadar sayar, sonra başa döner (güncelleme olayı).
- CCR (karşılaştırma): PWM kip 1'de sayaç CCR'den küçükken çıkış 1'dir.
Frekans ve çözünürlük
84 MHz zamanlayıcı saatiyle 20 kHz motor PWM'i için PSC = 0, ARR = 4199: görev oranı 4200 adımda ayarlanır (≈ 12 bit). Aynı saatle 1 MHz istersen ARR = 83 olur, yalnız 84 adım kalır. Yüksek frekans, düşük çözünürlük demektir. Motorlar için 20 kHz insan kulağının üstündedir: motor ötmez.
Servo
Servo 50 Hz'te 1–2 ms darbe ister. PSC = 83 (84 MHz → 1 MHz, 1 µs adım) ve ARR = 19999 seçilirse CCR doğrudan mikrosaniyedir: CCR = 1500 orta konum. 16 bitlik sınır (ARR ≤ 65535) yüzünden düşük frekanslarda PSC büyütülmek zorundadır.
Aynı zamanlayıcı giriş yakalama (enkoder ya da darbe ölçümü), tek darbe ve enkoder arayüzü kiplerinde de çalışır; TIM1 ve TIM8 motor sürmek için ölü zamanlı tamamlayıcı çıkışlar sunar.
Simülasyonlar
Zamanlayıcı ve PWM: PSC/ARR/CCR hesaplayıcı
Sayaç (CNT) testere dişi biçiminde sayar, CCR ile karşılaştırılır, çıkış PWM olur. İstenen frekansı yaz, çözücü 16 bit sınırında PSC ve ARR'yi bulsun; görev oranını ve çözünürlüğü izle.
3B STM32 geliştirme kartı
Nucleo benzeri kart 3B masada: GPIOA saatini aç, PA5'i MODER ile çıkış yap ve BSRR yaz; başlık pinleri ve LD2 anında değişir. TIM3 PWM'i açınca motor görev oranına göre döner, kartın üstünde zamanlayıcı dalgası görünür. Sürükleyerek döndür, iki parmakla yakınlaştır. Durum testli saf mantıktan gelir.
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; GPIOA->MODER = (GPIOA->MODER & ~(3U << (5 * 2))) | (0U << (5 * 2)); GPIOA->PUPDR = (GPIOA->PUPDR & ~(3U << (5 * 2))) | (0U << (5 * 2)); uint32_t level = (GPIOA->IDR >> 5) & 1U;
Kendin dene
PWM hesaplayıcıda 84 MHz ile 20 kHz iste: çözücünün bulduğu PSC/ARR'yi ve çözünürlüğü (bit) not et. Aynı şeyi 50 Hz ve 1 MHz için yap. CCR'yi ARR'den büyük yap: görev oranı ne oluyor? Servo düğmesine bas ve 90° için CCR'nin 1500 olduğunu doğrula.
Wokwi'de
- Wokwi'de STM32 kartına bir servo bağla ve 50 Hz PWM üret: PSC = f_tim(MHz) − 1, ARR = 19999 ile CCR doğrudan mikrosaniye olur.
- Wokwi'nin mantık analizörü parçasını PWM pinine bağlayıp görev oranını ölç.
Kod
TIM_HandleTypeDef htim3;
void pwm_init(void) {
/* TIM3 APB1'de: 84 MHz. 84e6 / (0+1) / (4199+1) = 20 kHz */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 0;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 4199; /* ARR */
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_PWM_Init(&htim3); /* PA6 AF2 ayarı HAL_TIM_MspPostInit içinde */
TIM_OC_InitTypeDef oc = {0};
oc.OCMode = TIM_OCMODE_PWM1; /* CNT < CCR iken 1 */
oc.Pulse = 0; /* CCR */
oc.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim3, &oc, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}
void pwm_set_percent(uint32_t pct) {
uint32_t arr = __HAL_TIM_GET_AUTORELOAD(&htim3);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (arr + 1) * pct / 100); /* bir sonraki güncellemede geçerli */
}void pwm_init_reg(void) {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
GPIOA->MODER = (GPIOA->MODER & ~(3U << 12)) | (2U << 12); /* PA6 = AF */
GPIOA->AFR[0] = (GPIOA->AFR[0] & ~(0xFU << 24)) | (2U << 24); /* AF2 = TIM3_CH1 */
TIM3->PSC = 0;
TIM3->ARR = 4199;
TIM3->CCR1 = 0;
TIM3->CCMR1 = (6U << TIM_CCMR1_OC1M_Pos) | TIM_CCMR1_OC1PE; /* PWM kip 1, ön yükleme */
TIM3->CCER |= TIM_CCER_CC1E; /* kanal çıkışını aç */
TIM3->CR1 |= TIM_CR1_ARPE | TIM_CR1_CEN; /* sayacı başlat */
}Sık yapılan hatalar
- PSC ve ARR'ye "bölücü" değerini yazmak: donanım +1 ekler; 1000'e bölmek için 999 yazılır.
- APB bölücüsü yüzünden zamanlayıcı saatini yanlış almak (4. ünite).
- Alternatif işlev numarasını (AF2) ayarlamamak: pin PWM vermez.
- Çalışırken ARR'yi ön yükleme (ARPE) olmadan değiştirmek: o periyotta tuhaf bir darbe çıkar.
Yazılım mühendisliği bağlantısı
PSC/ARR hesabını sihirli sayı olarak değil, derleme zamanında hesaplanan sabitlerle yaz: #define ARR (TIM_HZ / PWM_HZ - 1).
- Bir
static_assert(ARR <= 0xFFFF)sınırı derlemede yakalar. - PWM ayarı ile görev oranı hesabını saf fonksiyonda tutup bilgisayarda test et.
Kendini sına
f_TIM = 84 MHz, PSC = 83, ARR = 999. PWM frekansı?
ARR = 999 iken %25 görev için CCR?
Aynı zamanlayıcı saatinde PWM frekansını artırmak çözünürlüğe ne yapar?
Servo için PSC = 83 ve ARR = 19999 seçmenin faydası?
Bu üniteden sonra bitirme projesinde
- PWM katmanı: pwm_set_percent(), frekans ayarı ve yön pinleri; mantık analizörüyle ölçülmüş frekans ve görev oranı tablosu.