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