Author Topic: PIC12F1571 PWM Issues  (Read 5336 times)

0 Members and 1 Guest are viewing this topic.

Offline X4vKTopic starter

  • Newbie
  • Posts: 4
  • Country: gb
PIC12F1571 PWM Issues
« on: March 02, 2016, 04:43:51 pm »
Hi Guys,
So I'm trying to set up PWM control of two motors with two independent duty cycles using the PIC12F1571. Before the PIC arrived I wrote some code I thought would work, but it didn't so I'm now just focusing on getting PWM to work. After hours of programming-testing-programming I've thinned the program down to just attempt to turn on PWM for 1 pin (PWM1 on RA5) at a constant duty cycle.

This is the code:

Code: [Select]
#include <xc.h>

// CONFIG1
#pragma config FOSC = INTOSC    // ->INTOSC oscillator; I/O function on CLKIN pin
#pragma config WDTE = OFF    // Watchdog Timer Enable->WDT disabled
#pragma config PWRTE = OFF    // Power-up Timer Enable->PWRT disabled
#pragma config MCLRE = ON    // MCLR Pin Function Select->MCLR/VPP pin function is MCLR
#pragma config CP = OFF    // Flash Program Memory Code Protection->Program memory code protection is disabled
#pragma config BOREN = OFF    // Brown-out Reset Enable->Brown-out Reset disabled
#pragma config CLKOUTEN = OFF    // Clock Out Enable->CLKOUT function is enabled on the CLKOUT pin

// CONFIG2
#pragma config WRT = OFF    // Flash Memory Self-Write Protection->Write protection off
#pragma config PLLEN = OFF    // PLL Enable->4x PLL enabled
#pragma config STVREN = ON    // Stack Overflow/Underflow Reset Enable->Stack Overflow or Underflow will cause a Reset
#pragma config BORV = LO    // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (Vbor), low trip point selected.
#pragma config LPBOREN = OFF    // Low Power Brown-out Reset enable bit->LPBOR is disabled
#pragma config LVP = ON    // Low-Voltage Programming Enable->Low-voltage programming enabled

#define _XTAL_FREQ 8000000

void main(void) {

    APFCONbits.P1SEL = 1; //set the alternative pin for PWM1

    OSCCONbits.SCS1 = 1;
    OSCCONbits.IRCF = 0b1110; //choose 8Mhz
    TRISA = 0b00000000;
    LATA = 0;

    PWM1CLKCONbits.CS = 0b00; //Select FOSC as clock source
    PWM1CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4

    PWM1PRH = 0b11111111; //Period high bit
    PWM1PRL = 0b11111111; //Period low bit

    PWM1DCH = 0b01111111; //DC high bit
    PWM1DCL = 0b11111111; //DC low bit

    PWM1PHL = 0x00;  // Set Phase to 0
    PWM1PHH = 0x00;

    PWM1CON = 0b11100000;

    while(1)
    {
        NOP();
    }

    return;
}

There is essentially no voltage on the PWM1 pin - I feel like I've tried changing everything and nothing results in a change. Any ideas?

Thanks in advance!
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5339
  • Country: gb
Re: PIC12F1571 PWM Issues
« Reply #1 on: March 02, 2016, 06:15:09 pm »
I just ran your code on a breadboarded PIC12F1572 (same chip, a bit more memory than a 1571), and it works perfectly, 30.7Hz square wave on pin 2.

I'm using an ICD3. Only external components are a decoupling cap and a 10k pullup on MCLR (pin 4) to Vdd (pin 1).

Edit: try setting CLKOUTEN=ON and checking there's a a 2MHz clock on pin 3.
« Last Edit: March 02, 2016, 06:18:48 pm by Howardlong »
 

Offline X4vKTopic starter

  • Newbie
  • Posts: 4
  • Country: gb
Re: PIC12F1571 PWM Issues
« Reply #2 on: March 07, 2016, 02:57:46 pm »
Hi Howard,

It turns out that for some reason I skipped the decoupling cap and once that was in place, it worked! So thank you.

I'm now having a problem with reloading the PWM to dynamically vary the duty cycle. At the moment, the PWM initialises fine but doesn't change at all.

This is the code:

Code: [Select]
#include <xc.h>
#include <math.h>

// CONFIG1
#pragma config FOSC = INTOSC    //  (INTOSC oscillator; I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOREN = OFF    // Low Power Brown-out Reset enable bit (LPBOR is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#define _XTAL_FREQ 8000000
void InitPWM (void);

void main(void) {
   
    ///////////////
    // PWM Setup //
    ///////////////
    InitPWM();
    ///////////////////////
    // Main Program Loop //
    ///////////////////////
    while(1)
    {
        __delay_ms(10);              //Wait the acquisition time (about 5us).
        PWM1DC++;
        PWM1LDCONbits.LDA = 1; //trigger the change

        PWM2DC++;
        PWM2LDCONbits.LDA = 1;
    }

    return;
}

 void InitPWM(void)
 {
     APFCONbits.P1SEL = 1;
     APFCONbits.P2SEL = 1;
     OSCCONbits.SCS1 = 1; // internal clock
     OSCCONbits.IRCF = 0b1110; // 8 MHz
     
     PWM1CON = 0x00;
     PWM2CON = 0x00;// Clear PWMCon
     
     TRISAbits.TRISA4 = 0;
     TRISAbits.TRISA5 = 0;

     PWM1CLKCONbits.CS = 0b00; //Select FOSC as clock source
     PWM1CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4
     PWM2CLKCONbits.CS = 0b00;
     PWM2CLKCONbits.PS = 0b010;
     
     PWM1PR = 10000;
     PWM2PR = 10000;
     
     PWM1DC = 3000;
     PWM2DC = 3000;
     
     PWM1PHL = 0x00;  // Set Phase to 0
     PWM1PHH = 0x00;
     PWM2PHL = 0x00;  // Set Phase to 0
     PWM2PHH = 0x00;
     PWM1LDCONbits.LDT = 1; //Set reload mode to triggered
     PWM2LDCONbits.LDT = 1;
     PWM1CON = 0b11100000; //Enable output on the pin
     PWM2CON = 0b11100000;
 }
 

Thanks in advance!
 

Offline luvini

  • Contributor
  • Posts: 33
  • Country: br
  • PIC C programming!
Re: PIC12F1571 PWM Issues
« Reply #3 on: March 08, 2016, 04:28:42 am »
you may want to read my topic on making a NEC IR Remote, where i had to learn how to use PWM too
i even made a PWMcalculator!

https://www.eevblog.com/forum/microcontrollers/nec-ir-remote-using-pic/

it seems your chip have 4 PWM outputs, but i dont see them being called CCP1 like in the PIC16F628A i use, i guess they are just called PWMx (x=1~4)

but i use the PIC CCS C Compiler...

if i want to change the DutyCycle, i use
Code: [Select]
set_pwm1_duty(12);

if i want to change frequency, i use
Code: [Select]
setup_timer_2(T2_DIV_BY_1,25, 1); //38Khz PWM using 4Mhz Osc.

i guess you could put a "INT" where that 25 is and just change its value when you want to change frequency?
(or just a "unsigned char" instead of "int", because the needed range is just 0~255 http://www.tutorialspoint.com/cprogramming/c_data_types.htm )

PIC12F1571 Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/40001723D.pdf

PIC16F628A Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/40044F.pdf
« Last Edit: March 08, 2016, 04:51:57 am by luvini »
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5339
  • Country: gb
Re: PIC12F1571 PWM Issues
« Reply #4 on: March 08, 2016, 06:28:44 pm »
I just had chance to breadboard this up again. It looks like you haven't set a valid trigger for trigger mode (LDT==1) in the PWMxLDCONbits.LDS, this needs to be 1, 2 or 3: 0 (the default) is reserved.

Either set the LDTs to 0 or select a trigger for the trigger mode.

Code: [Select]
#include <xc.h>

// CONFIG1
#pragma config FOSC = INTOSC    // ->INTOSC oscillator; I/O function on CLKIN pin
#pragma config WDTE = OFF    // Watchdog Timer Enable->WDT disabled
#pragma config PWRTE = OFF    // Power-up Timer Enable->PWRT disabled
#pragma config MCLRE = ON    // MCLR Pin Function Select->MCLR/VPP pin function is MCLR
#pragma config CP = OFF    // Flash Program Memory Code Protection->Program memory code protection is disabled
#pragma config BOREN = OFF    // Brown-out Reset Enable->Brown-out Reset disabled
#pragma config CLKOUTEN = OFF    // Clock Out Enable->CLKOUT function is enabled on the CLKOUT pin
//#pragma config CLKOUTEN = ON    // Clock Out Enable->CLKOUT function is enabled on the CLKOUT pin

// CONFIG2
#pragma config WRT = OFF    // Flash Memory Self-Write Protection->Write protection off
#pragma config PLLEN = OFF    // PLL Enable->4x PLL enabled
#pragma config STVREN = ON    // Stack Overflow/Underflow Reset Enable->Stack Overflow or Underflow will cause a Reset
#pragma config BORV = LO    // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (Vbor), low trip point selected.
#pragma config LPBOREN = OFF    // Low Power Brown-out Reset enable bit->LPBOR is disabled
#pragma config LVP = ON    // Low-Voltage Programming Enable->Low-voltage programming enabled

#define _XTAL_FREQ 8000000

void main(void) {

    APFCONbits.P1SEL = 1; //set the alternative pin for PWM1
    APFCONbits.P2SEL = 1; //set the alternative pin for PWM2

    OSCCONbits.SCS1 = 1;
    OSCCONbits.IRCF = 0b1110; //choose 8Mhz
    TRISA = 0b00000000;
    LATA = 0;

    PWM1CLKCONbits.CS = 0b00; //Select FOSC as clock source
    PWM1CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4
    PWM2CLKCONbits.CS = 0b00; //Select FOSC as clock source
    PWM2CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4

    PWM1PR = 10000;
    PWM2PR = 10000;

    PWM1DC = 3000;
    PWM2DC = 3000;

    PWM1PHL = 0x00;  // Set Phase to 0
    PWM1PHH = 0x00;
    PWM2PHL = 0x00;  // Set Phase to 0
    PWM2PHH = 0x00;
    PWM1LDCONbits.LDT = 0; //Set reload mode to work from setting LDA
    PWM2LDCONbits.LDT = 0;
    PWM1CON = 0b11100000; //Enable output on the pin
    PWM2CON = 0b11100000;

    while(1)
    {
        __delay_ms(10);              //Wait the acquisition time (about 5us).
        PWM1DC++;
        PWM1LDCONbits.LDA = 1; //arm the load

        PWM2DC++;
        PWM2LDCONbits.LDA = 1;
    }

    return;
}

 

Offline X4vKTopic starter

  • Newbie
  • Posts: 4
  • Country: gb
Re: PIC12F1571 PWM Issues
« Reply #5 on: March 08, 2016, 07:27:13 pm »
Hi Howard,

Ah! It looks like I may have misread the datasheet. I will give this a go tomorrow and let you know how it goes. Thank you so much for all the help.
 

Offline X4vKTopic starter

  • Newbie
  • Posts: 4
  • Country: gb
Re: PIC12F1571 PWM Issues
« Reply #6 on: March 13, 2016, 05:28:14 pm »
Okay that worked great, thank you! I just have one more question.

I'm reading values via the ADC pins and trying to use them to vary the duty cycle of the PWM output to the motors on my device. This works fine in some implementations, but when I try and involve numbers that could possibly be negative it doesn't work so well.

My question is: what form is the ADC result in (i.e. is it a signed integer? the header seems to suggest a char..) and how can I do calculations using the result?

This is the code:

Code: [Select]


#include <xc.h>
#include <stdlib.h>
// CONFIG1
#pragma config FOSC = INTOSC    //  (INTOSC oscillator; I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOREN = OFF    // Low Power Brown-out Reset enable bit (LPBOR is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#define _XTAL_FREQ 8000000
void InitPWM (void);
void SetDutyCycle (short left,short right);
void main(void) {
    short result_left, result_right;     //variables to store our ADC result

    TRISA = 0xFF;   //set all digital I/O to inputs
    ANSELA = 0x00;   //disable all analog ports

    TRISAbits.TRISA0 = 1;   //Disable the output driver for pin RA0, SET RA0 TO INPUT
    TRISAbits.TRISA1 = 1;   //Disable the output driver for pin RA1, SET RA1 TO INPUT
    ANSELAbits.ANSA0 = 1;     //set ANSA0 to analog mode
    ANSELAbits.ANSA1 = 1;   //set ANSA1 to analog mode
    // RA0,1 ARE SET TO ANALOG INPUTS
   
    ///////////////
    // ADC Setup //
    ///////////////
   
    ADCON1bits.ADCS = 0b001;    //Select ADC conversion clock as FOSC/8 = 1MHz
    ADCON1bits.ADPREF = 0b00;   //Vdd is the +ve reference,(ADCON0bits.VCFG = 0)
 
   
    ///////////////
    // PWM Setup //
    ///////////////
    InitPWM();
    ///////////////////////
    // Main Program Loop //
    ///////////////////////
    __delay_ms(1000);
    while(1)
    {
        //Set to use Channel 0 (left)
       
        ADCON0bits.CHS = 0b00000;    //Select ADC Channel AN0
        ADCON0bits.ADON = 1; //Activate the ADC port
        __delay_us(500); //Wait the acquisition time (about 5us).
       
        ADCON0bits.GO = 1;          //start the conversion
        while(ADCON0bits.GO==1){};  //wait for the conversion to end

        result_left = (ADRESH<<8)+ADRESL;//combine the 10 bits of the conversion
       
        //Set to use Channel 1
        ADCON0bits.CHS = 0b00001;
        ADCON0bits.ADON = 1;
        __delay_us(500);              //Wait the acquisition time (about 5us).

        ADCON0bits.GO = 1;          //start the conversion
        while(ADCON0bits.GO==1){};  //wait for the conversion to end
        result_right = (ADRESH<<8)+ADRESL; //combine the 10 bits of the conversion
       
        SetDutyCycle(result_left, result_right); //change duty cycle
    }

    return;
}

 void InitPWM(void)
 {
     APFCONbits.P1SEL = 1; //select alternative PWM pins
     APFCONbits.P2SEL = 1;
     OSCCONbits.SCS1 = 1; //use the internal clock
     OSCCONbits.IRCF = 0b1110; // 8 MHz
     
     PWM1CON = 0x00;
     PWM2CON = 0x00;// Clear PWMCon
     
     TRISAbits.TRISA4 = 0; //enable output driver of RA4 and RA5
     TRISAbits.TRISA5 = 0;

     PWM1CLKCONbits.CS = 0b00; //Select FOSC as clock source
     PWM1CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4
     PWM2CLKCONbits.CS = 0b00;
     PWM2CLKCONbits.PS = 0b010;
     
     PWM1PR = 32000; //set period to 32000
     PWM2PR = 32000;
     
     PWM1DC = 0000; //set duty cycle to zero (initially 0 speed)
     PWM2DC = 0000;
     
     PWM1PHL = 0x00;  // Set Phase to 0
     PWM1PHH = 0x00;
     PWM2PHL = 0x00;  // Set Phase to 0
     PWM2PHH = 0x00;
     PWM1LDCONbits.LDT = 0; //Set reload mode to triggered
     PWM2LDCONbits.LDT = 0;
     PWM1CON = 0b11100000; //Enable output on the pin
     PWM2CON = 0b11100000;
     
     return;
 }
 
  void SetDutyCycle(short left, short right, short maxleft, short maxright)
 {
    short difference = left - right;
    short basespeed = 20000;

    PWM1DC = basespeed - difference; ///right
    PWM2DC = basespeed + difference; //left
   
    PWM2LDCONbits.LDA = 1;
    PWM1LDCONbits.LDA = 1;
   
    return;
  }
   
 

I have tried a number of variations on this code and nothing works unless there are no negative numbers involved. Thanks in advance for any input!
 

Offline Buriedcode

  • Super Contributor
  • ***
  • Posts: 1642
  • Country: gb
Re: PIC12F1571 PWM Issues
« Reply #7 on: March 14, 2016, 04:43:55 pm »
The ADC result is 10-bit, from a single ended input.  That is always a positive integer, not twos-compliment.  It's stored in two byte registers (ADRESH, ADRESL as its an 8-bit device) either left, or right justified.  Often for controls that don't need the full 10-bits, I'll left justify it, and just read the high byte.  As C compilers deal with multibyte numbers,  I've only ever seen 'ADRES' as an unsigned int, or unsigned short (depends on compiler).  You can of course read the two byte registers individually as chars and merge them to an unsigned int, but no point as thats what the compiler does for you.

you could make this a signed value by subtracting the mid value, but I don't see much point.

Code: [Select]
        short result_left, result_right;     //variables to store our ADC result

Should be

Code: [Select]
        unsigned short result_left, result_right;     //variables to store our ADC result

As the XC8 compiler defaults to signed for integers, except char's, which are of course, always unsigned.  Annoying I know, I would have thought the default should always be unsigned.
« Last Edit: March 14, 2016, 11:41:57 pm by Buriedcode »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf