Использует ли Klipper программный или аппаратный PWM?
TL;DR: Klipper по умолчанию использует программный PWM, но может использовать аппаратный PWM на поддерживаемых MCU, если вы явно включите его в конфигурации.
Программный PWM по умолчанию
Когда вы настраиваете вентилятор, нагреватель или обычный выходной пин в Klipper без специальных флагов, Klipper использует программный PWM. Важно понимать, что бит-бэнгинг выполняется не на стороне хоста/Linux — хост только планирует переключения и отправляет их на MCU. Фактическое переключение GPIO происходит внутри прошивки MCU.
В klippy/mcu.py класс MCU_pwm решает, какой путь использовать. Если hardware_pwm имеет значение False (по умолчанию), он настраивает пин как обычный цифровой выход и отправляет config_digital_out плюс set_digital_out_pwm_cycle на MCU (строки 509-530). Для каждого обновления коэффициента заполнения он отправляет queue_digital_out со значением on_ticks, которое сообщает MCU, сколько тактов часов пин должен оставаться в высоком состоянии в течение цикла PWM.
На стороне MCU src/gpiocmds.c реализует конечный автомат программного PWM:
digital_load_event()считывает следующее запланированное обновление, устанавливает начальное состояние пина и вычисляет, нужно ли переключать пин.digital_toggle_event()переключает пин и перепланирует себя на время включения или выключения в течение цикла.
Таким образом, форма сигнала PWM генерируется планировщиком прерываний таймера MCU, управляемым списком будущих переключений от хоста.
Аппаратный PWM включается опционально
Аппаратный PWM включается для каждого пина с помощью hardware_pwm: True в конфигурации. Когда это установлено, хост отправляет команды config_pwm_out и queue_pwm_out вместо цифровых, и MCU использует периферию PWM платформы.
Диспетчер на стороне MCU находится в src/pwmcmds.c. Платформо-зависимые реализации существуют для нескольких MCU:
src/atsam/hard_pwm.csrc/atsamd/hard_pwm.csrc/avr/hard_pwm.csrc/hc32f460/hard_pwm.csrc/lpc176x/hard_pwm.csrc/rp2040/hard_pwm.csrc/stm32/hard_pwm.c
Например, реализация для STM32 в gpio_pwm_setup() ищет таймер/канал для запрошенного пина, вычисляет делитель и период счётчика, наилучшим образом соответствующие запрошенному времени цикла, а затем записывает значение коэффициента заполнения в регистр захвата/сравнения таймера.
Документация Klipper явно предупреждает, что “большинство вентиляторов не работают хорошо с аппаратным PWM, поэтому не рекомендуется включать его, если нет электрической необходимости переключения на очень высоких скоростях” (Config_Reference.md).
Что определяет частоту PWM?
Частота просто равна 1 / cycle_time, задаётся в конфигурации:
[fan]:cycle_time: 0.010→ 100 Гц (по умолчанию).[output_pin]:cycle_time: 0.100→ 10 Гц (по умолчанию).[extruder]/[heater_bed]:pwm_cycle_time: 0.100→ 10 Гц (по умолчанию).
Для программного PWM это фактическая частота, на которой MCU переключает пин. Для аппаратного PWM это запрашиваемая частота, но аппаратный таймер MCU может округлить её до ближайшего достижимого значения из-за ограничений делителя/счётчика. Кроме того, несколько аппаратных PWM-пинов, использующих один таймер, должны работать на одинаковой частоте; иначе Klipper выдаст ошибку “PWM already programmed at different speed”.
Пример конфигурации
Вентилятор с программным PWM (по умолчанию)
[fan]
pin: PA8
cycle_time: 0.010
hardware_pwm: FalseВыходной пин с аппаратным PWM
[output_pin my_pwm]
pin: PB0
pwm: True
cycle_time: 0.001
hardware_pwm: TrueПериод PWM нагревателя
[extruder]
heater_pin: PA2
pwm_cycle_time: 0.100