// 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 ulPin, uint32_t ulValue ) { #if 0 // pwm test #if 1 // port configuration *(volatile unsigned int *)0x44000010 |= 1 << 8; //GPIOx->OUTENSET //PAD_AFConfig(PAD_PC, GPIO_Pin_8, 0x01/*PAD_AF1*/); ///< PAD Config - LED used 2nd Function // (0x01 << 8) *(volatile unsigned int *)(0x41002080 + 0x20) &= ~0x03/*PAD_AF1*/; // (0x01 << 8) // *(volatile unsigned int *)(0x41002080 + 0x20) |= 0x01/*PAD_AF1*/; // (0x01 << 8) *(volatile unsigned int *)0x44000010 |= 1 << 9; //GPIOx->OUTENSET *(volatile unsigned int *)(0x41002080 + 0x24) &= ~0x03/*PAD_AF1*/; // (0x01 << 8) // *(volatile unsigned int *)(0x41002080 + 0x24) |= 0x01/*PAD_AF1*/; // (0x01 << 8) #endif // port configuration //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ /* Select Timer/Counter mode as Timer mode */ //PWM_CHn->TCMR = PWM_CHn_TCMR_TimerMode; // *(volatile unsigned int *)0x40005324 = (0x0ul); // ch3 *(volatile unsigned int *)0x40005024 = (0x0); /* Set Prescale register value */ //PWM_CHn->PR = PWM_TimerModeInitStruct->PWM_CHn_PR; // *(volatile unsigned int *)0x40005314 = (20000000 / 1000000) / 10 - 1; //PrescalerValue - 1; *(volatile unsigned int *)0x40005014 = (20000000 / 1000000) / 10 - 1; //PrescalerValue - 1; /* Set Match register value */ //PWM_CHn->MR = PWM_TimerModeInitStruct->PWM_CHn_MR; // *(volatile unsigned int *)0x40005318 = 80000; *(volatile unsigned int *)0x40005018 = 80000; /* Set Limit register value */ //PWM_CHn->LR = PWM_TimerModeInitStruct->PWM_CHn_LR; // *(volatile unsigned int *)0x4000531C = 100000; // 80% duty cycle *(volatile unsigned int *)0x4000501C = 100000; // 80% duty cycle /* Select Up-down mode */ //PWM_CHn->UDMR = PWM_TimerModeInitStruct->PWM_CHn_UDMR; // *(volatile unsigned int *)0x40005320 = (0x0ul); //PWM_CHn_UDMR_UpCount *(volatile unsigned int *)0x40005020 = (0x0); //PWM_CHn_UDMR_UpCount /* Select Periodic mode */ //PWM_CHn->PDMR = PWM_TimerModeInitStruct->PWM_CHn_PDMR; // *(volatile unsigned int *)0x40005334 = (0x1ul); //PWM_CHn_PDMR_Periodic *(volatile unsigned int *)0x40005034 = (0x1); //PWM_CHn_PDMR_Periodic //------------------------------------------------------------------------------------------ /* Select Timer/Counter mode as Timer mode */ //PWM_CHn->TCMR = PWM_CHn_TCMR_TimerMode; // *(volatile unsigned int *)0x40005324 = (0x0ul); // ch3 *(volatile unsigned int *)0x40005124 = (0x0ul); /* Set Prescale register value */ //PWM_CHn->PR = PWM_TimerModeInitStruct->PWM_CHn_PR; // *(volatile unsigned int *)0x40005314 = (20000000 / 1000000) / 10 - 1; //PrescalerValue - 1; *(volatile unsigned int *)0x40005114 = (20000000 / 1000000) / 10 - 1; //PrescalerValue - 1; /* Set Match register value */ //PWM_CHn->MR = PWM_TimerModeInitStruct->PWM_CHn_MR; // *(volatile unsigned int *)0x40005318 = 80000; *(volatile unsigned int *)0x40005118 = 80000; /* Set Limit register value */ //PWM_CHn->LR = PWM_TimerModeInitStruct->PWM_CHn_LR; // *(volatile unsigned int *)0x4000531C = 100000; // 80% duty cycle *(volatile unsigned int *)0x4000511C = 100000; // 80% duty cycle /* Select Up-down mode */ //PWM_CHn->UDMR = PWM_TimerModeInitStruct->PWM_CHn_UDMR; // *(volatile unsigned int *)0x40005320 = (0x0ul); //PWM_CHn_UDMR_UpCount *(volatile unsigned int *)0x40005120 = (0x0ul); //PWM_CHn_UDMR_UpCount /* Select Periodic mode */ //PWM_CHn->PDMR = PWM_TimerModeInitStruct->PWM_CHn_PDMR; // *(volatile unsigned int *)0x40005334 = (0x1ul); //PWM_CHn_PDMR_Periodic *(volatile unsigned int *)0x40005134 = (0x1ul); //PWM_CHn_PDMR_Periodic //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ //PWM->SSR &= PWM_SSR_SS3_Stop; // *(volatile unsigned int *)0x40005804 &= ~(0x1ul << 3); *(volatile unsigned int *)0x40005804 &= ~(0x1ul << 0); //PWM_CHn->PEEER = outputEnDisable; // *(volatile unsigned int *)0x40005328 = (0x2ul); *(volatile unsigned int *)0x40005028 = (0x2ul); //------------------------------------------------------------------------------------------ //PWM->SSR &= PWM_SSR_SS3_Stop; // *(volatile unsigned int *)0x40005804 &= ~(0x1ul << 3); *(volatile unsigned int *)0x40005804 &= ~(0x1ul << 1); //PWM_CHn->PEEER = outputEnDisable; // *(volatile unsigned int *)0x40005328 = (0x2ul); *(volatile unsigned int *)0x40005128 = (0x2ul); //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------ //PWM->SSR |= PWM_SSR_SS3_Start; // *(volatile unsigned int *)0x40005804 |= (0x1ul << 3); *(volatile unsigned int *)0x40005804 |= (0x1ul << 0); //------------------------------------------------------------------------------------------ //PWM->SSR |= PWM_SSR_SS3_Start; // *(volatile unsigned int *)0x40005804 |= (0x1ul << 3); *(volatile unsigned int *)0x40005804 |= (0x1ul << 1); while( !((*(volatile unsigned long *) 0x4000D018) & (0x01 << 7)) ) ; *(volatile unsigned long *) 0x4000D000 = 'P'; while( !((*(volatile unsigned long *) 0x4000D018) & (0x01 << 7)) ) ; *(volatile unsigned long *) 0x4000D000 = 'W'; while( !((*(volatile unsigned long *) 0x4000D018) & (0x01 << 7)) ) ; *(volatile unsigned long *) 0x4000D000 = 'M'; while(1); #endif #if 1 //------------------------------------------------------------------------------------------ *(volatile unsigned int *)(0x42000010 + pwm_pin_tbl[ulPin].port_num) |= 1 << pwm_pin_tbl[ulPin].pin_num; //GPIOx->OUTENSET //PAD_AFConfig(PAD_PC, GPIO_Pin_8, 0x01/*PAD_AF1*/); ///< PAD Config - LED used 2nd Function // (0x01 << 8) *(volatile unsigned int *)(0x41002000 + pwm_pin_tbl[ulPin].af_base + pwm_pin_tbl[ulPin].pin_num * 4) &= ~0x03/*PAD_AF1*/; // (0x01 << 8) *(volatile unsigned int *)(0x41002000 + pwm_pin_tbl[ulPin].af_base + pwm_pin_tbl[ulPin].pin_num * 4) |= pwm_pin_tbl[ulPin].af_num/*PAD_AF1*/; // (0x01 << 8) //------------------------------------------------------------------------------------------ //PWM-n /* Select Timer/Counter mode as Timer mode */ *(volatile unsigned int *)(0x40005024 + pwm_pin_tbl[ulPin].pwm_num) = (0x0); /* Set Prescale register value */ *(volatile unsigned int *)(0x40005014 + pwm_pin_tbl[ulPin].pwm_num) = (20000000 / 1000000) / 10 - 1; //PrescalerValue - 1; /* Set Match register value */ *(volatile unsigned int *)(0x40005018 + pwm_pin_tbl[ulPin].pwm_num) = 400 * ulValue; //MR /* Set Limit register value */ *(volatile unsigned int *)(0x4000501C + pwm_pin_tbl[ulPin].pwm_num) = 102400; // 80% duty cycle /* Select Up-down mode */ *(volatile unsigned int *)(0x40005020 + pwm_pin_tbl[ulPin].pwm_num) = (0x0); //PWM_CHn_UDMR_UpCount /* Select Periodic mode */ *(volatile unsigned int *)(0x40005034 + pwm_pin_tbl[ulPin].pwm_num) = (0x1); //PWM_CHn_PDMR_Periodic //------------------------------------------------------------------------------------------ //PWM->SSR &= PWM_SSR_SS3_Stop; *(volatile unsigned int *)0x40005804 &= ~(0x1 << pwm_pin_tbl[ulPin].pwm_pin); //PWM_CHn->PEEER = outputEnDisable; *(volatile unsigned int *)(0x40005028 + pwm_pin_tbl[ulPin].pwm_num) = 0x2; //------------------------------------------------------------------------------------------ //PWM->SSR |= PWM_SSR_SS3_Start; *(volatile unsigned int *)0x40005804 |= (0x1ul << pwm_pin_tbl[ulPin].pwm_pin); #if 0 // pwm serial character out while( !((*(volatile unsigned long *) 0x4000D018) & (0x01 << 7)) ) ; *(volatile unsigned long *) 0x4000D000 = 'P'; while( !((*(volatile unsigned long *) 0x4000D018) & (0x01 << 7)) ) ; *(volatile unsigned long *) 0x4000D000 = 'W'; while( !((*(volatile unsigned long *) 0x4000D018) & (0x01 << 7)) ) ; *(volatile unsigned long *) 0x4000D000 = 'M'; #endif // pwm serial character out #endif #if 0 uint32_t attr = g_APinDescription[ulPin].ulPinAttribute ; // uint32_t pwm_name = g_APinDescription[ulPin].ulTCChannel ; uint8_t isTC = 0 ; uint8_t Channelx ; Tc* TCx ; Tcc* TCCx ; if ( (attr & PIN_ATTR_ANALOG) == PIN_ATTR_ANALOG ) { if ( ulPin == 24 ) // Only 1 DAC on A0 (PA02) { ulValue = mapResolution(ulValue, _writeResolution, DAC_RESOLUTION); DAC->DATA.reg = ulValue & 0x3FF; // Dac on 10 bits. DAC->CTRLA.bit.ENABLE = 1; // DAC Enabled ///////////////////////////////////// syncDAC(); return; } } if ( (attr & PIN_ATTR_PWM) == PIN_ATTR_PWM ) { if ( (g_APinDescription[ulPin].ulPinType == PIO_TIMER) || g_APinDescription[ulPin].ulPinType == PIO_TIMER_ALT ) { pinPeripheral( ulPin, g_APinDescription[ulPin].ulPinType ) ; } switch ( g_APinDescription[ulPin].ulPWMChannel ) { case PWM3_CH0 : TCx = TC3 ; Channelx = 0 ; isTC = 1 ; break; case PWM3_CH1: TCx = TC3 ; Channelx = 1; isTC = 1; break; case PWM0_CH0 : TCCx = TCC0; Channelx = 0; break; case PWM0_CH1 : TCCx = TCC0; Channelx = 1; break; case PWM0_CH4 : TCCx = TCC0; Channelx = 0; break; case PWM0_CH5 : TCCx = TCC0; Channelx = 1; break; case PWM0_CH6 : TCCx = TCC0; Channelx = 2; break; case PWM0_CH7 : TCCx = TCC0; Channelx = 3; break; case PWM1_CH0 : TCCx = TCC1; Channelx = 0; break; case PWM1_CH1 : TCCx = TCC1; Channelx = 1; break; case PWM2_CH0 : TCCx = TCC2; Channelx = 0; break; case PWM2_CH1 : TCCx = TCC2; Channelx = 1; break; } // Enable clocks according to TCCx instance to use switch ( GetTCNumber( g_APinDescription[ulPin].ulPWMChannel ) ) { case 0: // TCC0 //Enable GCLK for TCC0 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC0_TCC1 )) ; break; case 1: // TCC1 //Enable GCLK for TCC1 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC0_TCC1 )) ; break; case 2: // TCC2 //Enable GCLK for TCC2 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC2_TC3 )) ; break; case 3: // TC3 //Enable GCLK for TC3 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC2_TC3 )); break; case 4: // TC4 //Enable GCLK for TC4 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TC4_TC5 )); break; case 5: // TC5 //Enable GCLK for TC5 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TC4_TC5 )) ; break; } // Set PORT if ( isTC ) { // -- Configure TC //DISABLE TCx TCx->COUNT8.CTRLA.reg &=~(TC_CTRLA_ENABLE); //Set Timer counter Mode to 8 bits TCx->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8; //Set TCx as normal PWM TCx->COUNT8.CTRLA.reg |= TC_CTRLA_WAVEGEN_NPWM; //Set TCx in waveform mode Normal PWM TCx->COUNT8.CC[Channelx].reg = (uint8_t) ulValue; //Set PER to maximum counter value (resolution : 0xFF) TCx->COUNT8.PER.reg = 0xFF; // Enable TCx TCx->COUNT8.CTRLA.reg |= TC_CTRLA_ENABLE; } else { // -- Configure TCC //DISABLE TCCx TCCx->CTRLA.reg &=~(TCC_CTRLA_ENABLE); //Set TCx as normal PWM TCCx->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM; //Set TCx in waveform mode Normal PWM TCCx->CC[Channelx].reg = (uint32_t)ulValue; //Set PER to maximum counter value (resolution : 0xFF) TCCx->PER.reg = 0xFF; //ENABLE TCCx TCCx->CTRLA.reg |= TCC_CTRLA_ENABLE ; } return ; } // -- Defaults to digital write pinMode( ulPin, OUTPUT ) ; if ( ulValue < 128 ) { digitalWrite( ulPin, LOW ) ; } else { digitalWrite( ulPin, HIGH ) ; } #endif }
// 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 analogFastWrite(uint32_t pin, uint32_t value) { PinDescription pinDesc = g_APinDescription[pin]; uint32_t attr = pinDesc.ulPinAttribute; if ((attr & PIN_ATTR_ANALOG) == PIN_ATTR_ANALOG) { // DAC handling code if (pin != PIN_A0) { // Only 1 DAC on A0 (PA02) return; } value = mapResolution(value, _writeResolution, 10); syncDAC(); DAC->DATA.reg = value & 0x3FF; // DAC on 10 bits. syncDAC(); DAC->CTRLA.bit.ENABLE = 0x01; // Enable DAC syncDAC(); return; } if ((attr & PIN_ATTR_PWM) == PIN_ATTR_PWM) { value = mapResolution(value, _writeResolution, 8); // change to 10 for 10 bit... must also change TCx->COUNT8.PER.reg = 0x3FF uint32_t tcNum = GetTCNumber(pinDesc.ulPWMChannel); uint8_t tcChannel = GetTCChannelNumber(pinDesc.ulPWMChannel); static bool tcEnabled[TCC_INST_NUM+TC_INST_NUM]; if (!tcEnabled[tcNum]) { tcEnabled[tcNum] = true; 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 { // We suppose that attr has PIN_ATTR_TIMER_ALT bit set... pinPeripheral(pin, PIO_TIMER_ALT); } 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->COUNT8.CTRLA.bit.ENABLE = 0; syncTC_8(TCx); // Set Timer counter Mode to 8 bits, normal PWM TCx->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8 | TC_CTRLA_WAVEGEN_NPWM; syncTC_8(TCx); // Set the initial value TCx->COUNT8.CC[tcChannel].reg = (uint8_t) value; syncTC_8(TCx); // Set PER to maximum counter value (resolution : 0xFF) TCx->COUNT8.PER.reg = 0xFF; syncTC_8(TCx); // Enable TCx TCx->COUNT8.CTRLA.bit.ENABLE = 1; syncTC_8(TCx); } else { // -- Configure TCC Tcc* TCCx = (Tcc*) GetTC(pinDesc.ulPWMChannel); // Disable TCCx TCCx->CTRLA.bit.ENABLE = 0; syncTCC(TCCx); // Set TCx 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 : 0xFF) TCCx->PER.reg = 0xFF; //change to 0x43FF for 10 bit... must also change mapping above syncTCC(TCCx); // Enable TCCx TCCx->CTRLA.bit.ENABLE = 1; syncTCC(TCCx); } } else { if (tcNum >= TCC_INST_NUM) { Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel); TCx->COUNT8.CC[tcChannel].reg = (uint8_t) value; syncTC_8(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; } // -- Defaults to digital write pinMode(pin, OUTPUT); value = mapResolution(value, _writeResolution, 8); if (value < 128) { digitalWrite(pin, LOW); } else { digitalWrite(pin, HIGH); } }