I'm trying to make PWM to work on the PIC12F683. According to my calculations I should get an 8 bit 20kHz PWM at GPIO2, but that does not happen. Instead I get a 5kHz "weird" PWM signal such that when I set the duty to be 255, which should be maximum, I get this 5kHz wave. What could the problem be?
EDIT: I am following this procedure which is written in the PIC12F683 datasheet, but I am either not doing it right or there's something else I need to do.
This is the code:
/*
* File: main.c
* Author: Calin
*
* Created on November 9, 2015, 11:10 PM
*/
#define _XTAL_FREQ 8000000
#include <xc.h>
#include <PIC12F683.h>
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
/* Prototypes *****************************************************************/
long calculatePower(void);
unsigned int readVoltage(void);
unsigned int readCurrent(void);
void PWM_setup(void);
void PWM_set_duty(int);
void interrupt ISR(void);
/******************************************************************************/
void main(void) {
// Select 8Mhz internal clock
OSCCON |= 0b01110001;
// Configure GP0 and GP1 as analog inputs
TRISIO = 0b00000011; //input
ANSEL = 0b00000011; // clock = 1 meg and analog configure
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
INTCONbits.T0IE = 1;
PIE1bits.CCP1IE = 1;
PIE1bits.TMR2IE = 1;
PWM_setup();
PWM_set_duty(255);
while (1){
//long power = calculatePower();
}
return;
}
void PWM_setup(){
TRISIO &= 0b11111011; // make sure GP2 is OUTPUT
PR2 = 0x65;
CCP1CON = 0b00001100; // active high PWM
PIR1bits.TMR2IF = 0;
T2CONbits.T2CKPS = 0x1; // set prescaler to 1
T2CONbits.TMR2ON = 1; // enable Timer 2 and therefor PWM
}
void PWM_set_duty(int duty_cycle){
// Sets the PWM duty cycle by setting
// the 2 LSB's in DCB and the 8 MSB's
// in CCPR1L. 10 bit resolution
CCP1CONbits.DC1B = duty_cycle;
CCPR1L = duty_cycle >> 2;
}
long calculatePower(){
return readVoltage()*readCurrent();
}
unsigned int readVoltage(){
/*
* Reads and returns the voltage at AN0
*/
// Select channel 0 and turn on ADC
ADCON0 = 0b10000001; // enable ADC
ADCON0 = 0b10000011; // GO
while (CHECK_BIT(ADCON0, 1)){
// wait
}
// 10 bit ADC result
unsigned int voltage = ADRESL | (ADRESH << 8);
return voltage;
}
unsigned int readCurrent(){
/*
* Reads and returns the current at AN1
*/
// Select channel 1 and turn on ADC
ADCON0 = 0b10000101; // enable ADC & select channel
ADCON0 = 0b10000111; // GO
while(CHECK_BIT(ADCON0, 1)){
// wait
}
unsigned int current = ADRESL | (ADRESH << 8);
return current;
}
void interrupt ISR(){
// Timer2 overflow => start a new PWM cycle
if(PIR1bits.TMR2IF == 1){
PIR1bits.TMR2IF = 0;
TRISIObits.TRISIO2 = 0;
}
}
Best Answer
A few issues I see:
This line is actually setting the prescaler to 4, not 1. Based on the values you have chosen for PR2, Tosc and the TMR2 prescaler I calculate a PWM frequency of 4902 Hz. If you set the prescaler to 1 you should get a frequency of ~19608 Hz. I think this is what you intended. You should write 0b00 to T2CONbits.T2CKPS instead.
Also the PWM duty cycle is set by a 10 bit value. The equation for duty cycle ratio is:
To achieve 100% duty cycle you need to write 408 (or 0x198), not 255.