PIC12F1571 PWM Issues - Page 1
Forum section: electronics / general

author: X4vK
author_rank: Newbie
timestamp: March 02, 2016, 04:43:51 pm
content: 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:#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 8000000void main(void) {APFCONbits.P1SEL = 1; //set the alternative pin for PWM1OSCCONbits.SCS1 = 1;OSCCONbits.IRCF = 0b1110; //choose 8MhzTRISA = 0b00000000;LATA = 0;PWM1CLKCONbits.CS = 0b00; //Select FOSC as clock sourcePWM1CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4PWM1PRH = 0b11111111; //Period high bitPWM1PRL = 0b11111111; //Period low bitPWM1DCH = 0b01111111; //DC high bitPWM1DCL = 0b11111111; //DC low bitPWM1PHL = 0x00; // Set Phase to 0PWM1PHH = 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!

author: Howardlong
author_rank: Super Contributor
timestamp: March 02, 2016, 06:15:09 pm
content: 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.

author: X4vK
author_rank: Newbie
timestamp: March 07, 2016, 02:57:46 pm
content: 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:#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 8000000void 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 changePWM2DC++;PWM2LDCONbits.LDA = 1;}return;}void InitPWM(void){APFCONbits.P1SEL = 1;APFCONbits.P2SEL = 1;OSCCONbits.SCS1 = 1; // internal clockOSCCONbits.IRCF = 0b1110; // 8 MHzPWM1CON = 0x00;PWM2CON = 0x00;// Clear PWMConTRISAbits.TRISA4 = 0;TRISAbits.TRISA5 = 0;PWM1CLKCONbits.CS = 0b00; //Select FOSC as clock sourcePWM1CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4PWM2CLKCONbits.CS = 0b00;PWM2CLKCONbits.PS = 0b010;PWM1PR = 10000;PWM2PR = 10000;PWM1DC = 3000;PWM2DC = 3000;PWM1PHL = 0x00; // Set Phase to 0PWM1PHH = 0x00;PWM2PHL = 0x00; // Set Phase to 0PWM2PHH = 0x00;PWM1LDCONbits.LDT = 1; //Set reload mode to triggeredPWM2LDCONbits.LDT = 1;PWM1CON = 0b11100000; //Enable output on the pinPWM2CON = 0b11100000;}Thanks in advance!

author: luvini
author_rank: Contributor
timestamp: March 08, 2016, 04:28:42 am
content: you may want to read my topic on making a NEC IR Remote, where i had to learn how to use PWM tooi 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 useCode:set_pwm1_duty(12);if i want to change frequency, i useCode: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~255http://www.tutorialspoint.com/cprogramming/c_data_types.htm)PIC12F1571 Datasheet:http://ww1.microchip.com/downloads/en/DeviceDoc/40001723D.pdfPIC16F628A Datasheet:http://ww1.microchip.com/downloads/en/DeviceDoc/40044F.pdf

author: Howardlong
author_rank: Super Contributor
timestamp: March 08, 2016, 06:28:44 pm
content: 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:#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 8000000void main(void) {APFCONbits.P1SEL = 1; //set the alternative pin for PWM1APFCONbits.P2SEL = 1; //set the alternative pin for PWM2OSCCONbits.SCS1 = 1;OSCCONbits.IRCF = 0b1110; //choose 8MhzTRISA = 0b00000000;LATA = 0;PWM1CLKCONbits.CS = 0b00; //Select FOSC as clock sourcePWM1CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4PWM2CLKCONbits.CS = 0b00; //Select FOSC as clock sourcePWM2CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4PWM1PR = 10000;PWM2PR = 10000;PWM1DC = 3000;PWM2DC = 3000;PWM1PHL = 0x00; // Set Phase to 0PWM1PHH = 0x00;PWM2PHL = 0x00; // Set Phase to 0PWM2PHH = 0x00;PWM1LDCONbits.LDT = 0; //Set reload mode to work from setting LDAPWM2LDCONbits.LDT = 0;PWM1CON = 0b11100000; //Enable output on the pinPWM2CON = 0b11100000;while(1){__delay_ms(10); //Wait the acquisition time (about 5us).PWM1DC++;PWM1LDCONbits.LDA = 1; //arm the loadPWM2DC++;PWM2LDCONbits.LDA = 1;}return;}

author: X4vK
author_rank: Newbie
timestamp: March 08, 2016, 07:27:13 pm
content: 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.

author: X4vK
author_rank: Newbie
timestamp: March 13, 2016, 05:28:14 pm
content: 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:#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 8000000void InitPWM (void);void SetDutyCycle (short left,short right);void main(void) {short result_left, result_right; //variables to store our ADC resultTRISA = 0xFF; //set all digital I/O to inputsANSELA = 0x00; //disable all analog portsTRISAbits.TRISA0 = 1; //Disable the output driver for pin RA0, SET RA0 TO INPUTTRISAbits.TRISA1 = 1; //Disable the output driver for pin RA1, SET RA1 TO INPUTANSELAbits.ANSA0 = 1; //set ANSA0 to analog modeANSELAbits.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 = 1MHzADCON1bits.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 AN0ADCON0bits.ADON = 1; //Activate the ADC port__delay_us(500); //Wait the acquisition time (about 5us).ADCON0bits.GO = 1; //start the conversionwhile(ADCON0bits.GO==1){}; //wait for the conversion to endresult_left = (ADRESH<<8)+ADRESL;//combine the 10 bits of the conversion//Set to use Channel 1ADCON0bits.CHS = 0b00001;ADCON0bits.ADON = 1;__delay_us(500); //Wait the acquisition time (about 5us).ADCON0bits.GO = 1; //start the conversionwhile(ADCON0bits.GO==1){}; //wait for the conversion to endresult_right = (ADRESH<<8)+ADRESL;//combine the 10 bits of the conversionSetDutyCycle(result_left, result_right); //change duty cycle}return;}void InitPWM(void){APFCONbits.P1SEL = 1; //select alternative PWM pinsAPFCONbits.P2SEL = 1;OSCCONbits.SCS1 = 1; //use the internal clockOSCCONbits.IRCF = 0b1110; // 8 MHzPWM1CON = 0x00;PWM2CON = 0x00;// Clear PWMConTRISAbits.TRISA4 = 0; //enable output driver of RA4 and RA5TRISAbits.TRISA5 = 0;PWM1CLKCONbits.CS = 0b00; //Select FOSC as clock sourcePWM1CLKCONbits.PS = 0b010; //Set prescaler to FOSC/4PWM2CLKCONbits.CS = 0b00;PWM2CLKCONbits.PS = 0b010;PWM1PR = 32000; //set period to 32000PWM2PR = 32000;PWM1DC = 0000; //set duty cycle to zero (initially 0 speed)PWM2DC = 0000;PWM1PHL = 0x00; // Set Phase to 0PWM1PHH = 0x00;PWM2PHL = 0x00; // Set Phase to 0PWM2PHH = 0x00;PWM1LDCONbits.LDT = 0; //Set reload mode to triggeredPWM2LDCONbits.LDT = 0;PWM1CON = 0b11100000; //Enable output on the pinPWM2CON = 0b11100000;return;}void SetDutyCycle(short left, short right, short maxleft, short maxright){short difference = left - right;short basespeed = 20000;PWM1DC = basespeed - difference; ///rightPWM2DC = basespeed + difference; //leftPWM2LDCONbits.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!

author: Buriedcode
author_rank: Super Contributor
timestamp: March 14, 2016, 04:43:55 pm
content: 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:short result_left, result_right; //variables to store our ADC resultShould beCode:unsigned short result_left, result_right; //variables to store our ADC resultAs 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.