I want a pulse when TCNT0 compares with OC0A

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

MCU : ATTiny85

Fuses: (low) e2;  (high) df.

Mode: CTC

 

A stripped-down program is shown below.

 

The datasheet says that if, in TCCR0A, COM0A[1:0] = 0b11, then the output pin (OC0A) is set high on compare match - and so it is.  I just can't figure out how to set it back to zero.

In AS-7, the interrupt works OK, and TCNT0 is reset to zero.  PIN0 (on pin 5) goes high and stays high.  That looks suspicious to me - I had expected PORTB0 to high, not PIN0.  When I run it on a chip, OC0A (on pin 5) goes high and stays high.  I tried setting it low in the ISR by doing

clr R4

out PORTB,R4

... but that doesn't work.  The output compare setting seems to override it.

How can I set PORTB0 back to a zero, so it can go high again next compare interrupt?

 

The program :

.org 0x0
 rjmp main  ; Reset - Address 0
 rjmp INT0_handler    ; vector for INT0 (address 01)
 reti
 reti
 reti
 rjmp TCNT0_overflow ; Vector for Timer/Counter0 Overflow
 reti
 reti
 reti
 reti
 rjmp TCNT0_compare_a

INT0_handler:   ; INT0 vector points here
  in R15,SREG   ; save the status register
  ; lpm R4,Z+
  ; out TCNT0,R4
  clr R4
  out PORTB,R4
  out SREG,R15
  reti

TCNT0_overflow:
  ; increment overflow counter 
  in R15,SREG  ;
  out SREG,R15 ; restore SREG
  reti

TCNT0_compare_a:
  in R15,SREG  ;
  clr R2
  out PORTB,R2  ; >>>>>> THIS DOESN'T WORK! <<<<<<<
  out SREG,R15 ; restore SREG
  reti

; ==================================================
main:
  
  ;Initialize the AVR microcontroller stack pointer
  ldi R16, low(RAMEND)
  out SPL, R16
  ldi R16, high(RAMEND)
  out SPH, R16

  ; MCU setup
  ldi R16,0x03
  out DDRB,R16   ; PB0, PB1 OUT.  All other pins IN. PB0 is OC0A
  ldi R16,0xc2   ; "c" sets OC0A high on compare.  "2" sets CTC mode
  out TCCR0A,R16 ; Pin 5 = PB0.  Set to 0xc0 to make pin5 = OC0A
  ldi R16,0x02   ; <<==== 02 = Clk/8;  Change to 04 for Clk/256
  out TCCR0B,R16 ; CLK prescaler
  ldi R16,0x80   
  out GTCCR,R16  ; Synch mode in CTC mode protects timer prescaler from being reset by hardware
  ldi R16,0x40
  out GIMSK,R16  ; enable external INT0
  ldi R16,0x10   ; OCIE0A
  out TIMSK,R16  ; Enable the Compare interrupt
  ldi R16,0x03   ; Trigger INT0 on rising edge
  out MCUCR,R16  ; Enable INT0 interrupt on rising edge
  ldi R16,4   ; aritrary value for AS-7 simulation
  out OCR0A,R16
  sei           ; Enable interrupts
  again:
   rjmp again

 

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

I think I would do fast PWM with value as PWM and  top as your "compare value+ time on"

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

I think you have to set the port pin low, or clear the OC interrupt flag first.

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

No, for my application, I don't think that would do.  I need to time from a zero count up to 254 in some cases, and there are only 255 counts during my time window, so I have to start at zero.

Isn't it possible to get a pulse on compare?

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

MCU Counters are not designed that way. Period. Yes, you COULD design a counter to do that, but MCU counters are not - they are optimized for PWM generation and general delay timing. It sounds like you want "pulse position modulation".

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

armstack wrote:
Isn't it possible to get a pulse on compare?

As you are describing it:  No.

 

Think about it a bit -- define "pulse".

 

If "pulse" is long enough, then you could let the timer free run, toggle on compare match, and then update the compare match value each match.  But that is difficult (or maybe even "not possible") if your pulse is only a few AVR clocks wide.

 

As mentioned, probably a PWM mode would work OK, and when the timer event turns off your pulse then you could handle the event with interrupt (or polling) and disable the mechanism waiting for the next trigger.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

armstack wrote:
PIN0 (on pin 5) goes high and stays high. That looks suspicious to me - I had expected PORTB0 to high, not PIN0.
PORTB0 doesn't go high because the output is directly controlled by the timer, not by the PORT register. PINB0 goes high because you can always read the current physical state of the pin through the PIN register.

armstack wrote:
I tried setting it low in the ISR by doing clr R4 out PORTB,R4
Can't work, because ... (see my first comment)

 

When the pin is timer controlled you can set/clear/toggle it by a "force compare match".

Stefan Ernst

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

Thanks, Guys.  Initially, when I saw in the datasheet that CTC operation with COM0A[1:0] = 0b11 would set the port high, I assumed it would only be high for the duration of the compare, and that when the timer was zeroed, the output would fall again.

I finally got my pulse by disconnecting OC0A from the port pin, and simply driving the pin with "out PORTB, Rx"

In order to get the pulse I want, I had to delay a bit, which seems nasty in an ISR, but it works, anyway, and I don't think the delay will cause any friction.  This chip will live in quite a simple world.

There are only two active ISRs - this one, and INT0, which will only be triggered every 10mS - so the MCU has plenty of time.

TCNT0_compare_a:
  in R15,SREG  ;
  clr R4
  inc R4
  out PORTB,R4
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  clr R4
  out PORTB,R4
  out SREG,R15 ; restore SREG
  reti

... then, in main:

....
....
  ldi R16,0x02   ; sets CTC mode.  PORTB0 is PB0, not OC0A
  out TCCR0A,R16 ; Set to 0xc2 to make pin5 = OC0A
....
....
etc.

 

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

Edit: removed, because I missed something in your post.

Stefan Ernst

Last Edited: Mon. May 1, 2017 - 07:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you could develop an if statement that if you catch the compare value then you do a pulse high for as much as you want then pull the pin low.

 

I'm thinking it in C strategy; like:

 

if (OC0A==value)
{
    PORTB |= (1<<PB1);
    _delay_ms(500);
    PORTB &= ~(1<<PB1);
}