diff --git a/docs/G-Codes.md b/docs/G-Codes.md
index 68e659114..8e588a70e 100644
--- a/docs/G-Codes.md
+++ b/docs/G-Codes.md
@@ -242,7 +242,10 @@ is enabled:
 
 The following command is available when an "output_pin" config section
 is enabled:
-- `SET_PIN PIN=config_name VALUE=<value>`
+- `SET_PIN PIN=config_name VALUE=<value> CYCLE_TIME=<cycle_time>`
+
+Note: Hardware PWM does not currently support the CYCLE_TIME parameter and will
+use the cycle time defined in the config.
 
 ## Manually Controlled Fans Commands
 
diff --git a/klippy/extras/multi_pin.py b/klippy/extras/multi_pin.py
index efa9676a8..fcaf0bb6c 100644
--- a/klippy/extras/multi_pin.py
+++ b/klippy/extras/multi_pin.py
@@ -46,9 +46,9 @@ class PrinterMultiPin:
     def set_digital(self, print_time, value):
         for mcu_pin in self.mcu_pins:
             mcu_pin.set_digital(print_time, value)
-    def set_pwm(self, print_time, value):
+    def set_pwm(self, print_time, value, cycle_time=None):
         for mcu_pin in self.mcu_pins:
-            mcu_pin.set_pwm(print_time, value)
+            mcu_pin.set_pwm(print_time, value, cycle_time)
 
 def load_config_prefix(config):
     return PrinterMultiPin(config)
diff --git a/klippy/extras/output_pin.py b/klippy/extras/output_pin.py
index 92c055fd3..549c788b2 100644
--- a/klippy/extras/output_pin.py
+++ b/klippy/extras/output_pin.py
@@ -17,11 +17,12 @@ class PrinterOutputPin:
             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.)
+            self.last_cycle_time = self.default_cycle_time = cycle_time
         else:
             self.mcu_pin = ppins.setup_pin('digital_out', config.get('pin'))
             self.scale = 1.
         self.mcu_pin.setup_max_duration(0.)
-        self.last_value_time = 0.
+        self.last_print_time = 0.
         static_value = config.getfloat('static_value', None,
                                        minval=0., maxval=self.scale)
         if static_value is not None:
@@ -45,18 +46,23 @@ class PrinterOutputPin:
     def cmd_SET_PIN(self, gcmd):
         value = gcmd.get_float('VALUE', minval=0., maxval=self.scale)
         value /= self.scale
-        if value == self.last_value:
-            return
         print_time = self.printer.lookup_object('toolhead').get_last_move_time()
-        print_time = max(print_time, self.last_value_time + PIN_MIN_TIME)
+        print_time = max(print_time, self.last_print_time + PIN_MIN_TIME)
         if self.is_pwm:
-            self.mcu_pin.set_pwm(print_time, value)
+            cycle_time = gcmd.get_float('CYCLE_TIME', self.default_cycle_time,
+                                        above=0.)
+            if value == self.last_value and cycle_time == self.last_cycle_time:
+                return
+            self.mcu_pin.set_pwm(print_time, value, cycle_time)
+            self.last_cycle_time = cycle_time
         else:
+            if value == self.last_value:
+                return
             if value not in [0., 1.]:
                 raise gcmd.error("Invalid pin value")
             self.mcu_pin.set_digital(print_time, value)
         self.last_value = value
-        self.last_value_time = print_time
+        self.last_print_time = print_time
 
 def load_config_prefix(config):
     return PrinterOutputPin(config)
diff --git a/klippy/extras/replicape.py b/klippy/extras/replicape.py
index 9236264f1..27def9f34 100644
--- a/klippy/extras/replicape.py
+++ b/klippy/extras/replicape.py
@@ -74,7 +74,7 @@ class pca9685_pwm:
         cmd_queue = self._mcu.alloc_command_queue()
         self._set_cmd = self._mcu.lookup_command(
             "schedule_pca9685_out oid=%c clock=%u value=%hu", cq=cmd_queue)
-    def set_pwm(self, print_time, value):
+    def set_pwm(self, print_time, value, cycle_time=None):
         clock = self._mcu.print_time_to_clock(print_time)
         if self._invert:
             value = 1. - value
diff --git a/klippy/extras/sx1509.py b/klippy/extras/sx1509.py
index 7a4f24dbc..8b19dda80 100644
--- a/klippy/extras/sx1509.py
+++ b/klippy/extras/sx1509.py
@@ -134,7 +134,7 @@ class SX1509_digital_out(object):
         else:
             self._sx1509.clear_bits_in_register(REG_DATA, self._bitmask)
         self._sx1509.send_register(REG_DATA, print_time)
-    def set_pwm(self, print_time, value):
+    def set_pwm(self, print_time, value, cycle_time=None):
         self.set_digital(print_time, value >= 0.5)
 
 class SX1509_pwm(object):
@@ -191,7 +191,7 @@ class SX1509_pwm(object):
         self._start_value = max(0., min(1., start_value))
         self._shutdown_value = max(0., min(1., shutdown_value))
         self._is_static = is_static
-    def set_pwm(self, print_time, value):
+    def set_pwm(self, print_time, value, cycle_time=None):
         self._sx1509.set_register(self._i_on_reg, ~int(255 * value)
                                   if not self._invert
                                   else int(255 * value) & 0xFF)
diff --git a/klippy/mcu.py b/klippy/mcu.py
index 70e6b9690..53d1ff743 100644
--- a/klippy/mcu.py
+++ b/klippy/mcu.py
@@ -160,7 +160,7 @@ class MCU_digital_out:
         self._set_cmd.send([self._oid, clock, (not not value) ^ self._invert],
                            minclock=self._last_clock, reqclock=clock)
         self._last_clock = clock
-    def set_pwm(self, print_time, value):
+    def set_pwm(self, print_time, value, cycle_time=None):
         self.set_digital(print_time, value >= 0.5)
 
 class MCU_pwm:
@@ -249,8 +249,10 @@ class MCU_pwm:
         self._set_cmd = self._mcu.lookup_command(
             "schedule_soft_pwm_out oid=%c clock=%u on_ticks=%u off_ticks=%u",
             cq=cmd_queue)
-    def set_pwm(self, print_time, value):
-        cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time)
+    def set_pwm(self, print_time, value, cycle_time=None):
+        if cycle_time is None:
+            cycle_time = self._cycle_time
+        cycle_ticks = self._mcu.seconds_to_clock(cycle_time)
         clock = self._mcu.print_time_to_clock(print_time)
         if self._invert:
             value = 1. - value