0,0 → 1,355 |
#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 2 |
CCP1_TRIS = 1; // PWM output starts disabled |
CCP1CONbits.P1M = 0b00; // Single output, P1A modulated only |
CCP1CONbits.CCP1M = 0b1100; // PWM Mode, P1A active-high, P1B active-high |
PIR1bits.TMR2IF = 0; // Clear Timer 2 interrupt flag |
TMR2 = 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]) - 1 |
|
uint8_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 cycle |
Set_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 register |
uint32_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 values |
PR2 = computedFrequency; |
|
// Set duty cycle to 0% |
CCP1CONbits.DC1B = 0b00; |
CCPR1L = 0x00; |
|
// Start timer and wait for it to rollover to latch duty cycle value |
T2CONbits.TMR2ON = 1; |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
CCP1_TRIS = 0; |
|
// Bit 15 |
if (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, 0x7 |
0x27: GOTO 0x34 |
! CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
0x28: MOVLB 0x0 |
0x29: MOVF computedDutyCycleHigh_LowerBits, W |
0x2A: MOVWF 0x73 |
0x2B: SWAPF 0x73, F |
0x2C: MOVLB 0x5 |
0x2D: MOVF CCP1CON, W |
0x2E: XORWF 0x2F3, W |
0x2F: ANDLW 0xCF |
0x30: XORWF 0x2F3, W |
0x31: MOVWF CCP1CON |
! CCPR1L = computedDutyCycleHigh_UpperByte; |
0x32: MOVF computedDutyCycleHigh_UpperByte, W |
0x33: GOTO 0x41 |
! } else { |
! CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
0x34: MOVLB 0x0 |
0x35: MOVF computedDutyCycleLow_LowerBits, W |
0x36: MOVWF 0x73 |
0x37: SWAPF 0x73, F |
0x38: MOVLB 0x5 |
0x39: MOVF CCP1CON, W |
0x3A: XORWF 0x2F3, W |
0x3B: ANDLW 0xCF |
0x3C: XORWF 0x2F3, W |
0x3D: MOVWF CCP1CON |
! CCPR1L = computedDutyCycleLow_UpperByte; |
0x3E: MOVLB 0x0 |
0x3F: MOVF computedDutyCycleLow_UpperByte, W |
0x40: MOVLB 0x5 |
0x41: MOVWF CCPR1 |
! } |
! while (!PIR1bits.TMR2IF); |
0x42: MOVLB 0x0 |
0x43: BTFSS PIR1, 0x1 |
0x44: 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, 0x7 |
0x27: GOTO 0x2A |
! CCPR1L = computedDutyCycleHigh_UpperByte; |
0x28: MOVF computedDutyCycleHigh_UpperByte, W |
0x29: GOTO 0x2C |
! } else { |
! CCPR1L = computedDutyCycleLow_UpperByte; |
0x2A: MOVLB 0x0 |
0x2B: MOVF computedDutyCycleLow_UpperByte, W |
0x2C: MOVLB 0x5 |
0x2D: MOVWF CCPR1 |
! } |
! while (!PIR1bits.TMR2IF); |
0x2E: MOVLB 0x0 |
0x2F: BTFSS PIR1, 0x1 |
0x30: GOTO 0x2E |
! PIR1bits.TMR2IF = 0; |
0x31: BCF PIR1, 0x1 |
*/ |
|
// Bit 14 |
if (savedPattern & 0x4000) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 13 |
if (savedPattern & 0x2000) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 12 |
if (savedPattern & 0x1000) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 11 |
if (savedPattern & 0x0800) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 10 |
if (savedPattern & 0x0400) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 9 |
if (savedPattern & 0x0200) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 8 |
if (savedPattern & 0x0100) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 7 |
if (savedPattern & 0x0080) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 6 |
if (savedPattern & 0x0040) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 5 |
if (savedPattern & 0x0020) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 4 |
if (savedPattern & 0x0010) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 3 |
if (savedPattern & 0x0008) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 2 |
if (savedPattern & 0x0004) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 1 |
if (savedPattern & 0x0002) { |
CCP1CONbits.DC1B = computedDutyCycleHigh_LowerBits; |
CCPR1L = computedDutyCycleHigh_UpperByte; |
} else { |
CCP1CONbits.DC1B = computedDutyCycleLow_LowerBits; |
CCPR1L = computedDutyCycleLow_UpperByte; |
} |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
|
// Bit 0 |
if (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 timer |
while (!PIR1bits.TMR2IF); |
PIR1bits.TMR2IF = 0; |
T2CONbits.TMR2ON = 0; |
TMR2 = 0x0; |
} |