Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Generating PWM pulse using 16f628 Timer1 & Comparator

Status
Not open for further replies.

bpmelo

New Member
This is my first post on this forum, so, good morning/afternoon/night to everyone.

After reading the post from Mike, K8LH on this thread https://www.electro-tech-online.com/threads/pwm-basics.22650/ , I figured that using Timer1 & Comparator instead of the PWM module on 16f628 is better cause of the resolution I can get using 16bit on Timer1 instead of just 8bit on Timer2.

I code the same solution on C and compiled it on SDCC, I'm running only Linux on my PC, so can't use MPLab.

Here's the code :

Code:
#define PERIOD_CYCLE	20000 // 20 ms
#define DUTY_CYCLE	1500  // 1,5 ms

void InterruptHandler(void) interrupt 0 {

	// Disable global interrupts
	GIE = 0;

	if (CCP1IF == 1) { // Interrupt on comparator module

		// Clear the interrupt bit
		CCP1IF = 0;
		
		// Reset Timer 1 values
		//TMR1H = 0; 
		//TMR1L = 0; 
		
		/*
		 * + CCP1M3:CCP1M0: CCPx Mode Select bits
		 * 
		 * 1000 = Compare mode, set output on match (CCP1IF bit is set)
		 * 1001 = Compare mode, clear output on match (CCP1IF bit is set)
		 */

		// It's set to go HIGH on CCPR1 and TMR1 match
		if (CCP1M0 == 0) {
			// We are on the duty cycle, the output on RB3 will go HIGH
			// Change the setting to interrupt on LOW next time
			CCP1M0 = 1;

			// Setup next values for duty cycle
			CCPR1H = (DUTY_CYCLE / 256); // Clear the lower part
			CCPR1L = (DUTY_CYCLE % 256); // Clear the higher part
		}

		// It's set to go LOW on CCPR1 and TMR1 match 
		else {
			// We are on the period cycle, the output on RB3 will go LOW
			// Change the setting to interrupt on HIGH next time
			CCP1M0 = 0;

			// Setup next values for period cycle
			CCPR1H = ((PERIOD_CYCLE - DUTY_CYCLE) / 256);
			CCPR1L = ((PERIOD_CYCLE - DUTY_CYCLE) % 256);
		}
	}

	// Enable global interrupts
	//GIE = 1; // Not necessary since SDCC does that automatically
}

void init_servo(void) {

	/*
	 * Duty cycles on Futaba servos (Controls the position)
	 *   1ms : 0 degrees
	 *   1.5ms : 45 degrees
	 *   2.0ms : 90 degrees
	 *
	 * Period on Futaba servos (Controls the rotation speed)
	 *   10ms - 20ms
	 */

	// Stop Timer 1
	TMR1ON = 0;
	
	// Set PWM port (RB03) as an output
	TRISB3 = 0;

	// Reset Timer 1 values
	TMR1H = 0; 
	TMR1L = 0; 

	/* 
	 * 1000 = Compare mode, set output on match
	 * 1001 = Compare mode, clear output on match
	 */ 
	// Enable compare mode on CCP1CON register
	CCP1CON = 9;

	// Clean the interrupt register
	PIR1 = 0;
	
	// Enable Peripheral module interrupt
	PEIE = 1;

	// Enable CCP module interrupt
	CCP1IE = 1;
	
	// Enable Global Interrupt Enable bit
	GIE = 1;

	// Reset Comparator values
	// This will trigger the interrupt right away
	CCPR1H = 0;
	CCPR1L = 0;
	
	// Start Timer 1
	TMR1ON = 1;
	
}

I'm using the internal oscillator, so at 4Mhz I should get 1000 instructions per 1 ms.
So using 20000 for the period and 1500 for the duty cycle I should end up with 20ms and 1,5ms.

The problem is that the servo, a Futaba 3003 (the most common one), doesn't respond correctly, it goes all the way to left and stays there trying to go further, drawing current from the board.

The servo works correctly, I have a 1:10 RC model and always test it on the original Futaba radio, it works perfectly.

I use a 12v AC connected to a 7805 voltage regulator with a couple of capacitors on both legs to get a stable 5 V.

The strange thing is that if I plug a led on RB3, it blinks fast, so it's obvious that some kind of waveform is being feed to RB3.

The only difference of my code and Mike's is that he keeps adding the value to the CCPR register, and I put the period/duty value straight, without adding anything to it. This is the only part of his code that I couldn't figure out.

Code:
Mike, K8LH's -> CCPR1H [B][COLOR="Red"]+=[/COLOR][/B] ((Pulse*5/2)/256)
Mine -> CCPR1H [B][COLOR="Red"]=[/COLOR][/B] (DUTY_CYCLE / 256);

Can you guys spot any obvious error on my code ? I'm confident that I'm close to get this servo working correctly.

EDITED:
I just noticed that the servo tilts a little bit to right then it goes all the way to left and stays on trying to go further the limit.
Couple of questions that came in mind :

1. Do I need to reset the values on Timer1 on every interruption ?
2. What about that other "Special Event" comparator mode ?
3. How can I debug interruptions ? I installed MPLab on my father's PC, but it also doesn't stop on any breakpoint inside the interrupt method.

Thank you, and great forum by the way.
 
Last edited:
Forum, I finally got my servo working on a 16f628 using the SDCC compiler.
When reading an Application note from Microchip I came across, this :

14.4.1 CCP Pin Operation in Compare Mode
The user must configure the CCPx pin as an output by clearing the appropriate TRIS bit.
Note: Clearing the CCPxCON register will force the CCPx compare output latch to the default low level. This is not the Port I/O data latch.
Selecting the compare output mode, forces the state of the CCP pin to the state that is opposite of the match state. So if the Compare mode is selected to force the output pin low on match, then the output will be forced high until the match occurs (or the mode is changed).

To me this is a clear bug on the CCP module, since changing the CCP mode shouldn't invert the ouput pin. Again, this is not on the datasheet, got it from an Application Note.

After this I changed the mode to generate the PWM wave, using just Timer1 and it's overflow interruption, and for my surprise, it worked !!

I found this solution easier to understand, specially in C. And it's full with comments also, so whoever wants to look at it, here are the project files.

Thanks again, great forum to search for solutions.
 

Attachments

  • delay.c
    727 bytes · Views: 294
  • delay.h
    86 bytes · Views: 257
  • main.c
    1 KB · Views: 286
  • servo.c
    2.6 KB · Views: 319
  • servo.h
    825 bytes · Views: 224
  • main.h
    508 bytes · Views: 251
Why is this a bug? I mean it makes sense that if you want the pin to go LOW after some time, that pin would be HIGH when you initiate the process. Why should you use CCP to take the pin LOW after some time if its initial state was LOW at the first place. (same applies to HIGH).
 
Last edited:
Why is this a bug? I mean it makes sense that if you want the pin to go LOW after some time, that pin would be HIGH when you initiate the process. Why should you use CCP to take the pin LOW after some time if its initial state was LOW at the first place. (same applies to HIGH).

I think it's a bug because it should only change the state of the port when the match occurs, not because you changed the CCP config. The actual state of the CCP config should decide if the output goes HIGH or LOW, only when the interruption occurs. But the chip is changing the state of the port outside the interruption, which is bug.
 
It may or may not change the pin state. I've never checked it. But does it really matter? After all, you only tell it to set the pin 'lo' next match when the pin is currently 'hi'. We don't care if the module tries to set the pin 'hi' because it's already 'hi'. Same thing when the pin is 'lo' and well tell the module to set the pin 'hi' on the next match. Who cares if the module tries to set the pin 'lo'? It's already 'lo'...

Btw, if you haven't figured out why you add values to CCPR1 then you should have another read of the CCP module section in the Data Sheet...

Mike
 
It may or may not change the pin state. I've never checked it. But does it really matter? After all, you only tell it to set the pin 'lo' next match when the pin is currently 'hi'. We don't care if the module tries to set the pin 'hi' because it's already 'hi'. Same thing when the pin is 'lo' and well tell the module to set the pin 'hi' on the next match. Who cares if the module tries to set the pin 'lo'? It's already 'lo'...

Btw, if you haven't figured out why you add values to CCPR1 then you should have another read of the CCP module section in the Data Sheet...

Mike

The problem is that it changes the RB3 state when modifying the comparator mode, I know nothing about about chip designing, but this is not something I would consider a programmed behavior.

I figured the CCPR1 += thing, thanks for explaining it btw.
 
You're not taking into account the state of the pin when you change the CCP1M0 bit. When you change CCP1M0 to go 'hi' on the next match interrupt the CCP1 module may indeed force the pin 'lo' but it's already 'lo' so you shouldn't have a problem.

Did you observe something different?

I figured the CCPR1 += thing, thanks for explaining it btw.

I didn't explain it. I suggested you re-read the appropriate chapter. But I'm glad you figured it out.

Mike
 
Last edited:
You're not taking into account the state of the pin when you change the CCP1M0 bit. When you change CCP1M0 to go 'hi' on the next match interrupt the CCP1 module may indeed force the pin 'lo' but it's already 'lo' so you shouldn't have a problem.

Did you observe something different?



I didn't explain it. I suggested you re-read the appropriate chapter. But I'm glad you figured it out.

Mike

Humm, it's kinda confusing, when you put it that way makes sense, because it won't actually change the state on the port, will just "re-set" to same state.
Anyway, some more detailed info should be added to the datasheet, like the state of the Timer1 register after the interrupt, I couldn't find one single place where it says if the register gets cleared or not, only on that "special" mode.

I was being sarcastic about the += thing Mike :D

Now up to the next part on the project. Will keep you guys posted.
 
The timer keeps running and rolls-over and that's why you add the value to the CCPR1 register pair in those modes.

The special event trigger mode is the only mode that resets the timer to 0000 but that modes doesn't set or clear the CCPx bit in hardware so then you have to worry about jitter.

If you reset the timer in the ISR you may have a problem with pulse width jitter.

Mike
 
Last edited:
The timer keeps running and rolls-over and that's why you add the value to the CCPR1 register pair in those modes.

The special event trigger mode is the only mode that resets the timer to 0000 but that modes doesn't set or clear the CCPx bit in hardware so then you have to worry about jitter.

If you reset the timer in the ISR you may have a problem with pulse width jitter.

Mike

Yup, i understood it, it overflows and so does the timer, so the values work okay.
I'm not getting any jitter using just the Timer1 interruption Mike, is there some way to test it ?
 
Status
Not open for further replies.

Latest threads

Back
Top