uint32_t PWM_ConfigOutputChannelf(PWM_T *pwm, uint32_t u32ChannelNum, uint32_t u32Frequency, float u32DutyCycle) { uint32_t u32Src; uint32_t u32PWMClockSrc; uint32_t i; uint16_t u16Prescale = 1, u16CNR = 0xFFFF; if(pwm == PWM0) u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM0SEL_Msk; else//(pwm == PWM1) u32Src = CLK->CLKSEL2 & CLK_CLKSEL2_PWM1SEL_Msk; if(u32Src == 0) { //clock source is from PLL clock u32PWMClockSrc = CLK_GetPLLClockFreq(); } else { //clock source is from PCLK SystemCoreClockUpdate(); u32PWMClockSrc = SystemCoreClock; } for(u16Prescale = 1; u16Prescale < 0xFFF; u16Prescale++) { i = (u32PWMClockSrc / u32Frequency) / u16Prescale; if(i > (0x10000)) continue; u16CNR = i; break; } i = u32PWMClockSrc / ((u16Prescale + 1) * u16CNR); PWM_SET_PRESCALER(pwm, u32ChannelNum, --u16Prescale); /* Set Counter to upcount */ (pwm)->CTL1 = ((pwm)->CTL1 & ~(PWM_CTL1_CNTTYPE0_Msk << (2 * u32ChannelNum))) | (0UL << (2 * u32ChannelNum)); (pwm)->CTL1 &= ~(PWM_CTL1_CNTMODE0_Msk << u32ChannelNum); if(u32DutyCycle == 100) { PWM_SET_CMR(pwm, u32ChannelNum, u16CNR); PWM_SET_CNR(pwm, u32ChannelNum, --u16CNR); } else { PWM_SET_CMR(pwm, u32ChannelNum, (uint32_t)(u32DutyCycle * (u16CNR + 1) / 100 - 1)); PWM_SET_CNR(pwm, u32ChannelNum, --u16CNR); } (pwm)->WGCTL0 = ((pwm)->WGCTL0 & ~(PWM_WGCTL0_ZPCTL0_Msk << (u32ChannelNum * 2 + PWM_WGCTL0_ZPCTL0_Msk))) | (PWM_OUTPUT_HIGH << (u32ChannelNum * 2 + PWM_WGCTL0_ZPCTL0_Pos)); (pwm)->WGCTL1 = ((pwm)->WGCTL1 & ~(PWM_WGCTL1_CMPUCTL0_Msk << (u32ChannelNum * 2 + PWM_WGCTL1_CMPUCTL0_Pos))) | (PWM_OUTPUT_LOW << (u32ChannelNum * 2 + PWM_WGCTL1_CMPUCTL0_Pos)); return(i); }
void PWM0_Init() { /*---------------------------------------------------------------------------------------------------------*/ /* Init PWM0 */ /*---------------------------------------------------------------------------------------------------------*/ /* Set PWM0 clock prescaler */ PWM_SET_PRESCALER(PWM0, 0, 0); /* Set up counter type */ PWM0->CTL1 &= ~PWM_CTL1_CNTTYPEn_Msk; /* Set PWM0 timer duty */ PWM_SET_CMR(PWM0, 0, 360); /* Set PWM0 timer period */ PWM_SET_CNR(PWM0, 0, 720); /* PWM period point trigger DAC enable */ PWM0->DACTRGEN = 0x1 << PWM_DACTRGEN_PTEn_Pos; /* Set waveform generation */ PWM0->WGCTL0 = 0xAAA;//PWM zero point and period point output High. PWM0->WGCTL1 = 0x555;//PWM compare down point and compare up point output Low. /* Enable output of all PWM0 channels */ PWM0->POEN |= PWM_POEN_POENn_Msk; }
/** * @brief PWM0 IRQ Handler * * @param None * * @return None * * @details ISR to handle PWM0 interrupt event */ void PWM0_IRQHandler(void) { static int toggle = 0; // Update PWM0 channel 0 period and duty if(toggle == 0) { PWM_SET_CNR(PWM0, 0, 99); PWM_SET_CMR(PWM0, 0, 39); } else { PWM_SET_CNR(PWM0, 0, 399); PWM_SET_CMR(PWM0, 0, 199); } toggle ^= 1; // Clear channel 0 period interrupt flag PWM_ClearPeriodIntFlag(PWM0, 0); }
/*---------------------------------------------------------------------------------------------------------*/ int32_t main(void) { /* Init System, IP clock and multi-function I/O In the end of SYS_Init() will issue SYS_LockReg() to lock protected register. If user want to write protected register, please issue SYS_UnlockReg() to unlock protected register if necessary */ /* Unlock protected registers */ SYS_UnlockReg(); /* Init System, IP clock and multi-function I/O */ SYS_Init(); /* Lock protected registers */ SYS_LockReg(); /* Init UART to 115200-8n1 for print message */ UART0_Init(); printf("\n\nCPU @ %dHz(PLL@ %dHz)\n", SystemCoreClock, PllClock); printf("PWM1 clock is from %s\n", (CLK->CLKSEL2 & CLK_CLKSEL2_PWM1SEL_Msk) ? "CPU" : "PLL"); printf("+------------------------------------------------------------------------+\n"); printf("| PWM Driver Sample Code |\n"); printf("| |\n"); printf("+------------------------------------------------------------------------+\n"); printf(" This sample code will use PWM1 channel 2 to capture the signal from PWM1 channel 0.\n"); printf(" And the captured data is transferred by PDMA channel 0.\n"); printf(" I/O configuration:\n"); printf(" PWM1 channel 2(PC.11) <--> PWM1 channel 0(PC.6)\n\n"); printf("Use PWM1 Channel 2(PC.11) to capture the PWM1 Channel 0(PC.6) Waveform\n"); while(1) { printf("\n\nPress any key to start PWM Capture Test\n"); getchar(); /*--------------------------------------------------------------------------------------*/ /* Set the PWM1 Channel 0 as PWM output function. */ /*--------------------------------------------------------------------------------------*/ /* Assume PWM output frequency is 1500Hz and duty ratio is 30%, user can calculate PWM settings by follows. duty ratio = (CMR+1)/(CNR+1) cycle time = CNR+1 High level = CMR+1 PWM clock source frequency = PLL = 72000000 (CNR+1) = PWM clock source frequency/prescaler/PWM output frequency = 72000000/2/1500 = 24000 (Note: CNR is 16 bits, so if calculated value is larger than 65536, user should increase prescale value.) CNR = 23999 duty ratio = 30% ==> (CMR+1)/(CNR+1) = 30% CMR = 7199 Prescale value is 1 : prescaler= 2 */ /*Set counter as down count*/ PWM1->CTL1 = (PWM1->CTL1 & ~PWM_CTL1_CNTTYPE0_Msk) | 0x1; /*Set PWM Timer clock prescaler*/ PWM_SET_PRESCALER(PWM1, 0, 1); // Divided by 2 /*Set PWM Timer duty*/ PWM_SET_CMR(PWM1, 0, 7199); /*Set PWM Timer period*/ PWM_SET_CNR(PWM1, 0, 23999); /* Set waveform generation */ PWM1->WGCTL0 = 0x00010000; PWM1->WGCTL1 = 0x00020000; /* Enable PWM Output path for PWM1 channel 0 */ PWM1->POEN |= PWM_CH_0_MASK; /* Enable Timer for PWM1 channel 0 */ PWM1->CNTEN |= PWM_CH_0_MASK; /*--------------------------------------------------------------------------------------*/ /* Configure PDMA peripheral mode form PWM to memory */ /*--------------------------------------------------------------------------------------*/ /* Open Channel 0 */ PDMA->CHCTL |= 0x1; PDMA->DSCT[0].CTL &= ~(PDMA_DSCT_CTL_TXCNT_Msk | PDMA_DSCT_CTL_TXWIDTH_Msk); /* transfer width is half word(16 bit) and transfer count is 4 */ PDMA->DSCT[0].CTL |= ((0x1 << PDMA_DSCT_CTL_TXWIDTH_Pos) | ((4 - 1) << PDMA_DSCT_CTL_TXCNT_Pos)); /* Set source address as PWM capture channel PDMA register(no increment) and destination address as g_u32Count array(increment) */ PDMA->DSCT[0].SA = (uint32_t)&PWM1->PDMACAP2_3; PDMA->DSCT[0].DA = (uint32_t)&g_u32Count[0]; PDMA->DSCT[0].CTL &= ~(PDMA_DSCT_CTL_SAINC_Msk | PDMA_DSCT_CTL_DAINC_Msk); PDMA->DSCT[0].CTL |= ((0x3 << PDMA_DSCT_CTL_SAINC_Pos) | (0x2 << PDMA_DSCT_CTL_DAINC_Pos)); /* Select PDMA request source as PWM RX(PWM1 channel 2 should be PWM1 pair 2) */ PDMA->REQSEL0_3 = (PDMA->REQSEL0_3 & ~PDMA_REQSEL0_3_REQSRC0_Msk) | (0xF << PDMA_REQSEL0_3_REQSRC0_Pos); /* Select PDMA operation mode as basic mode */ PDMA->DSCT[0].CTL = (PDMA->DSCT[0].CTL & ~PDMA_DSCT_CTL_OPMODE_Msk) | 0x1; /* Set PDMA as single request type for PWM */ PDMA->DSCT[0].CTL = (PDMA->DSCT[0].CTL & ~(PDMA_DSCT_CTL_TXTYPE_Msk)) | (0x1 << PDMA_DSCT_CTL_TXTYPE_Pos); PDMA->INTEN |= (1 << 0); NVIC_EnableIRQ(PDMA_IRQn); /* Enable PDMA for PWM1 channel 2 capture function, and set capture order as falling first */ PWM1->PDMACTL &= ~(PWM_PDMACTL_CHSEL2_3_Msk | PWM_PDMACTL_CAPORD2_3_Msk); /* Select capture mode as both rising and falling to do PDMA transfer */ PWM1->PDMACTL |= (PWM_PDMACTL_CAPMOD2_3_Msk | PWM_PDMACTL_CHEN2_3_Msk); /*--------------------------------------------------------------------------------------*/ /* Set the PWM1 channel 2 for capture function */ /*--------------------------------------------------------------------------------------*/ /* If input minimum frequency is 1500Hz, user can calculate capture settings by follows. Capture clock source frequency = PLL = 72000000 in the sample code. (CNR+1) = Capture clock source frequency/prescaler/minimum input frequency = 72000000/2/1500 = 24000 (Note: CNR is 16 bits, so if calculated value is larger than 65536, user should increase prescale value.) CNR = 0xFFFF (Note: In capture mode, user should set CNR to 0xFFFF to increase capture frequency range.) */ /*Set counter as down count*/ PWM1->CTL1 = (PWM1->CTL1 & ~PWM_CTL1_CNTTYPE2_Msk) | (0x1 << PWM_CTL1_CNTTYPE2_Pos); /*Set PWM1 channel 2 Timer clock prescaler*/ PWM_SET_PRESCALER(PWM1, 2, 1); // Divided by 2 /*Set PWM1 channel 2 Timer period*/ PWM_SET_CNR(PWM1, 2, 0xFFFF); /* Enable capture function */ PWM1->CAPCTL |= PWM_CAPCTL_CAPEN2_Msk; /* Enable falling capture reload */ PWM1->CAPCTL |= PWM_CAPCTL_FCRLDEN2_Msk; // Start PWM1->CNTEN |= PWM_CNTEN_CNTEN2_Msk; /* Wait until PWM1 channel 2 Timer start to count */ while((PWM1->CNT[2]) == 0); /* Enable capture input path for PWM1 channel 2 */ PWM1->CAPINEN |= PWM_CAPINEN_CAPINEN2_Msk; /* Capture the Input Waveform Data */ //CalPeriodTime(PWM1, 2); CalPeriodTime(); /*---------------------------------------------------------------------------------------------------------*/ /* Stop PWM1 channel 0 (Recommended procedure method 1) */ /* Set PWM Timer loaded value(Period) as 0. When PWM internal counter(CNT) reaches to 0, disable PWM Timer */ /*---------------------------------------------------------------------------------------------------------*/ /* Set PWM1 channel 0 loaded value as 0 */ PWM1->PERIOD[0] = 0; /* Wait until PWM1 channel 0 Timer Stop */ while((PWM1->CNT[0] & PWM_CNT_CNT_Msk) != 0); /* Disable Timer for PWM1 channel 0 */ PWM1->CNTEN &= ~PWM_CNTEN_CNTEN0_Msk; /* Disable PWM Output path for PWM1 channel 0 */ PWM1->POEN &= ~PWM_CH_0_MASK; /*---------------------------------------------------------------------------------------------------------*/ /* Stop PWM1 channel 2 (Recommended procedure method 1) */ /* Set PWM Timer loaded value(Period) as 0. When PWM internal counter(CNT) reaches to 0, disable PWM Timer */ /*---------------------------------------------------------------------------------------------------------*/ /* Disable PDMA NVIC */ NVIC_DisableIRQ(PDMA_IRQn); /* Set loaded value as 0 for PWM1 channel 2 */ PWM1->PERIOD[2] = 0; /* Wait until PWM1 channel 2 current counter reach to 0 */ while((PWM1->CNT[2] & PWM_CNT_CNT_Msk) != 0); /* Disable Timer for PWM1 channel 2 */ PWM1->CNTEN &= ~PWM_CNTEN_CNTEN2_Msk; /* Disable Capture Function and Capture Input path for PWM1 channel 2*/ PWM1->CAPCTL &= ~PWM_CAPCTL_CAPEN2_Msk; PWM1->CAPINEN &= ~PWM_CAPINEN_CAPINEN2_Msk; /* Clear Capture Interrupt flag for PWM1 channel 2 */ PWM1->CAPIF = PWM_CAPIF_CFLIF2_Msk; PDMA->CHCTL = 0; } }
int32_t main (void) { /* Init System, IP clock and multi-function I/O In the end of SYS_Init() will issue SYS_LockReg() to lock protected register. If user want to write protected register, please issue SYS_UnlockReg() to unlock protected register if necessary */ SYS_Init(); /* Init UART to 115200-8n1 for print message */ UART_Open(UART0, 115200); printf("\nThis sample code will output PWM channel 0 to with different duty\n"); printf(", and enable/disable Precise Center Align function.\n"); printf("Polling 1 period interrupt flag to get PWM channel 0 output.\n"); // PWM-Timer 0 enable and Auto-reload PWM->CTL = PWM_CTL_CNTEN0_Msk | PWM_CTL_CNTMODE0_Msk; PWM_SET_PRESCALER(PWM, 0, 1); PWM_SET_DIVIDER(PWM, 0, PWM_CLK_DIV_1); // Set the PWM aligned type PWM_SET_ALIGNED_TYPE(PWM, 0, PWM_CENTER_ALIGNED); // Enable PWM channel 0 output PWM_EnableOutput(PWM, BIT0); // Start PWM_Start(PWM, BIT0); /* Precise Center Align and Center Align PWM channel 0 waveform of this sample shown below: |<- CNR-(2*(CMR+1)) ->| CNR-(2*(CMR+1) = 401 -(2*(100+1)) CLKs |<- CNR -( 2 *( CMR + 1)) ->| CNR-(2*(CMR+1) = 402 -(2*(99+1)) CLKs |<- 2 *(CNR-CMR) clk ->| 2 * (CNR - CMR) = 2 * (401-100) CLKs |<- 2 * (CNR-CMR) clk ->| 2 * (CNR - CMR) = 2 * (402-99) CLKs ________ ____________ ________ ____________ ________ ____________ ________ ____________ ____| 7.96us |_8.08us_| 24.08us |_8.08us_| 8.08us |_8us_| 24.24us |_8us_| 7.96us |_8.08us_| 24.08us |_8.08us_| 8.08us |_8us_| 24.24us |_8us_ */ while(1) { // Enable PWM Precise Center Aligned Type PWM->PCACTL = PWM_PCACTL_PCAEN_Msk; // PWM Channel 0 Output : duty = 7.96u, low = 8.08u PWM_SET_CMR(PWM, 0, 100); PWM_SET_CNR(PWM, 0, 401); // Polling, Wait 1 period interrupt flags while(PWM_GetPeriodIntFlag(PWM, 0) == 0); PWM_ClearPeriodIntFlag(PWM, 0); // Disable PWM Precise Center Aligned Type PWM->PCACTL &= ~(PWM_PCACTL_PCAEN_Msk); // PWM Channel 0 Output : duty = 24.08u, low = 8.08u PWM_SET_CMR(PWM, 0, 100); PWM_SET_CNR(PWM, 0, 401); // Polling, Wait 1 period interrupt flags while(PWM_GetPeriodIntFlag(PWM, 0) == 0); PWM_ClearPeriodIntFlag(PWM, 0); // Enable PWM Precise Center Aligned Type PWM->PCACTL = PWM_PCACTL_PCAEN_Msk; // PWM Channel 0 Output : duty = 8.08u, low = 8u PWM_SET_CMR(PWM, 0, 99); PWM_SET_CNR(PWM, 0, 402); // Polling, Wait 1 period interrupt flags while(PWM_GetPeriodIntFlag(PWM, 0) == 0); PWM_ClearPeriodIntFlag(PWM, 0); // Disable PWM Precise Center Aligned Type PWM->PCACTL &= ~(PWM_PCACTL_PCAEN_Msk); // PWM Channel 0 Output : duty = 24.24u, low = 8u PWM_SET_CMR(PWM, 0, 99); PWM_SET_CNR(PWM, 0, 402); // Polling, Wait 1 period interrupt flags while(PWM_GetPeriodIntFlag(PWM, 0) == 0); PWM_ClearPeriodIntFlag(PWM, 0); } }