aboutsummaryrefslogtreecommitdiffstats
path: root/arduino/cores/nRF5/HardwarePWM.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'arduino/cores/nRF5/HardwarePWM.cpp')
-rwxr-xr-xarduino/cores/nRF5/HardwarePWM.cpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/arduino/cores/nRF5/HardwarePWM.cpp b/arduino/cores/nRF5/HardwarePWM.cpp
new file mode 100755
index 0000000..881c054
--- /dev/null
+++ b/arduino/cores/nRF5/HardwarePWM.cpp
@@ -0,0 +1,220 @@
+/**************************************************************************/
+/*!
+ @file HardwarePWM.cpp
+ @author hathach (tinyusb.org)
+
+ @section LICENSE
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2018, Adafruit Industries (adafruit.com)
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holders nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+#include "Arduino.h"
+#include "HardwarePWM.h"
+
+HardwarePWM HwPWM0(NRF_PWM0);
+HardwarePWM HwPWM1(NRF_PWM1);
+HardwarePWM HwPWM2(NRF_PWM2);
+
+#ifdef NRF_PWM3
+HardwarePWM HwPWM3(NRF_PWM3);
+#endif
+
+HardwarePWM* HwPWMx[] =
+{
+ &HwPWM0, &HwPWM1, &HwPWM2
+#ifdef NRF_PWM3
+ ,&HwPWM3
+#endif
+};
+
+HardwarePWM::HardwarePWM(NRF_PWM_Type* pwm)
+{
+ _pwm = pwm;
+ arrclr(_seq0);
+
+ _max_value = 255;
+ _clock_div = PWM_PRESCALER_PRESCALER_DIV_1; // 16 Mhz
+
+ // FIXME workaround to fix bootloader 0.2.6 does not clean up PSEL[1] of PWM0
+ _pwm->PSEL.OUT[1] = 0xFFFFFFFFUL;
+}
+
+void HardwarePWM::setResolution(uint8_t bitnum)
+{
+ setMaxValue( bit(min8(bitnum, 15)) -1 );
+}
+
+void HardwarePWM::setMaxValue(uint16_t value)
+{
+ _max_value = value;
+ _pwm->COUNTERTOP = value;
+}
+
+void HardwarePWM::setClockDiv(uint8_t div)
+{
+ _clock_div = min8(div, PWM_PRESCALER_PRESCALER_DIV_128);
+ _pwm->PRESCALER = _clock_div;
+}
+
+/**
+ * Add pin to this group.
+ * @param pin Pin to add
+ * @return true if add succeeded, or pin is already added
+ */
+bool HardwarePWM::addPin(uint8_t pin)
+{
+ // succeed if pin is already configured
+ if ( pin2channel(pin) >= 0 ) return true;
+
+ int ch = -1;
+
+ // find free slot which is not connected
+ for(int i=0; i<MAX_CHANNELS; i++)
+ {
+ if ( _pwm->PSEL.OUT[i] & PWM_PSEL_OUT_CONNECT_Msk )
+ {
+ ch = i;
+ break;
+ }
+ }
+ VERIFY ( ch >= 0 );
+
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+
+ // Must disable before changing PSEL
+ if ( enabled() )
+ {
+ _pwm->ENABLE = 0;
+ _pwm->PSEL.OUT[ch] = g_ADigitalPinMap[pin];
+ _pwm->ENABLE = 1;
+ _start();
+ }else
+ {
+ _pwm->PSEL.OUT[ch] = g_ADigitalPinMap[pin];
+ }
+
+ return true;
+}
+
+bool HardwarePWM::removePin(uint8_t pin)
+{
+ int ch = pin2channel(pin);
+ VERIFY( ch >= 0 );
+
+ bool const en = enabled();
+
+ // Must disable before changing PSEL
+ if ( en ) _pwm->ENABLE = 0;
+
+ _pwm->PSEL.OUT[ch] = 0xFFFFFFFFUL;
+ _seq0[ch] = 0;
+
+ if ( en ) _pwm->ENABLE = 1;
+
+ return true;
+}
+
+bool HardwarePWM::enabled (void)
+{
+ return _pwm->ENABLE;
+}
+
+void HardwarePWM::begin(void)
+{
+ // Initialize Registers
+ _pwm->MODE = PWM_MODE_UPDOWN_Up;
+ _pwm->COUNTERTOP = _max_value; // default is 255 (8 bit), can be configured before begin()
+ _pwm->PRESCALER = _clock_div;
+ _pwm->DECODER = PWM_DECODER_LOAD_Individual;
+ _pwm->LOOP = 0;
+
+ _pwm->SEQ[0].PTR = (uint32_t) _seq0;
+ _pwm->SEQ[0].CNT = MAX_CHANNELS; // default mode is Individual --> count must be 4
+ _pwm->SEQ[0].REFRESH = 0;
+ _pwm->SEQ[0].ENDDELAY = 0;
+
+ _pwm->SEQ[1].PTR = 0;
+ _pwm->SEQ[1].CNT = 0;
+ _pwm->SEQ[1].REFRESH = 0;
+ _pwm->SEQ[1].ENDDELAY = 0;
+
+ _pwm->ENABLE = 1;
+}
+
+void HardwarePWM::_start(void)
+{
+ // update sequence count (depending on mode)
+ // _pwm->SEQ[0].CNT = MAX_CHANNELS;
+
+ // start sequence
+ _pwm->TASKS_SEQSTART[0] = 1;
+}
+
+void HardwarePWM::stop(void)
+{
+ _pwm->ENABLE = 0;
+}
+
+bool HardwarePWM::writeChannel(uint8_t ch, uint16_t value, bool inverted )
+{
+ VERIFY( ch < MAX_CHANNELS );
+
+ _seq0[ch] = value | (inverted ? 0 : bit(15));
+
+ // Start PWM if not already
+ if ( !enabled() ) begin();
+
+ _start();
+
+ return true;
+}
+
+bool HardwarePWM::writePin(uint8_t pin, uint16_t value, bool inverted)
+{
+ int ch = pin2channel(pin);
+ VERIFY( ch >= 0 );
+
+ return writeChannel(ch, value, inverted);
+}
+
+uint16_t HardwarePWM::readPin(uint8_t pin)
+{
+ int ch = pin2channel(pin);
+ VERIFY( ch >= 0, 0);
+
+ return readChannel(ch);
+}
+
+uint16_t HardwarePWM::readChannel(uint8_t ch)
+{
+ // remove inverted bit
+ return (_seq0[ch] & 0x7FFF);
+}
+