Arduino公式のSoftPWMを使ってみようと思って調べてみたら周波数が固定かつ低すぎる(60Hz)のとタイマー割込みを使ってるためボード依存があるのが気になったので全てのArduinoで使える汎用的なものを作ってみた。
波形は計算処理のみで作っているため最大周波数/分解能/精度などはCPUの処理能力に大きく依存する。また、パルス出力中はCPUを占有するため実用的ではないかもしれないが台形駆動もサポートしているため実験用として使うにはいいかもしれない。
armなどの32ビットCPUならまだしもさすがにavrなどの8ビットCPUにはかなり辛そうな計算処理ではあるが...それでもavr/16MHzで最大1KHz程度まで、samd21/48MHzなら5KHz程度までは使えそう。
【avr/16MHz-1KHzで台形駆動】
【サンプル・スケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include "swpwm.h" #define PIN_PWM 9 SWPWM pulse(1000, 1000000, 1000000, PIN_PWM); void setup() { pulse.begin(); } void loop() { pulse.output(1000000, 90); } |
【ライブラリ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
/* swpwm.h - Software PWM Library for Arduino Copyright (c) 2023 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if n, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once class SWPWM { public: SWPWM(uint32_t pulse, uint32_t accel, uint32_t decel, uint8_t port, bool polarity = 0) : _pulse(pulse) , _accel(accel) , _decel(decel) , _port(port) , _polarity(polarity) { } virtual ~SWPWM(void) { } void begin(void) { pinMode(_port, OUTPUT); digitalWrite(_port, _polarity); } void end(void) { pinMode(_port, INPUT); } void output(uint32_t width, uint8_t duty = 100) { if (duty > 100) duty = 100; uint32_t p = (uint64_t)_pulse * duty / 100; uint32_t a = (uint64_t)_accel * duty / 100; uint32_t d = (uint64_t)_decel * duty / 100; uint32_t t, u = micros(); uint8_t v = 0; while ((t = micros() - u) < a) { if (v != (t % _pulse < (uint64_t)p * t / a)) digitalWrite(_port, _polarity ^ (v ^= 1)); yield(); } width += a; while ((t = micros() - u) < width) { if (v != (t % _pulse < p)) digitalWrite(_port, _polarity ^ (v ^= 1)); yield(); } width += d; while ((t = micros() - u) < width) { if (v != (t % _pulse < (uint64_t)p * (width - t) / d)) digitalWrite(_port, _polarity ^ (v ^= 1)); yield(); } digitalWrite(_port, _polarity); } private: uint32_t _pulse; uint32_t _accel; uint32_t _decel; uint8_t _port; uint8_t _polarity; }; |