diff --git a/config/example-extras.cfg b/config/example-extras.cfg
index 37a3ca0bc..787a4a292 100644
--- a/config/example-extras.cfg
+++ b/config/example-extras.cfg
@@ -209,14 +209,13 @@
 #shutdown_value:
 #   The value to set the pin to on an MCU shutdown event. The default
 #   is 0.
-#hard_pwm:
-#   Set this value to force hardware PWM instead of software PWM. Set
-#   to 1 to force a hardware PWM at the fastest rate; set to a higher
-#   number to force hardware PWM with the given cycle time in clock
-#   ticks. The default is to use software PWM.
 #cycle_time: 0.100
-#   The amount of time (in seconds) per PWM cycle when using software
-#   based PWM. The default is 0.100 seconds.
+#   The amount of time (in seconds) per PWM cycle. It is recommended
+#   this be 10 milliseconds or greater when using software based
+#   PWM. The default is 0.100 seconds.
+#hardware_pwm: False
+#   Enable this to use hardware PWM instead of software PWM. The
+#   default is False.
 #scale:
 #   This parameter can be used to alter how the 'value' and
 #   'shutdown_value' parameters are interpreted. If provided, then the
@@ -234,8 +233,8 @@
 #[static_pwm_output my_output_pwm]
 #pin:
 #value:
-#hard_pwm:
 #cycle_time:
+#hardware_pwm:
 #scale:
 #    See the 'pwm_output' section for details on these parameters.
 
diff --git a/config/example.cfg b/config/example.cfg
index bc71d46c1..567b162dd 100644
--- a/config/example.cfg
+++ b/config/example.cfg
@@ -245,12 +245,13 @@ pin: ar9
 #   pin to be enabled for no more than half the time. This setting may
 #   be used to limit the total power output (over extended periods) to
 #   the fan. The default is 1.0.
-#hard_pwm: 0
-#   Set this value to force hardware PWM instead of software PWM. Set
-#   to 1 to force a hardware PWM at the fastest rate; set to a higher
-#   number to force hardware PWM with the given cycle time in clock
-#   ticks. The default is 0 which enables software PWM with a cycle
-#   time of 10ms.
+#cycle_time: 0.010
+#   The amount of time (in seconds) for each PWM power cycle to the
+#   fan. It is recommended this be 10 milliseconds or greater when
+#   using software based PWM. The default is 0.010 seconds.
+#hardware_pwm: False
+#   Enable this to use hardware PWM instead of software PWM. The
+#   default is False.
 #kick_start_time: 0.100
 #   Time (in seconds) to run the fan at full speed when first enabling
 #   it (helps get the fan spinning). The default is 0.100 seconds.
diff --git a/config/generic-mini-rambo.cfg b/config/generic-mini-rambo.cfg
index c04857e62..31e4af768 100644
--- a/config/generic-mini-rambo.cfg
+++ b/config/generic-mini-rambo.cfg
@@ -78,19 +78,22 @@ max_z_accel: 100
 pin: PL3
 scale: 2.0
 value: 1.3
-hard_pwm: 32640
+cycle_time: .002
+hardware_pwm: True
 
 [static_pwm_output stepper_z_current]
 pin: PL4
 scale: 2.0
 value: 1.3
-hard_pwm: 32640
+cycle_time: .002
+hardware_pwm: True
 
 [static_pwm_output stepper_e_current]
 pin: PL5
 scale: 2.0
 value: 1.25
-hard_pwm: 32640
+cycle_time: .002
+hardware_pwm: True
 
 [static_digital_output stepper_config]
 pins:
diff --git a/config/makergear-m2-2012.cfg b/config/makergear-m2-2012.cfg
index eec52640a..67df15375 100644
--- a/config/makergear-m2-2012.cfg
+++ b/config/makergear-m2-2012.cfg
@@ -72,7 +72,8 @@ pin: PH5
 [heater_fan nozzle_fan]
 pin: PH3
 max_power: 0.61
-hard_pwm: 1
+cycle_time: .000030
+hardware_pwm: True
 
 [mcu]
 serial: /dev/ttyACM0
diff --git a/klippy/chipmisc.py b/klippy/chipmisc.py
index 611823eca..575f06643 100644
--- a/klippy/chipmisc.py
+++ b/klippy/chipmisc.py
@@ -25,12 +25,9 @@ class PrinterPin:
         self.is_pwm = 'pwm' in config.get_name().split()[0]
         if self.is_pwm:
             self.mcu_pin = pins.setup_pin(printer, 'pwm', config.get('pin'))
-            hard_pwm = config.getint('hard_pwm', None, minval=1)
-            if hard_pwm is None:
-                self.mcu_pin.setup_cycle_time(config.getfloat(
-                    'cycle_time', 0.100, above=0.))
-            else:
-                self.mcu_pin.setup_hard_pwm(hard_pwm)
+            cycle_time = config.getfloat('cycle_time', 0.100, above=0.)
+            hardware_pwm = config.getboolean('hardware_pwm', False)
+            self.mcu_pin.setup_cycle_time(cycle_time, hardware_pwm)
             self.scale = config.getfloat('scale', 1., above=0.)
         else:
             self.mcu_pin = pins.setup_pin(
diff --git a/klippy/extras/fan.py b/klippy/extras/fan.py
index c7434ca57..7bc4a26df 100644
--- a/klippy/extras/fan.py
+++ b/klippy/extras/fan.py
@@ -5,8 +5,7 @@
 # This file may be distributed under the terms of the GNU GPLv3 license.
 import pins
 
-FAN_MIN_TIME = 0.1
-PWM_CYCLE_TIME = 0.010
+FAN_MIN_TIME = 0.100
 
 class PrinterFan:
     def __init__(self, config):
@@ -17,8 +16,9 @@ class PrinterFan:
         printer = config.get_printer()
         self.mcu_fan = pins.setup_pin(printer, 'pwm', config.get('pin'))
         self.mcu_fan.setup_max_duration(0.)
-        self.mcu_fan.setup_cycle_time(PWM_CYCLE_TIME)
-        self.mcu_fan.setup_hard_pwm(config.getint('hard_pwm', 0))
+        cycle_time = config.getfloat('cycle_time', 0.010, above=0.)
+        hardware_pwm = config.getboolean('hardware_pwm', False)
+        self.mcu_fan.setup_cycle_time(cycle_time, hardware_pwm)
     def set_speed(self, print_time, value):
         value = max(0., min(self.max_power, value))
         if value == self.last_fan_value:
diff --git a/klippy/extras/multi_pin.py b/klippy/extras/multi_pin.py
index 3e033a86d..5ffaed808 100644
--- a/klippy/extras/multi_pin.py
+++ b/klippy/extras/multi_pin.py
@@ -40,12 +40,9 @@ class PrinterMultiPin:
     def setup_start_value(self, start_value, shutdown_value):
         for mcu_pin in self.mcu_pins:
             mcu_pin.setup_start_value(start_value, shutdown_value)
-    def setup_cycle_time(self, cycle_time):
+    def setup_cycle_time(self, cycle_time, hardware_pwm=False):
         for mcu_pin in self.mcu_pins:
-            mcu_pin.setup_cycle_time(cycle_time)
-    def setup_hard_pwm(self, hard_cycle_ticks):
-        for mcu_pin in self.mcu_pins:
-            mcu_pin.setup_hard_pwm(hard_cycle_ticks)
+            mcu_pin.setup_cycle_time(cycle_time, hardware_pwm)
     def set_digital(self, print_time, value):
         for mcu_pin in self.mcu_pins:
             mcu_pin.set_digital(print_time, value)
diff --git a/klippy/extras/replicape.py b/klippy/extras/replicape.py
index 72ab45e9c..742607c58 100644
--- a/klippy/extras/replicape.py
+++ b/klippy/extras/replicape.py
@@ -3,6 +3,7 @@
 # Copyright (C) 2017,2018  Kevin O'Connor <kevin@koconnor.net>
 #
 # This file may be distributed under the terms of the GNU GPLv3 license.
+import logging
 import pins, mcu
 
 REPLICAPE_MAX_CURRENT = 3.84
@@ -37,11 +38,12 @@ class pca9685_pwm:
         return self._mcu
     def setup_max_duration(self, max_duration):
         self._max_duration = max_duration
-    def setup_cycle_time(self, cycle_time):
-        pass
-    def setup_hard_pwm(self, hard_cycle_ticks):
-        if hard_cycle_ticks:
-            raise pins.error("pca9685 does not support hard_pwm parameter")
+    def setup_cycle_time(self, cycle_time, hardware_pwm=False):
+        if hardware_pwm:
+            raise pins.error("pca9685 does not support hardware_pwm parameter")
+        if cycle_time != self._cycle_time:
+            logging.info("Ignoring pca9685 cycle time of %.6f (using %.6f)",
+                         cycle_time, self._cycle_time)
     def setup_start_value(self, start_value, shutdown_value, is_static=False):
         if is_static and start_value != shutdown_value:
             raise pins.error("Static pin can not have shutdown value")
diff --git a/klippy/mcu.py b/klippy/mcu.py
index d91e6314b..87c590a87 100644
--- a/klippy/mcu.py
+++ b/klippy/mcu.py
@@ -275,7 +275,7 @@ class MCU_digital_out:
 class MCU_pwm:
     def __init__(self, mcu, pin_params):
         self._mcu = mcu
-        self._hard_pwm = False
+        self._hardware_pwm = False
         self._cycle_time = 0.100
         self._max_duration = 2.
         self._oid = None
@@ -291,14 +291,9 @@ class MCU_pwm:
         return self._mcu
     def setup_max_duration(self, max_duration):
         self._max_duration = max_duration
-    def setup_cycle_time(self, cycle_time):
+    def setup_cycle_time(self, cycle_time, hardware_pwm=False):
         self._cycle_time = cycle_time
-        self._hard_pwm = False
-    def setup_hard_pwm(self, hard_cycle_ticks):
-        if not hard_cycle_ticks:
-            return
-        self._cycle_time = hard_cycle_ticks
-        self._hard_pwm = True
+        self._hardware_pwm = hardware_pwm
     def setup_start_value(self, start_value, shutdown_value, is_static=False):
         if is_static and start_value != shutdown_value:
             raise pins.error("Static pin can not have shutdown value")
@@ -309,19 +304,20 @@ class MCU_pwm:
         self._shutdown_value = max(0., min(1., shutdown_value))
         self._is_static = is_static
     def build_config(self):
-        if self._hard_pwm:
+        cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time)
+        if self._hardware_pwm:
             self._pwm_max = self._mcu.get_constant_float("PWM_MAX")
             if self._is_static:
                 self._mcu.add_config_cmd(
                     "set_pwm_out pin=%s cycle_ticks=%d value=%d" % (
-                        self._pin, self._cycle_time,
+                        self._pin, cycle_ticks,
                         self._static_value * self._pwm_max))
                 return
             self._oid = self._mcu.create_oid()
             self._mcu.add_config_cmd(
                 "config_pwm_out oid=%d pin=%s cycle_ticks=%d value=%d"
                 " default_value=%d max_duration=%d" % (
-                    self._oid, self._pin, self._cycle_time,
+                    self._oid, self._pin, cycle_ticks,
                     self._start_value * self._pwm_max,
                     self._shutdown_value * self._pwm_max,
                     self._mcu.seconds_to_clock(self._max_duration)))
@@ -341,8 +337,7 @@ class MCU_pwm:
             self._mcu.add_config_cmd(
                 "config_soft_pwm_out oid=%d pin=%s cycle_ticks=%d value=%d"
                 " default_value=%d max_duration=%d" % (
-                    self._oid, self._pin,
-                    self._mcu.seconds_to_clock(self._cycle_time),
+                    self._oid, self._pin, cycle_ticks,
                     self._start_value >= 0.5, self._shutdown_value >= 0.5,
                     self._mcu.seconds_to_clock(self._max_duration)))
             self._set_cmd = self._mcu.lookup_command(