Interrupts

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

I have a program where I am doing 20kHz PWM

 

Every 50us I interrupt and update the modulation indices

 


ISR (TIMER1_OVF_vect)
{   PD2_HIGH;
	int ma = vr*sin_theta[m] + (MAXMODINDEX/2);
	int mb = vr*sin_theta[n] + (MAXMODINDEX/2);
	
	MIA = ma;
	MIB = mb;
	n=n+1;
	m=m+1;
	
	if(n>=400)
	{   
		n=0;
		flag=0; 
		
	}
	if(m>=400)
	{
		m=0;
	}			
			   
	PD2_LOW;
}	

Obviously 50us isn't very long and there is plenty of things that take longer than this, now lets say I was wanting to use an LCD screen

 

My while loop might be this

 

while(1)
	{
		if(flag==0)
		{
		PD2_HIGH;
		sprintf(tempstr, "Temp is %.2fC", myflt1);
		sprintf(rhstr, "RH is %.2f%%", myflt2);
		
		LCD_Line(0);
		LCD_Str(tempstr);
		
		LCD_Line(1);
		LCD_Str(rhstr);
		
		LCD_Line(2);
		LCD_Str("Hello");
		
		LCD_Line(3);
		LCD_Str("World");
		flag =1;
		PD2_LOW;
		}		
	}

Obviously that lot will take more than 50us, a lot more (it takes about 10ms) and I would like to do more like read an SPI sensor or the ADC so I was wondering what are the pitfalls to doing this?

 

Edit I would only be doing this once a second but its the same thing!

 

Regards

 

 

Last Edited: Sun. Nov 2, 2014 - 08:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What do you perceive to be a problem? The immediate issue i see is variables named m and n not declared static in the isr. If they are used elsewhere, then a more descriptive name would be helpful.

 

[duplicate deleted]

Last Edited: Mon. Nov 3, 2014 - 11:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman, I always appreciate your input

 

I don't perceive a problem as such its just something I have never had to do before ie all my programs  have always fully executed with an ISR interval so the first time doing this it doesn't feel right

 

Is there any things we are not allowed to interrupt? how do you know when you would have to disable interrupts before doing something?

 

<confused>

 

The m and n are declared volatile globally, the ISR increses their value, they are the index of a lookup table, m is 180 degrees ahead as this is is H bridge inverter

 

To be honest you have hit on something as I understand why we use volatile if an ISR uses a variable its essential to stop the compiler optimising it away but me being me I usually declare all variables that are used by different functions volatile because I am scared! I don't think I actually need to but the cost to me is nothing compared to the possible consequences

 

I need to read some stuff

Last Edited: Sun. Nov 2, 2014 - 09:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wrote a tutorial "the traps when using interrupts" that should explain the requirements. I now see you set/clear pd2 in both functions - two asynchronous tasks doing the same thing will cause some interesting results when you scope the pin.

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

I can also see you share 'flag'. This will need to be declared volatile and be a uint8_t

the compiler will try to keep vars in registers for efficiency. If the compiler finds it doesn't need to write the var back, it wont. With a shared variable, it doesnt know something else is wanting the value. Volatile forces its hand to write the value unconditionally. Then there is the issue of atomicity.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	int ma = vr*sin_theta[m] + (MAXMODINDEX/2);
	int mb = vr*sin_theta[n] + (MAXMODINDEX/2);

...

No real AVR8 app would do it that way.

 

The calculations for that might well take more than 50us -- every time.

 

400 point sign table?  Really?

 

It looks (at first glance) two identical signals/calculated values.  So why do it twice?

 

In any case, use a lookup table, with a reasonable number of values.

 

You might want to look at the AVr8 app notes on motor control, where they indeed use PWM and a fast tick and a lookup table.

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

I wrote a tutorial "the traps when using interrupts" that should explain the requirements

I will check it out

 

I now see you set/clear pd2 in both functions

 Yers the code thats posted here but not in my program, I commented it out rather than delete so I can scope it fast when needed, I have done and still do stupid things like that regularly though

 

No real AVR8 app would do it that way.

 Well I suppose you will have to define real but this for me isn't for work, its not academic its just silly old me at home messing around hell this program doesnt even do much yet it is not functional its certainly not a professional level project thats for sure

 

Do you realise what I am doing though? because I didnt really explain

 

I know a 400 point table isn't very efficient and I know we can do it all in 90 degrees of lookup table but I just want to play around and get something working this is going nowhere to be honest its just fun but let me explain

 

I am modulating a H bridge with sinusoid reference PWM, the two signals are the same but 180 degrees apart, I want a fixed 50Hz output frequency with variable magnitude, its an electronic transformer

 

There is 400 50us samples in one period of 50Hz, my max clock count is 200 so it all works out nice with a 400 point table its the easiest way for me

 

I have max mod index of 200 so 100 is the zero crossing point

 

The mod index is given by

 

(MAXMODINDEX/2)*sin(2*PI*f_ref*i*TS) +MAXMODINDEX/2

 

@0 degrees it is MAXMODINDEX/2

 

@ max it is MAXMODINDEX

 

@min it is zero

 

To scale it I adjust this part

 

(MAXMODINDEX/2)*sin(2*PI*f_ref*i*TS)

 So I will select Vr to scale it, Vr can be 0-100 so

 

Vr*(MAXMODINDEX/200)*sin(2*PI*f_ref*i*TS)

Vr is variable so set up the table

volatile uint16_t n                  = 0;    
volatile uint16_t m                  = 200;     

uint8_t sin_theta[401]={};    
 

        
int main(void)
{
    for (int16_t i=0; i<=400; i++)
    {
        sin_theta[i]= (MAXMODINDEX/2)*sin(2*PI*f_ref*i*TS);
    }

 

So that in the ISR

ISR (TIMER1_OVF_vect)
{   PD2_HIGH;
	int ma = vr*sin_theta[m] + (MAXMODINDEX/2);
	int mb = vr*sin_theta[n] + (MAXMODINDEX/2);
	
	MIA = ma;
	MIB = mb;
	n=n+1;
	m=m+1;
	
	if(n>=400)
	{   
		n=0;
		flag=0; 
		
	}
	if(m>=400)
	{
		m=0;
	}			
			   
	PD2_LOW;
}	

 

I can work out vr in the main loop and use it to control the magnitude of the output voltage

 

Is there an easier way to do it than this?

 

It takes 10us with an 8MHz clock to do the ISR

 

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

Is there an easier way to do it than this?

Answers on a postcard please, it seems the logical way to me I multiply a lookuptable value by a variable and add on an offset but is there a better way?

Last Edited: Mon. Nov 3, 2014 - 01:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you're happy with using 20% cpu and it does what you want, then you're home free. If you wanted to chew up some ram, you could compute the table 

when you change the amplitude and store the table in ram. This would remove the multiplies from the isr. So you trade ram for performance. You choose the compromise! 

 

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

Bignoob wrote:

Is there an easier way to do it than this?

Answers on a postcard please, it seems the logical way to me I multiply a lookuptable value by a variable and add on an offset but is there a better way?

The main thing I see is that you are pretty much doing everything twice (since you say m and n are fixed at 180 degrees out of phase).  Just use one index, calculate vr*table[i] once, then for phase 1, add to the offset, and for phase 2, subtract from the offset.

 

A smaller thing, but counting down to zero is faster than doing a compare to 400, and won't mess up your code.

 

i = 399;
...
temp = vr * table[i];
if (i-- == 0)
  i = 399;

 

 

Last Edited: Mon. Nov 3, 2014 - 03:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Answers on a postcard please, it seems the logical way to me I multiply a lookuptable value by a variable and add on an offset but is there a better way?

Many AVR8 models have complementary PWM outputs, which give you 180-degree-phase-shift "for free", right?

 

I'd have to think about whether a normal AVR8 timer1, with two PWM outputs one inverted, would give you the same thing.

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

theusch wrote:

Answers on a postcard please, it seems the logical way to me I multiply a lookuptable value by a variable and add on an offset but is there a better way?

Many AVR8 models have complementary PWM outputs, which give you 180-degree-phase-shift "for free", right?

 

I'd have to think about whether a normal AVR8 timer1, with two PWM outputs one inverted, would give you the same thing.

 

I love simple h/w solutions, why do it in s/w when the h/w is already there!

 

JC

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

Ok if it works do it :)

 

If you want more speed here are suggestions.

Have 256 steps not 400 (or even 512).

count down (if 256 step just let a char run over up or down then don't matter).

if always 50Hz it means n and m just are incremented each time then use a pointer++ (so the code don't need to deal with the offset of table each time).

perhaps only one pointer since the distance always are 180.

put the pointer in registers 

or just write it in ASM :)  

 

You say that it take 10uS at 8MHz that give only 80 clk that seems fast , what type are vr  and the elements in your table?

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

If you're happy with using 20% cpu and it does what you want, then you're home free

=-)

 

I wouldn't say I was unhappy it doesn't really bother me hammering the memory, if its there why not is my moto! but this is just a bit of fun

 

add to the offset, and for phase 2, subtract from the offset

 Why yes of course, I hadn't thought of doing that!, excellent suggestion thanks

 

which give you 180-degree-phase-shift "for free", right?

 Another excellent suggestion, I need to look at this but you have definitely won a Mars bar

 

Thanks

 

Have 256 steps not 400

 I thought of this and it is certainly an option but I have done some more work on this program, hell it wasn't even a proper program it was just used to demonstrate my question but I have now gone on and its getting there

 

 

Notice how in my original I had used a uint for a sin table, obviously it would never work as I need signed

 

So the problem is that I need to calculate

 

Vrsin(wt)

 

Vr is an int between 0-100

 

So

 

Vrsin(wt) has max value 100 and min value 0

 

To do this then sin would have to be float and it would take a longtime, it takes a lot longer

 

So what I have done is this

 

int16_t sin_theta[400]={};   

int main(void)
{
    for (int16_t i=0; i<=399; i++)
    {
        sin_theta[i]= 256*sin(2*PI*F_REF*i*TS);
    }   

 Times the table by 256 so it can be an int

 

ISR (TIMER1_OVF_vect)
{   PD2_HIGH;
    int16_t table_value = (vr*sin_theta[n])>>8;
    MIA = (MAXMODINDEX/2 + table_value);
    MIB = (MAXMODINDEX/2 - table_value);
  
      if(n++ ==399)
    {   
        if(p++ ==50)
        {
         p=0;
         flag=0;    
        }
        n=0;
        
    }
    
    PD2_LOW;
}   

 So I times it by 256 when the table is initialised, I use Vr to scale the result and then I right shift 8 bits to divide by 256

 

It seems to work but I am not near a scope at the minute to check the execution time

 

I will look at the inverted PWM mode now

 

What do you guys think?, is there any further improvements?

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

I wrote a tutorial "the traps when using interrupts" that should explain the requirements

 Kartman I read your tutorial

 

This bit stands out

 

Atomicity
Certain operations need to be done without interruption - 'atomically' which means 'as one' or 'indivisable'. This needs to happen when:

1. Reading/writing variables that are larger than one byte. The AVR being an 8 bit cpu, reads/writes in 8bit chunks. so to read/write a int variable (in C), we need to r/w in two chunks, the low byte and the high byte. This takes a few processor instructions to achieve this. What happens if an interrupt occurs between these two operations and the ISR modifies the variable we're accessing? We get half the unmodified variable and the other half modified

 And it seems like it could be a problem, my main loop is taking the p1ss!, just for fun I have tried to make it sweat!

 

if(flag==0)
        {
        //PD2_HIGH;
        Read_SPI(0x55);
        max   = Read_ADC(POT_CHAN);
        temp       = result_array[1];
        error = temp-max_temp;
        humidity   = result_array[0];
        
        
        sprintf(tempstr, "Temp is %.2fC", temp);
        sprintf(rhstr, "  RH is %.2f%%", humidity);
        sprintf(maxtempstr, "Max temp is %.2fC", max_temp);
        sprintf(vrstr, "vr %d vmag @ %d%%", vr,vmag);
        LCD_Command(LCD_LINE_0);
        LCD_String(tempstr);
        LCD_Command(LCD_LINE_1);
        LCD_String(rhstr);
        LCD_Command(LCD_LINE_2);
        LCD_String(maxtempstr);
        LCD_Command(LCD_LINE_3);
        LCD_String(vrstr);
        flag =1;
        //PD2_LOW;
        }        
    }

 SPI comms, LCD displays, formatting floats to strings its all cycle consuming stuff,  it does seem to be working but whether there are atomicity issues or not I really don't know

Last Edited: Tue. Nov 4, 2014 - 01:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With a constant table, i would normally precalculate it (using excel etc) and store it in flash. This would save 800 bytes of ram. If you were tight on flash, you could store only 90degrees worth and translate at run time - this would cost a few more cycles. These are some of the tradeoffs you can make.

'Flag' is a shared variable, thus:

Needs to be declared volatile

Since it is a byte variable, that makes atomicity less of an issue. We have the isr set it to 1 and the mainline code test it, then clear it. If the test and clear happen before the isr sets it, then we are ok. Otherwise it becomes a game of 'snap' - who sets or clears it first. Worst case is we miss a time tick. I see in your code that the flag logic is reversed. By convention, '1' would denote set and in your instance that a time tick has occurred. Thus the isr would set it, your main line code would test for non zero and if non zero, clear it and perform the timed code.

Last Edited: Tue. Nov 4, 2014 - 01:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is the table not circular? If so you usually only need a quarter revolution. The other 3 quadrants then only differ in signedness.

 

But I agree with kartman, don't calculate it at run time and waste RAM (800 bytes!!) but use a generator (perhaps just Excel or equivalent?) to generate a CSV list of the table values then put it in __flash as part of the code.

 

BTW is there some reason to use "flag" the "wrong way up"? Most folks would use 1=do something, 0=do nothing not the other way round.

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

Bignoob - i also wrote another tutorial on 'multi-tasking' that should give you some ideas.

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

With a constant table, i would normally precalculate it (using excel etc) and store it in flash

Yes I am aware of this and I can actually do it as well, using the PROGMEM stuff, I do fully understand

 

'Flag' is a shared variable, thus:

Needs to be declared volatile

Yes, I have done that

 

 

Is the table not circular? If so you usually only need a quarter revolution. The other 3 quadrants then only differ in signedness.

I am aware of this, I could define it for 90 degrees and step up then down and repeat as negative, not hard to do but I would have to think about the best way to do it its really easy for me to use 400 entries, not the best solution but definitely the easiest for me

I see in your code that the flag logic is reversed. By convention

 BTW is there some reason to use "flag" the "wrong way up"

Theres a good reason, because I am a n00b!

 

I usually do it this way thinking that a 1 means its completed and a 0 means it hasn't but if its convention I will do it the other way from now on it makes no difference to me but it will be the same as most common

 

but use a generator (perhaps just Excel or equivalent?) to generate a CSV list of the table values then put it in __flash

I will do that, is there a way to get the best of both worlds? i.e save it in flash but be able to assign it with a for loop? I suspect this isn't possible

 

 

i also wrote another tutorial on 'multi-tasking'

I will check it out

 

I need to scope my interrupt to see how long it takes, that will be tonight

 

I am a little further forward with my original question but I still don't know how to tell if I am in trouble and what I can and cant interrupt as it stands I am doing a hell of a lot in the wj=hile loop and I havent seen a problem but that doesn't mean a lot

 

 

Thanks all

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

I suspect this isn't possible

Well technically it is (SPM) but presumably you wouldn't want the table rewritten (with the same values) into flash every time the program started so I don't see the point. Why not work out what the 100/256/400/512/whatever values are and just fix them for all time in the source of your code:

const __flash int8_t sindata[] = { 0, 3, 7, 12, 18, 30 ... };

As I say, you use Excel (or whatever) to calculate the "0, 3, 7, 12, 18, 30 ..." bit in the middle of this.

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

I got a bit carried away last night!

 

This beauty takes 6.56us which is fast by my reckoning

 

ISR (TIMER1_OVF_vect)
{  
	int16_t table_value = (vr*sin_theta[n])>>8;
	MIA = (MAXMODINDEX/2 + table_value);
	MIB = (MAXMODINDEX/2 - table_value);
	
	if(n++ ==399)
	{   
		if(p++ ==50)
		{
		 p=0;
		 flag=0;	
		}
		n=0;
		 
	}
	 
}	

So I am looking at doing the inverted PWM that Lee mentioned and I am a little confused!

 

    TCCR1A|= (1<<WGM11)|(1<<COM1A1)|(1<<COM1B1)|(1<<COM1A0)|(1<<COM1B0);      // Phase correct PWM WGMn1 =1, WGMn3 =1, TOP set by ICR1, Inverted mode COMnB1=1 COMnA1=1
	TCCR1B|= (1<<WGM13)|(1<<CS10);                                        // Prescale 1 CSn0=1
	ICR1   = CLOCK_COUNT;                                                 // Timer 1 mode 10, Top set by ICR1, 8MHz clock, 200 ticks to reach top, 200 ticks to reach bottom 
		
	TIMSK1 =  (1 << TOIE1);  // Timer one overflow interrupt on ICR1, 

 

Is my setup and looking at

 

##################################################################################
    MODE    WGM13    WGM12    WGM11    WGM10     DESCRIPTION                        TOP
    0        0        0         0         0          Normal                             0xFFFF
    1        0        0         0         1          PWM, Phase Corrected, 8bit            0x00FF
    2        0        0         1         0          PWM, Phase Corrected, 9bit            0x01FF
    3        0        0        1         1          PWM, Phase Corrected, 10bit        0x03FF
    5        0         1         0         1          Fast PWM, 8bit                     0x00FF
    6        0         1         1         0          Fast PWM, 9bit                     0x01FF
    7        0         1         1        1          Fast PWM, 10bit                     0x03FF
    8        1         0         0         0          PWM, Phase and Frequency Corrected ICR1
    9        1         0         0         1          PWM, Phase and Frequency Corrected OCR1A
    10        1         0         1         0          PWM, Phase Correct                 ICR1
    11        1         0         1        1          PWM, Phase Correct                 OCR1A
    14        1         1         1         0          Fast PWM                             ICR1
    15        1         1         1         1         Fast PWM                            OCR1A
    #################################################################################

I am in mode 10, phase correct with top set by ICR1

 

I have had a good look and its not clear how to get my outputs inverted

 

Reading this

 

Phase Correct PWM Mode
The phase correct Pulse Width Modulation or phase correct PWM mode (WGM13:0 = 1, 2, 3,
10, or 11) provides a high resolution phase correct PWM waveform generation option. The
phase correct PWM mode is, like the phase and frequency correct PWM mode, based on a dual-slope operation. The counter counts repeatedly from BOTTOM (0x0000) to TOP and then from
TOP to BOTTOM. In non-inverting Compare Output mode, the Output Compare (OC1x) is
cleared on the compare match between TCNT1 and OCR1x while upcounting, and set on the
compare match while downcounting. In inverting Output Compare mode, the operation is
inverted. The dual-slope operation has lower maximum operation frequency than single slope
operation. However, due to the symmetric feature of the dual-slope PWM modes, these modes
are preferred for motor control applications

 My COM1A1:0=3

and COM1B1:0=3

 

So I suspect I just change one of these to 2

 

So I think

 

  TCCR1A|= (1<<WGM11)|(1<<COM1A1)|(1<<COM1B1)|(0<<COM1A0)|(1<<COM1B0);      // Phase correct PWM WGMn1 =1, WGMn3 =1, TOP set by ICR1, Inverted mode COMnB1=1 COMnA1=1
	TCCR1B|= (1<<WGM13)|(1<<CS10);                                        // Prescale 1 CSn0=1
	ICR1   = CLOCK_COUNT;                                                 // Timer 1 mode 10, Top set by ICR1, 8MHz clock, 200 ticks to reach top, 200 ticks to reach bottom 
		
	TIMSK1 =  (1 << TOIE1);  // Timer one overflow interrupt on ICR1, 

 

Will give me naturally inverted outputs, am I barking up the right tree?

 

 

I will implement the lookup table in flash I havent had chance yet

 

 

Regards

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

This beauty takes 6.56us which is fast by my reckoning

You sure? I don't know what "vr" and "n" are but doing a multiply in an ISR() that's supposed to be fast sounds "expensive".

 

(I've just been NEON optimising some ARM code and before I did the NEON I was able to take a multiply out of a large for() loop (replacing it with repeated addition on each iteration instead) and it had quite an impact)

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

You sure? I don't know what "vr" and "n"

 I used my scope last night and it takes 6.56us

 

n and vr are volatile ints

 

I have to multiply by vr in order to scale the output magnitude

 

n is the lookup table index

 

Am I on the right track with getting an inverted output?, I cant try anytjing at the minute as I am drowning in work!

 

 

Last Edited: Wed. Nov 5, 2014 - 01:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

n and vr are volatile ints

Why volatile? Are they really shared between paths of execution?

I have to multiply by vr in order to scale the output magnitude

Can't the  values be prescaled? How often/why does "vr" vary? This might be an argument for having the data table in RAM not flash. If vr changes infrequently just have the table rescaled each time it changes.

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

Am I on the right track with getting an inverted output?

[It is very frustrating with the new site to not be able to see "all pages" for situations like this...]

 

I saw your code snippet for timer setup:

 

  TCCR1A|= (1<<WGM11)|(1<<COM1A1)|(1<<COM1B1)|(0<<COM1A0)|(1<<COM1B0); 
  // Phase correct PWM WGMn1 =1, WGMn3 =1, TOP set by ICR1, 
  //    Inverted mode COMnB1=1 COMnA1=1
	TCCR1B|= (1<<WGM13)|(1<<CS10); 
	// Prescale 1 CSn0=1
	ICR1   = CLOCK_COUNT; 
	// Timer 1 mode 10, Top set by ICR1, 8MHz clock, 200 ticks to reach top, 
	//  200 ticks to reach bottom 
		
	TIMSK1 =  (1 << TOIE1);  // Timer one overflow interrupt on ICR1, 

...edited a bit to minimize wrapping.  Now, AFAICS you haven't told us which AVR model, what clock speed, desired PWM frequency, and similar.

 

From the comment above, it is apparently 8MHz AVR clock.  And if the comment is correct, ICR1 is set to 200?  So then we can infer that the 400 clocks is 50us, and a 20kHz desired PWM frequency?

 

Is there a reason to use an up/down mode rather than Fast PWM?  (I don't recall how Atmel does it in the motor-control app notes.)

 

Why is the setup of TCCR1A and TCCR1B done with |= ?  Why not just = ?  Any previously-set bits are going to mess you up.

 

I assume OC1A and OC1B are made outputs?  [It helps everyone if you post a complete stripped-down test program, along with the AVR model, clock speed, toolchain and version.  As it is, the info is coming out piecemeal.

 

If I pick e.g. Mega88 model with 8MHz and Timer1 and arbitrary starting values for OCR1x about half of the 0xc8 TOP, CodeVision Wizard generates:

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000.000 kHz
// Mode: Ph. correct PWM top=ICR1
// OC1A output: Non-Inverted PWM
// OC1B output: Inverted PWM
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 0.05 ms
// Output Pulse(s):
// OC1A Period: 0.05 ms Width: 0.024 ms
// OC1B Period: 0.05 ms Width: 0.026 ms
// Timer1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (0<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0xC8;
OCR1AH=0x00;
OCR1AL=0x60;
OCR1BH=0x00;
OCR1BL=0x60;

...which looks like your code fragment, without the |=.

 

Now, the experts will have to comment on when the OCR values will be updated.  I think it will be the next TOP, just before the next overflow, so you will always be "one behind"?  And for your needs, OCR1A and OCR1B would always contain the same value?

 

[If this is really the core of the app, then I'd use an AVR model with complementary outputs.  And depending on what you are driving, one with dead-time allowance.]

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

Why volatile? Are they really shared between paths of execution?

 Well the plan would be to use its value in the interrupt as you see but it will be altered in the main while loop, vr is the magnitude of the output and it will be my controlled variable so I can do PID or whatever to control it

 

Now I think a variable thats altered in the main and used in an ISR has to be volatile, is that right?

 

Can't the  values be prescaled? How often/why does "vr" vary?

 Vr will vary about once every second for my application, which could be an argument for recalculating the table

 

 

you haven't told us which AVR model

Sorry, its an ATMega328P @8MHz

 

we can infer that the 400 clocks is 50us, and a 20kHz desired PWM frequency

 Is there a reason to use an up/down mode rather than Fast PWM?

 Well for three phase inverters centre aligned PWM is more attractive mainly because of harmonic cancellation and discontinuous modulation (where one device switches at a time) is easy so the switching losses are less, there are other benefits such as reduced ripple current and others which I can't think of right now!

 

Now how well these carry over to a single phase inverter I am not sure to be honest, I know the harmonics can not cancel as well, there is a lot of discussion on this in the book PWM for Power Converters, a very good book for anyone interested in the subject

 

Why is the setup of TCCR1A and TCCR1B done with |=

 Hmmmmm

 

 

Well Lee, this is something I have always been doing, its something I picked up a good while ago now

 

 

In Deans tutorial

 

https://www.avrfreaks.net/forum/t...

 

TCCR1B |= ((1 << CS10) | (1 << CS11)); // Set up timer at Fcpu/64

 This is exactly where I have gotten it from, the way I see it is that it OR's the register with the bits I want set, so the bits already set are left unchanged because Or'ing with a zero keeps a bit unchanged

 

 

I assume OC1A and OC1B are made outputs?  [It helps everyone if you post a complete stripped-down test program

 


/*-----------------------------------------------------Included Libraries-------------------------------------------------------*/
#define F_CPU          8000000UL
#include <avr/io.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <math.h>
#include <string.h>       // string manipulation routines
#include <stdlib.h>
#include <util/delay.h>
/*------------------------------------------------------------------------------------------------------------------------------*/


 /*-----------------------------------------------------Preprocessor-------------------------------------------------------------*/
#define PD0_HIGH    PORTD    |= 0x01      // PORT D Pin 2 HIGH
#define PD0_LOW     PORTD    &= 0xFE      // PORT D Pin 2 LOW
#define PD0_TOGGLE  PORTD    ^=0x04
 
#define PD1_HIGH    PORTD    |= 0x02      // PORT D Pin 2 HIGH
#define PD1_LOW     PORTD    &= 0xFD      // PORT D Pin 2 LOW
#define PD1_TOGGLE  PORTD    ^=0x04 
 
#define PD2_HIGH    PORTD    |= 0x04      // PORT D Pin 2 HIGH   
#define PD2_LOW     PORTD    &= 0xFB      // PORT D Pin 2 LOW
#define PD2_TOGGLE  PORTD    ^=0x04

#define PD3_HIGH    PORTD    |= 0x08      // PORT D Pin 2 HIGH
#define PD3_LOW     PORTD    &= 0xF7      // PORT D Pin 2 LOW
#define PD3_TOGGLE  PORTD    ^=0x08

#define MIA OCR1A                         // MIA, PWM phase B gatedrive signal on Port B pin 1
#define MIB OCR1B                         // MIB, PWM phase C gatedrive signal on Port B pin 2
/*------------------------------------------------------------------------------------------------------------------------------*/
  
  //****************************** HD44780 commands *****************************************/
  #define CLEAR        0x01
  #define LCD_LINE_0   0x80
  #define LCD_LINE_1   0xC0
  #define LCD_LINE_2   0x94
  #define LCD_LINE_3   0xD4
  /******************************************************************************************/

  /******************************  HD44780 Port Connections ********************************/

  #define LCD_RS 0    // pin for LCD R/S    PB0
  #define LCD_E  7    // pin for LCD enable PD7
  #define DAT4   6    // pin for d4         PB6
  #define DAT5   7    // pin for d5         PB7
  #define DAT6   5    // pin for d6         PD5
  #define DAT7   6    // pin for d7         PD6

  #define LCD_RS_PORT    PORTB
  #define LCD_E_PORT     PORTD
  #define DAT4_PORT      PORTB
  #define DAT5_PORT      PORTB
  #define DAT6_PORT      PORTD
  #define DAT7_PORT      PORTD

  #define LCD_RS_HIGH    LCD_RS_PORT |= (1<<LCD_RS)
  #define LCD_RS_LOW     LCD_RS_PORT &=~(1<<LCD_RS)
  #define LCD_E_HIGH     LCD_E_PORT  |= (1<<LCD_E)
  #define LCD_E_LOW      LCD_E_PORT  &=~(1<<LCD_E)
  #define SET_DAT4       DAT4_PORT   |= (1<<DAT4)
  #define SET_DAT5       DAT5_PORT   |= (1<<DAT5)
  #define SET_DAT6       DAT6_PORT   |= (1<<DAT6)
  #define SET_DAT7       DAT7_PORT   |= (1<<DAT7)
  /******************************************************************************************/
  
  #define CS_HIGH      PORTD |= 0x10
  #define CS_LOW       PORTD &= 0xEF
  
  #define POT_CHAN                     0            // Potentiometer output connected to Port C ADC Channel 0
  #define TEMP_MAX                  30.0
  #define TEMP_MIN                  18.0
  #define TEMP_RANGE                TEMP_MAX - TEMP_MIN
 
 /*------------------------------------------------------------------------------------------------------------------------------*/
  #define F_REF           50                // Output frequency
  #define VR_MAX          100
  #define VR_MIN          10
  
  
  #define TS              0.00005           // 1/PWM_FREQ          // sampling frequency time period
  #define PWM_FREQ        20000UL
  #define CLOCK_COUNT     F_CPU/PWM_FREQ/2  // PWM up dowm mode so max clock count is @ half the time period !!
  #define MAXMODINDEX     CLOCK_COUNT       // 200//(0.5)*(F_CPU)/PWM_FREQ(0.5*F_CPU)/PWM_FREQ
  #define PI              3.14592654        // pi
  

/*-----------------------------------------------------Function Prototypes------------------------------------------------------*/
void     Setup_PWM(void);
void     Setup_GPIO(void);

/*------------------------------------------------------------------------------------------------------------------------------*/

/*-----------------------------------------------------Global Variables---------------------------------------------------------*/
volatile uint16_t      n             = 0;      // n is an unsigned 16 Bit global variable which represents count
volatile uint16_t      p             = 0;
volatile  uint8_t   flag             = 0;
volatile  uint8_t     vr             = 50;



/*-----------------------------------------------------Arrays-------------------------------------------------------------------*/
int16_t sin_theta[400]={};	

 /*
*/	
 /*------------------------------------------------------------------------------------------------------------------------------*/

        
int main(void)
{
	for (int16_t i=0; i<=399; i++)
	{
		sin_theta[i]= 256*sin(2*PI*F_REF*i*TS);
	}	
		
    Setup_GPIO();
	Setup_PWM();
	sei();
	
	while(1)
	{
		if(flag==0)
		{
	
		
		
		
		flag=1;
	
		}		
	}
	
	
}



ISR (TIMER1_OVF_vect)
{   PD2_HIGH;
	
	int16_t table_value = (vr*sin_theta[n])>>8;
	MIA = (MAXMODINDEX/2 + table_value);
	MIB = (MAXMODINDEX/2 - table_value);
	
	if(n++ ==399)
	{   
		if(p++ ==50)
		{
		 p=0;
		 flag=0;	
		}
		n=0;
		 
	}
	 
	PD2_LOW;
}	

void Setup_GPIO(void)
{
	DDRD  |= 0xFF;         
	DDRB  |= 0xEF;	       
}


void Setup_PWM()
{
	
	TCCR1A|= (1<<WGM11)|(1<<COM1A1)|(1<<COM1B1)|(1<<COM1A0)|(1<<COM1B0);  // Timer 1 counter control register A  Phase correct PWM WGMn1 =1, WGMn3 =1, TOP set by ICR1, Inverted mode COMnB1=1 COMnA1=1
	TCCR1B|= (1<<WGM13)|(1<<CS10);                                        // Prescale 1 CSn0=1
	ICR1   = CLOCK_COUNT;                                                          // Timer 1 mode 10, Top set by ICR1, 8MHz clock, 200 ticks to reach top, 200 ticks to reach bottom 400 ticks of 8MHz clock = 20kHz
	TIMSK1 =  (1 << TOIE1);  // Timer one overflow interrupt on ICR1, 
	TCNT1  = 0;              // set timer1 to 0

}

 

I have chopped out all the while loop, I haven't ran it  but hopefully it makes it more clear?

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

 Well for three phase inverters centre aligned PWM is more attractive mainly ...

 Certainly.  But your work on 180 degree phase shift didn't imply 3-phase to me.  And now that you mention it, >>have<< you looked at the app note(s)?

 

 This is exactly where I have gotten it from...

Suit yourself.

Quote:

There must be a sale on "|" in full-reel quantities.

I searched both DigiKey and Mouse, but neither had them...

https://www.avrfreaks.net/comment...

http://legacy.avrfreaks.net/inde...

https://www.avrfreaks.net/comment...

 

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

I wouldn't worry about the multiply.  According to http://www.atmel.com/images/doc1..., it should only take 9 clocks.

 

I wonder why some of the variables are global, when it looks like they could be static inside the ISR.  And I question some of the sizes.  Why is p 16 bits, when it only counts to 50?  And why is vr 8 bits when it gets multiplied by a 16-bit value (it will have to be extended to 16 bits anyway)?

 

And I would count p from 50 down to 0 to gain a few clocks (although making it 8 bits will make the gain smaller).

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

 Certainly.  But your work on 180 degree phase shift didn't imply 3-phase to me.

What I was trying to say is that the benefits are certain for three phase inverters but I haven't really studied H bridge inverters and there are gaps in my knowledge, off the top of my head when writing out the post I didn't know for sure

 

The first priority of a PWM scheme is to work out the width of the pulse for your switching frequency and then you will get the voltage output expected after that the only other thing to consider is the placement of the pulses within the switching period and this in fact turns out to be the very thing that separates all the different modulation strategies

 

Now its all very complicated stuff and its not obvious at all, I won't pretend to be any sort of expert on the matter, I am just a student

 

The placement of the pulses doesnt have any effect on the magnitude of the output voltage but it does have a dramatic effect on the harmonic performance of an inverter, there aren't that many ways to place the pulses but you can double the ripple current between the best way and the worst way

 

The best way is centre aligned

 

PWM for Power Converters has the hardcore mathematical derivation (Its too much for me to swallow) look at the theoretical weighted harmonic spectrum for a single phase leg of an inverter

 

 

 

 

 

 

I can't see a reason why not to do centre aligned PWM every time

Suit yourself.

=-(

 

There must be a sale on "|" in full-reel quantities.

If lots of people are doing the same thing then surely alarm bells ring as to why?, its in lots of tutorial even the good ones

 

Look at my ADC program

 

void Init_A2D(void)
{   
	ADCSRA = (1<<ADPS1) | (1<<ADPS2)|= (1<<ADSC)|= (1<<ADEN);  
	ADMUX  = (1<<REFS0);
             
	
}
uint16_t A2D(uint8_t pin)
{
	ADMUX  &= 0xF0;                       
	ADMUX  |= pin;                      
	ADCSRA |= (1<<ADSC);                   
	while(ADCSRA & (1<<ADSC));             
	return ADCW;
}

 

I use the | and & so i dont alter the other pins when reading the pin, is that right?

 

 

 

I wonder why some of the variables are global, when it looks like they could be static inside the ISR.  And I question some of the sizes.  Why is p 16 bits, when it only counts to 50?  And why is vr 8 bits when it gets multiplied by a 16-bit value (it will have to be extended to 16 bits anyway)?

 

And I would count p from 50 down to 0 to gain a few clocks (although making it 8 bits will make the gain smaller).

I will tidy it up and repost, I am done for tonight 

 

Thanks for everyones input

 

 

Last Edited: Wed. Nov 5, 2014 - 09:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

...but I haven't really studied H bridge inverters and there are gaps in my knowledge,...

I haven't either, but from discussions here there is an important thing called "shoot through" that can generate clouds of burning semiconductor part smoke.  And "dead-time insertion" to avoid it. As I mentioned:

 

[If this is really the core of the app, then I'd use an AVR model with complementary outputs.  And depending on what you are driving, one with dead-time allowance.]

 

 

 

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

there is an important thing called "shoot through" that can generate clouds of burning semiconductor part smoke.  And "dead-time insertion" to avoid it

 It goes without saying

 

As I mentioned this is just me messing about, I already have hardware which takes care of dead time for me

 

I ran the program on hardware last night, with a resistor as the load and the output is very nice, 20kHz switching, output magnitude controlled by a pot, with an LCD display

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

if this just is for a motor a 256 step sine wave works fine, if it's for 3 phase use 255 steps, so there are 120 deg between phases.

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

its in lots of tutorial even the good ones

The point is this. You use = when you first set a register. You use |= when you just want to set some bits that aren't set already. In the Dean/timer tutorial (which I haven't read recently so I'm just guessing) I think you'll find that TCCR1B is set with some configuration bits (WGM and so on) then as a last act a TCCR1B |= (1 << some CS bits) is done which simply turns on the timer leaving the WGM and other bits as they were. In fact he could have just left it to the last minute to do "TCCR1B = WGM bits | CS bits" to both complete configuring it and start it.

 

But anyway, use assignment when first setting a register (you may have got to this line with it in an indeterminate state so you're not only setting 1's but also 0's). Use OR to later set some bits (or AND to clear some) while leaving bits you already set in their current state.

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

It makes sense Clawson, in fact its blatently obvious!

 

Lesson learnt

 

Thanks