Timer overflow ISR execution is delayed - precise timing needed.

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

I am trying to produce some waveforms using the PWM and a timer interrupt. The problem is I am also doing envelope and LFO calculations in the main loop.

So, at certain points, I can listen to the note being produced bend towards a lower pitch. The only way this could be happening is if my interrupt was delayed and therefore the sample rate dropped.

What kind of CPU operation could be causing the delayed ISR execution? Is there a way to give higher priority to the ISR?

This topic has a solution.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Mon. Oct 12, 2020 - 01:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The only "delay" is that it has to finish the current opcode (which might be more than 1 cycle) then it takes 12..20 cycles to vector into the ISR.

 

So what kind if "delay" are you talking about?

 

BTW if you are a fan of LFOs/envelopes etc I can't recommend Teensy4 and its audio library high enough. 600MHz (more if overclocked) gives you a LOT of CPU headroom for all kinds of audio processing: LFO/modulated oscillators/envelopes/filters/flanger/chorus/delay/reverb/bitcrusher/S&H/panning/fader/etc, etc

 

I'm just toying with what's possible so far but already my design chain looks like...

It's like synth "Lego bricks" (of course you do need to add supporting code to manipulate each block).

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

I will definitely look into this in the future - I was thinking of working on a more powerful CPU in the future.

 

20 cycles just to initiate the ISR? I was not expecting that

So here is what happens right now with my code:

There is an audible pitch drop during the attack phase of the envelope but it goes away when I use some _delay_us(10) functions in those two spots.

It seems to me that it has to do with the time of execution of the attack calculations - if the calculations start right before the ISR is about to execute, the ISR is delayed and the pitch drops.

Could this be the problem?

 

inline void doEnvelope(uint8_t oscillator)
{
	switch (envelopeStage[oscillator])
	{
		case 0: //Attack
		_delay_us(10);
		if (button[0] && ((millisecsEnvelope[oscillator] << 4) / attack[oscillator] < 255))
		{
			_delay_us(10);
			volume[oscillator] = (millisecsEnvelope[oscillator] << 4) / attack[oscillator];		// millisecsEnvelope[oscillator] / (attack/16)
		}
		else if (button[0])
		{
			volume[oscillator] = 255;
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		else
		{
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		break;
		
		.......
		
	ISR (TIMER0_OVF_vect)
	{
	
  	if (notePlayingSum)
	{
 		phaccu[0] += tword[0];//738;440Hz
 		osc[0] = (pgm_read_byte( &waveTable[ waveKind[0] ][ phaccu[0] >> 8 ]) * 128) / 256;

		
phaccu[1] += tword[1]; 
 		osc[1] = (pgm_read_byte( &waveTable[ waveKind[1] ][ phaccu[1] >> 8 ]) * 128) / 256;

		OCR0A = (( osc[0] * volume[0] ) >> 8) + (( osc[1] * volume[1] ) >> 8); //;pgm_read_byte(&(waveTable[tremolo]))/0xff;
 	}
}

 

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Are you sharing variables between main() and your ISR? Are they volatile? Likewise, if you use anything other than 8-bit variables then you should look at atomic access.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

If global interrupts are disabled then the execution of an ISR is delayed. I doubt this is the problem though.

 

Why do you think that a delay in the execution of an ISR would change the frequency of the PWM generated?

 

How is "volume" and "osc" declared? Are they volatile? Are they 8-bit values? Are you sure that all the variables used in the ISR are changed atomically? I.e. does your main loop ensure that all variables used by the ISR are consistent at all times that global interrupts are enabled?

 

 

/Jakob Selbing

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

If you have function calls in an ISR the compiler has to push/pop caller-saved registers (r18-r27,r30-r31) at least.

I'm not sure how pgm_read_byte is expanded.  Can you check the generated asm code?  (avr-objdump -d will do that.)

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

Hey I was searching around about atomic. Why should I use atomic in stead of using cli() before the command and sei() after the command? Is there something more to it?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

We have no idea of what "command" is. We also have no idea of the types of your variables. You might want to time the isr code in the AS7 simulator. The results might surprise you.

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

tellSlater wrote:

Hey I was searching around about atomic. Why should I use atomic in stead of using cli() before the command and sei() after the command? Is there something more to it?

You did not reply any of the questions asked. How is any one gonna be able to help you?

 

I am guessing that the problem here is that your main loop code modifies variables that are used by the ISR. You need to make sure to make access to those variables atomic.

 

But... you can also have a problem if your ISR accesses multiple variables that must be consistent with respect to each other. Having atomic access of each of those is not gonna be enough - you must change all those in one atomic block (or possibly use an duplicate set of variables that are updated atomically). I see you have at least two variables of this kind: volume[0] and volume[1].

 

It would help a lot if you showed some more context like variable declarations, the calling context of doEnvelope() etc.

 

EDIT: for more help regarding atomic access have a look here: https://www.nongnu.org/avr-libc/...

/Jakob Selbing

Last Edited: Mon. Oct 12, 2020 - 06:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

tellSlater wrote:

Hey I was searching around about atomic. Why should I use atomic in stead of using cli() before the command and sei() after the command? Is there something more to it?

 

The subtle difference is that 'atomic', on ending the block, will restore the interrupt state to how it was before you entered the block. So, if the interrupts were already disabled when you entered an atomic block they will still be disabled when you leave it. Using cli and sei does not do this.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

I am using Direct Digital Synthesis from PWM at ORC0A. One sample of the wave is being passed to OCR0A in every TOVF0 interrupt - in the OVF ISR.

Now when the sample rate drops, the pitch of the note drops - I can listen to the note dropping so I guessed that the ISR execution was delayed. I think that could be the only reason for a slower sample rate and therefore a pitch bend downwards on the note.

The frequency of the PWM is not altered, my sample rate is altered - this is how Direct Digital Synthesis works. Every sample (that is essentially a voltage level) is output through PWM in the ISR. ISR is called about 39K times a second so I get a 39KHz sample rate that drops if ISR execution is delayed.

 

volume and osc are volatile uint8_t. Also, after the first reply to this post I made sure the only variables shared between main code and ISR are volatile uint8_t.

If I have understood what is going on with shared variables in ISR and main code - now that only 8bit variables are shared, I don't need to use atomic... correct?

 

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

I really don't know how to do this and where I am supposed to use this command.. I use AtmelStudio - can I export my code in assembly from there?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

If you are doing DDS then that must be given the very highest priority and you shouldn't let anything interfere with it. I'd suggest that doing >>8 and *128 in the ISR is not a wise choice as these can be computationally expensive. For one thing >>8 generally just means (use higher byte(s) and ignore the lowest byte. Hopefully the compiler is smart enough to recognise this but an alternative is to make a union with phaccu and just pick out the relevant bytes to avoid the shift. But look at the generated Asm - perhaps the compiler spotted this already. 

 

Similarly for >>128, which is a 7 bit shift it might be "better" to <<1 and then use the next byte up.

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

Not necessarily. There maybe a relationship between a number of those variables that might create a problem.
Again, you might want to measure the execution time of your isr. It might be that you simply do not have enough time to complete the isr before the next interrupt comes along. Thus your ‘delay’. Everything takes a finite amount of time.

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

Yeah Simulator really helps. I used it these last days to make sure that the ISR was not called while certain code was executing.

I also made a function that halted the main code in a while loop until the next ISR was called. That way I made sure that the complex calculations took place right after the ISR and not before it and the sound is ok now.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Thank you for your help. Although I think I understood the importance of atomic and not letting a more-than-one-byte variable be mistreated I think that was not the my main problem here.

I asked above if it is ok not using atomic when accessing one byte variables and am waiting for your answer! Thanks again

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Mon. Oct 12, 2020 - 03:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
const uint8_t wf_Width    = 84;
const uint8_t wf_Height   = 48;
const uint8_t PROGMEM wf_Bitmap[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x40, 0x60, 0x20, 0x20, 0x20, 0x10, 0x90, 0xd0, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x60, 0x30, 0xf8, 0x80, 0x00, 0x00, 0x80, 0xc0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x18, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x01, 0x01, 0x00, 0x80, 0xe0, 0x30, 0x1c, 0x06, 0x02, 0x03, 0x01, 0x01, 0x00, 0x00, 0x80, 0xc0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0x40, 0x60, 0x20, 0x20, 0x30, 0x10, 0x10, 0x18, 0x08, 0x08, 0x0c, 0x04, 0x04, 0x06, 0x82, 0x83, 0x41, 0x61, 0x30, 0x18, 0x08, 0x0c, 0x1f, 0xf2, 0x83, 0xe1, 0x38, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x7c, 0x07, 0x00, 0x80, 0x80, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x37, 0x1f, 0x80, 0x80, 0xb1, 0xef, 0xbe, 0xc3, 0x41, 0x3f, 0xf0, 0x90, 0x10, 0x08, 0x08, 0x04, 0x07, 0x3e, 0xf2, 0x01, 0x01, 0x00, 0x08, 0x08, 0x08, 0x78, 0xc0, 0x80, 0xc0, 0xc0, 0x80, 0xe0, 0x38, 0x00, 0xd0, 0x00, 0xc0, 0x80, 0x80, 0x00, 0xc0, 0x40, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x10, 0x18, 0x08, 0x08, 0x08, 0x08, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x18, 0x30, 0x20, 0x20, 0x30, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xc0, 0x79, 0x49, 0x48, 0x08, 0x00, 0xd1, 0x00, 0x41, 0x20, 0xa1, 0x00, 0xf1, 0x41, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x06, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

PROGMEM const unsigned char waveTable[5][256]  ={
	//Random numbers wave
	{156,164, 53, 27,167,174,146, 99,157, 61, 85, 46, 80, 79, 58,159,
	56,115,234,119,182,127,150,170,133,173,219, 66,227,249,  6, 57,
	203,255, 45,229, 29, 30, 19, 91,202,158,201, 98,172, 63, 69,235,
	106, 95,220, 35,131, 59,248, 81,154, 86, 31,243, 62,238,242,171,
	90,147,109, 60, 94,130, 43,103,122,132, 84,143, 22,247,105, 15,
	169,187,  3, 51,254,251,110,114,108, 49, 36, 13,181,225,188,104,
	230, 41,144,168,183,  9,252, 93,124,107,141,177,140,  1,175, 28,
	26,233,138,231,185,  8,221,214,  4, 89,208, 47,  0,237, 34,176,
	40,120,212,  2,236, 64,153,134,224,209, 44, 97, 16,218,200,165,
	178,180,102,137,118,125,161,245,226, 48, 12, 65, 83,101,190,116,
	217,198,142,192,199,155, 38,126,184, 25,189, 17,151,232,207,240,
	96,186, 74,128,196,211,129,163, 21, 73,117,135,197, 14, 24,179,
	20,113, 10,152, 77,160, 70,149,121, 33, 75,112, 88,195,194, 42,
	136,100, 78, 32,191,193, 68, 55,223,204,  7,215,162, 11, 52, 92,
	250,239, 23, 87,  5,205, 54,139, 71,228,222, 37,241,206,210,213,
	148,244, 67,253, 50, 76, 82, 18,246,166, 72, 39,111,216,145,123,},
	
	//square wave
	{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255},
	
	
	//left saw (negative slope)
	{255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,
	212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,
	176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,
	140,139,138,137,136,135,134,133,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,
	89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,
	41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0},
	
	//triangle wave
	{1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,
	87,89,91,93,95,97,99,101,103,105,107,109,111,113,115,117,119,121,123,125,127,129,131,133,135,137,139,141,143,145,147,149,151,153,155,157,159,161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,193,195,
	197,199,201,203,205,207,209,211,213,215,217,219,221,223,225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,255,253,251,249,247,
	245,243,241,239,237,235,233,231,229,227,225,223,221,219,217,215,213,211,209,207,205,203,201,199,197,195,193,191,189,187,185,183,181,179,177,
	175,173,171,169,167,165,163,161,159,157,155,153,151,149,147,145,143,141,139,137,135,133,131,129,127,125,123,121,119,117,115,113,111,109,107,
	105,103,101,99,97,95,93,91,89,87,85,83,81,79,77,75,73,71,69,67,65,63,61,59,57,55,53,51,49,47,45,43,41,39,37,35,33,31,29,27,25,23,21,19,17,15,
	13,11,9,7,5,3,1},	
	
	//sine wave
	{0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,
	70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124,127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,
	221,223,225,227,229,231,233,234,236,238,239,240,242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,
	253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,221,219,217,215,212,210,208,205,
	203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,
	102,99,96,93,90,87,84,81,78,76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,
	3,2,2,1,1,1,0,0,0} };
	


//Note frequencies

// PROGMEM float const keyFreq[88] = {
// 	27.5, 29.1352, 30.8677,																					   //Octave 0
// 	32.7032, 34.6478, 36.7081, 38.8909, 41.2034, 43.6535, 46.2493, 48.9994, 51.9131, 55, 58.2075, 61.7354,     //Octave 1
// 	65.4064, 69.2957, 73.4162, 77.7817, 82.4069, 87.3071, 92.4986, 97.9989, 103.826, 110, 116.541, 123.471,    //Octave 2
// 	130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942,    //Octave 3
// 	261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 394.995, 415.305, 440, 466.164, 493.883,    //Octave 4
// 	523.251, 554.365, 587.330, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880, 932.328, 987.767,    //Octave 5
// 	1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760, 1864.66, 1975.53,   //Octave 6
// 	2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520, 3729.31, 3951.07,   //Octave 7
// 	4186.01                                                                                                    //Octave 8
// };

// PROGMEM uint16_t const twordNotes[88] = {
// 	46, 49, 52,																	//Octave 0
// 	55, 58, 62, 66, 69, 74, 78, 82, 87, 92, 98, 104,							//Octave 1
// 	110, 116, 123, 131, 138, 147, 155, 164, 174, 184, 195, 207,					//Octave 2
// 	219, 232, 246, 261, 276, 293, 310, 328, 348, 368, 390, 414,					//Octave 3
// 	438, 464, 492, 521, 552, 585, 619, 661, 695, 736, 780, 827,					//Octave 4
// 	876, 928, 983, 1041, 1103, 1169, 1238, 1312, 1390, 1472, 1560, 1653,		//Octave 5
// 	1751, 1855, 1965, 2082, 2206, 2337, 2476, 2623, 2779, 2944, 3119, 3305,		//Octave 6
// 	3501, 3709, 3930, 4163, 4411, 4673, 4951, 5245, 5557, 5888, 6238, 6609,		//Octave 7
// 	7002																		//Octave 8
// };


PROGMEM uint16_t const twordNotes[88] = {
	46, 49, 52,																	//Octave 0
	55, 58, 62, 66, 69, 74, 78, 82, 87, 92, 98, 104,							//Octave 1
	110, 116, 123, 131, 138, 147, 155, 164, 174, 184, 195, 207,					//Octave 2
	219, 232, 246, 261, 276, 293, 310, 328, 348, 368, 390, 414,					//Octave 3
	438, 464, 492, 521, 552, 585, 619, 661, 695, 735, 779, 826,					//Octave 4
	875, 927, 982, 1040, 1102, 1168, 1237, 1311, 1389, 1470, 1558, 1651,		//Octave 5
	1749, 1853, 1963, 2080, 2203, 2334, 2473, 2620, 2776, 2940, 3115, 3301,		//Octave 6
	3497, 3704, 3925, 4158, 4405, 4667, 4945, 5238, 5550, 5880, 6230, 6600,		//Octave 7
	6993																		//Octave 8
};


volatile uint16_t	refclk=39185;		// refclk=3906 = 20MHz / 510 ... increase to compensate for higher pitch
										// 510 comes from formula for calculating phase correct PWM frequency
																			
volatile uint16_t	phaccu[2] = {0, 0};
volatile uint16_t	tword[2] = {0, 0};
volatile uint16_t	twordcalc[2] = {0, 0};
volatile uint8_t	key[2] = {48, 48};
volatile uint8_t	osc[2] = {0, 0};

volatile uint8_t	waveKind[2] = {4, 4};

volatile uint16_t	dutyCycle;
volatile uint16_t	ADCinputs[INPsize];
volatile uint8_t	inputsi;
volatile uint32_t	rollingMeanADC;

volatile uint8_t	smallTimer = 0;				//Counts 1/40ms - every timer0 overflow interrupt - starts at 0
volatile uint8_t	wait = 1;					//A part of note calculations is done once after every small timer tick. After the calculation this is set to 0
volatile uint8_t	millisecs = 0;				//Counts 1 ms every 40 smallTimer clicks

volatile uint16_t	millisecsAutoButton = 0;	//Counts ms - used for AutoButton function
volatile uint16_t	millisecsSerial = 0;		//Counts ms - used for timing some serial debugging commands

volatile uint8_t	notePlaying[2] = {0, 0};	//Boolean  - enables oscillators if a note is playing
volatile uint8_t	notePlayingSum = 0;			//Boolean  - enables oscillators if a note is playing

volatile uint8_t	volume[2] = {0, 0};
volatile uint8_t	volumeRelease[2] = {0, 0};

volatile uint8_t	mainNoteADC[2] = {48, 48};		//INPUT - main note
volatile uint8_t	fine[2] = {127, 127};			//INPUT - fine
volatile uint16_t	finalNoteADC[2] = {550, 550};	//Note after sweep and vibrato are applied

volatile uint8_t	attack[2] = {80, 80};			//INPUT - Envelope attack
volatile uint8_t	decay[2] = {90, 90};			//INPUT - Envelope decay
volatile uint8_t	sustain[2] = {70, 70};			//INPUT - Envelope sustain
volatile uint8_t	release[2] = {150, 150};		//INPUT - Envelope release
volatile uint8_t	releaseMode[2] = {1, 1};		//INPUT - Release mode (0->linear or 1->nonlinear)

volatile uint8_t	envelopeStage[2] = {4, 4};		//Tracks the current stage of the envelope
volatile uint16_t	millisecsEnvelope[2] = {0, 0};	//Timer for the envelope incremented in ms

volatile uint8_t	sweepSpeed[2] = {0, 0};			//INPUT - Sweep speed (0->one semi a second, 255->fast!)
volatile uint8_t	sweepDirection[2] = {0, 0};		//INPUT - Sweep direction (0->down, 1->up, 2->from down, 3->from up)
volatile uint16_t	millisecsSweep[2] = {0, 0};		//Timer for the sweep incremented in ms

volatile uint16_t  vibratoSpeed[2] = {400, 400};	//INPUT - Vibration speed
volatile uint8_t   vibratoDepth[2] = {20, 20};		//INPUT - Vibration intensity
volatile uint8_t   vibratoWaveKind[2] = {4, 4};		//INPUT - Vibration
volatile uint16_t  vibratoPhacc[2] = {0, 0};		//Vibrato phase accumulator

volatile uint8_t   buttonPin[4] = {0x80, 0x04, 0x08, 0x10};
volatile uint8_t   button[4] = {0, 0, 0, 0};
volatile uint8_t   timerb[4] = {0, 0, 0, 0};

// volatile uint8_t buttonPin[4] = {1 << PORTB0, 1 << PORTB1, 1 << PORTB2, 1 << PORTB3};
// volatile uint8_t buttonState[4] = {0, 0, 0, 0};
// volatile uint8_t millisecsButton[4] = {0, 0, 0, 0};

//Display stuff
volatile uint8_t   millisecsFPS=0;				// Timer for 20FPS
volatile uint8_t   itemSelected[3]={0,0,0};		// Keeps track of selections across pages
volatile const uint8_t   pageItems[3]={1,5,5};	// Number of page items is +1 of the value of this variable. pageItems[0] has a value of 1 - therefore page 0 has 2 items
volatile uint8_t   page=0;						// Current page

declarations ^^^

 


inline void doEnvelope(uint8_t oscillator)
{
	waitForSample();
	switch (envelopeStage[oscillator])
	{
		case 0:
		waitForSample();
		if (button[0] && ((millisecsEnvelope[oscillator] << 4) / attack[oscillator] < 255))
		{
			volume[oscillator] = (millisecsEnvelope[oscillator] << 4) / attack[oscillator];		// millisecsEnvelope[oscillator] / (attack/16)
		}
		else if (button[0])
		{
			volume[oscillator] = 255;
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		else
		{
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		break;
	
		case 1:
		waitForSample();
		if (button[0] && volume[oscillator] > sustain[oscillator])
		{
			volume[oscillator] = 255 - ((millisecsEnvelope[oscillator] * 16) / decay[oscillator]) ;	// 255 - millisecsEnvelope[oscillator] / (decay/16);
		}
		else if (button[0])
		{
			volume[oscillator] = sustain[oscillator];
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		else
		{
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		break;
	
		case 2:
		waitForSample();
		if (!button[0] || (sustain[oscillator] == 0))
		{
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		break;
	
		case 3:
		waitForSample();
		if (volume[oscillator] <= 1)
		{
			volume[oscillator] = 0;
			millisecsEnvelope[oscillator] = 0;
			++envelopeStage[oscillator];
			notePlaying[oscillator] = 0;
		}
		else if (release[oscillator] > 253)
		{
			++envelopeStage[oscillator];
			break;
		}
		else
		{
			if (!volumeRelease[oscillator]) volumeRelease[oscillator] = volume[oscillator];
		
			if (!releaseMode[oscillator])
				if (volumeRelease[oscillator] > ((millisecsEnvelope[oscillator] << 4) / release[oscillator]))
					volume[oscillator] = volumeRelease[oscillator] - ((millisecsEnvelope[oscillator] << 4) / release[oscillator]); //Linear release
				else volume[oscillator] = 0;
			else
				volume[oscillator] = (volumeRelease[oscillator] * release[oscillator]) / ((millisecsEnvelope[oscillator] >> 1) + release[oscillator]) - (millisecsEnvelope[oscillator]/1000); //Non linear release
		}
	}
}

envelope function ^^^

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

here is the ISR 

 

ISR (TIMER0_OVF_vect)
{
  	if (notePlayingSum)
	{
 		phaccu[0] += tword[0];//738;440Hz
 		osc[0] = (pgm_read_byte( &waveTable[ waveKind[0] ][ phaccu[0] >> 8 ]) * 128) / 256;

		phaccu[1] += tword[1];
 		osc[1] = (pgm_read_byte( &waveTable[ waveKind[1] ][ phaccu[1] >> 8 ]) * 128) / 256;

		OCR0A = (( osc[0] * volume[0] ) >> 8) + (( osc[1] * volume[1] ) >> 8);
	}

	smallTimer++; //increment small timer 1
	if(smallTimer > 40)
	{
		smallTimer = 0;
		++millisecs;
	}
	wait = 0;
}

 

There was no problem with it until I started writing more code in my main so I kinda guessed the ISR was not the problem.

Using phase correct the 8bit timer upcounts then downcounts and interrupt flag is set at bottom. Timer clock is the CPU clock (prescale = 1). 20MHz external crystal and ISR called every 510 cycles. So ISR executed at 20MHz / 510 = 39216Hz

First ISR command is executed at TCNT0 = 32 and upcounting. Execution then leaves the ISR when TCNT0 = 177 and upcounting.

 

As I mentioned above I am now using a function that traps the execution of the main program in a while loop until the ISR is executed.

That way I can position pieces of my main code to be executed right after the ISR and not before it. The sound is fine again.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Mon. Oct 12, 2020 - 01:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are using some terms that I am not familiar with. I was under the impression that a bit shift is the easiest operation and this is the reason why I am trying to as much as I can use it in my code.

For example - in stead of dividing by 9 I will prefer bit shifting >>3 is possible (I know it is equal to /8). I am very interested in learning how to make lighter code, could you point me to a source I could read?

I get what you mean by picking the higher byte, yeah that's all I want there. Union huh? I see it is some kind of.. container? Imma watch some tutorials on it

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Mon. Oct 12, 2020 - 01:46 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So all in all my solution - I position my main code to be executed right after the ISR executed by trapping the execution of the main program in a while loop and freeing it from the while loop in the ISR. So it goes something like this:

 

void waitForSample()
{
    while(wait)
    wait = true
}

int main()
{
    waitForSample();
    //coomands
    //commands
    //coomands
    //commands
    waitForSample();
    //coomands
    //commands
    //coomands
    //commands
    waitForSample();
    //coomands
    //commands
    //coomands
    //commands
}

ISR(TIMER0_OVF_vect)
{
    //DDS commands
    //DDS commands
    //DDS commands
    
    //timing commands
    
    wait = false;
}

 

As Clawson said since I am using DDS this ISR needs to be on time at all costs and it wasn't. That was the problem

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Since you know about DDS would you also happen to know what are some acceptable rates of updating the envelope volumes pitch bend effects?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:
I asked above if you it is ok not using atomic when accessing one byte variables and am waiting for your answer! Thanks again

For single read or write, yes these are safe, but you still need to be careful with read-modifiy-write operations.  i.e. x |= (1<<y); or x = x + y;  as the value of x could change in the ISR() during the operation.

 

Jim

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

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

Very helpful, thank you

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Your isr is chewing up around 80% of cpu time. As for optimising your isr, it is a matter of looking at the generated code and finding ways to minimise the amount of computation in the isr - like indexing arrays. Bit shifts can be efficient, but if you need a number of them those cycles add up. Rather than do the amplitude calcs inthe isr, other methods might be to dothis in the analog domain and use another pwm for this.

The ppg wave synth used a 1ms timebase for adsr and modulation if iremember correctly. There’s also plenty of examples of mini synths using the avr, so you mightwant tolook at them to see how they approach the problem.

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

80%? How did you come up with this number?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

If you are making a DDS, maybe do your IRQ in assembler, you will have more finesses (control) over exactly what  is being done as efficiently as possible.

 

Here are some video examples...they also have some code postings

 

https://www.youtube.com/watch?v=...

 

if you pay a lot attention to what you are doing, you can generate 3D video at the same time

   ...both the video and audio are entirely generated using only the AVR.  

https://www.youtube.com/watch?v=...

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Mon. Oct 12, 2020 - 04:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looking at #17 that is a LOT of volatile variables!! Remember that volatile really means "turn off optimisation for this thing and make the code as inefficient as you like". You should ONLY use "volatile" for the things that are shared between paths of execution and therefore MUST be volatile. For anything else you are just making the code bloated and slow.

 

So analyse the ISRs and just determine which globals in the ISRs are also accessed in main(), non-interrupt code and keep volatile on those - remove it from everything else or you are severely hampering the compiler's ability to optimize - never a great thing in time critical code.

 

(the only other use for volatile is when you are debugging and need to "see" something in the debugger that would otherwise have been optimised away - but when you are happy the code is working as intended remove volatile from any variable you hampered in this way - UNLESS it really is a shared variable).

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

Great stuff! Thanks

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

I have had the simulator and my MCU do very bizarre stuff when using non volatile variables in the past so I started only using volatiles without knowing why.

Truth is I don't exactly know where I can omit it without causing problems that would take me 3h to solve so I just kept all of them as volatile.

 

So am I only to use volatiles for variables that are --> (CHANGED in ISR) and (ACCESSED in main)?

 

If there is a global variable in ISR that is not changed in ISR but is accessed in main should I also make it a volatile?

 

If it is a constant does it automatically not need to be a volatile?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Mon. Oct 12, 2020 - 05:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

tellSlater wrote:

If there is a global variable in ISR that is not changed in ISR but is accessed in main should I also make it a volatile?

If it's not changed in ISR then it does not need to be volatile.

tellSlater wrote:

If it is a constant does it automatically not need to be a volatile?

Correct.

 

BTW I am still intrigued of how the frequency in your DDS can drop. The timer doesn't care what the CPU is doing; it will run at the configured rate at all times. Even if you have an interrupt latency that would not change the interrupt rate itself as long as you leave the timer running and don't manipulate the configuration (mode, prescaler, etc) or the timer value itself.

 

I have a DDS application that runs at 8192 Hz sample rate. It generates a sine wave with programmable amplitude superimposed on a linear ramp. Samples are written to a 12-bit D/A through timer interrupt. One trick I used to achieve fastest possible ISR was to have the main loop calculate the sample values and put them in a small (4 values or so) FIFO. All the ISR does is to fetch the next sample value and write to D/A. Maybe that trick could work in your application too?

/Jakob Selbing

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

Hmmm I see, thank you

I was thinking that the reason my ISR was delayed was that the interrupt flag was set while a 32bit calculation was executed in main or some other heavy calculation. Could that not be it?

Also, it could be a side effect of making all my variables volatile. I will comment again on this once I try to fix that

The reason why I couldn't use a fifo and wait for the ISR to fetch my samples is that it takes about 20-40 ISR cycles to calculate my envelope, note, fine, sweep and vibrato effects in main.

I don't have time-space to calculate every sample. But 40 ISR calls are about 1ms so updating the effects once a millisec is not bad

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Mon. Oct 12, 2020 - 08:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guessed based on the numbers you provided. It takes around 32 clocks to get in and out of your isr then add the clocks you measured. What value did you come up with?

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

I would say 177 / 510 * 100 % of CPU time in the ISR. Timer OVF takes 510 CLK ticks (I am using the phase correct mode) and my ISR exits when TCNT0 = 177 and upcounting.. So I would guess my ISR occupies about 35% of CPU time and not 80%

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Mon. Oct 12, 2020 - 08:52 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why guess when you can actually measure it? I had guessed at 256 clocks for the timer period, so that would explain my number being twice the value.

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

I said guess because I am not an expert and not sure if that method I used was the right way to measure it. If it is then yup - about 35%

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:

I was thinking that the reason my ISR was delayed was that the interrupt flag was set while a 32bit calculation was executed in main or some other heavy calculation. Could that not be it?

Unless interrupts are disabled during that calculation, there should not be any extra latency. Also, as I pointed out, even if there is - it would not change the rate at which interrupts are triggered. Wouldn't you agree on that?

 

Having variable latency only means that there is some jitter in the actual time between ISR execution - it does not change the interrupt triggering rate.

 

It can be really helpful to hook up an oscilloscope to one of the I/O pins and have the ISR set the pin upon ISR entry and clear on ISR exit. This is a quick and easy way to check the interrupt rate and jitter. It also indicates the amount of CPU time consumed by the ISR (and thus how much left for main loop).

 

tellSlater wrote:

The reason why I couldn't use a fifo and wait for the ISR to fetch my samples is that it takes about 20-40 ISR cycles to calculate my envelope, note, fine, sweep and vibrato effects in main.

I don't have time-space to calculate every sample. But 40 ISR calls are about 1ms so updating the effects once a millisec is not bad

You mean that if the main loop is busy doing some processing then there is no "time slot" to do the sample calculation? Well, that is the whole point of using a FIFO. In this case you'd have to use a FIFO depth of at least 40 then. Main loop fills it first, then goes on with other processing. With a bigger FIFO you can have even longer processing in the main loop. Obviously you need to write all code in a non-blocking fashion (i.e. state machines). But you do that already, I hope? ;-)

 

 

/Jakob Selbing

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

tellSlater wrote:
I said guess because I am not an expert and not sure if that method I used was the right way to measure it. If it is then yup - about 35%

 

How to become an expert? Start off knowing f-all about something and some persistence - you'll be an expert in no time. As I've harped on earlier - the simulator can reveal many secrets. You just have to look.

 

Your isr as it is now will become a lot slower once you start selecting different waveforms etc - the compiler has optimised your fixed indexes into the tables. Give it a try and see if my expectation is valid.

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

I note a _delay_us(10) in the main loop.

_delay_us does not allow for interrupts.

How many interrupts can occur in 200 cycles?

Moderation in all things. -- ancient proverb

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

In my usage of _delay_us(x) or _delay_ms(x), on those rare occasions where I do use them, I do not see them blocking interrupts in any fashion.

 

Indeed, interrupts executing during a _delay_Xs() serve only to increase the delay by whatever time is spent processing the interrupt(s).

David

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

frog_jr wrote:
In my usage of _delay_us(x) or _delay_ms(x), on those rare occasions where I do use them, I do not see them blocking interrupts in any fashion.
I think Michael's point was that with interrupts likely to go off at any moment something like _delay_us(10) does not do what it says on the tin. It could be a delay of 10us but is could be 53us or 2791us or 72894329430us (depending on how long the ISR held it from completing).

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

jaksel wrote:
rate at which interrupts are triggered. Wouldn't you agree on that?

 

Yeah I was thinking about that actually - even if the ISR was delayed, it should have been delayed by the same amount each iteration and therefore not drop in execution rate.

So after thinking that I arrived at the explanation that each one ISR call was delayed (for some reason I do not know (could be me having all variables volatile?)).

Each ISR would be called further and further delayed from normal execution and in the end one ISR would be skipped (TOVF0 would set while it was already high).

If interrupt timing gives me more problems I will use the method you provided (toggling a pin in ISR) to test that theory

 

jaksel wrote:
have the ISR set the pin upon ISR entry and clear on ISR exit

 

I will use this if I run into more ISR timing problems. Fortunately that while() trick really helped for now

 

jaksel wrote:
You mean that if the main loop is busy doing some processing then there is no "time slot" to do the sample calculation?

 

I mean that the sample takes more time to be calculated than the period of ISR callings so if I pop one sample from the FIFO in every ISR the FIFO would be emptied. Am I not getting something? Could you send me an example?

 

jaksel wrote:
Obviously you need to write all code in a non-blocking fashion 

 

Everything is written in a non blocking fashion except for my silenceOscilator() function that takes 2ms to execute and silence an oscillation. I didn't see no reason to make a more complicated, non-blocking implementation for this

 

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Tue. Oct 13, 2020 - 10:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh god... Not my ISR. I am starting to have some tremors just by thinking that. I will try it

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Only one. An interrupt triggers per 510 cycles. I was testing things out with that delay. It is not part of the code really.

Is it true that no ISRs are called during _delay_ms and _delay_us functions?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Can I delete a post? I was just messing around trying to figure out how to quote

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Tue. Oct 13, 2020 - 05:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
does not do what it says on the tin.
Oh that is ok then I am aware of this - I didn't need precise timing there.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

I have been anxiously thinking about this the past hour but now I realize - this is the final form of the ISR. These indexes (0 and 1) represent the two oscillators I am running. This will be the final form of my ISR

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void waitForSample()
{
    while(wait)
    wait = true
}

 

This bothers me, what if the interrupt occurs after the while statement, but before "wait = true".  In this case the wait flag is cleared by the interrupt, then set with "wait = true", and you miss that the interrupt occurred.

void waitForSample()
{
    wait = true
    while (wait)
}

 

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

Very unlikely to happen but then again..

Yep! Much better - I fixed it, thanks

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:
Only one. An interrupt triggers per 510 cycles. I was testing things out with that delay. It is not part of the code really.

Is it true that no ISRs are called during _delay_ms and _delay_us functions?

Those functions do not call other functions.

If by "called", you mean invoked by an interrupt, the answer is maybe.

Neither disables interrupts.  They will have little effect on the timing of interrupts.

In principle, ISRs can be called.

'Tis rarely, if ever, done.

If, for some reason, you choose to do so, be careful:

An ISR could include code that relies on interrupts being disabled.

An ISR ends with a RETI instruction that will enable interrupts.

That might not be what you want.

Moderation in all things. -- ancient proverb

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

After I removed the volatile keywords from all variables that were not shared with the ISR I discovered that I no longer have the sound problem - I don't need to use the waitForSample() function.

So my guess now is that the ISR was not called while a calculation that involved volatile variables took place.

Now that almost all variables in main are not volatile, the calculations that involve them are probably paused when the interrupt flag is set and the ISR is not delayed

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:
After I removed the volatile keywords from all variables that were not shared with the ISR I discovered that I no longer have the sound problem

Doesn't sound like a surprise: the whole point of 'volatile' is to prevent certain compiler optimisations.

 

So if you use 'volatile' where it's not needed, it will unnecessarily slow down your code.

 

Edit: clawson already said this in #27

 

Nine ways to break your systems code using volatile

 

 

my guess now is that the ISR was not called while a calculation that involved volatile variables took place.

No, that's not how it works.

 

volatile just prevents the compiler from "caching" old values of a variable - it has nothing to do with blocking interrupts.

 

An important upshot of this is that 'volatile' does not give atomic access.

 

EDIT

 

clawson wrote a Tutorial on it: https://www.avrfreaks.net/forum/tutcoptimization-and-importance-volatile-gcc

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Oct 15, 2020 - 09:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's worth noting that if you really need 'to-the-clock-cycle' timing precision you need external hardware, because the AVR will finish executing what might be a multi-cycle instruction before branching to the interrupt handler, and you have no way of knowing where you were in that multi-cycle instruction when the interrupt fires.  Probably not a big problem for audio frequency work.   S.

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

tellSlater wrote:
I mean that the sample takes more time to be calculated than the period of ISR callings so if I pop one sample from the FIFO in every ISR the FIFO would be emptied. Am I not getting something? Could you send me an example?

This is very confusing. If each sample takes more time to calculate than the ISR period then it would not help to do the calculation in the ISR - you'd still fail to supply samples at the rate you are trying to achineve! Obviously if you do the calculation in the ISR then a new interrupt would trigger before the ISR is done. If you do the calculation in the main loop then it would not matter how large the FIFO is - the main loop still would not be able to produce samples quickly enough.

 

So I think you have misunderstood something.

 

You only have one CPU and it can only do one thing at a time. For each sample to produce it must execute a bunch of instructions. That can either be done in the main loop (using FIFO) or in the ISR. The amount of CPU time required is the same for both solutions - they are equally efficient in that respect. However in general you want as short ISR as possible so the former solution (main loop) would be preferred in my opinion. Obviously your solution may still work as long as the application is designed to tolerate the longer interrupt latency.

 

/Jakob Selbing

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

These were some great reads, thanks. Now then, I wonder why making my variables non-volatile solved the ISR timing problem

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:

These were some great reads, thanks. Now then, I wonder why making my variables non-volatile solved the ISR timing problem

Maybe the ISR became too slow to execute that it would not complete before next interrupt triggered. Did you check that?

/Jakob Selbing

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

I would appreciate if you could send me a small FIFO sampling example, I don't think I have understood how it should be used

 

It is necessary that this part of the sample is calculated in the ISR - the calculation needs to be executed at 39K times a sec to get the right note. I cannot omit this part of the sample calculation from the ISR.

However calculations calculations regarding envelope volumes vibratos etc are calculated in main about 1000 times a sec.

 

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

No that was not it - as I stated in previous post the ISR was executed 35% of the CPU time. It exited when the TCNT0 was 177 and upcounting. Therefore 177/510 = 35% give or take (510 timer ticks until overflow because phase-correct mode is used)

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Wed. Oct 14, 2020 - 05:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

tellSlater wrote:
It is necessary that this part of the sample is calculated in the ISR - the calculation needs to be executed at 39K times a sec to get the right note.

But you don't really need to calculate the sample just at the specific time at which it is needed. You could calculate a bunch of samples and between each increment just increment the time variable. 

 

Using pseudo-code, you could go from this

ISR
{
   level = calculate_sample(t); // takes long time!
   PWM = level;
   t++;
}

 

to this

ISR
{
   level = fifo_read(); // takes short time
   PWM = level;
}

main
{

  while(1)
  {
      while (fifo_not_full())
      {
        sample = calculate_sample(t);
        fifo_write(sample)
        t++;
      }

      // do other stuff here...

  }
}

 

/Jakob Selbing

Last Edited: Wed. Oct 14, 2020 - 06:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

tellSlater wrote:

These were some great reads, thanks. Now then, I wonder why making my variables non-volatile solved the ISR timing problem


Suggest you re-read #27
.
https://www.avrfreaks.net/comment/3011266#comment-3011266

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

I get it now, thanks for taking the time to explain

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

I already read that many times! I did exactly that.

But I thought that even if a calculation involving volatile variables was taking place, the ISR call still should not have been delayed - which it did (most probably anyways, since the sound pitched down).

Also when I removed the volatile from the appropriate variables that delay disappeared. This is why I am a bit baffled

I wish I knew how to read assembly - maybe then I could get a better understanding on this. I was looking for some tutorials on AVR assembly or assembly in general?(I dunno the difference)

If anyone knows a good source please link it for me

 

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

This example is extreme but:

#include <avr/io.h>

int value() {
	int a, b, c;
	a = 57;
	b = 31;
	c = a * b;
	return c;
}

int main(void) {
	PORTB = value();
	while (1) {
	}
}

When I don't use volatile:

0000007a <value>:
	int a, b, c;
	a = 57;
	b = 31;
	c = a * b;
	return c;
}
  7a:	87 ee       	ldi	r24, 0xE7	; 231
  7c:	96 e0       	ldi	r25, 0x06	; 6
  7e:	08 95       	ret

You don't have to be an Asm expert to recognise (I hope) that all that is happening here is that the compiler has already calculated what 57 * 31 is. It is 1767 which is 0x06E7 so this code just returns the R25:24 pair with 0x06 and 0xE7 in it.

 

Now I make them volatile:

int value() {
	volatile int a, b, c;
	a = 57;
	b = 31;
	c = a * b;
	return c;
}

which results in:

0000007a <value>:
#include <avr/io.h>

int value() {
  7a:	cf 93       	push	r28
  7c:	df 93       	push	r29
  7e:	00 d0       	rcall	.+0      	; 0x80 <value+0x6>
  80:	00 d0       	rcall	.+0      	; 0x82 <value+0x8>
  82:	00 d0       	rcall	.+0      	; 0x84 <value+0xa>
  84:	cd b7       	in	r28, 0x3d	; 61
  86:	de b7       	in	r29, 0x3e	; 62
	volatile int a, b, c;
	a = 57;
  88:	89 e3       	ldi	r24, 0x39	; 57
  8a:	90 e0       	ldi	r25, 0x00	; 0
  8c:	9a 83       	std	Y+2, r25	; 0x02
  8e:	89 83       	std	Y+1, r24	; 0x01
	b = 31;
  90:	8f e1       	ldi	r24, 0x1F	; 31
  92:	90 e0       	ldi	r25, 0x00	; 0
  94:	9c 83       	std	Y+4, r25	; 0x04
  96:	8b 83       	std	Y+3, r24	; 0x03
	c = a * b;
  98:	49 81       	ldd	r20, Y+1	; 0x01
  9a:	5a 81       	ldd	r21, Y+2	; 0x02
  9c:	2b 81       	ldd	r18, Y+3	; 0x03
  9e:	3c 81       	ldd	r19, Y+4	; 0x04
  a0:	42 9f       	mul	r20, r18
  a2:	c0 01       	movw	r24, r0
  a4:	43 9f       	mul	r20, r19
  a6:	90 0d       	add	r25, r0
  a8:	52 9f       	mul	r21, r18
  aa:	90 0d       	add	r25, r0
  ac:	11 24       	eor	r1, r1
  ae:	9e 83       	std	Y+6, r25	; 0x06
  b0:	8d 83       	std	Y+5, r24	; 0x05
	return c;
  b2:	8d 81       	ldd	r24, Y+5	; 0x05
  b4:	9e 81       	ldd	r25, Y+6	; 0x06
}
  b6:	26 96       	adiw	r28, 0x06	; 6
  b8:	0f b6       	in	r0, 0x3f	; 63
  ba:	f8 94       	cli
  bc:	de bf       	out	0x3e, r29	; 62
  be:	0f be       	out	0x3f, r0	; 63
  c0:	cd bf       	out	0x3d, r28	; 61
  c2:	df 91       	pop	r29
  c4:	cf 91       	pop	r28
  c6:	08 95       	ret

You don't have to try and decode that to know you don't want it !!!

 

Imagine the same thing happening all over your code because you made all your principle variable "volatile" ! It doesn't just make it "big and bloaty", It's also hundreds if not thousands of extra machine cycles to execute all that additional stuff.

Last Edited: Thu. Oct 15, 2020 - 08:33 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

tellSlater wrote:
the ISR call still should not have been delayed - which it did

Did it? What makes you think that?

 

If the rest of the code was too bloated and slow to keep up with the interrupts, couldn't that also have caused the problem ... ?

 

Note that making variables (and functions) static can give the compiler some extra optimisation opportunities ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could always take a different approach and ditch the whole idea of main() plus an ISR, and just do everything in the ISR. You can write code that guarantees no jitter on your output.

 

Timer_ISR() {

    //Reload Timer

    //Do timing critical stuff

    //Do non-timing critical stuff

    //Ditch return address

    //SEI

    //SLEEP

    While (1) {
        ;
    }

}

main() {

    Initialise();
    
    start_timer();

    SEI;

    while (1) {
        ;
    }

}

 

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

Yeah that is a hell of a difference. I am only concluding the ISR was delayed by the sound which was pitched down. I don't really have good evidence yet and am not 100% sure that it was delayed

 

So I should make static functions - only visible to the current main.cpp?

And also prefer using static local variables rather than global variables?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Thank you for bringing that example up.

Truth is I already read one of your posts on this forum on the subject and was pretty helpful in understanding this.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

The interrupt call would need to happen during

//Do non-timing critical stuff.

where the interrupt flag is not yet set in your psudo-code thus probably causing timing problems again.

I see where you're going with this though

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:

The interrupt call would need to happen during...

 

I think you misunderstand, you do the non-critical stuff in the ISR after the critical stuff. Nothing is done in main() (in fact, once the ISR has fired it NEVER returns to main().

 

My proposal guarantees a fixed, and non-varying, response time to the timer interrupt; there can be no jitter.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

Brian Fairchild wrote:

tellSlater wrote:

The interrupt call would need to happen during...

 

My proposal guarantees a fixed, and non-varying, response time to the timer interrupt; there can be no jitter.

 

I really cannot see how this can happen. The interrupt would take more than 510 clock cycles to execute the non time critical stuff which is more time than the demanded ISR call period

 

Timer_ISR() {

    //Reload Timer

    //Do timing critical stuff

    //Do non-timing critical stuff !!!Secont ISR call needs to happen here but interrupt flag is disabled

    //Ditch return address

    //SEI

    //SLEEP

    While (1) {
        ;
    }

}

main() {

    Initialise();
    
    start_timer();

    SEI;

    while (1) {
        ;
    }

}

 

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

You split up the non-critical stuff. Do some of it one time through the ISR, and something different the next time. By doing it in the ISR you automatically have a timebase to do things like ADSR.

 

If you can't do that then you have a more fundamental problem with not enough CPU cycles per unit-time to do what needs to be done.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

Hmm yeah if you split the non-critical it is doable. Would you say this method is better? I mean putting all the code into the ISR.. and if so why?

I like your #5 quote

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:

Would you say this method is better? I mean putting all the code into the ISR.. and if so why?

 

It's the method I use to generate accurate, predictable, jitter-free signals. It's the way I have generated video timing signals which are 100% correct and have absolutely zero timing jitter.

 

Is it the best method? I think so but YMMV.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

Brian Fairchild wrote:

Timer_ISR() {

    :
    :

    //Ditch return address

How do you do that in 'C' ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian Fairchild wrote:
fundamental problem with not enough CPU cycles per unit-time to do what needs to be done

That was the point I was trying to make in #63 - and others had also said previously.

 

Not sure OP has (fully) grasped it yet?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
How do you do that in 'C' ?

SP += 2;

perhaps? (well OK you'd have to examine the stack frame but as ISR entry is naked I think it's just removing the stacked PC by an adjustment of 2 to SP).

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

clawson wrote:

perhaps? (well OK you'd have to examine the stack frame but as ISR entry is naked I think it's just removing the stacked PC by an adjustment of 2 to SP).

 

Exactly that, or whatever syntax your compiler supports.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

I just don't understand how some bloated code in main could be the reason of a delayed interrupt call

when it was earlier mentioned that whatever calculation happens in main atm the interrupt is supposed to be called immediately - no delay.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Would you mind explaining or linking some reads on this matter? I mean ditching return address and what that SP register is

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

SP = Stack Pointer. It's a fairly universal thing. Most CPU architectures have one (sometimes several).

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

http://www.avr-asm-tutorial.net/...

 

For some reason Assembler has the aura of magic runes only to be conjured by the high priests. Reality is that it is stunningly simple. Like building a house from Lego bricks - each instruction does a very simple operation. Even having a basic idea of how the C code is being translated is useful especially when you need to do things fast.

 

The main reason us oldies know assembler is simply because when we started, C compilers were inefficient, stupid expensive and besides the micros we had might have been lucky to have 256 bytes. 4K on a ZX80- luxury! And, we didn't have a PC as we know them today that had the resources to run a compiler.

 

You might want to seek out other AVR projects where they have done synthesis - 

https://create.arduino.cc/projec...

 

There the whole gamut from very simple to ones that blow my mind where the designer has pulled some weird ass tricks and made them work. Or you get a 600MHz processor like Cliff has. One channel of DDS can be done at over 100MHz. Also being a 32bit cpu helps.

 

 

 

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

If the ISR needs 60% of the CPU and the main line needs 50%, something will give.

Absent disabling interrupts, the main line cannot delay an

interrupt by more than the few cycles required for an instruction.

What will happen is that the main line will not get its work done.

If the ISR relies on the main line getting its work done,

the ISR will produce the wrong results.

Moderation in all things. -- ancient proverb

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

Something is nagging me about this, I may be wrong and need to think more but...

 

for DDS you run the accumulation at a constant clock speed and do something when it overflows, or you simply use the upper n bits to address a LUT. You say that you are running your timer in phase correct pwm mode which means the interrupt/accumulation rate will vary along with the PWM width. It just feels wrong.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

tellSlater wrote:
I just don't understand how some bloated code in main could be the reason of a delayed interrupt call

Again, who says that the interrupt call is being delayed ?

 

That's the point.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you have a 'scope?

 

Two output pins, even if temporarily repurposed, one which goes high at the start of main and one at the start of the ISR which clear sat the end of it. Each clears the other.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

No the OVF happens constantly every 510 CLK cycles and has no relation to the PWM width - OCR0A. Is LUT a lookup table? I think I am using one LUT for every wave form but I am only accessing it in the OVF interrupt where the final sample calculations happen before outputting.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

I don't remember seeing your code which sets up the timers. Can you post it?

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

jaksel also suggested I use a similar trick to make sure I know what is going on. I am going to use this in the future if I get more timing problems and make a more detailed post about it.

Right now I am not really in the mood of re-writing all the code as it was 4 days ago just to see what was going on

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
inline void setupTIMERS() //Sets up TIMER0 overflow interrupt and PWM
{
	OCR0A  = 0;
	TIMSK0 = (1 << TOIE0);   //overflow interrupt - timer0
	TCCR0A = (1 << COM0A1) | (1 << WGM00);   //PWM and timer modes - timer0
	
	TIMSK1 = (0 << TOIE1);   //overflow interrupt - timer1 (I am not using timer1 anymore but kept the commands here for future use)
 	TCCR1A = (0 << WGM10);   //timer mode - timer1
	
	TCNT0 = 0x00;
	TCNT1 = 0xff;
	TCCR0B = (1 << CS00);   //clock source = CLK, start PWM - timer0
	TCCR1B = (0 << CS10);   //clock source = CLK, start PWM - timer1
}

 

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Again, I concluded that from the sound which I realize is not hard evidence. But still strong enough IMO to give it a 80% chance that it actually was due to ISR delayed execution. I may revisit this in the future and use jaksel - Brian method to ensure and post results.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Brian Fairchild wrote:
You say that you are running your timer in phase correct pwm mode which means the interrupt/accumulation rate will vary along with the PWM width. It just feels wrong.

I don't think the interrupt rate will vary in phase-correct PWM. Unless you change the top value, of course...

/Jakob Selbing

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

tellSlater wrote:
Right now I am not really in the mood of re-writing all the code as it was 4 days ago just to see what was going on

I assume you are not using any version control software? I would strongly recommend to do so!

 

Take a look at e.g. tortoise SVN: https://tortoisesvn.net/

/Jakob Selbing

Last Edited: Sun. Oct 18, 2020 - 06:01 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks! I will

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:

void waitForSample()
{
    while(wait)
    wait = true
}

This is not safe.  Here is an example of a failure sequence.

 

// assume wait is 1 here

load wait into register

interrupt occurs, setting wait to 0

test register 

if register == 1 (which it does), set wait to 1

// at this point, the setting of wait to 0 by the ISR is lost

 

Perhaps you meant

void waitForSample()
{
    while(wait)
        ;
    wait = true
}

 

Last Edited: Sun. Oct 18, 2020 - 09:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, thank you for bringing it up. In an earlier post it was proposed that I change it to:

void waitForSample()
{
    wait = true;
    while(wait); 
}

Would you say this form is ok? This is what I am using now.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:

Yes, thank you for bringing it up. In an earlier post it was proposed that I change it to:

void waitForSample()
{
    wait = true;
    while(wait); 
}

Would you say this form is ok? This is what I am using now.

What if wait is false when you enter the function?  You will miss it.  Problems like this are why many computer instruction sets had instructions like test-and-set, where you could get the value of a flag and set it in the same instruction, so you would never miss a valid setting.

 

The best thing to do is to set wait = true once, as soon as possible after you detect that it is false, and no other time.  Then you have more assurance that the ISR has very recently set wait to false.  So actually this is better, because wait is set back to true immediately after you detect that it is false:

void waitForSample()
{
    while(wait)
        ;           // I always put ; here to make it more visible
    wait = true;
}

 

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

But if wait is false when you enter waitForSample, the first assignment turns it to true.. I don't think you can miss it.

On the contrary wait could be assigned false outside the function. Then after the function is called and wait is false, the "waiting" does not happen at all.

That is something we were talking about here on a previous post. Because I am changing the value of wait in an ISR the assignment must happen before the while loop in the function.

If I set wait to true after the while loop there is no guarantee that it wont change to false before the next call of the function.

If I set wait to 1 right before the while loop it is guaranteed that the execution will stall between this assignment and inside the while loop until wait is changed to false in the ISR.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:

But if wait is false when you enter waitForSample, the first assignment turns it to true.. I don't think you can miss it.

You just did miss it.  On entering waitForSample, either wait will be true or it will be false.  It seems to me that those are not the same situations, and you should not treat them the same.

Quote:
That is something we were talking about here on a previous post. Because I am changing the value of wait in an ISR the assignment must happen before the while loop in the function.

If I set wait to true after the while loop there is no guarantee that it wont change to false before the next call of the function.

If I set wait to 1 right before the while loop it is guaranteed that the execution will stall between this assignment and inside the while loop until wait is changed to false in the ISR.

The only guarantee you can have is if you set wait to true immediately after the ISR sets it to false.  If you wait a long time before setting wait to true, it may get set to false by the ISR just before you set it to true, and then you missed it getting set to false.

 

The sequence you generally want is this

while (1)
{
    wait_for_ISR_signal();
    reset_ISR_signal();  // do as soon as possible after receiving signal
    do_stuff();
}

What you seem to be doing is this

 

while (1)
{
    reset_ISR_signal();  // do as soon as possible after receiving signal
    wait_for_ISR_signal();
    do_stuff();
}

I'm not saying your sequence won't work, I'm just saying it gives you less timing margin and that makes it more fragile.

 

Last Edited: Mon. Oct 19, 2020 - 07:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I picked testandclear instead of tesetandset because avr-gcc keeps zero in R1.

uint8_t testandclear(volatile uint8_t *ptr) {
    uint8_t sreg=SREG;
    SREG &= ~_BV(SREG_I);  // i.e. cli
    uint8_t retval= *ptr;
    // SREG=sreg, if brave or hand-compiling
    *ptr=0;     // should be a single machine insntruction
    SREG=sreg;  // here if neither brave nor hand-compiling
    return retval;
}  // testandclear

Except for the return, all statements are volatile accesses and may not be reordered.

Moderation in all things. -- ancient proverb

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

I was curious so I looked at the AVR instruction set.  AVR has XCH, which exchanges (Z) and some register r.  This will give the same safe result as a test-and-set, or test-and-clear, depending on what value you load into r before executing the instruction.  

Last Edited: Mon. Oct 19, 2020 - 08:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm wrote:
On entering waitForSample, either wait will be true or it will be false.  It seems to me that those are not the same situations, and you should not treat them the same.

 

Ok then I really am missing something big I have not understood:

void waitForSample
{
    wait = true;
    while(wait);
} 

a) If wait is false, true will be assigned to it and then execution will continue to while loop.

b) If wait is true, true will be assigned to it and then execution will continue to while loop.

ISR could be called at any point, setting wait to false. But this is ok - the while loop will be skipped and that is the whole purpose of it anyways.

 

What am I missing? Is this not how it will work? I am not a programmer nor computers engineer so please help me understand - if you have a source please link it

Is this code in #98 equivalent?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Mon. Oct 19, 2020 - 09:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I drew this diagram to try and explain the difference in the two versions of waitForSample in #95.  Yours is the top code there, mine is the bottom code.

 

The diagram assumes timer interrupts every 10ms, hence the marks at 0ms, 10ms, 20ms, etc.  For each figure A, B, C and D, the line is high when main code is running, then goes low while looping in waitForSample before resynchronizing with the timer at point X, where main code runs its next pass.

 

In figure A, main code runs for 6ms, then calls waitForSample which waits for 4ms until the timer ISR sets wait = false, which causes waitForSample to return and main code begins running again (at X).

 

In figure B, main code runs for 9ms, then calls waitForSample which waits for 1ms until the timer ISR sets wait = false, which causes waitForSample to return and main code begins running again (at X).  In both A and B, main code begins running again at 10ms point (plus a few microseconds to return from waitForSample).  In both examples A and B, the code behaves the same (and as desired) for either version of waitForSample.  But now comes the problem (examples C and D).

 

In figure C, which uses my version of waitForSample, main code runs for 11ms, running past the timer interrupt at 10ms.  This is not an ideal situation, but in many systems it might happen.  Now when waitForSample is called at 3A it sees that wait has been set to false (at 10ms) and returns immediately, and main code runs slightly late but still within the interval of 10ms-20ms.

 

In figure D, which uses your version of waitForSample, main code runs for 11ms, running past the timer interrupt at 10ms.  Now when waitForSample is called at 3B it misses the fact that wait was set to false at 10ms, so it waits all the way until the next tick at 20ms before main code resumes.  The entire interval 10ms-20ms has been lost.  An entire slot of work has not occurred.  If you were maintaining a tick counter in main code, in figure D you have lost a tick count, while in figure C you would not lose a tick count.

 

 

Last Edited: Tue. Oct 20, 2020 - 07:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, first of all thank you for taking the time to do something like this! This is an awesome explanation.

So, I think I prefer 3b because I have many  "slots of work" to spare and I primarily am trying to push main code execution away from the ISR calls so that the ISR's periodic execution is not threatened.

Since I am using direct digital synthesis ISR timing is the most important thing here. If and when a case 3 happens I would rather waste a whole interval waiting than start a delayed execution of a main code segment (3A) and risking next ISR delayed execution.

I also try to limit case 3 in my code by using the simulator.

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

NULL

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

OK, as long as you understand the tradeoffs, choose the approach that makes the most sense for you.  And good luck with the project.

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

Thanks again! You're awesome

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

Hello friends! You helped me complete my project and I wanted to post my results here:

 

Code

 

/*
 *  ZNEAR - note producing controlled by potentiometer - DDS technique used
 *
 *  Author	: WindFish
 *
 *	Rel Date: 7/11/2020
 *
 *	OUTPUT of DDS at pin 12 - OC0A. Sample rate approximately 39KHz
 *
 *	chip ATMega328P
 */

#define F_CPU   20000000 //CPU speed in Hz for use with util/delay library and Serial communication
#define BUAD    9600	//For Serial communication
#define BRC     ((F_CPU/16/BUAD) - 1) //For Serial communication

#define RAND_MAX 255 //for random generation (not actually used)

#define INPsize   32 //for rolling mean filtering of the ADC input

//C++ libraries
#include <stdint.h>
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include <math.h>
#include <avr/eeprom.h>
#include "helping_functions.h"

//Imporetd C libraries
extern "C"
{
	#include "nokia5110.h"
	#include "nokia5110_chars.h"
};

//Welcome screen bitmap
const uint8_t wf_Width    = 84;
const uint8_t wf_Height   = 48;
const uint8_t PROGMEM wf_Bitmap[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x40, 0x60, 0x20, 0x20, 0x20, 0x10, 0x90, 0xd0, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x60, 0x30, 0xf8, 0x80, 0x00, 0x00, 0x80, 0xc0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x18, 0x0c, 0x04, 0x06, 0x02, 0x03, 0x01, 0x01, 0x00, 0x80, 0xe0, 0x30, 0x1c, 0x06, 0x02, 0x03, 0x01, 0x01, 0x00, 0x00, 0x80, 0xc0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0x40, 0x60, 0x20, 0x20, 0x30, 0x10, 0x10, 0x18, 0x08, 0x08, 0x0c, 0x04, 0x04, 0x06, 0x82, 0x83, 0x41, 0x61, 0x30, 0x18, 0x08, 0x0c, 0x1f, 0xf2, 0x83, 0xe1, 0x38, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x7c, 0x07, 0x00, 0x80, 0x80, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x37, 0x1f, 0x80, 0x80, 0xb1, 0xef, 0xbe, 0xc3, 0x41, 0x3f, 0xf0, 0x90, 0x10, 0x08, 0x08, 0x04, 0x07, 0x3e, 0xf2, 0x01, 0x01, 0x00, 0x08, 0x08, 0x08, 0x78, 0xc0, 0x80, 0xc0, 0xc0, 0x80, 0xe0, 0x38, 0x00, 0xd0, 0x00, 0xc0, 0x80, 0x80, 0x00, 0xc0, 0x40, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x10, 0x18, 0x08, 0x08, 0x08, 0x08, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x18, 0x30, 0x20, 0x20, 0x30, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xc0, 0x79, 0x49, 0x48, 0x08, 0x00, 0xd1, 0x00, 0x41, 0x20, 0xa1, 0x00, 0xf1, 0x41, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x06, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'legendOSCbitmap', 11x48px
const uint8_t leg_Width    = 11;
const uint8_t leg_Height   = 48;
const uint8_t PROGMEM leg_Bitmap[] = {
	0xfe, 0xff, 0x03, 0x03, 0x03, 0xff, 0xfe, 0x00, 0x00, 0x33, 0x44,
	0xc1, 0xe3, 0x63, 0x6b, 0x63, 0x63, 0x61, 0x00, 0x00, 0xe7, 0x08,
	0x63, 0x67, 0x66, 0x66, 0x66, 0x7e, 0x3c, 0x00, 0x80, 0xb9, 0x42,
	0xf8, 0xfc, 0x0c, 0x0d, 0x0c, 0x1c, 0x18, 0x00, 0x01, 0x9d, 0x42,
	0x07, 0x0f, 0x0c, 0x0c, 0x0c, 0x0e, 0x06, 0x00, 0x00, 0xe7, 0x10,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x22
};

// 'legend1', 7x9px
const uint8_t leg1_Width    = 7;
const uint8_t leg1_Height   = 10;
const uint8_t PROGMEM leg1_Bitmap[] = {
	0x00, 0x00, 0x06, 0xff, 0xff, 0x00, 0x00,
	0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00
};

// 'legend2', 7x9px
const uint8_t leg2_Width    = 7;
const uint8_t leg2_Height   = 10;
const uint8_t PROGMEM leg2_Bitmap[] = {
	0x0e, 0x8e, 0xc3, 0xe3, 0x73, 0x3e, 0x1e,
	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03
};

const uint8_t wave_Width    = 15;
const uint8_t wave_Height   = 8;
const uint8_t PROGMEM rand_Bitmap[] = {
	0x40, 0x06, 0x21, 0x10, 0x42, 0x20, 0x0c, 0x42, 0x10, 0x22, 0x09, 0x44, 0x40, 0x12, 0x44
};

const uint8_t PROGMEM square_Bitmap[] = {
	0x40, 0x40, 0x40, 0x40, 0x7f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x40, 0x40, 0x40
};

const uint8_t PROGMEM saw_Bitmap[] = {
	0x7f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20
};

const uint8_t PROGMEM triangle_Bitmap[] = {
	0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02
};

const uint8_t PROGMEM sin_Bitmap[] = {
	0x40, 0x40, 0x60, 0x30, 0x1c, 0x06, 0x03, 0x01, 0x01, 0x03, 0x06, 0x1c, 0x30, 0x60, 0x40
};

//wave tables
PROGMEM const unsigned char waveTable[5][256]  ={
	//Random numbers wave
	{156,164, 53, 27,167,174,146, 99,157, 61, 85, 46, 80, 79, 58,159,
	56,115,234,119,182,127,150,170,133,173,219, 66,227,249,  6, 57,
	203,255, 45,229, 29, 30, 19, 91,202,158,201, 98,172, 63, 69,235,
	106, 95,220, 35,131, 59,248, 81,154, 86, 31,243, 62,238,242,171,
	90,147,109, 60, 94,130, 43,103,122,132, 84,143, 22,247,105, 15,
	169,187,  3, 51,254,251,110,114,108, 49, 36, 13,181,225,188,104,
	230, 41,144,168,183,  9,252, 93,124,107,141,177,140,  1,175, 28,
	26,233,138,231,185,  8,221,214,  4, 89,208, 47,  0,237, 34,176,
	40,120,212,  2,236, 64,153,134,224,209, 44, 97, 16,218,200,165,
	178,180,102,137,118,125,161,245,226, 48, 12, 65, 83,101,190,116,
	217,198,142,192,199,155, 38,126,184, 25,189, 17,151,232,207,240,
	96,186, 74,128,196,211,129,163, 21, 73,117,135,197, 14, 24,179,
	20,113, 10,152, 77,160, 70,149,121, 33, 75,112, 88,195,194, 42,
	136,100, 78, 32,191,193, 68, 55,223,204,  7,215,162, 11, 52, 92,
	250,239, 23, 87,  5,205, 54,139, 71,228,222, 37,241,206,210,213,
	148,244, 67,253, 50, 76, 82, 18,246,166, 72, 39,111,216,145,123,},

	//square wave
	{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,30,45,60,75,90,105,120,135, 150,165,180,195,210,225,240,
	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,240,225,210,195,180,165,150,135,120,105,90,75,60,45,30,15},

	//left saw (negative slope)
	{255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,
	212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,
	176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,
	140,139,138,137,136,135,134,133,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,
	89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,
	41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0},

	//triangle wave
	{1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,
	87,89,91,93,95,97,99,101,103,105,107,109,111,113,115,117,119,121,123,125,127,129,131,133,135,137,139,141,143,145,147,149,151,153,155,157,159,161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,193,195,
	197,199,201,203,205,207,209,211,213,215,217,219,221,223,225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,255,253,251,249,247,
	245,243,241,239,237,235,233,231,229,227,225,223,221,219,217,215,213,211,209,207,205,203,201,199,197,195,193,191,189,187,185,183,181,179,177,
	175,173,171,169,167,165,163,161,159,157,155,153,151,149,147,145,143,141,139,137,135,133,131,129,127,125,123,121,119,117,115,113,111,109,107,
	105,103,101,99,97,95,93,91,89,87,85,83,81,79,77,75,73,71,69,67,65,63,61,59,57,55,53,51,49,47,45,43,41,39,37,35,33,31,29,27,25,23,21,19,17,15,
	13,11,9,7,5,3,1},	

	//sine wave
	{0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,
	70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124,127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,
	221,223,225,227,229,231,233,234,236,238,239,240,242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,
	253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,221,219,217,215,212,210,208,205,
	203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,
	102,99,96,93,90,87,84,81,78,76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,
	3,2,2,1,1,1,0,0,0} };

//Note frequencies but in TWORD values
PROGMEM uint16_t const twordNotes[88] = {
	46, 49, 52,																	//Octave 0
	55, 58, 62, 66, 69, 74, 78, 82, 87, 92, 98, 104,							//Octave 1
	110, 116, 123, 131, 138, 147, 155, 164, 174, 184, 195, 207,					//Octave 2
	219, 232, 246, 261, 276, 293, 310, 328, 348, 368, 390, 414,					//Octave 3
	438, 464, 492, 521, 552, 585, 619, 661, 695, 735, 779, 826,					//Octave 4
	875, 927, 982, 1040, 1102, 1168, 1237, 1311, 1389, 1470, 1558, 1651,		//Octave 5
	1749, 1853, 1963, 2080, 2203, 2334, 2473, 2620, 2776, 2940, 3115, 3301,		//Octave 6
	3497, 3704, 3925, 4158, 4405, 4667, 4945, 5238, 5550, 5880, 6230, 6600,		//Octave 7
	6993																		//Octave 8
};

volatile uint16_t	refclk=39185;			// refclk=3906 = 20MHz / 510 ... increase to compensate for higher pitch
											// 510 comes from formula for calculating phase correct PWM frequency

volatile uint16_t	phaccu[2] = {0, 0};		//These variables are used for
volatile uint16_t	tword[2] = {0, 0};		//outputting them with DDS
uint16_t			twordcalc[2] = {0, 0};	//
uint8_t				osc[2] = {0, 0};		//

uint16_t	dutyCycle;						//Variables for rollingMeanADC calculation
uint16_t	ADCinputs[INPsize];				//
uint8_t		inputsi;						//
uint32_t	rollingMeanADC;					//

bool	inputActive = false;				//When true rollingMean is being used to input values to an INPUT setting variable

uint8_t				smallTimer = 0;			//Counts 1/40ms - every timer0 overflow interrupt - starts at 0
volatile uint8_t	wait = 1;				//A part of note calculations is done once after every small timer tick. After the calculation this is set to 0
volatile uint8_t	millisecs = 0;			//Counts 1 ms every 40 smallTimer clicks

uint16_t	millisecsAutoButton = 0;		//Counts ms - used for AutoButton function
uint16_t	millisecsSerial = 0;			//Counts ms - used for timing some serial debugging commands
bool		serialGO = false;					//Starts the serial out

uint8_t	notePlaying[2] = {0, 0};		//Boolean  - enables oscillators if a note is playing
uint8_t	notePlayingSum = 0;				//Sum of notes that play

uint8_t		volume[2] = {0, 0};				//Current oscillator volume - calculated in envelope
uint8_t		volumecalc[2] = {0, 0};			//Used for calculations before assigning it to volume
uint8_t		volumeRelease[2] = {0, 0};		//Used for envelope calculations

uint8_t		waveKind[10] = {4,4, 4,4, 4,4, 4,4, 4,4};						//INPUT - oscillator wave used - for every button
uint8_t		phase0[10] = {0,0, 0,0, 0,0, 0,0, 0,0,};						//INPUT - beginning phase of a wave - for every button
uint8_t		key[10] = {57,57, 55,55, 52,52, 50,50, 48,48};					//INPUT - main note key - for every button
uint16_t	noteADC[10] = {550,550, 550,550, 550,550, 550,550, 550,550, };	//INPUT - main note ADC - for every button
bool		noteSource[10] = {1,1, 1,1, 1,1, 1,1, 1,1};						//INPUT - note source (key or ADC) - for every button
uint8_t		fine[10] = {127,127, 127,127, 127,127, 127,127, 127,127};		//INPUT - fine - for every button
uint8_t		oscVolume[10] = {255,255, 255,255, 255,255, 255,255, 255,255};	//INPUT - oscillator volume - for every button
uint8_t*	pwaveKind = waveKind;			//points to setting in use
uint8_t*	pphase0 = phase0;				//points to setting in use
uint8_t*	pkey = key;						//points to setting in use
uint16_t*	pnoteADC = noteADC;				//points to setting in use
bool*		pnoteSource = noteSource;		//points to setting in use
uint8_t*	pfine = fine;					//points to setting in use
uint8_t*	poscVolume = oscVolume;			//points to setting in use

uint8_t		attack[10] = {50,50, 50,50, 50,50, 50,50, 50,50};				//INPUT - Envelope attack - for every button
uint8_t		decay[10] = {190,190, 190,190, 190,190, 190,190, 190,190};		//INPUT - Envelope decay - for every button
uint8_t		sustain[10] = {70,70, 70,70, 70,70, 70,70, 70,70};				//INPUT - Envelope sustain - for every button
uint8_t		release[10] = {150,150, 150,150, 150,150, 150,150, 150,150};	//INPUT - Envelope release - for every button
uint8_t		releaseMode[10] = {1,1, 1,1, 1,1, 1,1, 1,1};					//INPUT - Release mode (0->linear or 1->nonlinear) - for every button
uint8_t*	pattack = attack;				//points to setting in use
uint8_t*	pdecay = decay;					//points to setting in use
uint8_t*	psustain = sustain;				//points to setting in use
uint8_t*	prelease = release;				//points to setting in use
uint8_t*	preleaseMode = releaseMode;		//points to setting in use

uint8_t		sweepSpeed[10] = {0,0, 0,0, 0,0, 0,0, 0,0};				//INPUT - Sweep speed (0->one semi a second, 255->fast!) - for every button
uint8_t		sweepDirection[10] = {0,0, 0,0, 0,0, 0,0, 0,0};			//INPUT - Sweep direction (0->down, 1->up, 2->from down, 3->from up) - for every button
uint8_t*	psweepSpeed = sweepSpeed;								//points to setting in use
uint8_t*	psweepDirection = sweepDirection;						//points to setting in use
bool		sweepStop[10] = {0, 0};									//Sweep should stop when 0 or 20KHz are reached
uint16_t	millisecsSweep[10] = {0, 0};							//Timer for the sweep incremented in ms

uint16_t			vibratoSpeed[10] = {0,0, 0,0, 0,0, 0,0, 0,0};				//INPUT - Vibration speed - for every button
uint8_t				vibratoDepth[10] = {35,35, 35,35, 35,35, 35,35, 35,35};		//INPUT - Vibration intensity - for every button
uint8_t				vibratoWaveKind[10] = {4,4, 4,4, 4,4, 4,4, 4,4};			//INPUT - Vibration wave kind - for every button
uint16_t*			pvibratoSpeed = vibratoSpeed;		//points to setting in use
uint8_t*			pvibratoDepth = vibratoDepth;		//points to setting in use
uint8_t*			pvibratoWaveKind = vibratoWaveKind;	//points to setting in use
uint16_t			vibratoPhacc[2] = {0, 0};			//Vibrato phase accumulator
volatile uint8_t	millisecsVibrato[2] = {0, 0};		//Vibrato millisecs

#define BTNno 9 //Number of buttons
volatile uint8_t*	buttonPort[BTNno] = {&PIND, &PIND, &PIND, &PIND, &PINC, &PINC, &PINC, &PINC, &PINC};	//Port of buttons in IC
uint8_t				buttonPin[BTNno] = {PIND0, PIND2, PIND3, PIND4, PINC0, PINC1, PINC2, PINC3, PINC4};		//Pin of buttons in IC
bool				buttonState[BTNno] = {0, 0, 0, 0, 0, 0, 0, 0, 0};										//Button states
uint8_t				millisecsButton[BTNno] = {0, 0, 0, 0, 0, 0, 0, 0, 0};									//Timers for muttons in ms for software debouncing

uint8_t		envelopeStage[2] = {4, 4};		//Tracks the current stage of the envelope
bool*		pEnvButton = &buttonState[0];	//The button envelope tracks for releasing
uint16_t	millisecsEnvelope[2] = {0, 0};	//Timer for the envelope incremented in ms

//Display stuff
uint8_t			millisecsFPS=0;						//Timer for 20FPS
bool			doRefresh = true;					//One frame refreshes after a button press and during the next silence
uint8_t			itemSelected[7]={0,0,0,0,0,0,0};	//Keeps track of selections across pages
const uint8_t   pageItems[7]={2,3,4,4,1,2,0};		//Number of page items is +1 of the value of this variable. pageItems[0] has a value of 1 - therefore page 0 has 2 items
uint8_t			page=0;								//Current page

bool EEPROMsaved = false;	//Turns true after save

inline void setupSerial() //serial for buade rate 9600 (UART)
{
	UBRR0H = (BRC >> 8);
	UBRR0L =  BRC;
	UCSR0B = (1 << TXEN0);
	UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

inline void setupPINS() //Sets DDRs and PORT registers
{
	DDRB  = 0b00101111;
	PORTB = 0b11010000;

	DDRC  = 0b00000000;
	PORTC = 0b00011111;

	DDRD  = 0b01000000;
	PORTD = 0b00011101;
}

inline void setupTIMERS() //Sets up TIMER0 overflow interrupt and PWM
{
	OCR0A  = 0;
	TIMSK0 = (1 << TOIE0);   //overflow interrupt - timer0
	TCCR0A = (1 << COM0A1) | (1 << WGM00);   //PWM and timer modes - timer0

	TIMSK1 = (0 << TOIE1);   //overflow interrupt - timer1
	TCCR1A = (0 << WGM10);   //timer mode - timer1

	TCNT0 = 0x00;
	TCNT1 = 0xff;
	TCCR0B = (1 << CS00);   //clock source = CLK, start PWM - timer0
	TCCR1B = (0 << CS10);   //clock source = CLK, start PWM - timer1
}

inline void setupADC() //Sets up ADC for and conversion complete interrupt. Then the first conversion starts. In the interrupt the ADC output is read and a new conversion is started
{
	ADMUX = (1 << REFS0) | (1 << MUX0) | (1 << MUX2);
	ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);
	DIDR0 = (1 << ADC5D);
}

inline void startConversion() //Sets the bit that starts a new ADC conversion
{
	ADCSRA |= (1 << ADSC);
}

void uint8toSerial(uint8_t gg) //Sends a uint8 to serial
{
	static uint8_t level = 0;
	if (millisecsSerial > 20)
	{
		millisecsSerial = 0;
		switch (level)
		{
			case 0:
			UDR0 = gg/100+48;
			break;

			case 1:
			UDR0 = (gg%100)/10+48;
			break;

			case 2:
			UDR0 = gg%10+48;
			break;

			case 3:
			UDR0 = '\n';
			break;
		}
		level > 3? level = 0 : ++level;
	}
}

inline void waitForSample() //Pauses execution until next sample output
{
	wait = 1;
	while (wait);
}

void readEEPROMsettings() //Reads all settings from EEPROM and uses them
{
	eeprom_read_block((void*)waveKind, (const void*)20, 10);
	eeprom_read_block((void*)oscVolume, (const void*)30, 10);
	eeprom_read_block((void*)key, (const void*)40, 10);
	eeprom_read_block((void*)fine, (const void*)50, 10);
	eeprom_read_block((void*)phase0, (const void*)60, 10);

	eeprom_read_block((void*)attack, (const void*)70, 10);
	eeprom_read_block((void*)decay, (const void*)80, 10);
	eeprom_read_block((void*)sustain, (const void*)90, 10);
	eeprom_read_block((void*)release, (const void*)100, 10);
	eeprom_read_block((void*)releaseMode, (const void*)110, 10);

	eeprom_read_block((void*)sweepDirection, (const void*)120, 10);
	eeprom_read_block((void*)sweepSpeed, (const void*)130, 10);

	eeprom_read_block((void*)vibratoWaveKind, (const void*)140, 10);
	eeprom_read_block((void*)vibratoDepth, (const void*)150, 10);
	eeprom_read_block((void*)vibratoSpeed, (const void*)160, 20);
}

void updateEEPROMsettings() //Writes all settings in use to the EEPROM
{
	eeprom_update_block((const void*)waveKind, (void*)20, 10);
	eeprom_update_block((const void*)oscVolume, (void*)30, 10);
	eeprom_update_block((const void*)key, (void*)40, 10);
	eeprom_update_block((const void*)fine, (void*)50, 10);
	eeprom_update_block((const void*)phase0, (void*)60, 10);

	eeprom_update_block((const void*)attack, (void*)70, 10);
	eeprom_update_block((const void*)decay, (void*)80, 10);
	eeprom_update_block((const void*)sustain, (void*)90, 10);
	eeprom_update_block((const void*)release, (void*)100, 10);
	eeprom_update_block((const void*)releaseMode, (void*)110, 10);

	eeprom_update_block((const void*)sweepDirection, (void*)120, 10);
	eeprom_update_block((const void*)sweepSpeed, (void*)130, 10);

	eeprom_update_block((const void*)vibratoWaveKind, (void*)140, 10);
	eeprom_update_block((const void*)vibratoDepth, (void*)150, 10);
	eeprom_update_block((const void*)vibratoSpeed, (void*)160, 20);
}

inline void incrementTimers() //Increments all timers in use and push another input in the queue for the rolling mean of the ADC
{
	waitForSample();
	uint8_t ms = millisecs;
	millisecs = 0;

	for (uint8_t i=0; i < BTNno; ++i)
	millisecsButton[i] += ms;

	millisecsAutoButton += ms;
	millisecsSerial += ms;

	millisecsFPS += ms;

	millisecsEnvelope[0] += ms;
	millisecsEnvelope[1] += ms;

	millisecsSweep[0] += ms;
	millisecsSweep[1] += ms;

	millisecsVibrato[0] += ms;
	millisecsVibrato[1] += ms;

	millisecsSerial += ms;

	ADCinputs[inputsi] = dutyCycle; //INPsize amount of inputs for the rolling mean
	inputsi > INPsize? inputsi=0 : inputsi++;
	startConversion();
}

void silenceOsc(uint8_t oscillator) //Silences one oscillator
{
	volumecalc[oscillator] = 0;
	while (volume[oscillator])
	{
		--volume[oscillator];
		_delay_us(32);
	}
}

void silenceOscs() //Silences all oscillators
{
// 	volumecalc[0] = 0;
// 	volumecalc[1] = 0;
	while (volume[0] + volume[1])
	{
		if (volume[0]>0) --volume[0];
		if (volume[1]>0) --volume[1];
		_delay_us(160);
	}
}

void play() //Starts playing a tone with the settings in use
{
	silenceOscs();
	waitForSample();
	notePlayingSum = 0;
	for (uint8_t i=0; i<2; ++i)
	{
		phaccu[i] = pphase0[i] << 8;
		vibratoPhacc[i] = 0;

		millisecsSweep[i] = 0;
		sweepStop[i] = false;
		millisecsEnvelope[i] = 0;
		volumeRelease[i] = 0;
		volume[i] = 0;
		volumecalc[i] = 0;
		notePlaying[i] = 1;
		envelopeStage[i] = 0;
		millisecs = 0;
	}
} 

void doInput() //Does all the Potentiometer inputs depending on menu item selected
{
	if (inputActive)
	{
		if (page==2)
		{
			switch (itemSelected[2])
			{
				case 0:
				if (rollingMeanADC < 200) pwaveKind[itemSelected[0]] = 0;//WAVE KIND INPUT
				else if (rollingMeanADC < 400) pwaveKind[itemSelected[0]] = 1;
				else if (rollingMeanADC < 600) pwaveKind[itemSelected[0]] = 2;
				else if (rollingMeanADC < 800) pwaveKind[itemSelected[0]] = 3;
				else pwaveKind[itemSelected[0]] = 4;
				break;

				case 1:
				poscVolume[itemSelected[0]] = rollingMeanADC >> 2;		//OSCILLATOR VOLUME INPUT
				break;

				case 2:
				pkey[itemSelected[0]] = (rollingMeanADC) * 87 / 1024;	//NOTE INPUT
				//twordcalc[0]=0x10000/refclk*( rollingMeanADC << 4 );// *16384/1024; Sweeps from 0 to 16384Hz
				break;

				case 3:
				pfine[itemSelected[0]]	= rollingMeanADC >> 2;			//FINE INPUT
				break;

				case 4:
				pphase0[itemSelected[0]] = rollingMeanADC >> 2;			//phase0 INPUT
				break;
			}
		}
		else if (page == 3)
		{
			switch (itemSelected[3])
			{
				case 0:
				(rollingMeanADC >> 2) < 1? pattack[itemSelected[0]] = 1 : pattack[itemSelected[0]] = (rollingMeanADC >> 2);		//ATTACK INPUT
				break;

				case 1:
				pdecay[itemSelected[0]] = (rollingMeanADC >> 2);			//DECAY INPUT
				if (pdecay[itemSelected[0]] < 2) pdecay[itemSelected[0]] = 2;
				break;

				case 2:
				psustain[itemSelected[0]] = (rollingMeanADC >> 2);			//SUSTAIN INPUT
				if (psustain[itemSelected[0]] < 2) psustain[itemSelected[0]] = 0;
				break;

				case 3:
				prelease[itemSelected[0]] = (rollingMeanADC >> 2);			//RELEASE INPUT
				if (prelease[itemSelected[0]] < 5) prelease[itemSelected[0]] = 5;
				break;

				case 4:
				preleaseMode[itemSelected[0]] = rollingMeanADC >> 9;		//RELEASE MODE INPUT
				break;
			}
		}
		else if (page == 4)
		{
			switch (itemSelected[4])
			{
				case 0:
				psweepDirection[itemSelected[0]] = rollingMeanADC >> 8;		//SWEEP DIRECTION INPUT
				break;

				case 1:
				psweepSpeed[itemSelected[0]] = rollingMeanADC >> 2;		//SWEEP SPEED INPUT
				break;

			}

		}
		else if (page == 5)
		{
			switch (itemSelected[5])
			{
				case 0:
				if (rollingMeanADC < 200) pvibratoWaveKind[itemSelected[0]] = 0;			//VIBRATO WAVE KIND INPUT
				else if (rollingMeanADC < 400) pvibratoWaveKind[itemSelected[0]] = 1;
				else if (rollingMeanADC < 600) pvibratoWaveKind[itemSelected[0]] = 2;
				else if (rollingMeanADC < 800) pvibratoWaveKind[itemSelected[0]] = 3;
				else pvibratoWaveKind[itemSelected[0]] = 4;
				break;

				case 1:
				pvibratoDepth[itemSelected[0]] = rollingMeanADC >> 2;		//VIBRATO DEPTH INPUT
				break;

				case 2:
				pvibratoSpeed[itemSelected[0]] = rollingMeanADC;			//VIBRATO SPEED INPUT
				break;
			}
		}
	}
} 

void doButtonPointers(const uint8_t button) //Changes pointers in use to one of the 5 sets of pointers for each "note button"
{
	pwaveKind = waveKind + button * 2;
	poscVolume = oscVolume + button * 2;
	pkey = key + button * 2;
	pfine = fine + button * 2;
	pphase0 = phase0 + button * 2;				

	pattack = attack + button * 2;
	pdecay = decay + button * 2;
	psustain = sustain + button * 2;
	prelease = release + button * 2;
	preleaseMode = releaseMode + button * 2;

	psweepDirection = sweepDirection + button * 2;
	psweepSpeed = sweepSpeed + button * 2;

	pvibratoWaveKind = vibratoWaveKind + button * 2;
	pvibratoDepth = vibratoDepth + button * 2;
	pvibratoSpeed = vibratoSpeed + button * 2;	

	pEnvButton = &buttonState[button + 4];
}

void action(uint8_t actionNum) //Every different button press action is in this function
{
	switch (actionNum)
	{
		case 0: //button up
		//UDR0 = 'U';
		doRefresh = true;
		EEPROMsaved = false;
		inputActive = false;
		if (itemSelected[page] > 0) --itemSelected[page];
		break;

		case 1: //button down
		//UDR0 = 'D';
		//EEPROMsaved = false;
		doRefresh = true;
		inputActive = false;
		if (itemSelected[page] < pageItems[page]) ++itemSelected[page];
		break;

		case 2: //button right
		//UDR0 = 'R';
		doRefresh = true;
		if (page == 0)
		{
 			if (itemSelected[0] < 2)
				page = 1;
 			else if (EEPROMsaved == false)
			{
				updateEEPROMsettings();
				EEPROMsaved = true;
			}
		}
		else if (page == 1)
		{
			switch (itemSelected[page])
			{
				case 0:
				page = 2;
				break;

				case 1:
				page = 3;
				break;

				case 2:
				page = 4;
				break;

				case 3:
				page = 5;
				break;
			}
		}
		else if (page <= 5)
		{
			inputActive ^= true;		//Toggle input!
		}
		break;

		case 3: //button left
		//UDR0 = 'L';
		doRefresh = true;
		inputActive = false;
		if (page == 1 || page == 6)
			page = 0;
		else if (page >= 2 && page <=5)
			page = 1;
		break;

		case 4: //button 0
		//UDR0 = '0';
		if (pwaveKind != waveKind)
		{
			inputActive = false;
			doButtonPointers(0);
		}
		play();
		break;

		case 5: //button 1
		//UDR0 = '1';
		if (pwaveKind != waveKind + 2)
		{
			inputActive = false;
			doButtonPointers(1);
		}
		play();
		break;

		case 6: //button 2
		//UDR0 = '2';
		if (pwaveKind != waveKind + 4)
		{
			inputActive = false;
			doButtonPointers(2);
		}
		play();
		break;

		case 7: //button 3
		//UDR0 = '3';
		if (pwaveKind != waveKind + 6)
		{
			inputActive = false;
			doButtonPointers(3);
		}
		play();
		break;

		case 8: //button 4
		//UDR0 = '4';
		if (pwaveKind != waveKind + 8)
		{
			inputActive = false;
			doButtonPointers(4);
		}
		play();
		break;
	}
} 

void buttonAction(volatile const uint8_t* PINX, const uint8_t PINXno) //Links microcontroller buttons to their actions
{
	for (uint8_t i=0; i<BTNno; ++i)
	{
		if (PINX == buttonPort[i] && PINXno == buttonPin[i])
		{
			action(i);
		}
	}
} 

void buttonCheck(bool& bState, uint8_t& timer, volatile const uint8_t* PINX, const uint8_t PINXno, const uint8_t msDebounce = 50) //Software debounced button check
{
	waitForSample();
	if (((*PINX & (1 << PINXno))) == 0)
	{
		if (!bState && timer > msDebounce)
		{
			bState = 1;
			timer= 0;

			//UDR0 = 'P';
			buttonAction(PINX, PINXno);
		}
	}
	else
	{
		if (bState && (timer > msDebounce))
		{
			bState = 0;
			timer = 0;
		}
	}
}

inline void CalcRollingMeanADC() //Calculating rolling mean for filtering(Low Pass) the potentiometer ADC input
{
	rollingMeanADC=0;
	for (uint8_t i = 0; i < INPsize; ++i)
		rollingMeanADC += ADCinputs[i];
	rollingMeanADC = rollingMeanADC >> 5;
}

int16_t fineAdj(uint8_t oscillator) //adjust the notes Fine
{
	if (pkey[oscillator] == 0)
	{
		if (pfine[oscillator] <= 120)
		return (-48 * (120 - pfine[oscillator]))/120;
		else if (pfine[oscillator] < 135)
		return 0L;
		else
		return (48 * (pfine[oscillator] - 135))/120;
	}
	else if (pkey[oscillator] < 87)
	{
		if (pfine[oscillator] <= 120)
			return -static_cast<int16_t>(pgm_read_word(&(twordNotes[pkey[oscillator]+1])) - pgm_read_word(&(twordNotes[pkey[oscillator]]))) * (120 - pfine[oscillator]) / 120;
		else if (pfine[oscillator] < 135)
			return 0L;
		else
			return (pgm_read_word(&(twordNotes[pkey[oscillator]+1])) - pgm_read_word(&(twordNotes[pkey[oscillator]]))) * (pfine[oscillator] - 135) / 120;
	}
	else
	{
		if (pfine[oscillator] <= 120)
			return (-420 * (120 - pfine[oscillator]))/120;
		else if (pfine[oscillator] < 135)
			return 0L;
		else
			return (420 * (pfine[oscillator] - 135))/120;
	}
}

uint16_t ADCtoTWORD(uint16_t ADC10bit) //Converts from ADC note to TWORD note
{
	if (ADC10bit < 93)
		return ( ADC10bit * 26 ) / 93;//
	else if (ADC10bit < 186)
		return ( ADC10bit * 52 ) / 186;
	else if (ADC10bit < 279)
		return ( (ADC10bit-93) * 104 ) / 186;
	else if (ADC10bit < 372)
		return ( (ADC10bit-186) * 207 ) / 186;
	else if (ADC10bit < 465)
		return ( (ADC10bit-279) * 414UL ) / 186;
	else if (ADC10bit < 558)
		return ( (ADC10bit-372) * 826UL ) / 186;
	else if (ADC10bit < 651)
		return ( (ADC10bit-465) * 1651UL ) / 186;
	else if (ADC10bit < 744)
		return ( (ADC10bit-558) * 3301UL ) / 186;
	else if (ADC10bit < 837)
		return ( (ADC10bit-651) * 6600UL ) / 186;
	else if (ADC10bit < 930)
		return ( (ADC10bit-744) * 13200UL ) / 186;
	else
		return ( (ADC10bit-838) * 26400UL ) / 194;
}

uint16_t TWORDtoADC(uint16_t TWORD16bit) //Converts from TWORD note to ADC note
{
	if (TWORD16bit < 26)
	return ( TWORD16bit * 93 ) / 26;
	else if (TWORD16bit < 52)
	return ( TWORD16bit * 186 ) / 52;
	else if (TWORD16bit < 104)
	return ( (TWORD16bit * 93) / 52 ) + 93;
	else if (TWORD16bit < 207)
	return ( (TWORD16bit * 93) / 104) + 186;
	else if (TWORD16bit < 414)
	return ( (TWORD16bit * 93) / 207) + 279;
	else if (TWORD16bit < 826)
	return ( (TWORD16bit * 93UL) / 414) + 372;
	else if (TWORD16bit < 1651)
	return ( (TWORD16bit * 93UL) / 826) + 465;
	else if (TWORD16bit < 3301)
	return ( (TWORD16bit * 93UL) / 1651) + 558;
	else if (TWORD16bit < 6600)
	return ( (TWORD16bit * 93UL) / 3301) + 651;
	else if (TWORD16bit < 13200)
	return ( (TWORD16bit * 93UL) / 6600) + 744;
	else
	return ( (TWORD16bit * 93UL) / 13200) + 838;
}

void doMainNote(uint8_t oscillator) //Uses key and fine settings to define the primary tuning word - this tword might be altered by sweep and vibrato effects before finally being output
{
	waitForSample();

// 	if (pnoteSource[oscillator])
// 	{
		twordcalc[oscillator] = pgm_read_word(&(twordNotes[pkey[oscillator]])) + fineAdj(oscillator);
// 	}
// 	else
// 	{
// 		twordcalc[oscillator] = ADCtoTWORD(0x10000/refclk*( pnoteADC[oscillator] << 4 )) + fineAdj(oscillator);
//
// 	}
} 

void doSweep(uint8_t oscillator) //Sweep effect
{
	waitForSample();

	if (psweepSpeed[oscillator] > 0 && !sweepStop[oscillator])
	switch (psweepDirection[oscillator])
	{
		case 0:
		if ((static_cast<uint32_t>(millisecsSweep[oscillator]) * psweepSpeed[oscillator] >> 7) > TWORDtoADC(twordcalc[oscillator]))
		{
			silenceOsc(oscillator);
			envelopeStage[oscillator] = 4;
			notePlaying[oscillator] = 0;
			sweepStop[oscillator] = true;
		}
		twordcalc[oscillator] = ADCtoTWORD( TWORDtoADC(twordcalc[oscillator]) - (static_cast<uint32_t>(millisecsSweep[oscillator]) * psweepSpeed[oscillator] >> 7) );
		break;

		case 1:
		if (TWORDtoADC(twordcalc[oscillator]) + (static_cast<uint32_t>(millisecsSweep[oscillator]) * psweepSpeed[oscillator] >> 7) > 1024)
		{
			silenceOsc(oscillator);
			envelopeStage[oscillator] = 4;
			notePlaying[oscillator] = 0;
			sweepStop[oscillator] = true;
		}
		twordcalc[oscillator] = ADCtoTWORD( TWORDtoADC(twordcalc[oscillator]) + (static_cast<uint32_t>(millisecsSweep[oscillator]) * psweepSpeed[oscillator] >> 7) );
		break;

		case 2:
		if (93 - (millisecsSweep[oscillator] * psweepSpeed[oscillator] >> 8) > TWORDtoADC(twordcalc[oscillator]))
			twordcalc[oscillator] = 0;
		else if ((millisecsSweep[oscillator] * psweepSpeed[oscillator] >> 8) < 93)
			twordcalc[oscillator] = ADCtoTWORD( TWORDtoADC(twordcalc[oscillator]) - 93 + (millisecsSweep[oscillator] * psweepSpeed[oscillator] >> 8));
		else
			sweepStop[oscillator] = true;
		break;

		case 3:
		if ((millisecsSweep[oscillator] * psweepSpeed[oscillator] >> 8) < 93)
			twordcalc[oscillator] = ADCtoTWORD( TWORDtoADC(twordcalc[oscillator]) + 93 - (millisecsSweep[oscillator] * psweepSpeed[oscillator] >> 8));
		else
			sweepStop[oscillator] = true;
		break;
	}
}

void doVibrato(uint8_t oscillator) //Vibrato effect
{
	waitForSample();

	if (pvibratoSpeed[oscillator] > 1)
	{
		uint8_t ms = millisecsVibrato[oscillator];
		millisecsVibrato[oscillator] = 0;

		if (pvibratoSpeed[oscillator] < 160)
		vibratoPhacc[oscillator] += ((pvibratoSpeed[oscillator] >> 4)) * ms;
		else if (pvibratoSpeed[oscillator] < 352)
		vibratoPhacc[oscillator] += (((pvibratoSpeed[oscillator] - 160) >> 3) + 10) * ms;
		else if (pvibratoSpeed[oscillator] < 512)
		vibratoPhacc[oscillator] += (((pvibratoSpeed[oscillator] - 352) >> 2) + 34) * ms;
		else if (pvibratoSpeed[oscillator] < 680)
		vibratoPhacc[oscillator] += (((pvibratoSpeed[oscillator] - 512) >> 1) + 74) * ms;
		else if (pvibratoSpeed[oscillator] < 850)
		vibratoPhacc[oscillator] += (((pvibratoSpeed[oscillator] - 680) << 2) + 158) * ms;
		else
		vibratoPhacc[oscillator] += (((pvibratoSpeed[oscillator] - 850) << 3) + 838) * ms;

		twordcalc[oscillator] = ADCtoTWORD( TWORDtoADC(twordcalc[oscillator]) + (((pgm_read_byte( &waveTable[ pvibratoWaveKind[oscillator] ][ vibratoPhacc[oscillator] >> 8 ] ) - 128) * pvibratoDepth[oscillator]) / 255));
	}
}

void doEnvelope(uint8_t oscillator, bool* bState) //Envelope
{
	waitForSample();

	switch (envelopeStage[oscillator])
	{
		case 0:
		if ((*bState) && ((millisecsEnvelope[oscillator] << 4) / pattack[oscillator] < 255))
		{
			volumecalc[oscillator] = (millisecsEnvelope[oscillator] << 4) / pattack[oscillator];		// millisecsEnvelope[oscillator] / (attack/16)
		}
		else if (*bState)
		{
			volumecalc[oscillator] = 255;
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		else
		{
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		break;

		case 1:
		if ((*bState) && (volumecalc[oscillator] > psustain[oscillator]) && (psustain[oscillator] > 0))
		{
			volumecalc[oscillator] = 255 - ((millisecsEnvelope[oscillator] * 16) / pdecay[oscillator]) ;	// 255 - millisecsEnvelope[oscillator] / (decay/16);
		}
		else if ((*bState) && (psustain[oscillator] > 0))
		{
			volumecalc[oscillator] = psustain[oscillator];
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		else
		{
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		break;

		case 2:
		if (!(*bState) || (psustain[oscillator] == 0))
		{
			++envelopeStage[oscillator];
			millisecsEnvelope[oscillator] = 0;
		}
		break;

		case 3:
		if (volumecalc[oscillator] <= 1)
		{
			volumecalc[oscillator] = 0;
			millisecsEnvelope[oscillator] = 0;
			++envelopeStage[oscillator];
			notePlaying[oscillator] = 0;
		}
		else if (release[oscillator] > 253)
		{
			++envelopeStage[oscillator];
			break;
		}
		else
		{
			if (!volumeRelease[oscillator]) volumeRelease[oscillator] = volumecalc[oscillator];

			if (!preleaseMode[oscillator])
				if (volumeRelease[oscillator] > ((millisecsEnvelope[oscillator] << 4) / prelease[oscillator]))
					volumecalc[oscillator] = volumeRelease[oscillator] - ((millisecsEnvelope[oscillator] << 4) / prelease[oscillator]); //Linear release
				else volumecalc[oscillator] = 0;
			else
				volumecalc[oscillator] = (volumeRelease[oscillator] * prelease[oscillator]) / ((millisecsEnvelope[oscillator] >> 1) + prelease[oscillator]) - (millisecsEnvelope[oscillator]/1000); //Non linear release
		}
		break;
	}
}

inline void updateNotes() //Updates note in use for the sampler
{
	waitForSample();

	tword[0] = twordcalc[0];
	volume[0] = volumecalc[0] * poscVolume[0] / 510UL;
	tword[1] = twordcalc[1];
	volume[1] = volumecalc[1] * poscVolume[1] / 510UL;

	notePlayingSum = 0;
	notePlayingSum += notePlaying[0];
	notePlayingSum += notePlaying[1];
}

bool BitmapXYaccess(const uint8_t* bitmap, const uint8_t& bwidth, const uint8_t& x = 0, const uint8_t& y = 0) //Returns the 0 or 1 of the bitmap on its x,y position
{
	return (pgm_read_byte( &bitmap[x + (y >> 3) * bwidth] ) >> (y % 8)) & 0x01;
}

void nokia_lcd_write_bitmap(const uint8_t* bitmap, const uint8_t& bwidth, const uint8_t& bheight, const uint8_t& x = 0, const uint8_t& y = 0) //Writes a bitmap of dimensions bwidth,bheight on the display, starting at x,y
{
	for (uint8_t i = 0; i < bwidth; ++i)
	{
		for (uint8_t j = 0; j < bheight; ++j)
		{
			nokia_lcd_set_pixel(i+x,j+y, BitmapXYaccess(bitmap, bwidth, i, j) );
		}
	}
}

uint16_t _map(uint16_t X, uint16_t A, uint16_t B, uint16_t C, uint16_t D) //Standard mapping function
{
	uint16_t Y = 0;
	if (X < A)
		C < D? Y = C : Y = D;
	else if (X > B)
		C < D? Y = D : Y = C;
	else
		Y = (X-A)*(D-C)/(B-A) + C;
	return Y;
}

inline void introDisplay() //Does the intro of the display after power on
{
	nokia_lcd_clear();
	nokia_lcd_write_bitmap(wf_Bitmap, wf_Width, wf_Height);
	nokia_lcd_render();
	_delay_ms(3000);

	nokia_lcd_clear();
	nokia_lcd_set_cursor(0, 0);
	nokia_lcd_write_string("ZNEAR", 3);
	nokia_lcd_set_cursor(5, 23);
	nokia_lcd_write_string("Synth", 3);
	nokia_lcd_render();
	_delay_ms(3000);
}

void drawCursor(uint8_t xpos, uint8_t fontSize) //Draws the cursor on the display
{
	nokia_lcd_set_cursor(xpos, itemSelected[page] * (8 * fontSize));
	if (inputActive) nokia_lcd_write_char(0x80 , fontSize); //20- ,80-?, 81-? void
	else nokia_lcd_write_char(0x81 , fontSize); //20- ,80-?, 81-? void
}

void drawLegend() //Draws the oscillator legend
{
	nokia_lcd_write_bitmap(leg_Bitmap, leg_Width, leg_Height);
	if (itemSelected[0] == 0)
		nokia_lcd_write_bitmap(leg1_Bitmap, leg1_Width, leg1_Height, 0, 38);
	else
		nokia_lcd_write_bitmap(leg2_Bitmap, leg2_Width, leg2_Height, 0, 38);
}

void drawNote(const uint8_t& oscillator) //draws the note in use in the NTE field
{
	if (pkey[oscillator] < 3)
		nokia_lcd_write_char('0', 1);
	else
		nokia_lcd_write_char(((pkey[oscillator] - 3)/12 + 1)+48, 1);

	switch (pkey[oscillator]%12)
	{
		case 0:
		nokia_lcd_write_string("A",1);
		break;

		case 1:
		nokia_lcd_write_string("A#",1);
		break;

		case 2:
		nokia_lcd_write_string("B",1);
		break;

		case 3:
		nokia_lcd_write_string("C",1);
		break;

		case 4:
		nokia_lcd_write_string("C#",1);
		break;

		case 5:
		nokia_lcd_write_string("D",1);
		break;

		case 6:
		nokia_lcd_write_string("D#",1);
		break;

		case 7:
		nokia_lcd_write_string("E",1);
		break;

		case 8:
		nokia_lcd_write_string("F",1);
		break;

		case 9:
		nokia_lcd_write_string("F#",1);
		break;

		case 10:
		nokia_lcd_write_string("G",1);
		break;

		case 11:
		nokia_lcd_write_string("G#",1);
		break;

	}

}

void draw8bitBar(const uint8_t& oscillator, const uint8_t* target) //Draws a bar for certain setting on the menu
{
	nokia_lcd_write_char(0x84, 1);
	for (uint8_t i=0; i<6; ++i)
	{
		if (target[oscillator] > i*42+21)
			nokia_lcd_write_char(0x83, 1);
		else
			nokia_lcd_write_char(0x82, 1);
	}
	nokia_lcd_write_char(0x85, 1);
}

inline void drawRmo(const uint8_t& oscillator) //Draws the release mode used
{
	if (preleaseMode[oscillator])
		nokia_lcd_write_string("NON-LIN", 1);
	else
		nokia_lcd_write_string("LINEAR", 1);
}

inline void drawSweepDir(const uint8_t& oscillator) //Draws the sweep direction used
{
	switch (psweepDirection[oscillator])
	{
		case 0:
		nokia_lcd_write_string("<--", 1);
		nokia_lcd_write_char(0x86, 1);
		break;

		case 1:
		nokia_lcd_write_char(0x86, 1);
		nokia_lcd_write_string("-->", 1);
		break;

		case 2:
		nokia_lcd_write_string("-->", 1);
		nokia_lcd_write_char(0x86, 1);
		break;

		case 3:
		nokia_lcd_write_char(0x86, 1);
		nokia_lcd_write_string("<--", 1);
		break;
	}
}

void drawWaveKind(const uint8_t& oscillator, const uint8_t* target) //Draws the wave kind used
{
	switch (target[oscillator])
	{
		case 0:
		nokia_lcd_write_char(' ', 1);
		nokia_lcd_write_bitmap(rand_Bitmap, wave_Width, wave_Height, 42, 0);
		break;

		case 1:
		nokia_lcd_write_char(' ', 1);
		nokia_lcd_write_bitmap(square_Bitmap, wave_Width, wave_Height, 42, 0);
		break;

		case 2:
		nokia_lcd_write_char(' ', 1);
		nokia_lcd_write_bitmap(saw_Bitmap, wave_Width, wave_Height, 42, 0);
		break;

		case 3:
		nokia_lcd_write_char(' ', 1);
		nokia_lcd_write_bitmap(triangle_Bitmap, wave_Width, wave_Height, 42, 0);
		break;

		case 4:
		nokia_lcd_write_char(' ', 1);
		nokia_lcd_write_bitmap(sin_Bitmap, wave_Width, wave_Height, 42, 0);
		break;
	}
}

inline void drawFine(const uint8_t& oscillator) //Draws the fine adjustment used in the FIN field
{
	nokia_lcd_write_char(' ', 1);
	for (uint8_t i=0; i<6; ++i)
	{
		if (i<3)
		{
			if (i==2 && pfine[oscillator] <= 120)
				nokia_lcd_write_char(0x83, 1);
			else if (pfine[oscillator] < i*42+21)
				nokia_lcd_write_char(0x83, 1);
			else
				nokia_lcd_write_char(0x82, 1);
		}
		else
		{
			if (i==3 && pfine[oscillator] >= 135)
				nokia_lcd_write_char(0x83, 1);
			else if (pfine[oscillator] > i*42+21)
				nokia_lcd_write_char(0x83, 1);
			else
				nokia_lcd_write_char(0x82, 1);
		}
	}
	for (uint8_t i=0; i<7; ++i)
		nokia_lcd_set_pixel(59, 24 + i, 1);
}

void drawPhase(const uint8_t& oscillator) //Draws the initial phase of the wave that is used in the field PHA
{
	uint16_t temp = pphase0[oscillator] * 360UL / 255;
	nokia_lcd_write_char(temp/100+48, 1);
	temp %= 100;
	nokia_lcd_write_char(temp/10+48, 1);
	temp %= 10;
	nokia_lcd_write_char(temp+48, 1);
} 

#define MENU_X 18
void doDisplay() //Draws on the display depending on what page it is on
{
	waitForSample();

	nokia_lcd_clear();

	switch (page)
	{
		case 0:
		nokia_lcd_set_cursor(12, 0);
		nokia_lcd_write_string("OSC1", 2);
		nokia_lcd_set_cursor(12, 16);
		nokia_lcd_write_string("OSC2", 2);
		nokia_lcd_set_cursor(12, 32);
		if (EEPROMsaved)
			nokia_lcd_write_string("OK", 2);
		else
			nokia_lcd_write_string("SAVE", 2);

 		drawCursor(1, 2);
		break;
		//////////////////////////////////////////////////////////////////////////
		case 1:
 		drawLegend();

		nokia_lcd_set_cursor(MENU_X,0);
		nokia_lcd_write_string("NOTE", 1);

		nokia_lcd_set_cursor(MENU_X,8);
		nokia_lcd_write_string("ENVELOPE", 1);

		nokia_lcd_set_cursor(MENU_X,16);
		nokia_lcd_write_string("SWEEP", 1);

		nokia_lcd_set_cursor(MENU_X,24);
		nokia_lcd_write_string("VIBRATO", 1);

 		drawCursor(13, 1);
		break;
		//////////////////////////////////////////////////////////////////////////
		case 2:
		drawLegend();

		nokia_lcd_set_cursor(MENU_X,0);
		nokia_lcd_write_string("WAV", 1);
		drawWaveKind(itemSelected[0], pwaveKind);

		nokia_lcd_set_cursor(MENU_X,8);
		nokia_lcd_write_string("VOL", 1);
		draw8bitBar(itemSelected[0], poscVolume);

		nokia_lcd_set_cursor(MENU_X,16);
		nokia_lcd_write_string("NTE ", 1);
		drawNote(itemSelected[0]);

		nokia_lcd_set_cursor(MENU_X,24);
		nokia_lcd_write_string("FIN", 1);
		drawFine(itemSelected[0]);

		nokia_lcd_set_cursor(MENU_X,32);
		nokia_lcd_write_string("PHA ", 1);
		drawPhase(itemSelected[0]);
		nokia_lcd_write_string("deg", 1);

 		drawCursor(13, 1);
		break;
		//////////////////////////////////////////////////////////////////////////
		case 3:
		drawLegend();

		nokia_lcd_set_cursor(MENU_X,0);
		nokia_lcd_write_string("ATK", 1);
		draw8bitBar(itemSelected[0], pattack);

		nokia_lcd_set_cursor(MENU_X,8);
		nokia_lcd_write_string("DEC", 1);
		draw8bitBar(itemSelected[0], pdecay);

		nokia_lcd_set_cursor(MENU_X,16);
		nokia_lcd_write_string("SUS", 1);
		draw8bitBar(itemSelected[0], psustain);

		nokia_lcd_set_cursor(MENU_X,24);
		nokia_lcd_write_string("REL", 1);
		draw8bitBar(itemSelected[0], prelease);

		nokia_lcd_set_cursor(MENU_X,32);
		nokia_lcd_write_string("Rmo", 1);
		nokia_lcd_write_char(' ', 1);
		drawRmo(itemSelected[0]);

 		drawCursor(13, 1);
		break;
		//////////////////////////////////////////////////////////////////////////
		case 4:
		drawLegend();
		nokia_lcd_set_cursor(MENU_X,0);
		nokia_lcd_write_string("DIR  ", 1);
		drawSweepDir(itemSelected[0]);

		nokia_lcd_set_cursor(MENU_X,8);
		nokia_lcd_write_string("SPE", 1);
		draw8bitBar(itemSelected[0], psweepSpeed);

 		drawCursor(13, 1);
		break;
		//////////////////////////////////////////////////////////////////////////
		case 5:
		drawLegend();
		nokia_lcd_set_cursor(MENU_X,0);
		nokia_lcd_write_string("WAV", 1);
		drawWaveKind(itemSelected[0], pvibratoWaveKind);

		nokia_lcd_set_cursor(MENU_X,8);
		nokia_lcd_write_string("DEP", 1);
		draw8bitBar(itemSelected[0], pvibratoDepth);

		nokia_lcd_set_cursor(MENU_X,16);
		nokia_lcd_write_string("SPE", 1);
		uint8_t temp = (pvibratoSpeed[itemSelected[0]]) >> 2;
		draw8bitBar(itemSelected[0], &temp-itemSelected[0]);

 		drawCursor(13, 1);
		break;
		//////////////////////////////////////////////////////////////////////////
// 		case 6:
// 		break;

	}
nokia_lcd_render();
}

int main(void)
{
 	setupSerial();

 	setupPINS();

	setupADC();
	startConversion();

	setupTIMERS();

	nokia_lcd_init();
	introDisplay();

	readEEPROMsettings();

	sei();

	doDisplay();

	while(1)
    {
		incrementTimers();

		if ((millisecsFPS > 200 && (inputActive || (!notePlayingSum))) || doRefresh)
		{
			millisecsFPS = 0;
 			doDisplay();
			doRefresh = false;
		}

		for (uint8_t i=0; i<BTNno; ++i)
		{
			buttonCheck(buttonState[i], millisecsButton[i], buttonPort[i], buttonPin[i]);
		}

		CalcRollingMeanADC();
		//ADCtoSerial();      //For Debugging purposes

		doInput();

		doMainNote(0);
		doMainNote(1);

		doEnvelope(0, pEnvButton);
		doEnvelope(1, pEnvButton);

		doSweep(0);
		doSweep(1);

		doVibrato(0);
		doVibrato(1);

		updateNotes();

		//uint8toSerial(notePlaying[0]);  //Debugging purpose
	}
}

ISR (TIMER0_OVF_vect) //This is the interrupt routine for the DDS and after calculations it occupies almost 1/3 of the total CPU time
{
  	if (notePlayingSum)
	{
 		phaccu[0] += tword[0];
 		osc[0] = (pgm_read_byte( &waveTable[ pwaveKind[0] ][ phaccu[0] >> 8 ]));

		phaccu[1] += tword[1];
 		osc[1] = (pgm_read_byte( &waveTable[ pwaveKind[1] ][ phaccu[1] >> 8 ]));

		OCR0A = (( osc[0] * volume[0] ) >> 8) + (( osc[1] * volume[1] ) >> 8);
	}

	smallTimer++; //increment small timer
	if(smallTimer > 40)
	{
		smallTimer = 0;
		++millisecs; //increment milliseconds every 40 small timer ticks
	}
	wait = 0;
}

ISR(ADC_vect) //Interrupt subroutine for extracting the ADC converted value every time the conversion is completed
{
	dutyCycle = ADC;
}

 

Photo of synth:

 

 

My first composition using it:

https://soundcloud.com/ilias-cha...

(although it sounds as though the synth has a polyphony >2 in this recording, it is only because I overlaped some samples from the synth in my DAW)

 

Sorry for no schematic I never drew one. If someone is curious I could try drawing one out

 

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

Last Edited: Wed. Dec 16, 2020 - 03:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nice use of comments ;-)

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

I would stay away from any other than timer ISR, and pull the adc in that ISR.

for sound small jitter is ok, but you can hear small clicks. 

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

Yeah I had some thoughts about that ADC ISR too. Maybe I'll try putting it in the main, timer-incrementation section.

Are you referring to the clicks the bells make at their start?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

sorry for nor being clear:

clicks would be that the DDS stop when in the other (ADC) ISR.

 

But it's a very short (read fast) ISR so perhaps it's ok.

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

Though little used these days it seems, there is a projects section where you can post your completed projects for others to view and learn from on this site.

Congratulations on a job well done!

 

Jim

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

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

sparrow2 wrote:
clicks would be that the DDS stop when in the other (ADC) ISR.

 

But it's a very short (read fast) ISR so perhaps it's ok.

The pin output is done by the hardware on a compare match.

The ADC is not going to interfere with DDS.

More likely the timer interrupt will delay reading ADC.

Not sure whether it matters.

Moderation in all things. -- ancient proverb

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

???

the ADC interrupt will delay the timer interrupt => delayed DDS update 

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

Here is how I understand it (correct me if I am wrong)
If the execution is inside the ADC ISR when the TOVF flag is set the TOVF ISR could be delayed
but if the execution is in main when somehow both ISR flags are set, the TOVF ISR is prioritized
It would probbaly be better to get rid of the ADC ISR then.. I could try reading it every 1ms

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

sparrow2 wrote:
the ADC interrupt will delay the timer interrupt => delayed DDS update
The timer interrupt ISR does not update the pin.

That is done by a compare match the hardware when the overflow occurs, regardless of when the ISR is taken.

There is wiggle room.

If it's more than 30 cycles, the ADC interrupt will never interfere with DDS updates.

Such wiggle room is one reason to use compare

match the hardware instead of directly modifying a pin.

Moderation in all things. -- ancient proverb

Last Edited: Wed. Dec 16, 2020 - 10:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am actually using a timer overflow interrupt and not a compare match isr. In my program the timer 0 overflow isr is used to update the pin OCR0A. Could you explain what you mean more thoroughly?

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

First I guess that it's as good it gets without using a DAC.

I just fear that the time it takes to make both ISR's can be to long, and the ADC ISR don't add anything, just place it in the other ISR down in the smalltimer part.

 

a bigger click problem could be that 

tword[0] = twordcalc[0];

 

needs to be atomic, (I'm not 100% sure of the flow but guess that tword get updated while playing).

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

Yep it does! Could this simple assignment be stopped mid execution by an interrupt? That would definitely be a no no

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!

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

tellSlater wrote:
I am actually using a timer overflow interrupt and not a compare match isr. In my program the timer 0 overflow isr is used to update the pin OCR0A. Could you explain what you mean more thoroughly?
Oops.

The principle still holds: the hardware flips the bit or not regardless of when the ISR is taken.

Moderation in all things. -- ancient proverb

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

it's a 8 bit micro, so 16 bit calculation values (at least in C) has to be atomic if used in both ISR and main, else you can be out of sync of high and low byte.

 

I believe it's still in  <util/atomic.h>

 

 

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

So I just simulated this segment

 

inline void updateNotes() //Updates note in use for the sampler
{
	waitForSample();
	tword[0] = twordcalc[0];
	volume[0] = volumecalc[0] * poscVolume[0] / 510UL;
	
	tword[1] = twordcalc[1];
	volume[1] = volumecalc[1] * poscVolume[1] / 510UL;
	
	notePlayingSum = notePlaying[0] + notePlaying[1];
}

 

I am not using atomic but in stead I use this waitforsample function that makes sure the execution will continue only after a return from the timer overflow ISR.

After the return from the ISR there are about 300 clock cycles until the next timer overflow which is enough for all 5 lines of code to be executed.

I just checked it out using the simulator. It is fine

TO THE FINDER... THE ISLE OF KOHOLINT, IS BUT AN ILLUSION... HUMAN, MONSTER, SEA, SKY... A SCENE ON THE LID OF A SLEEPER'S EYE... AWAKE THE DREAMER, AND KOHOLINT WILL VANISH MUCH LIKE A BUBBLE ON A NEEDLE... CAST-AWAY, YOU SHOULD KNOW THE TRUTH!