Last active
February 1, 2026 10:09
-
-
Save mtei/7e6929e9b6c2f8382a6a870b0b81bb45 to your computer and use it in GitHub Desktop.
soft PWM for Arduino
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <Arduino.h> | |
| //================================================== | |
| // softPWM クラス | |
| //================================================== | |
| class softPWM { | |
| public: | |
| using callback_t = void (*)(void); | |
| void setup(unsigned int period_ms, | |
| unsigned int duty_ms, | |
| unsigned long run_ms, | |
| callback_t on_cb, | |
| callback_t off_cb); | |
| void setup(unsigned int period_ms, | |
| unsigned int duty_ms, | |
| unsigned long run_ms); | |
| bool start(void); | |
| void stop(void); | |
| bool update(unsigned long now_ms); | |
| private: | |
| unsigned int period_ms = 0; | |
| unsigned int duty_ms = 0; | |
| unsigned long run_ms = 0; | |
| callback_t on_cb = nullptr; | |
| callback_t off_cb = nullptr; | |
| unsigned long last_cycle_ms = 0; | |
| unsigned long start_ms = 0; | |
| bool is_on = false; | |
| bool active = false; | |
| bool configured = false; | |
| bool started = false; | |
| }; | |
| //-------------------------------------------------- | |
| void softPWM::setup(unsigned int period, | |
| unsigned int duty, | |
| unsigned long run_time, | |
| callback_t on, | |
| callback_t off) | |
| { | |
| if (active) return; | |
| if (period == 0) period = 1; | |
| if (duty >= period) duty = period; | |
| period_ms = period; | |
| duty_ms = duty; | |
| if (run_time == 0) { | |
| run_ms = 0; | |
| } else if (run_time <= duty_ms) { | |
| run_ms = duty_ms; | |
| } else { | |
| const unsigned long remain = run_time - duty_ms; | |
| const unsigned long k = | |
| (remain + period_ms - 1UL) / period_ms; | |
| run_ms = k * (unsigned long)period_ms + (unsigned long)duty_ms; | |
| } | |
| on_cb = on; | |
| off_cb = off; | |
| configured = true; | |
| } | |
| //-------------------------------------------------- | |
| void softPWM::setup(unsigned int period, | |
| unsigned int duty, | |
| unsigned long run_time) | |
| { | |
| setup(period, duty, run_time, on_cb, off_cb); | |
| } | |
| //-------------------------------------------------- | |
| bool softPWM::start(void) | |
| { | |
| if (!configured) return false; | |
| if (active) { | |
| stop(); | |
| } | |
| active = true; | |
| is_on = false; | |
| started = false; | |
| last_cycle_ms = 0; | |
| start_ms = 0; | |
| return true; | |
| } | |
| //-------------------------------------------------- | |
| void softPWM::stop(void) | |
| { | |
| if (!active) return; | |
| active = false; | |
| if (is_on && off_cb) { | |
| off_cb(); | |
| } | |
| is_on = false; | |
| started = false; | |
| } | |
| //-------------------------------------------------- | |
| bool softPWM::update(unsigned long now_ms) | |
| { | |
| if (!active) return active; | |
| if (!started) { | |
| started = true; | |
| last_cycle_ms = now_ms; | |
| start_ms = now_ms; | |
| if (on_cb) on_cb(); | |
| is_on = true; | |
| return active; | |
| } | |
| if (run_ms > 0 && (now_ms - start_ms) >= run_ms) { | |
| stop(); | |
| return active; | |
| } | |
| const unsigned long elapsed = now_ms - last_cycle_ms; | |
| if (elapsed >= period_ms) { | |
| last_cycle_ms = now_ms; | |
| if (on_cb) on_cb(); | |
| is_on = true; | |
| return active; | |
| } | |
| if (is_on && elapsed >= duty_ms) { | |
| if (off_cb) off_cb(); | |
| is_on = false; | |
| } | |
| return active; | |
| } | |
| //================================================== | |
| // テスト用コード(UNO ビルトインLED) | |
| //================================================== | |
| constexpr int LED_PIN = LED_BUILTIN; | |
| void ledOn() | |
| { | |
| digitalWrite(LED_PIN, HIGH); | |
| } | |
| void ledOff() | |
| { | |
| digitalWrite(LED_PIN, LOW); | |
| } | |
| softPWM pwm; | |
| void setup() | |
| { | |
| pinMode(LED_PIN, OUTPUT); | |
| digitalWrite(LED_PIN, LOW); | |
| // 周期 2000ms / duty 300ms / 稼働 8000ms | |
| pwm.setup(2000, 300, 8000, ledOn, ledOff); | |
| pwm.start(); | |
| delay(3000); | |
| ledOn(); | |
| delay(3000); | |
| ledOff(); | |
| delay(500); | |
| } | |
| void loop() | |
| { | |
| if (! pwm.update(millis()) ) { | |
| ledOn(); | |
| delay(2000); | |
| ledOff(); | |
| delay(1000); | |
| pwm.setup(500, 250, 0, ledOn, ledOff); | |
| pwm.start(); | |
| } | |
| } | |
| // # Local Variables: | |
| // # mode: C++ | |
| // # End: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment