/* Copyright (c) 2014 Arduino LLC. All right reserved. SAMD51 support added by Adafruit - Copyright (c) 2018 Dean Miller for Adafruit Industries 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 not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Arduino.h" #include "wiring_private.h" #ifdef __cplusplus extern "C" { #endif static int _readResolution = 10; static int _ADCResolution = 10; #if defined(__SAMD51__) static int _writeResolution = 12; static int _dacResolution = 12; #else static int _writeResolution = 8; //static int _dacResolution = 10; #endif #if !defined(__SAMD51__) // Wait for synchronization of registers between the clock domains static __inline__ void syncADC() __attribute__((always_inline, unused)); static void syncADC() { while (ADC->STATUS.bit.SYNCBUSY == 1) ; } // ATSAMR, for example, doesn't have a DAC #ifdef DAC // Wait for synchronization of registers between the clock domains static __inline__ void syncDAC() __attribute__((always_inline, unused)); static void syncDAC() { while (DAC->STATUS.bit.SYNCBUSY == 1) ; } #endif // Wait for synchronization of registers between the clock domains static __inline__ void syncTC_16(Tc* TCx) __attribute__((always_inline, unused)); static void syncTC_16(Tc* TCx) { while (TCx->COUNT16.STATUS.bit.SYNCBUSY); } // Wait for synchronization of registers between the clock domains static __inline__ void syncTCC(Tcc* TCCx) __attribute__((always_inline, unused)); static void syncTCC(Tcc* TCCx) { while (TCCx->SYNCBUSY.reg & TCC_SYNCBUSY_MASK); } #else static bool dacEnabled[2]; #endif void analogReadResolution(int res) { _readResolution = res; #if defined(__SAMD51__) if (res > 10) { ADC0->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val; ADC1->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val; _ADCResolution = 12; } else if (res > 8) { ADC0->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val; ADC1->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val; _ADCResolution = 10; } else { ADC0->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_8BIT_Val; ADC1->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_8BIT_Val; _ADCResolution = 8; } while(ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_CTRLB); //wait for sync while(ADC1->SYNCBUSY.reg & ADC_SYNCBUSY_CTRLB); //wait for sync #else if (res > 10) { ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val; _ADCResolution = 12; } else if (res > 8) { ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val; _ADCResolution = 10; } else { ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_8BIT_Val; _ADCResolution = 8; } syncADC(); #endif } void analogWriteResolution(int res) { _writeResolution = res; } static inline uint32_t mapResolution(uint32_t value, uint32_t from, uint32_t to) { if (from == to) { return value; } if (from > to) { return value >> (from-to); } return value << (to-from); } /* * Internal Reference is at 1.0v * External Reference should be between 1v and VDDANA-0.6v=2.7v * * Warning : On Arduino Zero board the input/output voltage for SAMD21G18 is 3.3 volts maximum */ void analogReference(eAnalogReference mode) { #if defined(__SAMD51__) while(ADC0->SYNCBUSY.reg & ADC_SYNCBUSY_REFCTRL); //wait for sync while(ADC1->SYNCBUSY.reg & ADC_SYNCBUSY_REFCTRL); //wait for sync //TODO: fix gains switch (mode) { case AR_INTERNAL1V0: //ADC0->GAINCORR.reg = ADC_GAINCORR_GAINCORR(); // Gain Factor Selection SUPC->VREF.bit.SEL = SUPC_VREF_SEL_1V0_Val; // select 1.0V SUPC->VREF.bit.VREFOE = 1; // Turn on for use with ADC ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // Use SUPC.VREF ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // break; case AR_INTERNAL1V1: //ADC0->GAINCORR.reg = ADC_GAINCORR_GAINCORR(); // Gain Factor Selection SUPC->VREF.bit.SEL = SUPC_VREF_SEL_1V1_Val; // select 1.1V SUPC->VREF.bit.VREFOE = 1; // Turn on for use with ADC ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // Use SUPC.VREF ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // break; case AR_INTERNAL1V2: //ADC0->GAINCORR.reg = ADC_GAINCORR_GAINCORR(); // Gain Factor Selection SUPC->VREF.bit.SEL = SUPC_VREF_SEL_1V2_Val; // select 1V2 SUPC->VREF.bit.VREFOE = 1; // Turn on for use with ADC ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // Use SUPC.VREF ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // break; case AR_INTERNAL1V25: //ADC0->GAINCORR.reg = ADC_GAINCORR_GAINCORR(); // Gain Factor Selection SUPC->VREF.bit.SEL = SUPC_VREF_SEL_1V25_Val; // select 1.25V SUPC->VREF.bit.VREFOE = 1; // Turn on for use with ADC ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // Use SUPC.VREF ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // break; case AR_INTERNAL2V0: //ADC0->GAINCORR.reg = ADC_GAINCORR_GAINCORR(); // Gain Factor Selection SUPC->VREF.bit.SEL = SUPC_VREF_SEL_2V0_Val; // select 2.0V SUPC->VREF.bit.VREFOE = 1; // Turn on for use with ADC ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // Use SUPC.VREF ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // break; case AR_INTERNAL2V2: //ADC0->GAINCORR.reg = ADC_GAINCORR_GAINCORR(); // Gain Factor Selection SUPC->VREF.bit.SEL = SUPC_VREF_SEL_2V2_Val; // select 2.2V SUPC->VREF.bit.VREFOE = 1; // Turn on for use with ADC ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // Use SUPC.VREF ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // break; case AR_INTERNAL2V4: //ADC0->GAINCORR.reg = ADC_GAINCORR_GAINCORR(); // Gain Factor Selection SUPC->VREF.bit.SEL = SUPC_VREF_SEL_2V4_Val; // select 2.4V SUPC->VREF.bit.VREFOE = 1; // Turn on for use with ADC ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // Use SUPC.VREF ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // break; case AR_INTERNAL2V5: //ADC0->GAINCORR.reg = ADC_GAINCORR_GAINCORR(); // Gain Factor Selection SUPC->VREF.bit.SEL = SUPC_VREF_SEL_2V5_Val; // select 2.5V SUPC->VREF.bit.VREFOE = 1; // Turn on for use with ADC ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // Use SUPC.VREF ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTREF_Val; // break; case AR_EXTERNAL: //ADC0->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain Factor Selection ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_AREFA_Val; // AREF is jumpered to VCC, so 3.3V ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_AREFA_Val; break; case AR_INTERNAL1V65: //ADC0->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_DIV2_Val; ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0_Val; // 1/2 VDDANA = 1.65 ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0_Val; // break; case AR_DEFAULT: default: //ADC0->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_DIV2_Val; ADC0->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC1_Val; // VDDANA = 3V3 ADC1->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC1_Val; // break; } #else syncADC(); switch (mode) { case AR_INTERNAL2V23: ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain Factor Selection ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0_Val; // 1/1.48 VDDANA = 1/1.48* 3V3 = 2.2297 break; case AR_EXTERNAL: ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain Factor Selection ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_AREFA_Val; break; case AR_INTERNAL1V0: ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain Factor Selection ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INT1V_Val; // 1.0V voltage reference break; case AR_INTERNAL1V65: ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain Factor Selection ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC1_Val; // 1/2 VDDANA = 0.5* 3V3 = 1.65V break; case AR_DEFAULT: default: ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_DIV2_Val; ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC1_Val; // 1/2 VDDANA = 0.5* 3V3 = 1.65V break; } #endif } uint32_t analogRead(uint32_t pin) { uint32_t valueRead = 0; #if defined(PIN_A6) if (pin == 6) { pin = PIN_A6; } else #endif #if defined(PIN_A7) if (pin == 7) { pin = PIN_A7; } else #endif if (pin <= 5) { pin += A0; } pinPeripheral(pin, PIO_ANALOG); //ATSAMR, for example, doesn't have a DAC #ifdef DAC #if defined(__SAMD51__) if (pin == PIN_DAC0 || pin == PIN_DAC1) { // Disable DAC, if analogWrite(A0,dval) used previously the DAC is enabled uint8_t channel = (pin == PIN_DAC0 ? 0 : 1); if(dacEnabled[channel]){ dacEnabled[channel] = false; while (DAC->SYNCBUSY.bit.ENABLE || DAC->SYNCBUSY.bit.SWRST); DAC->CTRLA.bit.ENABLE = 0; // disable DAC while (DAC->SYNCBUSY.bit.ENABLE || DAC->SYNCBUSY.bit.SWRST); DAC->DACCTRL[channel].bit.ENABLE = 0; while (DAC->SYNCBUSY.bit.ENABLE || DAC->SYNCBUSY.bit.SWRST); DAC->CTRLA.bit.ENABLE = 1; // enable DAC } while (DAC->SYNCBUSY.bit.ENABLE); #else if (pin == PIN_DAC0) { // Disable DAC, if analogWrite(A0,dval) used previously the DAC is enabled syncDAC(); DAC->CTRLA.bit.ENABLE = 0x00; // Disable DAC //DAC->CTRLB.bit.EOEN = 0x00; // The DAC output is turned off. syncDAC(); #endif } #endif #if defined(__SAMD51__) Adc *adc; if(g_APinDescription[pin].ulPinAttribute & PIN_ATTR_ANALOG) adc = ADC0; else if(g_APinDescription[pin].ulPinAttribute & PIN_ATTR_ANALOG_ALT) adc = ADC1; else return 0; while( adc->SYNCBUSY.reg & ADC_SYNCBUSY_INPUTCTRL ); //wait for sync adc->INPUTCTRL.bit.MUXPOS = g_APinDescription[pin].ulADCChannelNumber; // Selection for the positive ADC input // Control A /* * Bit 1 ENABLE: Enable * 0: The ADC is disabled. * 1: The ADC is enabled. * Due to synchronization, there is a delay from writing CTRLA.ENABLE until the peripheral is enabled/disabled. The * value written to CTRL.ENABLE will read back immediately and the Synchronization Busy bit in the Status register * (STATUS.SYNCBUSY) will be set. STATUS.SYNCBUSY will be cleared when the operation is complete. * * Before enabling the ADC, the asynchronous clock source must be selected and enabled, and the ADC reference must be * configured. The first conversion after the reference is changed must not be used. */ while( adc->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync adc->CTRLA.bit.ENABLE = 0x01; // Enable ADC // Start conversion while( adc->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync adc->SWTRIG.bit.START = 1; // Clear the Data Ready flag adc->INTFLAG.reg = ADC_INTFLAG_RESRDY; // Start conversion again, since The first conversion after the reference is changed must not be used. adc->SWTRIG.bit.START = 1; // Store the value while (adc->INTFLAG.bit.RESRDY == 0); // Waiting for conversion to complete valueRead = adc->RESULT.reg; while( adc->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync adc->CTRLA.bit.ENABLE = 0x00; // Disable ADC while( adc->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE ); //wait for sync #else syncADC(); ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[pin].ulADCChannelNumber; // Selection for the positive ADC input // Control A /* * Bit 1 ENABLE: Enable * 0: The ADC is disabled. * 1: The ADC is enabled. * Due to synchronization, there is a delay from writing CTRLA.ENABLE until the peripheral is enabled/disabled. The * value written to CTRL.ENABLE will read back immediately and the Synchronization Busy bit in the Status register * (STATUS.SYNCBUSY) will be set. STATUS.SYNCBUSY will be cleared when the operation is complete. * * Before enabling the ADC, the asynchronous clock source must be selected and enabled, and the ADC reference must be * configured. The first conversion after the reference is changed must not be used. */ syncADC(); ADC->CTRLA.bit.ENABLE = 0x01; // Enable ADC // Start conversion syncADC(); ADC->SWTRIG.bit.START = 1; // Waiting for the 1st conversion to complete while (ADC->INTFLAG.bit.RESRDY == 0); // Clear the Data Ready flag ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; // Start conversion again, since The first conversion after the reference is changed must not be used. syncADC(); ADC->SWTRIG.bit.START = 1; // Store the value while (ADC->INTFLAG.bit.RESRDY == 0); // Waiting for conversion to complete valueRead = ADC->RESULT.reg; syncADC(); ADC->CTRLA.bit.ENABLE = 0x00; // Disable ADC syncADC(); #endif return mapResolution(valueRead, _ADCResolution, _readResolution); } // Right now, PWM output only works on the pins with // hardware support. These are defined in the appropriate // pins_*.c file. For the rest of the pins, we default // to digital output. void analogWrite(uint32_t pin, uint32_t value) { PinDescription pinDesc = g_APinDescription[pin]; uint32_t attr = pinDesc.ulPinAttribute; // ATSAMR, for example, doesn't have a DAC #ifdef DAC if ((attr & PIN_ATTR_ANALOG) == PIN_ATTR_ANALOG) { // DAC handling code #if defined(__SAMD51__) if (pin == PIN_DAC0 || pin == PIN_DAC1) { // 2 DACs on A0 (PA02) and A1 (PA05) #else if (pin == PIN_DAC0) { // Only 1 DAC on A0 (PA02) #endif #if defined(__SAMD51__) value = mapResolution(value, _writeResolution, _dacResolution); uint8_t channel = (pin == PIN_DAC0 ? 0 : 1); pinPeripheral(pin, PIO_ANALOG); if(!dacEnabled[channel]){ dacEnabled[channel] = true; while (DAC->SYNCBUSY.bit.ENABLE || DAC->SYNCBUSY.bit.SWRST); DAC->CTRLA.bit.ENABLE = 0; // disable DAC while (DAC->SYNCBUSY.bit.ENABLE || DAC->SYNCBUSY.bit.SWRST); DAC->DACCTRL[channel].bit.ENABLE = 1; while (DAC->SYNCBUSY.bit.ENABLE || DAC->SYNCBUSY.bit.SWRST); DAC->CTRLA.bit.ENABLE = 1; // enable DAC if(channel == 0){ while ( !DAC->STATUS.bit.READY0 ); while (DAC->SYNCBUSY.bit.DATA0); DAC->DATA[0].reg = value; } else if(channel == 1){ while ( !DAC->STATUS.bit.READY1 ); while (DAC->SYNCBUSY.bit.DATA1); DAC->DATA[1].reg = value; } delayMicroseconds(10000); } //ERROR! while(!DAC->DACCTRL[channel].bit.ENABLE); if(channel == 0){ while ( !DAC->STATUS.bit.READY0 ); while (DAC->SYNCBUSY.bit.DATA0); DAC->DATA[0].reg = value; // DAC on 10 bits. } else if(channel == 1){ while ( !DAC->STATUS.bit.READY1 ); while (DAC->SYNCBUSY.bit.DATA1); DAC->DATA[1].reg = value; // DAC on 10 bits. } #else syncDAC(); DAC->DATA.reg = value & 0x3FF; // DAC on 10 bits. syncDAC(); DAC->CTRLA.bit.ENABLE = 0x01; // Enable DAC syncDAC(); #endif // __SAMD51__ return; } } #endif // DAC #if defined(__SAMD51__) if(attr & (PIN_ATTR_PWM_E|PIN_ATTR_PWM_F|PIN_ATTR_PWM_G)){ uint32_t tcNum = GetTCNumber(pinDesc.ulPWMChannel); uint8_t tcChannel = GetTCChannelNumber(pinDesc.ulPWMChannel); static bool tcEnabled[TCC_INST_NUM+TC_INST_NUM]; if(attr & PIN_ATTR_PWM_E) pinPeripheral(pin, PIO_TIMER); else if(attr & PIN_ATTR_PWM_F) pinPeripheral(pin, PIO_TIMER_ALT); else if(attr & PIN_ATTR_PWM_G) pinPeripheral(pin, PIO_TCC_PDEC); if (!tcEnabled[tcNum]) { tcEnabled[tcNum] = true; GCLK->PCHCTRL[GCLK_CLKCTRL_IDs[tcNum]].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); //use clock generator 0 // Set PORT if (tcNum >= TCC_INST_NUM) { // -- Configure TC Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); //reset TCx->COUNT8.CTRLA.bit.SWRST = 1; while (TCx->COUNT8.SYNCBUSY.bit.SWRST); // Disable TCx TCx->COUNT8.CTRLA.bit.ENABLE = 0; while (TCx->COUNT8.SYNCBUSY.bit.ENABLE); // Set Timer counter Mode to 8 bits, normal PWM, prescaler 1/256 TCx->COUNT8.CTRLA.reg = TC_CTRLA_MODE_COUNT8 | TC_CTRLA_PRESCALER_DIV256; TCx->COUNT8.WAVE.reg = TC_WAVE_WAVEGEN_NPWM; while (TCx->COUNT8.SYNCBUSY.bit.CC0); // Set the initial value TCx->COUNT8.CC[tcChannel].reg = (uint8_t) value; while (TCx->COUNT8.SYNCBUSY.bit.CC0); // Set PER to maximum counter value (resolution : 0xFF) TCx->COUNT8.PER.reg = 0xFF; while (TCx->COUNT8.SYNCBUSY.bit.PER); // Enable TCx TCx->COUNT8.CTRLA.bit.ENABLE = 1; while (TCx->COUNT8.SYNCBUSY.bit.ENABLE); } else { // -- Configure TCC Tcc* TCCx = (Tcc*) GetTC(pinDesc.ulPWMChannel); TCCx->CTRLA.bit.SWRST = 1; while (TCCx->SYNCBUSY.bit.SWRST); // Disable TCCx TCCx->CTRLA.bit.ENABLE = 0; while (TCCx->SYNCBUSY.bit.ENABLE); // Set prescaler to 1/256 TCCx->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV256 | TCC_CTRLA_PRESCSYNC_GCLK; // Set TCx as normal PWM TCCx->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; while ( TCCx->SYNCBUSY.bit.WAVE ); while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1); // Set the initial value TCCx->CC[tcChannel].reg = (uint32_t) value; while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1); // Set PER to maximum counter value (resolution : 0xFF) TCCx->PER.reg = 0xFF; while (TCCx->SYNCBUSY.bit.PER); // Enable TCCx TCCx->CTRLA.bit.ENABLE = 1; while (TCCx->SYNCBUSY.bit.ENABLE); } } else { if (tcNum >= TCC_INST_NUM) { Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); TCx->COUNT8.CC[tcChannel].reg = (uint8_t) value; while (TCx->COUNT8.SYNCBUSY.bit.CC0 || TCx->COUNT8.SYNCBUSY.bit.CC1); } else { Tcc* TCCx = (Tcc*) GetTC(pinDesc.ulPWMChannel); while (TCCx->SYNCBUSY.bit.CTRLB); while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1); TCCx->CCBUF[tcChannel].reg = (uint32_t) value; while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1); TCCx->CTRLBCLR.bit.LUPD = 1; while (TCCx->SYNCBUSY.bit.CTRLB); } } return; } #else if ((attr & PIN_ATTR_PWM) == PIN_ATTR_PWM) { value = mapResolution(value, _writeResolution, 16); uint32_t tcNum = GetTCNumber(pinDesc.ulPWMChannel); uint8_t tcChannel = GetTCChannelNumber(pinDesc.ulPWMChannel); static bool tcEnabled[TCC_INST_NUM+TC_INST_NUM]; if (attr & PIN_ATTR_TIMER) { #if !(ARDUINO_SAMD_VARIANT_COMPLIANCE >= 10603) // Compatibility for cores based on SAMD core <=1.6.2 if (pinDesc.ulPinType == PIO_TIMER_ALT) { pinPeripheral(pin, PIO_TIMER_ALT); } else #endif { pinPeripheral(pin, PIO_TIMER); } } else if ((attr & PIN_ATTR_TIMER_ALT) == PIN_ATTR_TIMER_ALT){ //this is on an alt timer pinPeripheral(pin, PIO_TIMER_ALT); } else{ return; } if (!tcEnabled[tcNum]) { tcEnabled[tcNum] = true; uint16_t GCLK_CLKCTRL_IDs[] = { GCLK_CLKCTRL_ID(GCM_TCC0_TCC1), // TCC0 GCLK_CLKCTRL_ID(GCM_TCC0_TCC1), // TCC1 GCLK_CLKCTRL_ID(GCM_TCC2_TC3), // TCC2 GCLK_CLKCTRL_ID(GCM_TCC2_TC3), // TC3 GCLK_CLKCTRL_ID(GCM_TC4_TC5), // TC4 GCLK_CLKCTRL_ID(GCM_TC4_TC5), // TC5 GCLK_CLKCTRL_ID(GCM_TC6_TC7), // TC6 GCLK_CLKCTRL_ID(GCM_TC6_TC7), // TC7 }; GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_IDs[tcNum]); while (GCLK->STATUS.bit.SYNCBUSY == 1); // Set PORT if (tcNum >= TCC_INST_NUM) { // -- Configure TC Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); // Disable TCx TCx->COUNT16.CTRLA.bit.ENABLE = 0; syncTC_16(TCx); // Set Timer counter Mode to 16 bits, normal PWM TCx->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_NPWM; syncTC_16(TCx); // Set the initial value TCx->COUNT16.CC[tcChannel].reg = (uint32_t) value; syncTC_16(TCx); // Enable TCx TCx->COUNT16.CTRLA.bit.ENABLE = 1; syncTC_16(TCx); } else { // -- Configure TCC Tcc* TCCx = (Tcc*) GetTC(pinDesc.ulPWMChannel); // Disable TCCx TCCx->CTRLA.bit.ENABLE = 0; syncTCC(TCCx); // Set TCCx as normal PWM TCCx->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM; syncTCC(TCCx); // Set the initial value TCCx->CC[tcChannel].reg = (uint32_t) value; syncTCC(TCCx); // Set PER to maximum counter value (resolution : 0xFFFF) TCCx->PER.reg = 0xFFFF; syncTCC(TCCx); // Enable TCCx TCCx->CTRLA.bit.ENABLE = 1; syncTCC(TCCx); } } else { if (tcNum >= TCC_INST_NUM) { Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); TCx->COUNT16.CC[tcChannel].reg = (uint32_t) value; syncTC_16(TCx); } else { Tcc* TCCx = (Tcc*) GetTC(pinDesc.ulPWMChannel); TCCx->CTRLBSET.bit.LUPD = 1; syncTCC(TCCx); TCCx->CCB[tcChannel].reg = (uint32_t) value; syncTCC(TCCx); TCCx->CTRLBCLR.bit.LUPD = 1; syncTCC(TCCx); } } return; } #endif // -- Defaults to digital write pinMode(pin, OUTPUT); value = mapResolution(value, _writeResolution, 8); if (value < 128) { digitalWrite(pin, LOW); } else { digitalWrite(pin, HIGH); } } #ifdef __cplusplus } #endif