2
\$\begingroup\$

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;
  }
  }
\$\endgroup\$
3
\$\begingroup\$

A few issues I see:

T2CONbits.T2CKPS = 0x1; // set prescaler to 1

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:

Duty Cycle Ratio = (CCPR1L:CCP1CON<5:4>) / (4*(PR2 + 1))

To achieve 100% duty cycle you need to write 408 (or 0x198), not 255.

\$\endgroup\$
1
\$\begingroup\$

Two things I see:

Don't use #include and #include at the same time. Only use xc.h

Also your overloading your duty cycle. The duty cycle is a fraction of the period. Your period is 65 counts. The duty cycle your trying to set is 255. You can only set a duty cycle between 0-65.

With some trickery timer 2 (the timer for PWM) and the duty cycle supports up to 10 bits. The key word there is "up to".

Nothing in the processor can happen any faster then the provided clock. If your timer 2 prescalar is set to 1 then the timer is running as fast as possible. This means that the maximum resolution of the the timer is a single count. That also restricts the maximum resolution of the duty cycle to a single timer count. The fact that it can support up to 10 bits does not mean that the processor subdivides the period in to 1024 pieces, it can't because it is already running as fast as possible.

enter image description here

The way the PWM works is when the TMR2 register rolls over to zero the line goes high. From here when the counts in TMR2 match the CCPR1L(plus 2 bits) the signal goes low. Finally when the counts in TMR2 match PR2 the timer rolls over to zero and the whole thing starts all over.

\$\endgroup\$
  • \$\begingroup\$ Most PIC devices I've worked with use 10 bits for the duty cycle and 8 bits to set the period. See pages 78 and 79 in the datasheet linked above. \$\endgroup\$ – ConduitForSale Nov 11 '15 at 12:55
  • \$\begingroup\$ Thank you! So basically my actual resolution was "8.67 bits" therefor I could go up to 408 with the duty cycle. \$\endgroup\$ – Calin Nov 11 '15 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.