powerpic
Replacement board for a Casio CA-53W
/** @file pwm.h
*
* PWM driver for PIC16LF1919x.
*/
#include <xc.h>
#include <math.h> // for llroundf()
#include "drivers/pwm.h"
/** Currently cofigured duty cycle. */
unsigned char pwm_duty_ratio = 0;
/** Currently configured frequency. */
unsigned int pwm_frequency = 0;
/** Calculated timer period for the given frequency. */
volatile unsigned char pwm_timer_period = 0;
/** Calculated timer prescale value for the given frequency. */
volatile unsigned char pwm_timer_prescaler = 0;
/** Calculated duty-cycle value. */
volatile unsigned int pwm_duty_cycle = 0;
void
pwm_init (void)
{
# if (1 == PCB_REV)
// Set pwm pin to output and configure pps
TRISGbits.TRISG7 = 0;
RG7PPS = 0x0c;
# endif
# if (2 == PCB_REV)
// Set PWM pin as output
// We use RG6 for the buzzer
//
TRISGbits.TRISG6 = 0;
RG6PPS = 0x0C;
# endif
// set up timer2
//
T2CLKCONbits.CS = 0x01; // clock - Fosc/4
T2HLTbits.PSYNC = 1; // Synced with Fosc
// T2CONbits.CKPS = 0b011; // 1:8
// T2PR = 64;
T2CONbits.ON = 1;
}
void
pwm_enable (void)
{
// Enable timer
// T2CONbits.ON = 1;
// Enable PWM
PWM4CONbits.PWM4EN = 1;
}
void
pwm_disable (void)
{
// Disable pwm
PWM4CONbits.PWM4EN = 0;
// Disable timer2
// T2CONbits.ON = 0;
}
void
pwm_freq_set (unsigned int freq)
{
// To set the PWM to a given frequency we need to figure out two things:
// 1. Timer period
// 2. Timer prescaler
//
// To calculate the correct values, we start with the lowest available
// prescaler (1:1). We then calculate the timer period value for each value
// and stop at the first that is within the timer limits. This gives us the
// prescale option with the highest resolution available for the frequency.
int timer_period = 0;
// As prescaler values get lower, it allows higer frequencies.
//
for (unsigned char timer_prescale = 0; timer_prescale < 8; timer_prescale++)
{
// Magic formula from the datasheet gives us our timer period.
// This takes up about 7% of our flash because of the float arithmetic.
//
timer_period = (int)lroundf(
(1 / ((float)freq) /
(4 * (1 / (float)_XTAL_FREQ) * (1 << timer_prescale))) - 1
);
if ((0 < timer_period) && (timer_period < 255))
{
pwm_frequency = freq;
pwm_timer_period = (unsigned char)timer_period;
T2PR = pwm_timer_period; // TODO: Don't actually set the period registers until we enable.
pwm_timer_prescaler = timer_prescale;
T2CONbits.CKPS = pwm_timer_prescaler; // TODO: Same with prescaler
return;
}
}
// Error: bad frequency!
// What can we do? Should this function return something?
// Set some defaults?
}
unsigned int
pwm_freq_get (void)
{
return pwm_frequency;
}
void
pwm_duty_set (unsigned char duty)
{
// TODO: Comments
pwm_duty_ratio = duty;
pwm_duty_cycle = (unsigned int)lroundf(((float)duty/100) * (4 * (pwm_timer_period + 1)));
PWM4DC = (pwm_duty_cycle << 6);
}
unsigned char
pwm_duty_get (void)
{
return pwm_duty_ratio;
}
// EOF //