⏱️ hal::timer

Hardware Timers/Counters - PWM & Timing Control

366 lines ~22 function PWM/IC/OC

📖 Overview

Timer modülü, hardware zamanlayıcılar, PWM üretimi, input capture ve output compare işlevleri sağlar. Motor kontrolü, servo kontrol, frekans ölçümü for kullanılır.

🔑 Key Features

🚀 Quick Start - PWM

import hal::timer, hal::gpio

// PWM pin (PA0 = TIM2_CH1)
gpio.clock_enable(gpio.PORT_A)
gpio.pin_init(gpio.PORT_A, 0, do
    mode: gpio.MODE_AF,
    af: 1  // TIM2
end)

// Initialize PWM
let pwm = timer.pwm_init(timer.TIM2, timer.CH1, do
    frequency: 1000,  // 1 kHz
    duty_cycle: 50    // 50%
end)

timer.pwm_start(pwm)

// Change duty cycle
timer.pwm_set_duty(pwm, 75)  // 75%

💡 Example 1: LED Breathing Effect

import hal::timer, hal::gpio, hal::core

function ana() do
    core.system_init()
    
    // Setup PWM for LED (PA0)
    gpio.clock_enable(gpio.PORT_A)
    gpio.pin_init(gpio.PORT_A, 0, do
        mode: gpio.MODE_AF,
        speed: gpio.SPEED_HIGH,
        af: 1
    end)
    
    // Initialize PWM (1kHz)
    let pwm = timer.pwm_init(timer.TIM2, timer.CH1, do
        frequency: 1000,
        duty_cycle: 0
    end)
    
    timer.pwm_start(pwm)
    
    // Breathing effect
    loop do
        // Fade in
        each duty forde 0..101 for do
            timer.pwm_set_duty(pwm, duty)
            core.delay_ms(10)
        end
        
        // Fade out
        each duty forde (0..101).ters() for do
            timer.pwm_set_duty(pwm, duty)
            core.delay_ms(10)
        end
    end
end

💡 Example 2: Servo Motor Control

import hal::timer

struct Servo do
    pwm: timer.PWMHandle,
    min_pulse: int,  // microseconds
    max_pulse: int
end

function servo_init(tim: timer.Timer, channel: timer.Channel) -> Servo do
    // Servo PWM: 50Hz (20ms period), pulse: 1-2ms
    let pwm = timer.pwm_init(tim, channel, do
        frequency: 50,  // 50Hz
        duty_cycle: 0
    end)
    
    timer.pwm_start(pwm)
    
    return Servo do
        pwm: pwm,
        min_pulse: 1000,  // 1ms = 0°
        max_pulse: 2000   // 2ms = 180°
    end
end

function servo_set_angle(servo: Servo, angle: float) do
    // Map angle (0-180°) to pulse width (1-2ms)
    let pulse = servo.min_pulse.float() + 
                     (servo.max_pulse - servo.min_pulse).float() * 
                     (angle / 180.0)
    
    // Convert pulse width to duty cycle (20ms period)
    let duty = (pulse / 20000.0) * 100.0
    
    timer.pwm_set_duty(servo.pwm, duty.int())
end

function ana() do
    // Setup servo on PA0 (TIM2_CH1)
    let servo = servo_init(timer.TIM2, timer.CH1)
    
    loop do
        // Sweep 0° to 180°
        each angle forde 0..181 for do
            servo_set_angle(servo, angle.float())
            core.delay_ms(15)
        end
        
        // Sweep 180° to 0°
        each angle forde (0..181).ters() for do
            servo_set_angle(servo, angle.float())
            core.delay_ms(15)
        end
    end
end

💡 Example 3: DC Motor Speed Control (L298N Driver)

import hal::timer, hal::gpio

struct DCMotor do
    pwm: timer.PWMHandle,
    dir1: gpio.Pin,
    dir2: gpio.Pin
end

function motor_init(tim: timer.Timer, channel: timer.Channel) -> DCMotor do
    // PWM setup (10 kHz for motor control)
    let pwm = timer.pwm_init(tim, channel, do
        frequency: 10000,  // 10 kHz - reduces audible noise
        duty_cycle: 0
    end)
    
    // Direction pins
    gpio.clock_enable(gpio.PORT_B)
    let dir1 = gpio.pin_init(gpio.PORT_B, 0, do mode: gpio.MODE_OUTPUT end)
    let dir2 = gpio.pin_init(gpio.PORT_B, 1, do mode: gpio.MODE_OUTPUT end)
    
    timer.pwm_start(pwm)
    
    return DCMotor do
        pwm: pwm,
        dir1: dir1,
        dir2: dir2
    end
end

function motor_set_speed(motor: DCMotor, speed: i32) do
    // speed: -100 to +100 (negative = reverse)
    
    if speed > 0 ise do
        // Forward
        gpio.pin_write(motor.dir1, gpio.HIGH)
        gpio.pin_write(motor.dir2, gpio.LOW)
        timer.pwm_set_duty(motor.pwm, speed.mutlak())
    end else if speed < 0 ise do
        // Reverse
        gpio.pin_write(motor.dir1, gpio.LOW)
        gpio.pin_write(motor.dir2, gpio.HIGH)
        timer.pwm_set_duty(motor.pwm, speed.mutlak())
    end else do
        // Brake
        gpio.pin_write(motor.dir1, gpio.LOW)
        gpio.pin_write(motor.dir2, gpio.LOW)
        timer.pwm_set_duty(motor.pwm, 0)
    end
end

function ana() do
    core.system_init()
    
    // Initialize motor on TIM3_CH1 (PA6)
    gpio.clock_enable(gpio.PORT_A)
    gpio.pin_init(gpio.PORT_A, 6, do mode: gpio.MODE_AF, af: 2 end)
    
    let motor = motor_init(timer.TIM3, timer.CH1)
    
    loop do
        // Accelerate forward
        each speed forde 0..101 for do
            motor_set_speed(motor, speed)
            core.delay_ms(20)
        end
        
        core.delay_ms(1000)
        
        // Brake
        motor_set_speed(motor, 0)
        core.delay_ms(500)
        
        // Accelerate reverse
        each speed forde 0..101 for do
            motor_set_speed(motor, -speed)
            core.delay_ms(20)
        end
        
        core.delay_ms(1000)
        motor_set_speed(motor, 0)
        core.delay_ms(500)
    end
end

💡 Example 4: Rotary Encoder Reading (Quadrature Mode)

import hal::timer, hal::gpio

function encoder_init() -> timer.Handle do
    // Encoder pins: PA0=A, PA1=B (TIM2_CH1, TIM2_CH2)
    gpio.clock_enable(gpio.PORT_A)
    gpio.pin_init(gpio.PORT_A, 0, do
        mode: gpio.MODE_AF,
        pull: gpio.PULL_UP,
        af: 1
    end)
    gpio.pin_init(gpio.PORT_A, 1, do
        mode: gpio.MODE_AF,
        pull: gpio.PULL_UP,
        af: 1
    end)
    
    // Configure timer in encoder mode
    let encoder = timer.encoder_init(timer.TIM2, do
        mode: timer.ENCODER_MODE_TI12,  // Count on both edges
        polarity_ch1: timer.POLARITY_RISING,
        polarity_ch2: timer.POLARITY_RISING,
        filter: 5  // Noise filter
    end)
    
    // Set counter to mid-range (for bidirectional counting)
    timer.set_counter(encoder, 32768)
    
    timer.encoder_start(encoder)
    
    return encoder
end

function encoder_get_position(encoder: timer.Handle) -> i32 do
    let count = timer.get_counter(encoder)
    // Convert to relative position (from mid-range)
    return (count olarak i32) - 32768
end

function encoder_get_speed(encoder: timer.Handle) -> i32 do
    // Get direction (up/down counting)
    if timer.is_counting_up(encoder) ise do
        return 1
    end else do
        return -1
    end
end

function ana() do
    core.system_init()
    let encoder = encoder_init()
    
    let son_pos = 0
    
    loop do
        let pos = encoder_get_position(encoder)
        let diff = pos - son_pos
        
        if diff != 0 ise do
            let direction = if diff > 0 ise "CW" else "CCW"
            io.println("Position: {}  Delta: {}  Dir: {}".formatla(pos, diff, direction))
            son_pos = pos
        end
        
        core.delay_ms(50)
    end
end

💡 Example 5: Ultrasonic Sensor (HC-SR04)

import hal::timer, hal::gpio

let capture_start: u32 = 0
let capture_done: bool = false
let pulse_width: u32 = 0

function hcsr04_capture_callback(value: u32, edge: timer.Edge) do
    if edge == timer.EDGE_RISING ise do
        capture_start = value
        capture_done = false
    end else do
        pulse_width = value - capture_start
        capture_done = true
    end
end

function hcsr04_init() -> (gpio.Pin, timer.ICHandle) do
    // Trigger pin (PB0)
    gpio.clock_enable(gpio.PORT_B)
    let trigger = gpio.pin_init(gpio.PORT_B, 0, do
        mode: gpio.MODE_OUTPUT
    end)
    gpio.pin_write(trigger, gpio.LOW)
    
    // Echo pin (PA0 = TIM2_CH1)
    gpio.clock_enable(gpio.PORT_A)
    gpio.pin_init(gpio.PORT_A, 0, do
        mode: gpio.MODE_AF,
        af: 1
    end)
    
    // Input capture for echo (both edges)
    let ic = timer.input_capture_init(timer.TIM2, timer.CH1, do
        edge: timer.EDGE_BOTH,  // Capture both rising and falling
        prescaler: 0,
        callback: hcsr04_capture_callback
    end)
    
    timer.input_capture_start(ic)
    
    return (trigger, ic)
end

function hcsr04_read_distance(trigger: gpio.Pin) -> Optional[float] do
    // Send 10us trigger pulse
    gpio.pin_write(trigger, gpio.HIGH)
    core.delay_us(10)
    gpio.pin_write(trigger, gpio.LOW)
    
    // Wait for measurement (max 38ms)
    capture_done = false
    each i forde 0..380 for do
        if capture_done ise kır end
        core.delay_us(100)
    end
    
    if !capture_done ise return None end
    
    // Calculate distance: time(us) / 58 = distance(cm)
    // pulse_width in timer ticks, assuming 1 MHz timer (1us per tick)
    let distance = pulse_width.float() / 58.0
    
    if distance > 2.0 ve distance < 400.0 ise do
        return Some(distance)
    end else do
        return None
    end
end

function ana() do
    core.system_init()
    
    let (trigger, _ic) = hcsr04_init()
    
    loop do
        match hcsr04_read_distance(trigger) do
            Some(dist) => io.println("Distance: {:.1f} cm".formatla(dist)),
            None => io.println("Out of range")
        end
        
        core.delay_ms(500)
    end
end

💡 Example 6: WS2812 RGB LED Strip (DMA + PWM)

import hal::timer, hal::dma, hal::gpio

const LED_COUNT = 8
const BIT_COUNT = LED_COUNT * 24  // 24 bits per LED (GRB)

// PWM values for WS2812: 0 = 0.4us, 1 = 0.8us (1.25MHz PWM)
const PWM_0 = 17   // ~0.4us high
const PWM_1 = 35   // ~0.8us high
const PWM_PERIOD = 50  // 1.25us total

let pwm_buffer: [u8; BIT_COUNT] = [0; BIT_COUNT]

function rgb_to_grb(r: u8, g: u8, b: u8) -> [u8; 24] do
    let bits = [0u8; 24]
    
    // Green (bits 0-7)
    each i forde 0..8 for do
        bits[i] = if (g & (1 << (7-i))) != 0 ise PWM_1 else PWM_0
    end
    
    // Red (bits 8-15)
    each i forde 0..8 for do
        bits[8+i] = if (r & (1 << (7-i))) != 0 ise PWM_1 else PWM_0
    end
    
    // Blue (bits 16-23)
    each i forde 0..8 for do
        bits[16+i] = if (b & (1 << (7-i))) != 0 ise PWM_1 else PWM_0
    end
    
    return bits
end

function ws2812_set_pixel(index: int, r: u8, g: u8, b: u8) do
    let bits = rgb_to_grb(r, g, b)
    let offset = index * 24
    
    each i forde 0..24 for do
        pwm_buffer[offset + i] = bits[i]
    end
end

function ws2812_show(pwm: timer.PWMHandle, dma: dma.Handle) do
    // Start DMA transfer
    dma.start_memory_to_periph(dma, do
        source: pwm_buffer.adres(),
        dest: timer.get_ccr_address(pwm),
        count: BIT_COUNT
    end)
    
    // Wait for transfer complete
    loop do
        if dma.is_complete(dma) ise kır end
    end
    
    // Reset time (>50us)
    core.delay_us(60)
end

function ana() do
    core.system_init()
    
    // PWM setup on PA0 (TIM2_CH1)
    gpio.clock_enable(gpio.PORT_A)
    gpio.pin_init(gpio.PORT_A, 0, do
        mode: gpio.MODE_AF,
        af: 1,
        speed: gpio.SPEED_VERY_HIGH
    end)
    
    // 800kHz bit rate = 1.25us per bit
    let pwm = timer.pwm_init(timer.TIM2, timer.CH1, do
        frequency: 800000,
        duty_cycle: 0
    end)
    
    // DMA for automatic PWM duty cycle updates
    let dma_ch = dma.init(dma.DMA1_CH5, do
        direction: dma.MEMORY_TO_PERIPH,
        circular: false,
        memory_inc: true,
        periph_inc: false
    end)
    
    timer.pwm_start_dma(pwm, dma_ch)
    
    // Rainbow animation
    let hue = 0
    
    loop do
        each i forde 0..LED_COUNT for do
            // HSV to RGB conversion (simplified)
            let h = (hue + i * 30) % 360
            let r = if h < 120 ise 255 else if h < 240 ise 0 else 255
            let g = if h < 60 veya h >= 300 ise 0 else 255
            let b = if h < 180 ise 0 else 255
            
            ws2812_set_pixel(i, r, g, b)
        end
        
        ws2812_show(pwm, dma_ch)
        
        hue = (hue + 10) % 360
        core.delay_ms(50)
    end
end

💡 Example 7: Stepper Motor Control (A4988 Driver)

import hal::timer, hal::gpio

struct StepperMotor do
    pwm: timer.PWMHandle,  // Step pulses
    dir: gpio.Pin,
    enable: gpio.Pin,
    steps_per_rev: int
end

function stepper_init(tim: timer.Timer, channel: timer.Channel) -> StepperMotor do
    // Step pin (PWM)
    gpio.clock_enable(gpio.PORT_A)
    gpio.pin_init(gpio.PORT_A, 0, do
        mode: gpio.MODE_AF,
        af: 1
    end)
    
    // Direction and enable pins
    gpio.clock_enable(gpio.PORT_B)
    let dir = gpio.pin_init(gpio.PORT_B, 0, do mode: gpio.MODE_OUTPUT end)
    let enable = gpio.pin_init(gpio.PORT_B, 1, do mode: gpio.MODE_OUTPUT end)
    
    // Start with motor disabled
    gpio.pin_write(enable, gpio.HIGH)  // Active low
    
    // PWM for step pulses (default 1kHz)
    let pwm = timer.pwm_init(tim, channel, do
        frequency: 1000,
        duty_cycle: 50  // 50% duty cycle for step pulses
    end)
    
    return StepperMotor do
        pwm: pwm,
        dir: dir,
        enable: enable,
        steps_per_rev: 200  // Typical NEMA17 (1.8° per step)
    end
end

function stepper_move(motor: StepperMotor, steps: i32, speed_hz: int) do
    // Set direction
    if steps >= 0 ise do
        gpio.pin_write(motor.dir, gpio.HIGH)
    end else do
        gpio.pin_write(motor.dir, gpio.LOW)
    end
    
    let abs_steps = steps.mutlak()
    
    // Set speed (frequency)
    timer.pwm_set_frequency(motor.pwm, speed_hz)
    
    // Enable motor
    gpio.pin_write(motor.enable, gpio.LOW)
    
    // Generate step pulses
    timer.pwm_start(motor.pwm)
    
    // Wait for movement to complete
    let delay_ms = (abs_steps.float() / speed_hz.float()) * 1000.0
    core.delay_ms(delay_ms.int())
    
    // Stop
    timer.pwm_stop(motor.pwm)
    gpio.pin_write(motor.enable, gpio.HIGH)
end

function stepper_move_angle(motor: StepperMotor, degrees: float, speed_rpm: float) do
    // Convert angle to steps
    let steps = ((degrees / 360.0) * motor.steps_per_rev.float()).yuvarla().int()
    
    // Convert RPM to Hz
    let speed_hz = ((speed_rpm * motor.steps_per_rev.float()) / 60.0).int()
    
    stepper_move(motor, steps, speed_hz)
end

function ana() do
    core.system_init()
    
    let motor = stepper_init(timer.TIM2, timer.CH1)
    
    loop do
        // Rotate 360° clockwise at 60 RPM
        stepper_move_angle(motor, 360.0, 60.0)
        core.delay_ms(500)
        
        // Rotate 360° counter-clockwise at 120 RPM
        stepper_move_angle(motor, -360.0, 120.0)
        core.delay_ms(500)
    end
end

📦 Tipler ve Enum'lar

// Timer Instances
enum Timer {
    TIM1, TIM2, TIM3, TIM4, TIM5, TIM6, TIM7, TIM8,
    TIM9, TIM10, TIM11, TIM12, TIM13, TIM14
}

// Timer Channels
enum Channel {
    CH1, CH2, CH3, CH4
}

// PWM Mode
enum PWMMode {
    MODE_1,  // Active when CNT < CCR (standard)
    MODE_2   // Active when CNT > CCR (inverted)
}

// Input Capture Edge
enum Edge {
    EDGE_RISING,   // Rising edge
    EDGE_FALLING,  // Falling edge
    EDGE_BOTH      // Both edges
}

// Encoder Mode
enum EncoderMode {
    ENCODER_MODE_TI1,   // Count on TI1 edge only
    ENCODER_MODE_TI2,   // Count on TI2 edge only
    ENCODER_MODE_TI12   // Count on both TI1 and TI2 (x4 mode)
}

// Counter Direction
enum CounterMode {
    UP,    // Up-counting
    DOWN,  // Down-counting
    CENTER_ALIGNED_1,
    CENTER_ALIGNED_2,
    CENTER_ALIGNED_3
}

struct PWMConfig do
    frequency: int,     // Frequency in Hz
    duty_cycle: int,    // Duty cycle 0-100%
    mode: PWMMode,
    polarity: Polarity
end

struct InputCaptureConfig do
    edge: Edge,
    filter: int,        // Input filter (0-15)
    prescaler: int,     // Capture prescaler
    callback: İşlev
end

struct EncoderConfig do
    mode: EncoderMode,
    polarity_ch1: Edge,
    polarity_ch2: Edge,
    filter: int
end

⚙️ functionlar

PWM Control

function pwm_init(timer: Timer, channel: Channel, config: PWMConfig) -> PWMHandle
// Initialize PWM on specified timer channel

function pwm_start(handle: PWMHandle)
// Start PWM generation

function pwm_stop(handle: PWMHandle)
// Stop PWM generation

function pwm_set_duty(handle: PWMHandle, duty: int)
// Set duty cycle (0-100%)

function pwm_set_frequency(handle: PWMHandle, freq: int)
// Change PWM frequency

function pwm_start_dma(handle: PWMHandle, dma: dma.Handle)
// Start PWM with DMA (for LED strips, audio, etc.)

Input Capture

function input_capture_init(timer: Timer, channel: Channel, config: InputCaptureConfig) -> ICHandle
// Initialize input capture

function input_capture_start(handle: ICHandle)
// Start input capture

function input_capture_stop(handle: ICHandle)
// Stop input capture

function input_capture_read(handle: ICHandle) -> u32
// Read captured value

Encoder Mode

function encoder_init(timer: Timer, config: EncoderConfig) -> Handle
// Initialize encoder mode (quadrature decoder)

function encoder_start(handle: Handle)
// Start encoder counting

function encoder_stop(handle: Handle)
// Stop encoder counting

function get_counter(handle: Handle) -> u32
// Get current counter value

function set_counter(handle: Handle, value: u32)
// Set counter value

function is_counting_up(handle: Handle) -> bool
// Check if counting up (direction)

Basic Timer Control

function timer_init(timer: Timer, config: TimerConfig) -> Handle
// Initialize basic timer

function timer_start(handle: Handle)
// Start timer counting

function timer_stop(handle: Handle)
// Stop timer counting

function timer_enable_interrupt(handle: Handle, interrupt: TimerInterrupt)
// Enable timer interrupt

enum TimerInterrupt {
    UPDATE,     // Update event (overflow)
    CC1,        // Capture/Compare 1
    CC2,        // Capture/Compare 2
    CC3,        // Capture/Compare 3
    CC4,        // Capture/Compare 4
    TRIGGER,    // Trigger event
    BREAK       // Break input (advanced timers)
}

Advanced Control

function set_prescaler(handle: Handle, prescaler: u16)
// Set timer prescaler (clock divider)

function set_period(handle: Handle, period: u32)
// Set auto-reload value (period)

function get_ccr_address(handle: Handle) -> *mut u32
// Get CCR register address (for DMA)

function generate_update_event(handle: Handle)
// Generate update event (reload registers)

⚡ Performance Tips

📐 PWM Hesaplamaları

// PWM Frequency calculation:
// f_PWM = f_Timer / ((Prescaler + 1) * (Period + 1))

// Example: 1 kHz PWM on 72 MHz timer
// Prescaler = 71 (72 MHz / 72 = 1 MHz)
// Period = 999 (1 MHz / 1000 = 1 kHz)
// Resolution = 1000 steps (0.1% duty cycle precision)

// Duty Cycle:
// Duty% = (CCR / Period) * 100
// CCR = (Duty% * Period) / 100

// Example: 75% duty @ 1kHz
// CCR = (75 * 999) / 100 = 749

⚠️ Important Notes

🎯 Application Scenarios

UygulamaModFrequencyNotlar
Servo MotorPWM50 Hz1-2ms pulse width
DC Motor (L298N)PWM10-20 kHzAudible noise reduction
LED DimmingPWM500-1000 HzFlicker-free
WS2812 RGB LEDPWM+DMA800 kHzBit-banging via DMA
Rotary EncoderEncoder-x4 mode for precision
Frequency MeterInput Capture-Both edges for accuracy
Ultrasonic (HC-SR04)Input Capture-Pulse width measurement
Stepper MotorPWM100-2000 HzStep frequency = speed
BLDC MotorPWM (3ch)10-40 kHz3-phase complementary
Piezo BuzzerPWM2-4 kHzResonant frequency

🔧 Timer Pin Mapping (STM32F103)

TimerChannel 1Channel 2Channel 3Channel 4
TIM1PA8PA9PA10PA11
TIM2PA0PA1PA2PA3
TIM3PA6PA7PB0PB1
TIM4PB6PB7PB8PB9

📚 Platform Support

🔗 Related Modules

📖 References

← HAL Modülleri | Tüm Modüller | Ana Sayfa

c:\Projects\berk\berk_pages\stdlib\hal-timer-tr.html