PIC CCP Module in Compare Mode

Status
Not open for further replies.

jpanhalt

Well-Known Member
Microcontroller: PIC 16F1829
Language: MPASM

I am using the CCP2 module to create a servo pulse. The interrupt is called at 20-ms intervals by TMR2.

Code:
;******************************************************************************
;                        INTERRUPT SERVCE ROUTINE
;******************************************************************************
ORG       0x0004
nop
movlb     5               ;                                           |B5
movlw     b'00001001'     ;set ccp pin high until match               |B5
movwf     CCP2CON         ;                                           |B5
movlb     0               ;MC example sets TMR1 after CCP2CON         |B0
bsf       T1CON,TMR1ON    ;                                           |B0
btfss     PIR2,CCP2IF     ;                                           |B0
bra       $-1 ;wait for match, which sends CCP output low |B0 clrf PIR1 ;TMR1IF,TMR2IF |B0 ; clrf PIR2 ;CCP2IF |B0 bcf T1CON,TMR1ON ; |B0 movlb 5 ;CCP2 registers |B5 clrf CCP2CON ;force CCP2 output low |B5 movlb 0 ; |B0 clrf PIR2 ;CCP2IF |B0 clrf TMR1L ; |B0 clrf TMR1H ; |B0 bsf Flag0,0 ; |B0 retfie This code works. Once there is a match between the CCPR2L/H registers, which are set before the interrupt, the CCP2IF is set and the CCP2 pin is driven low. CCP2IF need to be cleared by software. An interrupt is not enabled for CCP2IF. I cannot find in the datasheet or in this example (Example 14-4, https://ww1.microchip.com/downloads/en/DeviceDoc/31014a.pdf )any description for how long the pin is driven low by the match, whether the module must be reset after each compare, nor the proper sequence for resetting the module (i.e, clrf CCP2CON) and clearing the flag (CCP2IF). I have tried various ways to get that information. For example, if I try to depend on CCP2IF to keep the pin low and not clear the flag until the next TMR2 interrupt, it doesn't work. It seems logical to me that resetting the module (i.e., clrf CCP2CON) should be done before clearing the flag, but either sequence works, even when separated by a few steps. What is the proper sequence and, if there is a proper sequence, why does it matter? Does CCP2CON need to be reset for each compare? Regards, John Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member Why use an interrupt?? I normally set a PWM frequency output of 250hz ( 4mS ) then using a 10 bit resolution you end up with 256 steps 1mS ~ 2mS I don't know why you are using compare? Compare is for input.... jpanhalt Well-Known Member Most Helpful Member The 20-ms repeat is to satisfy a hobby servo described in post #39 here: https://www.electro-tech-online.com/threads/driving-stepper-motor-gauges.144733/ I am using compare for input. Its registers are loaded with data from an EUSART link to another PIC (see next paragraph). The reason for the interrupt right now is to deal with asynchronous data from another PIC (call it PIC#1). If PIC#2 with the interrupt were simply cycling at 20 ms, you could get a nice "beat frequency" between what's happening in #1 and #2. Ultimately, a TMR2 interrupt may not be needed or can be shortened considerably, but that repeat rate makes it simple for now. I am not concerned about the interrupt as much as I am at understanding details and frails of the compare mode. After doing more Goggling, I came across this inquiry from 2002: https://www.microchip.com/forums/m20541.aspx If you read through the OP's deliberations, it appears that sequential compares with the CCP module in the same mode (i.e., CCPxM<3:0>) cannot be done. One option is to switch modes, e.g., go alternately from b'1000' to b'1001', or just reset the module. Considering the age of the comment attributed to Microchip and failure by MC to revise or put it into writing as best I can tell gives me a small concern about whether that "issue" has been fixed or is described accurately. If that is accurate, then my question is resolved, except for the small detail of whether there is a preferred sequence for clearing the IF and resetting the compare module. John Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member This code works. Once there is a match between the CCPR2L/H registers, which are set before the interrupt, the CCP2IF is set and the CCP2 pin is driven low. CCP2IF need to be cleared by software. An interrupt is not enabled for CCP2IF. I don't get this bit!!!! Using CCP module in compare mode, the CCP2 pin is configured as a input.... The CCPx pins are only outputs when in PWM mode!! jpanhalt Well-Known Member Most Helpful Member From the datasheet: Are you perhaps thinking of capture? One can do compare and not use the CCP pin, but I am using that pin to provide the servo signal. Here's the pertinent section from the control register: John Attachments • Capture.PNG 28.6 KB · Views: 177 Last edited: Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member Okay!!!! Sorry for the senior moment..... I'm thinking of capture!!!!! Duh!! Looking through the datasheet I see that CCPxCON = 00001001 Sets the pin then clears on a match... In your little example you are clearing the CCPxCON... I don't think this is the way to go.... Give me a few minutes to get this working!!! jpanhalt Well-Known Member Most Helpful Member Look at line 4 of my code. However, you touch on the issue I mention at the end of my post #3. Apparently, you cannot do sequential compares in the same mode. There are apparently only two choices, pick another mode or reset the module. John Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member Okay.... This is in C but you'll get the gist.. Timer2 is set for 20mS interval... The CCP2IE must be set!!! and PEIE must be set! C: void interrupt ISR(void) { if (TMR2IF) // timer fired { TMR1L = TMR1H = 0; CCP2CON = 0x09; TMR2IF = 0; } else // CCP2IF fired.. { CCP2CON = 0x0; CCP2IF = 0; } } CAUTION!!! The timer1 timer register pair do not get to 16bits within the 20mS time span Ian Rogers User Extraordinaire Forum Supporter Most Helpful Member Just as a quick test you get about 0x0010 ~ 0x9000 That's about 16 to just over 36864 EDIT!!! Me being stupid again.. 0x0800 represents around 1mS and 0x1000 represents 2mS 2048 bits.... Very workable!!! jpanhalt Well-Known Member Most Helpful Member I am sorry. I have not been very clear about what the interrupt is doing. Here is TMR2's code: Code: Timer2 ;set up recurring interrpts ca. 20 ms movlb 0 ; |B0 bsf INTCON,PEIE ;enable peripheral interrupts |B0 movlb 1 ; |B1 clrf PIE1 ;mask PIE1 peripheral interrupts |B1 bsf PIE1,TMR2IE ;enable TMR2 interrupt |B1 ;NB:INTCON, PIRx, TMR2, T2CON, PIEx are clear on all resets; PR2 is set movlb 0 ; |B0 movlw b'00010011' ;T2CON= 1:64 pre- & 1:3 post-scale |B0 movwf T2CON ; |B0 movlw 208 ;end count adjustment for overhead |B0 movwf PR2 ;64*3*(208)*Tcy = 19.97 ms |B0 bsf T2CON,TMR2ON ;start TMR2 (leave interrupt off) |B0 What that does is create an interrupt flag with the potential to produce an interrupt at 20-ms intervals (approximately). PIC#1 is reading a 14-bit encoder using SPI and sending the MSB 13-bits of that data via USART at 9600 baud. The delay between readings and transmissions can be quite short compared to the 20-ms refresh that a most hobby servos need. The SPI data are received as bytes SPI_L and SPI_H, which are in Common RAM. Those data are divided by 2 before being transferred to the compare registers (CCPR2L and CCPR2H) used in the compare step. The compare mode produces a positive pulse between 0-ms and 2.048-ms wide (i.e., 4096*0.5 usec Tcy). That pulse is outside the usual range of 1 ms to 2 ms for a hobby servo, but it is usable for testing purposes. Very roughly, the USART and the compare process each take about 2 ms. I do not want an interrupt to happen while I am receiving the data. So, for the time being, I have a delay in the transmission, and then enable the TMR2 interrupt after data reception is complete. Here is that part of the code: Code: Display nop movlb 5 ; |B5 movf SPI_L,w ; |B5 movwf CCPR2L ; |B5 movf SPI_H,w ; |B5 movwf CCPR2H ; |B5 bsf INTCON,GIE ;enable global interrupts |B5 btfss Flag0,0 ;set in ISR |B5 bra$-1                 ;wait for interrupt                     |B5
bcf       Flag0,0             ;                                       |B5
;   bcf       INTCON,GIE
bra       Main                 ;get new data                          |B5

As I mentioned above, I can easily eliminate the interrupt and do it all with just an interrupt flag. I am really not too concerned about that aspect right now. My real concern was that the compare didn't behave as I believed it should originally. And that, I think, has been answered by the Microchip forum post linked to above, which recognizes the non-documented need to reset or change/toggle the compare mode between sequential compares.

Just for emphasis, there is no waiting for a 16-bit value to be counted with this PIC. The SPI capture of the double-precision number is very quick compared to the USART transmission of its two bytes.

Thanks for the help and for simulating the compare.

Regards, John

Ian Rogers

User Extraordinaire
Forum Supporter
I've just realised.... I have done EXACTLY the same as you!!! Except for the use of the CCP2IF interrupt..

If the limitation of having to swap the CCP2CON from 0x08 to 0x09, the guy in the microchip forum is correct!!!

I suppose it is designed to flip... or just to be used singularly..

I've since found out that once the CCP2IF flag is set you need to CLEAR the timer to reset the CCP pin

This code works just as well
C:
void interrupt ISR(void)
{
if (TMR2IF)
{
TMR1L = TMR1H = 0;
CCP2CON = 0x09;
TMR2IF = 0;
}
else
{
TMR1L = TMR1H = 0; /// clear timer again
CCP2IF = 0;
}
}

Ian Rogers

User Extraordinaire
Forum Supporter
Yep!
Here is your original code ( in C ) not using the CCP2IF interrupt.. Just clearing the timer pair twice..

It works as well!
C:
void interrupt ISR(void)
{
if (TMR2IF)
{
TMR1L = TMR1H = 0;
CCP2CON = 0x09;
while(CCP2IF==0);
TMR1L = TMR1H = 0;
CCP2IF = 0;
TMR2IF = 0;
}
}

jpanhalt

Well-Known Member
I will play with clearing TMR1's registers twice. Since TMR1 is off and stays off until the next interrupt, I can't think of what that would do, but it is worth a try. Ironically, the Microchip example (14-4 linked to above) clears the TMR1 registers at the beginning of the code. I thought that was just because it was stand alone code, and those registers were not cleared at the end, as I do.

I've since found out that once the CCP2IF flag is set you need to CLEAR the timer to reset the CCP pin
If you are in mode 9 (set CCP pin high, change to low on match) and you clear the flag without changing the mode, does your simulation show the pin going high? If that is the case, then my gut feeling about disabling the compare module before clearing the flag has some justification.

It is a nice module to have.

John

Ian Rogers

User Extraordinaire
Forum Supporter
At first I thought it was too much bother.... But then I thought... For slower systems you can achieve up to 16bit resolution... I can see where it can be used..... The pin goes high when the timer is cleared and the CCP2CON = 0x09 is issued...

jpanhalt

Well-Known Member
I understood about that function of the pin.

As for 16-bit resolution of slow (20 ms) systems. The highest claimed resolution of such servos/transmitters is 10 or 11 bit, but I may be out of date. The young guys flying fast helicopters , like this:
and this:
really push or exceed that limit.

We are seeing much higher refresh rates and resolutions appearing on the hobby scene for helicopters. Still, I believe that a periodic interrupt, as with TMR2, and a compare with TMR1 may be a nice way to create high resolution PWM for slow servos. Albeit, it uses a lot of processor time compared to a simple PWM module implementation.

John

jpanhalt

Well-Known Member
Hi Mike,

Actually, I keep that link and a few related threads in a special folder. From my servo folder:

As a modeler, I found that thread one of the most interesting and helpful at ETO. I certainly referred to that folder during this project. For convenience I have attached a text file with some additional links from that era.

The project is not finished. Understanding the compare function was my main interest, so I used the TMR2 interrupt as a common method with which I was familiar.

John

Attachments

• Driving Multiple Servos.txt
1.3 KB · Views: 177

Mike - K8LH

Well-Known Member
Hi John,

You've come up with a simple, clever, and elegant solution. It's even relatively interrupt tolerant in that if you turn off interrupts momentarily to update some 16-bit value or such, you'll still get rock solid Servo pulses, with only minor, probably insignificant, PWM period jitter. However, I'm not sure I'd sit in the ISR for 1000 to 3000 usecs waiting for the CCP2 interrupt flag. With 17-msecs or more to service the CCP2 interrupt flag you could easily just poll the flag and service it in your main loop. Also, since you turn on Timer 1 after setting up CCP2 'compare' mode '1001' which sets the CCP2 pin, your Servo pulse will always be 2 cycles longer than your CCPR2 setting but that's insignificant in this application. If the extra 2 cycles were a problem you could simply reset Timer 1 to 0x0002 when you service the CCP2 interrupt flag.

Way to go, dude.

Cheerful regards, Mike

jpanhalt

Well-Known Member
Wow, you made my day. Thank you.

I appreciate your advice and will save it into that file. While written as an interrupt, if the PIC were doing anything else, I would probably use polling as you suggest.

Will 2 instructions make a difference? Not today at 8 MHz, but common servos are already down to 2 us deadband. As flyers and equipment improve, we may see that decrease even further. It won't make any difference to the way I fly, though.

Regards, John

Status
Not open for further replies.

Replies
6
Views
3K
Replies
18
Views
4K
Replies
8
Views
4K
Replies
0
Views
1K
Replies
3
Views
3K