OC1A, OC1B and synchronization

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have ATmega8.

I need generating two synchronized PWM signals at pins OC1A and OC1B; having the same frequency and duty cycle. Actually, the output signal at one pin is the complement of the other (by using the non-inverting and inverting modes).

 

My question is:

If the control registers are set properly, will the two generated signals be synchronous (exactly out of phase) automatically when enabled?

 

Thank you.

 

Kerim

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A caveat is that, since you can't >>update<< OCR1A and OCR1B at the same time (their updates will be separated by a minimum of 4 CPU cycles), you must take care to update them far enough away from the point at which the hardware latches new values.

 

For example, if using mode 5, the OCR1x registers are latched to the output compare unit at BOTTOM.  So in your code, you would poll TCNT1, and delay the update of OCR1A/B until just >>after<< TCNT1 has rolled over to 0.  In practice, when I need to do what you're describing, I do something like:

while (TCNT1 > some-low-number) {}

What that number is will depend on a few things like what prescaler you're using, and whether or not you have any ISRs enabled, and what the worst-case execution time of those ISRs might be.  The objective is to guarantee that OCR1B is not updated 'on the other side of' the point where OCR1A is updated.  If timing is tight, you may need to guarantee atomicity in the tests and updates.

 

EDIT:

uint16_t tcnt1;
const uint16_t threshold = 200;
do {
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    tcnt1 = TCNT1;
    if (tcnt1 < threshold) {
      OCR1A = new-value;
      OCR1B = new-value;
    }
  }
} while (tcnt1 < threshold);

Although the test is performed twice, it is necessary.  A break; statement within the if{} statement is not sufficient to break out of the do{}while loop.  Since the ATOMIC_BLOCK() is within its own code block, a break; statement would serve only to break out of that block.  The same could be achieved with separate cli() and sei() calls (or preserving SREG), avoiding the ATOMIC_BLOCK altogether, but the construction will be somewhat awkward anyway, and possibly incorrect unless memory barriers are included (ATOMIC_BLOCK already employs them).  YMMV.

 

There is likely a smarter programmer than I who could provide a better solution.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Tue. Aug 29, 2017 - 11:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you. joeymorin, for your remarks that I will refer to in my future tests since I didn't build the prototype of the project yet.

 

For instance, although I understand C, I write all my codes for MCUs in assembly.

 

Kerim

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So something like this:

; new duty:             r15:14
; tcnt threshold:       r13:12
; scratch:              r11:10
;
update_duty_wait:
        cli
        lds     r10, TCNT1L
        lds     r11, TCNT1H
        cp      r10, r12
        cpc     r11, r13
        brcs    update_duty_safe:
        sei
        rjmp    update_duty_wait
update_duty_safe:
        sts     OCR1AH, r15
        sts     OCR1AL, r14
        sts     OCR1BH, r15
        sei
        sts     OCR1BL, r14
        ret

 

Or, if you can't wait but would rather skip the update:

; new duty:             r15:14
; tcnt threshold:       r13:12
; scratch:              r11:10
;
update_duty_if_safe:
        cli
        lds     r10, TCNT1L
        lds     r11, TCNT1H
        cp      r10, r12
        cpc     r11, r13
        brcc    update_duty_skip_this_one:
        sts     OCR1AH, r15
        sts     OCR1AL, r14
        sts     OCR1BH, r15
        sei
        sts     OCR1BL, r14
update_duty_skip_this_one:
        reti

EDIT:  bug fix

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Tue. Aug 29, 2017 - 06:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you, joeymorin, for your updated code in assembly... and your time.

 

Kerim