Timer 1 Compare Interrupts Timing are Off on ATTiny861?

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

Hello,

 

I apologize if this is the wrong area to post this question... It's been a while since I have been here, and playing with AVRs.

 

So on Timer1 of the ATTiny861, I have OCR1A in complementary output PWM mode. I am using OCR1B as a compare value. What I am trying to do is to clear OCR1B once OCR1A is cleared. So what I am trying to do is:

 

1. set on compare match  when OCR1B = 19;  I am trying to use the OVF interrupt to do this, by updating the COM1Bxx values

2. clear on compare match when OCR1B = 39; I am trying to use the compare B interrupt to set this, by updating the COM1Bxx values

 

However, it just doesn't seem to work, the timing seems way off; I looked at the .lss file and see what I think are a bunch of unnecessary steps. What could I be doing wrong? Is updating compare values to on the same counter cycle even possible? I have tried different optimization settings, and other attempts at solving this problem has failed.

Here is the code I am using:

 


#include <avr/io.h>
#include <avr/interrupt.h>



int main(void)
{
	
	DDRB = 0x00;	// reset all bits
	DDRB |= (1<<DDB5)| (1<< DDB3) | (1 << DDB1) | (1 << DDB0);	//0x07;	// PB0 and PB1 output
	
    // set up timer interrupt
	TCCR1A = 0x00;
	TCCR1B =0x00;
	TCCR1C =0x00;
	TIMSK = 0x00;
	TCCR0B = 0x00;
	PCMSK1 = 0x00;
	PCMSK0 = 0x00;



//	PWM setting
	TCCR1A |= (1 << COM1A0) | (1 << PWM1B) | (1 << PWM1A); // set complimentary PWM on A,
	TCCR1A |= (1 << COM1B1) | (1 << COM1B0);	// Set on compare match	B	// | (1 << PWM1B);	// set on comp B

	OCR1C = 79;	// set PWM frequency
	OCR1A = 39;	// set complimentary flip time 1/2 OCR1C
	DT1 = 0x33;	// set dead time

// Setting Compare Triggers	
	TIMSK |= (1 << OCIE1B);// | (1 << OCIE1B);	// COMP B interrupt activated to flip C
	TIMSK |= (1 << TOIE1);// | (1 << OCIE1B);	// COMP B interrupt activated to flip C

	
	
	OCR1B |= 19;	//halfway between to OCR1A

		
	sei();
	

	TCCR1B |= (1 << CS10); // turn on timer 1
		
	
	while (1)	 
    {
				// do nothing here, let interrupts handle servicing
    }
}



ISR (TIMER1_COMPB_vect)
{
		TCCR1B &= ~(1 << COM1B0);	// COM1B1 = 10	; clear on compare match
		OCR1B = 39;					// clear on this value
}

ISR (TIMER1_OVF_vect)
{
	TCCR1B |= (1 << COM1B0);	// COM1B1 == 11;	set on compare match B
	OCR1B = 19;	// update OCR1B value
}

Thank you!

 

Yours truly,

Amrit

-Amrit

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

it just doesn't seem to work, the timing seems way off;

By a factor of about 8 times by any chance? If so uncheck the DIV8 fuse.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

It's not the clock frequency that's the issue. Here is what the .lss file looks like for the interrupts, and changing the optimization levels hasn't helped.

00000092 <__vector_4>:
  92:	1f 92       	push	r1
  94:	0f 92       	push	r0
  96:	0f b6       	in	r0, 0x3f	; 63
  98:	0f 92       	push	r0
  9a:	11 24       	eor	r1, r1
  9c:	8f 93       	push	r24
  9e:	8f b5       	in	r24, 0x2f	; 47
  a0:	8f 7e       	andi	r24, 0xEF	; 239
  a2:	8f bd       	out	0x2f, r24	; 47
  a4:	87 e2       	ldi	r24, 0x27	; 39
  a6:	8c bd       	out	0x2c, r24	; 44
  a8:	8f 91       	pop	r24
  aa:	0f 90       	pop	r0
  ac:	0f be       	out	0x3f, r0	; 63
  ae:	0f 90       	pop	r0
  b0:	1f 90       	pop	r1
  b2:	18 95       	reti

000000b4 <__vector_5>:
  b4:	1f 92       	push	r1
  b6:	0f 92       	push	r0
  b8:	0f b6       	in	r0, 0x3f	; 63
  ba:	0f 92       	push	r0
  bc:	11 24       	eor	r1, r1
  be:	8f 93       	push	r24
  c0:	8f b5       	in	r24, 0x2f	; 47
  c2:	80 61       	ori	r24, 0x10	; 16
  c4:	8f bd       	out	0x2f, r24	; 47
  c6:	83 e1       	ldi	r24, 0x13	; 19
  c8:	8c bd       	out	0x2c, r24	; 44
  ca:	8f 91       	pop	r24
  cc:	0f 90       	pop	r0
  ce:	0f be       	out	0x3f, r0	; 63
  d0:	0f 90       	pop	r0
  d2:	1f 90       	pop	r1
  d4:	18 95       	reti

 

The goal is to have OCR1B switch on on the second half of OCR1A being on, then switch it off along with OCR1A. I am trying to do asm interrupt routine since it should be relatively simple to adjust the registers around... I remember something about making sure all the interrupts were defined and listed properly, in order, but I can't find an example on how to set them for the life of me...

 

 

-Amrit

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

This is what I have in my asm file so far, but I am not getting any output when trying to use the interrupt... I can get the PWM to work and set just fine if I don't include interrupts...

 

; My first assembler program 
; 
.include "861def.inc"     ; include the 8515 definition file 

// Interrupt Service Routine

.org OC1Baddr	rjmp  IntV2	;

.org OVF1addr	rjmp  IntV3	;
// Register definitions

// Register definitions



.def temp = r16            ; define a temporary register 
.def regs = r15
; 
; In this example, we will output values to PORTB 
; 
RESET: 
       ; Let's set the Data Direction Register for PORTB (DDRB) 
       ; (0 = input, 1 = output) 
       ; pin nums: 76543210 
       ;           |||||||| 
       ;           VVVVVVVV 
       ldi temp, 0b00101011  ;     set PB5, PB3, PB1 and PB0 as outputs		
       out DDRB, temp       ; output the value to DDRB 
       ldi temp, 79	;		; set timer PWM frequency
	   out OCR1C, temp	;	// set value of OCR1C, frequency
	   ldi temp, 39	;		// set complementary switching time
	   out OCR1A, temp	;	// set value of OCR1A
	   ldi temp, 19	;		// set value of OCR1B compare
	   out OCR1B, temp	;	// set initial compare value B
	   ldi temp, 0b01110010	;	// Complementary PWM on OCR1A, Set on compare matchB,
	   out TCCR1A, temp       ; output the value to TCCR1A
	   ldi temp, 0b00100100	;		// CompB Interrupt, Timer1 Overflow Interrupt
	   out TIMSK, temp	;	//   turn on interrupt on Compare B1
	   ldi temp, 0b00000001 ;	
	   out TCCR1B, temp ;	// start clock
	   sei	;	// set interrupts


LOOP: 
       rjmp LOOP                  ; jump back to LOOP 


INTV2:
	in r15, SREG;
	ldi temp, 0b00010000	;// COM1B1 = 10	//clear on compare match
	in r14, TCCR1A;
	eor	temp, r14 ;	// clear on compare match
	out TCCR1A, temp	;  
	ldi temp, 39;
	out OCR1B, temp;
	out SREG, r15;

INTV3:
	in r15, SREG;
	ldi temp, 0b00010000	;// COM1B1 = 10	//clear on compare match
	in r14, TCCR1A;
	or	temp, r14 ;	
	out TCCR1A, temp	;   // set on compare match
	ldi temp, 19;
	out OCR1B, temp;
	out SREG, r15;
	reti	;

 

-Amrit

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

amrit wrote:
and see what I think are a bunch of unnecessary steps. What could I be doing wrong?

They are unnecessary - but they aren't going anywhere any time soon..

 

https://gcc.gnu.org/bugzilla/sho...

 

(and he made that comment in 2005!)

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

Thank you for the thread- it is very insightful. It's just that I have never had this issue using interrupts before, and this was only a few years ago, so I was thinking it might be a compiler issue? I should mention the last version I used was like AVRstudio 5, and now am using the latest version, 7.

 

Anyway, that aside, any recommendations on how should I best address this situation?

 

As I mention in my above thread, I am trying to do this with asm code, which I also posted. I can get the complimentary OCR1A working just fine, but the moment I introduce interrupts, it stops functions. Any advice on this?

 

 

 

-Amrit

Last Edited: Fri. Oct 7, 2016 - 06:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

... Aside from having a

 

reti    ;

statement in there...

-Amrit

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

Let's back up just a bit.

 

How fast is your '861 running?  Doesn't matter too much for your goal, but the timer is at /1, right, so an 80-cycle period.  At 8MHz that is a 100kHz PWM if I did the math in my head correctly.  Tell me the use of such a speed, with a sophisticated sub-cycle.

 

I'm not a big user of that particular AVR family.  I have to dig through the datasheet for the complementary output(s) [Complimentary:  "That's a darned nice looking output you have there, honey!"] and the dead time.

 

Tell you what:  make an ASCII-art timing diagram of your needed output(s).  Indeed you are going to have a hard time with your chosen toolchain doing couple of ISRs that fast.  With another toolchain with 'skinny" ISRs perhaps doable.  But if I read the requirements correctly, wouldn't inverted fast PWM do your job without ISRs?

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

PS forgot to suggest two things. If the unnecessary stuff in the ISR() is a problem:

 

1) first try:

ISR (TIMER1_COMPB_vect, ISR_NAKED)
{
		TCCR1B &= ~(1 << COM1B0);	// COM1B1 = 10	; clear on compare match
		OCR1B = 39;					// clear on this value
		reti();
}

etc but watch for register corruption - this will almost certainly use R24 so you probably need:

ISR (TIMER1_COMPB_vect, ISR_NAKED)
{
        asm("PUSH R24\n")
		TCCR1B &= ~(1 << COM1B0);	// COM1B1 = 10	; clear on compare match
		OCR1B = 39;					// clear on this value
        asm("POP R24\n")
		reti();
}

but that is "dangerous" as you cannot be sure that it is R24 it uses (it will be though). So next suggestion:

 

2) do this in .S as shown in the user manual:

 

http://www.nongnu.org/avr-libc/u...

specifically:

http://www.nongnu.org/avr-libc/e...

 

so you have something like:

#define __SFR_OFFSET 0
#include <avr/io.h>

// myISRs.S
.global TIMER1_COMPB_vect

TIMER1_COMPB_vect:
    push R24
    ldi R24, ~(1 << COM1B0)
    out TCCR1B, R24
    ldi R24, 39
    out OCR1B, R24
    reti
    
etc.

The only "magic" to implementing ISR()s in .S files is to ensure you use exactly the same name as you would have done in ISR() and make sure it is declared as ".global". It will then be a strong link to replace the weak linked "JMP _bad_interrupt" at the vector location. (which is all that ISR() is really achieving too).

 

What will actually happen is that the include of <avr/io.h> will lead to TIMER1_COMPB_vect being replaced (because of a #define) by __vector_4: or whatever the internal name is.

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

I'd still like to see the timing diagram.  And hear the answers to the rest of my queries.  But anyway:

 

TCCR1B |= (1 << COM1B0);	// COM1B1 == 11;	set on compare match B
	OCR1B = 19;	// update OCR1B value

There is something weird going on here--if you really intend to change COM1B0, that is in TCCR1A, isn't it?  Is that the root of your problems?

 

Assume you really meant TCCR1A.  Now, you already >>know<< what value is in TCCR1A, and what you want the new value to be.  So check the generated code for a simple assignment:

TCCR1A = (1 << COM1A0) | (1 << PWM1B) | (1 << PWM1A) | (1 << COM1B1) | (1 << COM1B0);
OCR1B = 19;

(and adjust for the other ISR)  For sure you will save a few cycles.

 

In CodeVision I'd have a few global low register variables with the constants and the "smart ISR" would make the sequence effectively naked:

 

#include <io.h>

register unsigned char B0set;
register unsigned char OCR1Blow;
// Timer1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
    TCCR1B = B0set;
	OCR1B = OCR1Blow;
}

// Timer1 output compare B interrupt service routine
interrupt [TIM1_COMPB] void timer1_compb_isr(void)
{
	TCCR1B &= ~(1 << COM1B0);	// COM1B1 = 10	; clear on compare match
	OCR1B = 39;					// clear on this value

}

void main(void) 
{
B0set = (1 << COM1A0) | (1 << PWM1B) | (1 << PWM1A) | (1 << COM1B1) | (1 << COM1B0);
OCR1Blow = 19;  
   while(1)
   {
   PORTB++;
   }

}
                 	.CSEG
                 _timer1_ovf_isr:
                 ;.FSTART _timer1_ovf_isr
                 ;0000 0008     TCCR1B = B0set;
000035 bc3f      	OUT  0x2F,R3
                 ;0000 0009 	OCR1B = OCR1Blow;
000036 bc2c      	OUT  0x2C,R2
                 ;0000 000A }
000037 9518      	RETI
                 ;.FEND

 

 

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

You are right, it is designed to be running at 100 kHz.

 

Attached is the output diagram I am seeking. You can see the complimentary outputs OCR1A and !OCR1A are pretty straight forward to set up. So is OCR1D, since I only need to set on compare.

 

The problem lies with OCR1B, which I need to set halfway into the OCR1A on cycle, and set it off when OCR1A is off.  If I wanted to set OCR1B and clear it with the Timer, it would work; but I am trying to make OCR1B react with OCR1A, not OCR1C... therefore the need to switch between setting and clearing OCR1B on the same Timer 1 count cycle.

 

I couldn't find much help in the datasheet, and I have been poring over it first to make sure there isn't anything that restricts me from doing this...

Attachment(s): 

-Amrit

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

Thanks for the diagram.  Now, to finish the job do it again with the dead time.  ;)

 

Anyway, I think inverted PWM should work fine.  Both A and B will then turn off at the end of the period, together.  So it is just the start of the pulse that needs to adjust.

 

Again, not being real familiar with using this model family and similar, it may work out that instead of the inverted "PWM1X" the COM bits could be used:

(with the typo corrected; should be /OC1A in the last column)  But from the table, complementary is only available in one combination.  So inverted PWM it is...  Figuring out OC1D is left as an exercise for the reader.

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

Hi clawson,

 

Thank you for your suggestions, clawson. I will try them and see what happens.

 

Thanks for pointing that out, theush. I fixed the interrupt updates, and still don't get expected results.

 

I will work with this new information you have provided, and let you know how things turn out...

 

Amrit

 

 

-Amrit

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

CodeVision Wizard doesn't have provisions for the PWM1X bit.  Let's see if I can adapt the Wizard code to what I think it should be fir 1A, /1A, and 1B.

 

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000.000 kHz
// Mode: Fast PWM top=OCR1C
// OC1A output: OC1A=PWM, /OC1A=/PWM
// OC1B output: OC1B=PWM, /OC1B disc.
// OC1C output: Disconnected
// Fault Protection Mode: Off
// Fault Protection Noise Canceler: Off
// Fault Protection triggered on Falling edge
// Fault Protection triggered by the Analog Comparator: Off
// Dead Time Rising Edge: 0.375 us
// Dead Time Falling Edge: 0.375 us
// Timer Period: 0.01 ms
// Output Pulse(s):
// OC1A Period: 0.01 ms Width: 5.0633 us
// OC1B Period: 0.01 ms Width: 7.5949 us
// Timer1 Overflow Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
// Compare D Match Interrupt: Off
// Fault Protection Interrupt: Off
PLLCSR=(0<<PCKE) | (0<<PLLE) | (0<<PLOCK);

TCCR1A=(0<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (1<<PWM1A) | (1<<PWM1B);
TCCR1B=(1<<PWM1X) | (0<<PSR1) | (0<<DTPS11) | (0<<DTPS10) | (0<<CS13) | (0<<CS12) | (0<<CS11) | (1<<CS10);
TCCR1C=(0<<COM1A1S) | (1<<COM1A0S) | (1<<COM1B1S) | (0<<COM1B0S) | (0<<COM1D1) | (0<<COM1D0) | (1<<PWM1D);
TCCR1D=(0<<FPIE1) | (0<<FPEN1) | (0<<FPNC1) | (0<<FPES1) | (0<<FPAC1) | (0<<FPF1) | (0<<WGM11) | (0<<WGM10);
TCCR1E=(0<<OC1OE5) | (0<<OC1OE4) | (0<<OC1OE3) | (0<<OC1OE2) | (0<<OC1OE1) | (0<<OC1OE0);
TC1H=0x00;
TCNT1=0x00;
TC1H=0x00;
OCR1A=0x28; // 79-39
TC1H=0x00;
OCR1B=0x3C; // 79-19
TC1H=0x00;
OCR1C=0x4F; // 79
TC1H=0x00;
OCR1D=0x00;
DT1=0x33;

 

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

The COM1B bits are what I am trying to use- so setting it is fine for when I want it on, and switching the register to clear on the next compare match along with OCR1A is being the issue here...

 

I should have mentioned with dead times ignored... :)
 

-Amrit

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

amrit wrote:
The COM1B bits are what I am trying to use- so setting it is fine for when I want it on, and switching the register to clear on the next compare match along with OCR1A is being the issue here...

You don't get it  PWM1X -- inverted PWM.  Both will turn off at the same time, at the end of the timer period.  OC1A will start before OC1B based on the OCR1x settings.

 

The datasheet doesn't have much of a diagram for inverted/PWM1X.

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

Hi theusch, I think I understand what you mean by switching oCR1B with the OCR1A. So perhaps I should explain what I am trying to achieve:

 

I would like to control 8 pwm switching cycles independent of each other, each with variable PWM. So if I can't just be switching two switches independently with the timer interrupts, then I have an issue.

 

So perhaps I am using the wrong platform for this? I am looking at the ATxMega64D3-15AT1 I found with the product search. I googled to see if it is compatible with this: the google search produced the attached results, but the link doesn't provide STK500 options... I am wondering if I can use the STK500 with that chip?

 

 

Attachment(s): 

-Amrit

Last Edited: Sat. Oct 8, 2016 - 10:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello folks,

 

So I found a solution to my problem:

 

1. First I set OCR1B as toggle on compare match (COM1B = 01), and set the corresponding compare match interrupt.

2. I check the value and update it to the next value I want in the interrupt routine.

3. And thank you, clawson, I use the ISR_NAKED statement to get it to work... so here is what I have on the code to make this work:

/*
 * PWM_ChangeTest.c
 *
 * Created: 10/8/2016 4:09:45 PM
 * Author : daha6824
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>


int main(void)
{
	TCCR1A = 0;
    /* Replace with your application code */
	DDRB	|=	(1<<DDB5)	|	(1<<DDB3) | (1<<DDB1)	|(1<<DDB0)	;	// set port outputs
		
	TCCR1A |= (1<<COM1A0)	| (1<<COM1B0)	|	(1<<PWM1A)	;	// set OCR1A complementary, OCR1B as toggle on compare match

	OCR1A	=	39;	// set OCR1A switch
	OCR1C	=	79;	// set frequecy
	OCR1B	=	19;	// set initial port B value

	
	TIMSK |= (1 <<OCIE1B)	;   // set CompareB1 interrupt
	
	sei()	;   // enable interrupts
	
	TCCR1B	=	1;  // start clock with no prescalar
	
	
    while (1) 
    {
		
	}
}

ISR(TIMER1_COMPB_vect, ISR_NAKED)
{                                   // update PB3 toggle value
	if (OCR1B	==	19)
		OCR1B = 39;	        
	else
	{
		OCR1B = 19;
	}

	reti()	;
}

Thanks for all your feedback, it was all helpful and is appreciated!

-Amrit