Rev 333 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
#include "PWM.h"#include "INTERRUPTS.h"static volatile uint8_t computedFrequency = 0;static volatile uint8_t computedDutyCycleHigh_UpperByte = 0;static volatile uint8_t computedDutyCycleHigh_LowerBits = 0;static volatile uint8_t computedDutyCycleLow_UpperByte = 0;static volatile uint8_t computedDutyCycleLow_LowerBits = 0;static volatile uint8_t savedDutyCycleHigh = PWM_DEFAULT_HIGH_CYCLE;static volatile uint8_t savedDutyCycleLow = PWM_DEFAULT_LOW_CYCLE;static volatile uint16_t savedPattern = 0xAAAA;void PWM_Init() {// Initialize CCP1 / Timer 2CCP1_TRIS = 1; // PWM output starts disabledCCP1CONbits.P1M = 0b00; // Single output, P1A modulated onlyCCP1CONbits.CCP1M = 0b1100; // PWM Mode, P1A active-high, P1B active-highPIR1bits.TMR2IF = 0; // Clear Timer 2 interrupt flagTMR2 = 0x0;Set_PWM_Frequency(PWM_DEFAULT_FREQ);Set_PWM_Duty_Cycle(PWM_DEFAULT_HIGH_CYCLE, PWM_DEFAULT_LOW_CYCLE);}void Set_PWM_Frequency(uint32_t frequency) {// Timer 2 clocked at FOSC/4 (20 Mhz)// Prescaler 1:1 = minimum frequency of 19,532 Hz// Prescaler 1:4 = minimum frequency of 4,883 Hz// Prescaler 1:16 = minimum frequency of 1,221 Hz// Prescaler 1:64 = minimum frequency of 306 Hz// PWM Period = [PR2 + 1] * 4 * TOSC * Prescale// = [PR2 + 1] * 4 * (1/FOSC) * Prescale// = ([PR2 + 1] * 4 * Prescale) / FOSC// PWM Freq = 1/(PWM Period)// = 1/(PR2 + 1) * 1/4 * FOSC * 1/Prescale)// = FOSC / ([PR2 + 1] * 4 * Prescale)// PR2 = (FOSC / [(PWM Freq) * 4 * Presccale]) - 1uint8_t preScaleValue;if (frequency > 19532) {preScaleValue = 1;T2CONbits.T2CKPS = 0b00;} else if (frequency > 4883) {preScaleValue = 4;T2CONbits.T2CKPS = 0b01;} else if (frequency > 1221) {preScaleValue = 16;T2CONbits.T2CKPS = 0b10;} else {preScaleValue = 64;T2CONbits.T2CKPS = 0b11;}uint32_t tmp = frequency * 4 * preScaleValue;computedFrequency = (_XTAL_FREQ / tmp) - 1;// Updated duty cycleSet_PWM_Duty_Cycle(savedDutyCycleHigh, savedDutyCycleLow);}void Set_PWM_Duty_Cycle(uint8_t highPercent, uint8_t lowPercent) {// Duty cycle specified by 10 bit value in CCPR1L:DC1B<1:0>savedDutyCycleHigh = highPercent;savedDutyCycleLow = lowPercent;// Compute values to store in registeruint32_t highValue = (computedFrequency + 1) * 4;highValue *= highPercent;highValue /= 100;computedDutyCycleHigh_LowerBits = highValue & 0x3;computedDutyCycleHigh_UpperByte = (highValue >> 2) & 0xFF;uint32_t lowValue = (computedFrequency + 1) * 4;lowValue *= lowPercent;lowValue /= 100;computedDutyCycleLow_LowerBits = lowValue & 0x3;computedDutyCycleLow_UpperByte = (lowValue >> 2) & 0xFF;}void Set_PWM_Pattern(uint16_t pattern) {savedPattern = pattern;}void PWM_Transmit_Pattern() {// Set PWM frequency pre-computed valuesPR2 = computedFrequency;// Set duty cycle to 0%CCP1CONbits.DC1B = 0b00;CCPR1L = 0x00;// Start timer and wait for it to rollover to latch duty cycle valueT2CONbits.TMR2ON = 1;while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;CCP1_TRIS = 0;// Bit 15if (savedPattern & 0x8000) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;/* The above section of code disassembles to the following assembly code* According to the instruction set table, this should take 22 cycles to execute* 22 cycles corresponds to a maximum of ~227.272 kHz PWM frequency* If higher PWM frequency is needed, DC1B can be omitted for lower duty cycle accuracy! if (pattern & 0x8000) {0x26: BTFSS 0x72, 0x70x27: GOTO 0x34! CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;0x28: MOVLB 0x00x29: MOVF computedDutyCycleHigh_LowerBits, W0x2A: MOVWF 0x730x2B: SWAPF 0x73, F0x2C: MOVLB 0x50x2D: MOVF CCP1CON, W0x2E: XORWF 0x2F3, W0x2F: ANDLW 0xCF0x30: XORWF 0x2F3, W0x31: MOVWF CCP1CON! CCPR1L = computedDutyCycleHigh_UpperByte;0x32: MOVF computedDutyCycleHigh_UpperByte, W0x33: GOTO 0x41! } else {! CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;0x34: MOVLB 0x00x35: MOVF computedDutyCycleLow_LowerBits, W0x36: MOVWF 0x730x37: SWAPF 0x73, F0x38: MOVLB 0x50x39: MOVF CCP1CON, W0x3A: XORWF 0x2F3, W0x3B: ANDLW 0xCF0x3C: XORWF 0x2F3, W0x3D: MOVWF CCP1CON! CCPR1L = computedDutyCycleLow_UpperByte;0x3E: MOVLB 0x00x3F: MOVF computedDutyCycleLow_UpperByte, W0x40: MOVLB 0x50x41: MOVWF CCPR1! }! while (!PIR1bits.TMR2IF);0x42: MOVLB 0x00x43: BTFSS PIR1, 0x10x44: GOTO 0x42! PIR1bits.TMR2IF = 0;0x45: BCF PIR1, 0x1* If DC1B is ignored, the disassembly is as follows:* According to the instruction set table, this should take 14 cycles to execute* 14 cycles corresponds to a maximum of ~357.142 kHz PWM frequency! // Bit 15! if (pattern & 0x8000) {0x26: BTFSS 0x72, 0x70x27: GOTO 0x2A! CCPR1L = computedDutyCycleHigh_UpperByte;0x28: MOVF computedDutyCycleHigh_UpperByte, W0x29: GOTO 0x2C! } else {! CCPR1L = computedDutyCycleLow_UpperByte;0x2A: MOVLB 0x00x2B: MOVF computedDutyCycleLow_UpperByte, W0x2C: MOVLB 0x50x2D: MOVWF CCPR1! }! while (!PIR1bits.TMR2IF);0x2E: MOVLB 0x00x2F: BTFSS PIR1, 0x10x30: GOTO 0x2E! PIR1bits.TMR2IF = 0;0x31: BCF PIR1, 0x1*/// Bit 14if (savedPattern & 0x4000) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 13if (savedPattern & 0x2000) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 12if (savedPattern & 0x1000) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 11if (savedPattern & 0x0800) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 10if (savedPattern & 0x0400) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 9if (savedPattern & 0x0200) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 8if (savedPattern & 0x0100) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 7if (savedPattern & 0x0080) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 6if (savedPattern & 0x0040) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 5if (savedPattern & 0x0020) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 4if (savedPattern & 0x0010) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 3if (savedPattern & 0x0008) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 2if (savedPattern & 0x0004) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 1if (savedPattern & 0x0002) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Bit 0if (savedPattern & 0x0001) {CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits;CCPR1L = computedDutyCycleHigh_UpperByte;} else {CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits;CCPR1L = computedDutyCycleLow_UpperByte;}while (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;// Set next duty cycle to 0% (idle line low)CCP1CONbits.DC1B = 0b00;CCPR1L = 0x00;// Wait for timer to rollover, then turn off timerwhile (!PIR1bits.TMR2IF);PIR1bits.TMR2IF = 0;T2CONbits.TMR2ON = 0;TMR2 = 0x0;}