[PIC] PWM Weirdness [ PIC12f1840]

Status
Not open for further replies.

rocky79

Member level 5
Joined
Sep 20, 2010
Messages
83
Helped
1
Reputation
2
Reaction score
0
Trophy points
1,286
Visit site
Activity points
1,903
Hello
I have been trying to make PWM work on PIC 12f1840 using CCS compiler
Since I am using a 10 bit PWM I am expecting a 1023 level to be close to 100% duty cycle
and 512 to be close to 50%
however I keep getting the wrong PWM duty cycle, at level of 100 I get close to 60% duty cycle. It sounds like I am getting a really low resolution PWM but why?
Here are my PWM settings which should get me a 10 bit PWM
Clock frequency=4Mhz
Pr2=255
TMR2 prescaler=16.

Here is the code:
Code:
#include <12f1840.h>
#fuses   INTRC_IO, NOWDT, NOPROTECT, NOMCLR
#use     delay(clock = 4000000)
#use     fast_io (A)

//----------------------------------- ---------
//GPIO BIT ASSIGNMENT FOR I/O REGISTERS PORT A
//--------------------------------------------
//address of the TRISIO registe
#byte   TRISA  = 0x8C//getenv("SFR:TRISA") 
#byte    ANSELA = 0x18C//getenv("SFR:ANSELA")                            

// setting up PWM parameters
#byte PR2=0x1B
#byte T2CON=0x1C
#word CCP1CON=0x293
#bit  CCP1CON_4=CCP1CON.4
#bit  CCP1CON_5=CCP1CON.5

#word CCPR1L=0x291
#byte PIR1=0x11
#bit PIR1_1=PIR1.1

int16 duty;
#bit duty_0=duty.0
#bit duty_1=duty.1

void pwm(int16 a2d);

void main(void){

   int16 i;
    //SET UP THE CAPSENSE PINS [RA0-RA1] FOR ANALOG INPUT
   ANSELA=0b00000011;
   //----------------------manual PWM config--------------------------------
   //Disable the PWM pin (CCPx) output driver(s) by
   //setting the associated TRIS bit(s) to INPUT
   TRISA= 0b00001111;
   //Load the PR2 register with the PWM period value.
   PR2=255; // 255hz   
   //Configure the CCP module for the PWM mode
   //by loading the CCPxCON register with the
   //appropriate values.
   CCP1CON=0b00001100;   // Set CCP1 PWM active high
   //Load the CCPRxL register and the DCxBx bits of
   //the CCPxCON register, with the PWM duty cycle
   //value.
   duty = 0;                   // set duty cycle to 0
   CCP1CON_4 = duty_0;         // Store duty to registers as
   CCP1CON_5 = duty_1;         // a 10-bit word
   //store the eight MSbs in CCPR1L.
   CCPR1L = duty >> 2;         // store the remaining low byte
   //clear the TMR21F interrupt flag bit of PIR1 register
   PIR1_1=0;
   // Set prescaler to 16
   T2CON=0b00000110; 
   
   //Wait until Timer2 overflows, TMR2IF bit of the
   //PIR1 register is set.
   wait_for_TMR2IF:            
   if (PIR1_1 == 0)
     {
     goto wait_for_TMR2IF;
     } 
   //enable PWM by setting pin to output
   TRISA= 0b00001011;
   
   while(1)
   {
   
    for(i=0;i<1020;++i)
        {
        //Output a PWM wave
       pwm(i);
       delay_ms(2);
        }
        delay_ms(2000);
   }
}
   
void pwm(int16 a2d)
  {
   duty=a2d;
   CCP1CON_4 = duty_0;        // Store duty to registers as
   CCP1CON_5 = duty_1;        // a 10-bit word
   CCPR1L = duty >> 2;        // store the remaining low byte}
   }
 

Can you kindly tell us, why you have added that 2ms delay in your for loop?
 

It is an unusual procedure.

By the way, had you used PIC Wizard tool which come with CCS toolset to get the values that you configured for PWM registers ?

+++

Hello, it's actually not unusual, I do that all the time with other PICS to slowly ramps up the PWM. If an LED is connected to the output, It will slowly becomes brighter and brighter.

I haven't tried the CCS configurator however I am thinking it could be either I am missing something new specific to this chip or there is a compiler bug which is not unusual for CCS.
 

Hello,

Have you tried plugging in different values for the PR2? PR2 and CPPR1L:CCP1CON<5:4> are the only two things that affect duty cycle, so trying different numbers may be useful. (Assuming the clock frequency and timer2 pre-scalar don't change.) All of your settings looked correct.

You may consider removing the following:
Code:
//Wait until Timer2 overflows, TMR2IF bit of the
   //PIR1 register is set.
   wait_for_TMR2IF:            
   if (PIR1_1 == 0)
     {
     go
Code:
#byte PIR1=0x11
#bit PIR1_1=PIR1.1
Code:
//clear the TMR21F interrupt flag bit of PIR1 register
   PIR1_1=0;

This is not really needed if all you are doing is driving an led. The datasheet lists it as optional.

The delay has no effect on the PWM functionality it only slows the change in duty cycle.
 

Hello and thanks for your replies. I tried removing the optional code and it still doesn't work. The PWM alternates between high and low randomly.
There might be addional settings this chip requires for the PWM to function normally.
 

Try increasing the delay to 250 instead of 2.

- - - Updated - - -

This will create a very slow increase. With a delay of 2ms it goes through the entire loop in just over 2 seconds, so it will appear to not work when in reality it is. Sorry should have caught that sooner.
 

I used the CCS compiler predefined commands and it worked.
So what have I missed in setting up the PWM?
Code:
void main(void)
{
   setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM
   setup_timer_2(T2_DIV_BY_16, 255, 1);
   int16 i;
   
    while( 1 ) 
    {
    for(i=0;i<1020;++i)
        {
        //Output a PWM wave
       set_pwm1_duty(i);
       delay_ms(3);
        }
       delay_ms(500); 
    }
}

The question is what does setup_ccp1 and set_pwm1_duty do differently?
Here is the Assembly language output for the above command:
Code:
.................... { 
....................    setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM 
0021:  BCF    1D.0
0022:  BCF    1D.1
0023:  MOVLB  01
0024:  BCF    0C.2
0025:  MOVLB  02
0026:  BCF    0C.2
0027:  MOVLW  0C
0028:  MOVLB  05
0029:  MOVWF  13
002A:  CLRF   14
002B:  CLRF   15
002C:  MOVLW  01
002D:  MOVWF  16
....................    setup_timer_2(T2_DIV_BY_16, 255, 1); 
002E:  MOVLW  00
002F:  MOVLB  00
0030:  MOVWF  21
0031:  IORLW  06
0032:  MOVWF  1C
0033:  MOVLW  FF
0034:  MOVWF  1B

....................         { 
....................         //Output a PWM wave 
....................        set_pwm1_duty(i); 
0041:  MOVF   26,W
0042:  MOVWF  22
0043:  MOVF   25,W
0044:  MOVWF  21
0045:  RRF    22,F
0046:  RRF    21,F
0047:  RRF    22,F
0048:  RRF    21,F
0049:  RRF    22,F
004A:  MOVF   21,W
004B:  MOVLB  05
004C:  MOVWF  11
004D:  MOVLB  00
004E:  RRF    22,F
004F:  RRF    22,W
0050:  ANDLW  30
0051:  MOVWF  20
0052:  MOVLB  05
0053:  MOVF   13,W
0054:  ANDLW  CF
0055:  MOVLB  00
0056:  IORWF  20,W
0057:  MOVLB  05
0058:  MOVWF  13
 

0023: MOVLB 01
0024: BCF 0C.2
0025: MOVLB 02
0026: BCF 0C.2
0027: MOVLW 0C
0028: MOVLB 05
0029: MOVWF 13
002A: CLRF 14
002B: CLRF 15
002C: MOVLW 01
002D: MOVWF 16

Equals
clear bit 2 of TRISA
clear bit 2 of LATA
write 0C to CCP1CON
clear PWM1CON
clear CCP1AS
write 01 PSTR1CON

I cannot translate this without seeing the code ahead of it. I do not know which bank it is operating in.
.................... {
.................... setup_ccp1(CCP_PWM); // Configure CCP1 as a PWM
0021: BCF 1D.0
0022: BCF 1D.1

I have the same problem with the beginning of the next fragment, without the whole assembly I cannot make a translation. Assembly sometimes setups a function inside another function. So to know the state of the registers I need to see the whole code.
 
Bluelaser, thank you for your reply, that's very helpful.
I am including the original code and the full assembly code, looking forward to your interpratation:

Code:
#include <12f1840.h>
#fuses   INTRC_IO, NOWDT, NOPROTECT, NOMCLR
#use     delay(clock = 4000000)
//#use     fast_io (A)


void main(void)
{
   setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM
   setup_timer_2(T2_DIV_BY_16, 255, 1);
   int16 i;
   
    while(1) 
    {
    for(i=0;i<1020;++i)
        {
        //Output a PWM wave
       set_pwm1_duty(i);
       delay_ms(3);
        }
       delay_ms(500); 
     }
}


Code:
               ROM used: 114 words (3%)
                         Largest free fragment is 2048
               RAM used: 6 (2%) at main() level
                         24 (9%) worst case
               Stack:    1 locations

*
0000:  MOVLP  00
0001:  GOTO   019
0002:  NOP
.................... #include <12f1840.h> 
.................... //////// Standard Header file for the PIC12F1840 device //////////////// 
.................... #device PIC12F1840 
.................... #list 
....................  
.................... #fuses   INTRC_IO, NOWDT, NOPROTECT, NOMCLR 
.................... #use     delay(clock = 4000000) 
0003:  MOVLW  20
0004:  MOVWF  05
0005:  MOVLW  08
0006:  MOVWF  04
0007:  MOVF   00,W
0008:  BTFSC  03.2
0009:  GOTO   018
000A:  MOVLW  01
000B:  MOVWF  21
000C:  CLRF   20
000D:  DECFSZ 20,F
000E:  GOTO   00D
000F:  DECFSZ 21,F
0010:  GOTO   00C
0011:  MOVLW  4A
0012:  MOVWF  20
0013:  DECFSZ 20,F
0014:  GOTO   013
0015:  GOTO   016
0016:  DECFSZ 00,F
0017:  GOTO   00A
0018:  RETURN
.................... //#use     fast_io (A) 
....................  
....................  
.................... void main(void) 
.................... { 
0019:  CLRF   05
001A:  CLRF   04
001B:  MOVLW  1F
001C:  ANDWF  03,F
001D:  MOVLW  6A
001E:  MOVLB  01
001F:  MOVWF  19
0020:  MOVLB  03
0021:  CLRF   0C
0022:  MOVLB  02
0023:  CLRF   12
0024:  CLRF   11
0025:  CLRF   14
0026:  CLRF   13
....................    setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM 
0027:  BCF    1D.0
0028:  BCF    1D.1
0029:  MOVLB  01
002A:  BCF    0C.2
002B:  MOVLB  02
002C:  BCF    0C.2
002D:  MOVLW  0C
002E:  MOVLB  05
002F:  MOVWF  13
0030:  CLRF   14
0031:  CLRF   15
0032:  BCF    1E.0
0033:  BCF    1E.1
0034:  MOVLW  01
0035:  MOVWF  16
....................    setup_timer_2(T2_DIV_BY_16, 255, 1); 
0036:  MOVLW  00
0037:  MOVLB  00
0038:  MOVWF  21
0039:  IORLW  06
003A:  MOVWF  1C
003B:  MOVLW  FF
003C:  MOVWF  1B
....................    int16 i; 
....................     
....................     while(1)  
....................     { 
....................     for(i=0;i<1020;++i) 
003D:  CLRF   26
003E:  CLRF   25
003F:  MOVF   26,W
0040:  SUBLW  03
0041:  BTFSS  03.0
0042:  GOTO   069
0043:  BTFSS  03.2
0044:  GOTO   049
0045:  MOVF   25,W
0046:  SUBLW  FB
0047:  BTFSS  03.0
0048:  GOTO   069
....................         { 
....................         //Output a PWM wave 
....................        set_pwm1_duty(i); 
0049:  MOVF   26,W
004A:  MOVWF  22
004B:  MOVF   25,W
004C:  MOVWF  21
004D:  RRF    22,F
004E:  RRF    21,F
004F:  RRF    22,F
0050:  RRF    21,F
0051:  RRF    22,F
0052:  MOVF   21,W
0053:  MOVLB  05
0054:  MOVWF  11
0055:  MOVLB  00
0056:  RRF    22,F
0057:  RRF    22,W
0058:  ANDLW  30
0059:  MOVWF  20
005A:  MOVLB  05
005B:  MOVF   13,W
005C:  ANDLW  CF
005D:  MOVLB  00
005E:  IORWF  20,W
005F:  MOVLB  05
0060:  MOVWF  13
....................        delay_ms(3); 
0061:  MOVLW  03
0062:  MOVLB  00
0063:  MOVWF  28
0064:  CALL   003
....................         } 
0065:  INCF   25,F
0066:  BTFSC  03.2
0067:  INCF   26,F
0068:  GOTO   03F
....................        delay_ms(500);  
0069:  MOVLW  02
006A:  MOVWF  27
006B:  MOVLW  FA
006C:  MOVWF  28
006D:  CALL   003
006E:  DECFSZ 27,F
006F:  GOTO   06B
....................      } 
0070:  GOTO   03D
.................... }
 

.................... {
.................... setup_ccp1(CCP_PWM); // Configure CCP1 as a PWM
0021: BCF 1D.0
0022: BCF 1D.1

This disables alternative pin assignments for CCP1. CCP1/P1A is on RA2 and CCP1/P1B is on RA0.
clear bit 0 of APFCON
clear bit 1 of APFCON

- - - Updated - - -

.................... setup_timer_2(T2_DIV_BY_16, 255, 1);
0036: MOVLW 00
0037: MOVLB 00
0038: MOVWF 21
0039: IORLW 06
003A: MOVWF 1C
003B: MOVLW FF
003C: MOVWF 1B
puts 06 in T2CON
puts FF in PR2
 
Last edited:
BlueLasers, this was helpfull however I am still missing the translation for the following code from above.
I appreciate your help on this. Thanks again
Code:
....................    setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM 
0027:  BCF    1D.0
0028:  BCF    1D.1
0029:  MOVLB  01
002A:  BCF    0C.2
002B:  MOVLB  02
002C:  BCF    0C.2
002D:  MOVLW  0C
002E:  MOVLB  05
002F:  MOVWF  13
0030:  CLRF   14
0031:  CLRF   15
0032:  BCF    1E.0
0033:  BCF    1E.1
0034:  MOVLW  01
0035:  MOVWF  16
 

clear bit 0 of APFCON
clear bit 1 of APFCON
clear bit 2 of TRISA
clear bit 2 of LATA
write 0C to CCP1CON
clear PWM1CON
clear CCP1AS
write 01 PSTR1CON
 

Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…