nimphea/per/pwm

Source   Edit  

PWM (Pulse Width Modulation) support for libDaisy Nim wrapper

This module provides hardware PWM support using the STM32 timer peripherals. PWM is useful for LED control, motor control, servo control, and generating analog-like signals.

Example - Simple LED dimming:

import nimphea, per/pwm

var hw = initDaisy()

# Initialize PWM on TIM3, channel 2 (internal LED on Daisy Seed)
var pwm = initPwm(TIM_3, frequency = 1000.0)  # 1kHz
pwm.channel2.init()

# Fade LED in and out
while true:
  for brightness in 0..100:
    pwm.channel2.set(brightness / 100.0)
    hw.delay(10)
  for brightness in countdown(100, 0):
    pwm.channel2.set(brightness / 100.0)
    hw.delay(10)

Example - Multiple channels:

# TIM4 with 4 channels (RGB LED + servo)
var pwm = initPwm(TIM_4, frequency = 50.0)  # 50Hz for servo

# Configure channels with specific pins
pwm.channel1.init(D13())  # Red LED
pwm.channel2.init(D14())  # Green LED
pwm.channel3.init(D11())  # Blue LED
pwm.channel4.init(D12())  # Servo control

# Set RGB color and servo position
pwm.channel1.set(1.0)    # Red full
pwm.channel2.set(0.5)    # Green half
pwm.channel3.set(0.0)    # Blue off
pwm.channel4.set(0.075)  # Servo center (1.5ms @ 50Hz)

Types

PwmChannel {.importcpp: "daisy::PWMHandle::Channel".} = object
Source   Edit  
PwmChannelConfig {.importcpp: "daisy::PWMHandle::Channel::Config", bycopy.} = object
  pin* {.importc: "pin".}: Pin
  polarity* {.importc: "polarity".}: PwmPolarity
Source   Edit  
PwmConfig {.importcpp: "daisy::PWMHandle::Config", bycopy.} = object
  periph* {.importc: "periph".}: PwmPeripheral
  prescaler* {.importc: "prescaler".}: uint32
  period* {.importc: "period".}: uint32
Source   Edit  
PwmHandle {.importcpp: "daisy::PWMHandle".} = object
Source   Edit  
PwmHandleImpl {.importcpp: "daisy::PWMHandle::Impl".} = object
Source   Edit  
PwmPeripheral {.importcpp: "daisy::PWMHandle::Config::Peripheral", size: 4.} = enum
  TIM_3 = 0,                ## TIM3 - 16-bit counter
  TIM_4 = 1,                ## TIM4 - 16-bit counter  
  TIM_5 = 2                  ## TIM5 - 32-bit counter
Source   Edit  
PwmPolarity {.importcpp: "daisy::PWMHandle::Channel::Config::Polarity", size: 4.} = enum
  POLARITY_HIGH = 0,        ## Output high when active
  POLARITY_LOW               ## Output low when active
Source   Edit  
PwmResult {.importcpp: "daisy::PWMHandle::Result", size: 4.} = enum
  PWM_OK = 0, PWM_ERR = 1
Source   Edit  

Consts

TIM3_CH1_DEFAULT = "D19"
PA6 Source   Edit  
TIM3_CH2_DEFAULT = "LED"
PC7 (internal LED) Source   Edit  
TIM3_CH3_DEFAULT = "D4"
PC8 Source   Edit  
TIM3_CH4_DEFAULT = "D17"
PB1 Source   Edit  
TIM4_CH1_PIN = "D13"
PB6 Source   Edit  
TIM4_CH2_PIN = "D14"
PB7 Source   Edit  
TIM4_CH3_PIN = "D11"
PB8 Source   Edit  
TIM4_CH4_PIN = "D12"
PB9 Source   Edit  
TIM5_CH1_PIN = "D25"
PA0 Source   Edit  
TIM5_CH2_PIN = "D24"
PA1 Source   Edit  
TIM5_CH3_PIN = "D28"
PA2 Source   Edit  
TIM5_CH4_PIN = "D16"
PA3 Source   Edit  

Procs

proc channel1(pwm: var PwmHandle): var PwmChannel {.inline, ...raises: [],
    tags: [], forbids: [].}
Source   Edit  
proc channel2(pwm: var PwmHandle): var PwmChannel {.inline, ...raises: [],
    tags: [], forbids: [].}
Source   Edit  
proc channel3(pwm: var PwmHandle): var PwmChannel {.inline, ...raises: [],
    tags: [], forbids: [].}
Source   Edit  
proc channel4(pwm: var PwmHandle): var PwmChannel {.inline, ...raises: [],
    tags: [], forbids: [].}
Source   Edit  
proc cppInit(pwm: var PwmHandle; peripheral: PwmPeripheral;
             frequency: float = 1000.0) {....raises: [], tags: [], forbids: [].}

Initialize PWM peripheral in-place with a target frequency

This function initializes an existing PwmHandle variable. Use this to avoid copy constructor issues.

Parameters: pwm: The PwmHandle variable to initialize peripheral: TIM_3, TIM_4, or TIM_5 frequency: Target frequency in Hz (default 1000Hz / 1kHz)

Example:

var pwm {.noinit.}: PwmHandle
pwm.cppInit(TIM_3, 1000.0)  # 1kHz
pwm.channel1.init()

Source   Edit  
proc deinit(channel: var PwmChannel): PwmResult {....raises: [], tags: [],
    forbids: [].}
Deinitialize a PWM channel Source   Edit  
proc deinit(pwm: var PwmHandle) {....raises: [], tags: [], forbids: [].}
Deinitialize PWM peripheral Source   Edit  
proc init(channel: var PwmChannel; pin: Pin = Pin();
          polarity: PwmPolarity = POLARITY_HIGH): PwmResult {....raises: [],
    tags: [], forbids: [].}

Initialize a PWM channel

Parameters: pin: GPIO pin for output (use Pin() for default) polarity: POLARITY_HIGH (normal) or POLARITY_LOW (inverted)

Example:

pwm.channel1.init(D13())
pwm.channel2.init()  # Use default pin

Source   Edit  
proc initPwm(peripheral: PwmPeripheral; frequency: float = 1000.0): PwmHandle {.
    ...raises: [], tags: [], forbids: [].}

Initialize PWM peripheral with a target frequency

Note: Due to C++ copy constructor restrictions, you may need to use cppInit() instead if you get compiler errors.

Parameters: peripheral: TIM_3, TIM_4, or TIM_5 frequency: Target frequency in Hz (default 1000Hz / 1kHz)

Returns: Initialized PWM handle (channels must be initialized separately)

Example:

var pwm {.noinit.}: PwmHandle
pwm.cppInit(TIM_3, 1000.0)  # Recommended

Source   Edit  
proc initPwmCustom(peripheral: PwmPeripheral; prescaler: uint32; period: uint32): PwmHandle {.
    ...raises: [], tags: [], forbids: [].}

Initialize PWM with explicit prescaler and period values

For advanced users who want exact control over timing. Frequency = SYSCLK / (2 * (period + 1) * (prescaler + 1))

Parameters: peripheral: TIM_3, TIM_4, or TIM_5 prescaler: Clock prescaler (0 = no division) period: Counter period before reset

Example:

# 100Hz PWM with maximum resolution
var pwm = initPwmCustom(TIM_5, 0, 2_400_000)

Source   Edit  
proc set(channel: var PwmChannel; dutyCycle: float) {....raises: [], tags: [],
    forbids: [].}

Set PWM duty cycle as a float (0.0 to 1.0)

Parameters: dutyCycle: Duty cycle from 0.0 (0%) to 1.0 (100%)

Example:

channel.set(0.5)   # 50% duty cycle
channel.set(0.25)  # 25% duty cycle
channel.set(1.0)   # 100% duty cycle (fully on)

Source   Edit  
proc setPeriod(pwm: var PwmHandle; period: uint32) {....raises: [], tags: [],
    forbids: [].}
Change the period after initialization This affects frequency and resolution Source   Edit  
proc setPrescaler(pwm: var PwmHandle; prescaler: uint32) {....raises: [], tags: [],
    forbids: [].}
Change the prescaler after initialization Source   Edit  
proc setRaw(channel: var PwmChannel; value: uint32) {....raises: [], tags: [],
    forbids: [].}

Set PWM duty cycle as raw counter value

Value must be <= timer's period

Parameters: value: Raw counter value

Example:

# If period is 1000, this sets 75% duty cycle
channel.setRaw(750)

Source   Edit