ATtiny85 TC0 timer problem with OCR0B.

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

Could somebody please explain why my program is doing this?
I'm working in Studio-7, and I haven't transferred it to a chip yet, but when I do, it will be on an ATtiny85.

I'm trying to generate a 100uS pulse every 66667 uS.
I wrote a timing routine, and if I breakpoint it at some point in its cycle, it returns to that point every 66667 uS.
That part works perfectly.  Here's the flow :

 

Running the chip (simulator) at 1MHz, and using TC0, I set OCR0A to 249, and the ISR is called exactly 4 times per millisec.
My required time is 66667 uS, which is 66.667 mS.  The ISR is called 4 times/mS, and 66.5 mS = 266 quarter-mS, so I set that value into R19:R18 (0x10A), and count it down in the ISR.
When it counts down to zero, the ISR has been called 266 times, so 66.5 mS (66500 uS) have elapsed.
I need to time a further 167 uS, so I re-load OCR0A = 166, and next time the ISR is called, I have my 66667 uS.
There are two timing sessions - the initial 66500 uS, and the later 167 uS, and I use R20[0] as a semaphore between the program loop and the ISR.

 

Now I introduce the 100 uS pulse.  I set OCR0B to 100.
At the beginning of the 66667 uS timing loop, I set PB0 to start the pulse.
When TCC0_cmpB_handler is called, I disable the compare (OCIE0B) in the ISR, and also end the pulse on PB0
The pulse is 100 uS and the time between OCR0A compares is 250 uS, so the pulse happens during the first call to TCC0_cmpA_handler.
As previously described, there are a further 265 calls, then R19:R18 times out and the ISR triggers 167 uS later.
We're looking at the last few instructions in the program now.
At the end of that period, OCR0A is set back to 250 (quarter-mS) and OCIE0B and OCIE0A are asserted to re-enable both ISRs
 - but here's the thing :
 The value of OCR0B has not been changed - it is still at 100.  It was disabled; now it is re-enabled, but the count in TCNT0 is only 18 at that point
 - yet TCC0_cmpB_handler is triggered on the next clock pulse.

Why?  It's nowhere near a compare.

 

If you load the code into AS-7, set a breakpoint on the third line from last, then single-step it.

 

.org 0x0

 rjmp main                   ; Reset - Address 0
 reti                        ; INT0 (address 01)
 reti                        ; Pin Change Interrupt Request 0        
 reti                        ; Timer/Counter1 Compare Match A
 reti                        ; Timer/Counter1 Overflow
 reti                        ; Timer/Counter0 Overflow
 reti                        ; EEPROM Ready
 reti                        ; Analog Comparator
 reti                        ; ADC Conversion Complete
 reti                        ; Timer/Counter1 Compare Match B
 rjmp TCC0_cmpA_handler      ; Timer/Counter0 Compare Match A
 rjmp TCC0_cmpB_handler      ; Timer/Counter0 Compare Match B
 reti                        ; Watchdog Time-out
 reti                        ; USI START
 reti                        ; USI Overflow

;===================================================================
TCC0_cmpA_handler:   ; called every 1/4 mS
; This is the guts of the program.  In "main", the timer compare reg was set to 250.  The timer counts at clock speed (1MHz), 
; so this ISR is called every 250uS.  R18:19 are counted down from 266, and reach zero after 66.5 mS.

in R15,SREG
sbrc R20,0  ; if this bit is set, we are timing from 66500uS - 66667uS
rjmp timing_to_66667
subi R19,1  ; R20[0] was not set.  dec R19:R18
sbci R18,0  ; dec R18 by the amount of Carry
brne TCC0_return
sbr R20,0x01 ; indicates end of 66500uS to loop
rjmp TCC0_return
timing_to_66667:
cbr R20,0x01
TCC0_return:
out SREG,R15
reti

TCC0_cmpB_handler:
; Triggered at end of pulse (currently 100 uS)
; Switch off the pulse and Disable this interrupt, so it only happens
;  once during the 66667 uS timing cycle.
; The ISR will be switched on again in the loop.
ldi R22,(1<<OCIE0A|0<<OCIE0B)  ; OC0A Interrupt enable, OC0B disable
out TIMSK,R22
cbi PORTB,PB0  ; Switch off the pulse
reti

; main sets the stack pointer, port directions, pull-ups, CTC mode
; and sets some program variables
main:
; Load stack register
LDI R16, HIGH(RAMEND) ; Upper byte
OUT SPH,R16 ; to stack pointer
LDI R16, LOW(RAMEND) ; Lower byte
OUT SPL,R16 ; to stack pointer

; Define directions for port pins
ldi r16,(1<<PB0)  ; pins 5, 6 & 3
out DDRB,R16      ; Set PB0 to OUTput

ldi R16,(0<<COM0A0)|(0<<COM0B0)|(2<<WGM00)  ; WGM[2:0] = 0x2 sets CTC mode. COM0cn =0 : do not control pins with compare match.
out TCCR0A,R16                              ; COM0A0 toggles OC0A (pin 5) on compare match

; the counter is clocked every 1uS.  
; it counts up to a match with RCO0A (250uS), then TCC0_cmpA_handler is called
ldi R16,249  
out OCR0A,R16

ldi R16,100   ; 100 uS pulse length
out OCR0B,R16

ldi R16,(1<<OCIE0A|1<<OCIE0B)  ; OC0 int ena
out TIMSK,R16


ldi R16,(1<<CS00)  ; start counter without prescaling. 1MHz.
out TCCR0B,R16

ldi R16,95
out OSCCAL,R16

cbi PORTB,PB0 ; switch the pulse off

ldi R18,1    ; R19:R18 loaded with 0x10A = 266 qSecs
ldi R19,0x0a     

cbr R20,0x01  ; Measuring initial 66500 uS 

sei

; before the loop, switch on the pulse and enable its interrupt
; The pulse will be ended by TCC0_cmpB_handler
start_pulse:
sbi PORTB,PB0  ; Start the pulse
ldi R22,(1<<OCIE0A|1<<OCIE0B)
out TIMSK,R22
; The loop waits for the end of a mS, then examines the mode register and jumps to Timer, Program or Replay accordingly.
loop:
; sbrc R20,0
; rjmp uS_66667
sbrs R20,0  ; test end of 66.5mS
rjmp loop   ; 
; End of 66500 uS
; set OCR0A to 167
ldi R16,166   ; set up to count the remaining 167uS
out OCR0A,R16 ; set the timer compare reg
sbr R20,0x01  ; to count the last 166uS
count_66667:
sbrc R20,0
rjmp count_66667
ldi R16,249  
out OCR0A,R16
ldi R18,1    ; R19:R18 loaded with 0x10A = 266 qSecs
ldi R19,0x0a 
ldi R16,(1<<OCIE0B|1<<OCIE0A)  ; Re-enable OCR0B compare
out TIMSK,R16
rjmp start_pulse

 

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

The compare still happens and sets the interrupt flag even though the actual interrupt source is disabled. When you enable the interrupt, you get an immediate response. Solution: clear the interrupt flag before enabling the interrupt. You clear the flag by writing a '1' to it.

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

That fixed it!  Thanks a million, Kartman - especially before 11AM on a Sunday morning.  I guess Covid-19 is keeping you away from church  ;-)

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

armstack wrote:
I guess Covid-19 is keeping you away from church  ;-)

The practices of modern Druids typically take place outside, in the daylight, in what is described as "the eye of the sun", meaning around midday. In some cases, they instead perform their rites indoors, or during the night. Druidic rituals usually reflect on the time of year and the changing of the seasons.

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

It was after 1pm my time, so church would’ve been over.

The mere mention of druids brings me images of Spinal Tap doing stonehenge.

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

"they instead perform their rites indoors "

 

Just as well!  You don't want anybody prancing around in the nude outside before 11 AM on a Sunday morning.

" or during the night " - better still!