Attiny 2313 Interrupt for use with delays

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

Hello,

I'm trying to make a simple bluetooth controlled RGB LED strip controller with Attiny2313. I have already set up 3 PWM's and the UART infrastructure (RX only). Everything is working fine: I'm able to connect to the HC-05 module via bluetooth and send the signal to the chip which calls in the Interrupt, changes certain value and in turn activates the LED's. The problem I'm facing is, when I have a long animation in the main() body (with dalays) and call in the interrupt, after it receives the value and gets back to the main() body, it CONTINUES to display the same animation UNTIL it ends. Then it cycles back to the beggining to check for the mentioned value, and ONLY then changes the animation.

 

I understand the problem behind the code (IF() has to be completed, before I check for the value change), but I am unable to come up with a solution to IMMEDIATELY change the animation when the Interrupt is called. (I'm not sure that is possible at all, maby I just need to re-design my code)

 

I have tried:

  • Putting the animation into the Interrupt itself, but with more than a couple animations it just retains the problem.
  • Looking into setjmp/longjmp used from within the ISR to jump stright to the While(1) beggining, but that seems to be bad programming practise

 

I have tried searching through forums, but all of the examples just seem to use this basic structure of setting a volatile flag in the ISR and using it in the main() block, which doesn't work when you need to bypass delays.

I would like to point out that I'm a begginer, simplified answers would be appreciated :)

 

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define BAUD 9600UL

int main(void)
{
        DDRB = 0xFF;
        PORTB = 0x00;

	unsigned long ubrr = (F_CPU / (16 * BAUD)) - 1;
	UBRRH = (unsigned char)(ubrr>>8);
	UBRRL = (unsigned char)ubrr;
	UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE);
	UCSRC = (1 << UCSZ1) | (1 << UCSZ0);
	sei();

	// init timers as fast PWM
	TCCR0A = (1 << WGM00) | (1 << WGM01);
	TCCR1A = (1 << WGM10) | (1 << WGM12);
	// set prescaler to 1
	TCCR0B |= (1 << CS00);
	TCCR1B |= (1 << CS00);
	// set outputs to PWM
	TCCR0A |= (1 << COM0A1);
	TCCR1A |= (1 << COM1A1);
	TCCR1A |= (1 << COM1B1);

    while(1)
    {
		if(UDR == '1')
		{
		    OCR0A = 0;
		    OCR1A = 0;
		    OCR1B = 0;
		    _delay_ms(1000);
		    OCR0A = 255;
		    OCR1A = 0;
		    OCR1B = 0;
		    _delay_ms(1000);
		    OCR0A = 0;
		    OCR1A = 255;
		    OCR1B = 0;
		    _delay_ms(1000);
		    OCR0A = 0;
		    OCR1A = 0;
		    OCR1B = 255;
		    _delay_ms(1000);
		    OCR0A = 255;
		    OCR1A = 0;
		    OCR1B = 255;
		    _delay_ms(1000);
		    OCR0A = 255;
		    OCR1A = 255;
		    OCR1B = 0;
		    _delay_ms(1000);
		    OCR0A = 0;
		    OCR1A = 255;
		    OCR1B = 255;
		    _delay_ms(1000);
		    OCR0A = 255;
		    OCR1A = 255;
		    OCR1B = 255;
		    _delay_ms(1000);
		}
		else
		{
                    OCR0A = 0;
                    OCR1A = 0;
                    OCR1B = 0;
                    _delay_ms(1000)
                    OCR0A = 255;
                    OCR1A = 255;
                    OCR1B = 255;
                    _delay_ms(1000)
		}
    }
}
ISR(USART_RX_vect) {
	char data = UDR;
	UDR = data;
}
Last Edited: Fri. Jan 13, 2017 - 10:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What I think you need to do is modify _delay_ms() so that it also detects the interrupt flag, and immediately bails out. 

 

This will, of course, corrupt their timing - _delay_ms(1000) won't be one second anymore - but unless that particular function has a 'cancel delay' (If you can find out where it puts its variables in the register file, you could zero (probably actually want to set to one, lest they decrement and then overflow the actual delay counting registers) you will need to modify the subroutine.

 

Does calling _delay_ms(0) reset them?  For that, do so in the interrupt.

 

PS - Yes, a longjmp out of a subroutine will smash your stack.  Don't do that.  S.

 

edited for missing subordinate clause  S.

 

Last Edited: Fri. Jan 13, 2017 - 10:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Scroungre wrote:

What I think you need to do is modify _delay_ms() so that it also detects the interrupt flag, and immediately bails out. 

 

This will, of course, corrupt their timing - _delay_ms(1000) won't be one second anymore - but unless that particular function has a 'cancel delay' (If you can find out where it puts its variables in the register file, you could zero (probably actually want to set to one, lest they decrement and then overflow) the actual delay counting registers.

 

Does calling _delay_ms(0) reset them?  For that, do so in the interrupt.

 

PS - Yes, a longjmp out of a subroutine will smash your stack.  Don't do that.  S.

 

 

Thanks for your advice, but I thing changing built-in functions is a bit too hard for me, I could really use some simple code examples

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

Well, that is the thing with interrupts.  When they fire, they then go back to doing what they did before.  If what they were doing before was in the middle of a long delay, the long delay will continue - And if they' re in the middle of a long program, that too will continue until you test the flag again.

 

I very much like the idea of having the interrupt set a flag and return as quickly as possible.  But you could add tests for that flag throughout your code, and then you'd only have to wait for one _delay_ms() to expire.  If you don't want to wait (up to a second) instead of calling _delay_ms(1000) call _delay_ms(100) ten times in a row, and test for your interrupt flag every time.

 

From there you can break; from the loop and do what you wanted as a new thing.

 

S.

 

PS - Basically, if you want it to react to the interrupt flag in the middle of the program, you have to keep testing for it.  Edited to add postscript  S.

 

Last Edited: Fri. Jan 13, 2017 - 11:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Scroungre wrote:

Well, that is the thing with interrupts.  When they fire, they then go back to doing what they did before.  If what they were doing before was in the middle of a long delay, the long delay will continue - And if they' re in the middle of a long program, that too will continue until you test the flag again.

 

I very much like the idea of having the interrupt set a flag and return as quickly as possible.  But you could add tests for that flag throughout your code, and then you'd only have to wait for one _delay_ms() to expire.  If you don't want to wait (up to a second) instead of calling _delay_ms(1000) call _delay_ms(100) ten times in a row, and test for your interrupt flag every time.

 

From there you can break; from the loop and do what you wanted as a new thing.

 

S.

 

PS - Basically, if you want it to react to the interrupt flag in the middle of the program, you have to keep testing for it.  Edited to add postscript  S.

 

I also thought of that, but that would mean adding a lot of

if(UDR==certain_value)
    break;

which I believe would increase my code size by quite a bit. Knowing that the chip only has 2K flash, I was hoping to optimize the code as much as possible for more animations. A lot of factory made products (cristmas lights) have this feature of instantly quitting the animation when the button is pressed, isn't there another way around this?

Last Edited: Fri. Jan 13, 2017 - 11:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Rather than use long delays, use short delays, say 10ms and count the required time from this. eg: 1 second is 100 times 10ms. So every 10ms you can test for a change and jump to the new sequence. You could also do things like have your sequences in tables eg:

R

G

B

delay in 10ms ticks

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

Maybe.  The other choice is add one if(UDR_flag==certain_value) break; to _delay_ms().  But you told me you didn't want to do that.  You could write your own delay routine with an interrupt flag bailout.  Might be educational, that exercise (left to the student.  I am not writing your code for you  smiley).

 

The gurus around here (I am NOT one of them) know I am a big fan of writing the whole thing out in assembler code, and that would be my next suggestion, but they might have ideas later in the day.  It's an odd hour for most of us, and I just happened to be up and poking around.

 

Still, the 'while' thing and the 'if' thing only ever test at the outset of the {} loop, and if you want to get out of it before it finishes, you have to test inside too.

 

As an aside, note that half a second delay is fast enough that few would care, and a tenth of a second delay is fast enough that very few would notice.  As another aside, they don't change the code by much.  Each 'if' should compile to a 'brne' instruction, and that's smaller than your reassigning variables to the same value all the time (which the compiler might optimize out.  You should comment them out).

 

Finally, two more things:  Put your animations into a lookup table, and then you only have to write the routine once.  Second, consider a dinky little serial EEprom if you want lots of animations - They can be had for pennies and are huge.

 

Still, have fun,

 

Scroungre Out.

 

 

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

You are running timers so arrange for them to have either compare or overflow interrupts. In that ISR increment some kind of "tick" variable. For the delays in main() do something like:

current = ISRtick;
while (ISRtick < (current + 100)) {
    // twiddle thumbs
}

Obviously the +100 in there will depend on what the ISR period is but the point is you wait until the ticker has moved on by N where N represents some delaying length of time. Now you are doing your delays like this instead of _delay_ms() you add code like:

current = ISRtick;
while (ISRtick < (current + 100)) {
    if (UART_says_anim_changed) break;
}

So now, even while you are waiting for the count to increase if the signal from the UART interrupt says the user just asked for a change you immediately break out of this waiting loop.

 

There is a bit of a complication to this called atomicity. The fact that ISRtick or whatever you are going to call it is unlikely to be just an int8/uint8. It might be 16 or 32 bits wide. So at the point you come to read it, while the 2 or 4 bytes of the counter are being accessed the timer ISR itself might occur and increment some parts of it. So you get a bit of old and a bit of new in the reading. So you need to protect your access to the wide counter so that no interrupt can occur while it is being read. There is a header file called atomic.h to help with this.

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

That's where the register values are.  S.

 

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

Interesting ideas! I'll definetely put my RGB variables into a lookup table. I really like your idea about a makeshift delay_ms() routine. Unluckily I can't really test the code a this moment, but I would assume it would be something along these lines? 

void delay_ms(uint16_t x)
{
	uint8_t y, z;
	for ( ; x > 0 ; x--){
		for ( y = 0 ; y < 90 ; y++){
			for ( z = 0 ; z < 6 ; z++){
				asm volatile ("nop");
					if(UDR==certain_value) break;
			}
		}
	}
}

 

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

Yeah, something like that.

 

One more suggestion:  Put your delay values into the lookup table also.  Costs you one (or two) byte(s) more per display, but gives you so much more flexibility for turning it into a disco (which I guess was the idea?  smiley).  S. out for now, really, honest. 

 

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

Snovah wrote:
something along these lines?
Not quite "break;" just breaks from one loop. You have three so it only breaks you from the z loop to the y loop. Also for() loop delays are very difficult to calculate/calibrate and if you ever change the F_CPU it all goes wonky. I still prefer the idea in #8 ;-)

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

Bad idea. Either use a timer tick interrupt or a shorter delay_ms(). You really are making it harder for yourself.

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

clawson wrote:

Snovah wrote:
something along these lines?
Not quite "break;" just breaks from one loop. You have three so it only breaks you from the z loop to the y loop. Also for() loop delays are very difficult to calculate/calibrate and if you ever change the F_CPU it all goes wonky. I still prefer the idea in #8 ;-)

 

Yeah, I had some unpleasant experience with these for() loop delays, but since exact timing is not an issue for me and clock is always the same I think I'll give it a shot. Unfortunately I'm not that familiar with all these ISR fiddles, this looks like some advanced stuff :). I'll try to experiment some more. Thanks for the help, you guys saved me tons of back and forth coding :)!

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

Kartman wrote:
or a shorter delay_ms().
As he says:

		    _delay_ms(1000);

just becomes something like:

for (int i = 0; i < 1000; i++) {
    _delay_ms(1);
    if (UART_event) break;
}

for the simplest solution to this. A user is unlikely to spot a 1ms delay in the thing responding!

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

clawson wrote:

Kartman wrote:
or a shorter delay_ms().
As he says:

		    _delay_ms(1000);

just becomes something like:

for (int i = 0; i < 1000; i++) {
    _delay_ms(1);
    if (UART_event) break;
}

for the simplest solution to this. A user is unlikely to spot a 1ms delay in the thing responding!

 

I see, I appreciate the simplified explanation. This is much better and more convienient. I'm sure 50ms or even 100ms is going to be fine by the way, no need for 1ms

Last Edited: Fri. Jan 13, 2017 - 12:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

just becomes something like:

for (int i = 0; i < 1000; i++) {
    _delay_ms(1);
    if (UART_event) break;
}

for the simplest solution to this. A user is unlikely to spot a 1ms delay in the thing responding!

I believe I pointed out that fartin' around that way would cause slight but irrelevant timing errors.

 

Stupid question:  Which leads to a smaller executable?   They mentioned maintaining maximum space for animations.

 

S.

 

Seriously guys, the last time I changed the fClk on a design after it was mostly built it was made of TTL and I wanted to see how fast it would go.  Since then, and I appreciate the configurability, I have never changed the base clock of any design that I have ever made.  The very idea is asinine.  I just don't do that.  And neither should you.  I appreciate the configurablility in the modules, but are you seriously trying to overclock your AVRs or something?  I have never found any reason to change the fundamental fClk in any embedded design after the very initial pieces of the design phase.  If you have, let me know, and why.  You nitwits.  S.

 

 

Last Edited: Fri. Jan 13, 2017 - 03:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Alright I got it working, but there seems to be a problem with holding the UART value. Animation goes through ONE cycle only, then doesnt enter the IF() anymore.

while(1)
{		
	if(UDR == '0')
	{
		OCR0A = 255;
		OCR1A = 0;
		OCR1B = 0;
		_delay_ms(1000);
	}
	if(UDR == '1')
	{
		OCR0A = 0;
		OCR1A = 255;
		OCR1B = 0;
		_delay_ms(1000);
	}
	if(UDR == '2')
	{
		OCR0A = 0;
		OCR1A = 0;
		OCR1B = 255;
		_delay_ms(1000);
	}
	OCR0A = 0;
	OCR1A = 0;
	OCR1B = 0;
}
ISR(USART_RX_vect) {
	char data = UDR;
	UDR = data;
}

 

This seems to suggest, that once I send in the byte via bluetooth the condition: 

if(UDR == '0')

triggers, but immediately after that the UDR value changes to something else, and it just displays nothing since I have 

OCR0A = 0;
OCR1A = 0;
OCR1B = 0;

Is the ISR supposed to be reading the values from bluetooth module even when I'm not sending anything? (giberrish signal), if so how can I only read the value when I send an actual signal through to the HC-05 module.

I am running on internal clock source and simply have connected the corresponding RX with TX and vice versa, no external crystals and stuff. Could it be that I need one to make the signal more accurate or is the problem elsewhere?

Last Edited: Fri. Jan 13, 2017 - 03:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My private comments

1.

Never use the the delay functions in anything other than where HW demand a delay (init CRT is a good example ).

Have a heartbeat ISR that handle delay's , how fast will depend, but for speed and size (and work atomic), make it so max delay can be  in a 8 bit variable. (if more are needed make a part of the ISR that only run for each 100 ISR or so). 

2.

I don't like this line:

		if(UDR == '1')

because read(and write) of UDR have side effects for the UART.

You should use the UART status bit to check if a new char have arrived and then read UDR. 

 

 

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

UDR goes away once read.  Move it into a temporary variable.  S.

 

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

Re #18: How can you have an ISR for RX that reads (consumes) UDR and then later poll UDR in the while(1) of main() ?

 

Either use the interrupts or poll but not both. And if you are going to poll you cannot just read UDR at will you have to ascertain that RXC has been set to say there is something relevant to be read.

 

What you presumably want is something more like:

volatile uint8_t UART_recvd;

ISR(USART_RX_vect) {
    UART_recvd = UDR;
}

int main(void) {
  while(1)
  {		
	if(UART_recvd == '0')
	{
		OCR0A = 255;
		OCR1A = 0;
		OCR1B = 0;
		_delay_ms(1000);
	}
	etc.
  }

So it's the interrupt that collects the character and the code in main() that acts upon it. But you are back to this same issue with your 1000ms delays that the UART may not apparently "respond" to the user for a second. So you still need delay loops that can break early when new UART traffic arrives.

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

It is very bad practice to use the delay() functions for anything but trivial things.

The problem you're facing is that the complete program flow is setup in an inflexible way.

 

A much better / more flexible way is to put your animation code in a separate function and use a little state machine there.

Note that there is no delay() in this code, so you have to figure out a way to call this function at your desired rate.

And once you've figured that out it's also easy to stop calling this animation function when you don't want to update your animation :)

Youre micro has a whole second to figure out how to stop calling this function...


do_animation(void) {
    
    static int state;
    
    switch( ++state) {
    case 0x01: OCR0A = 2; break;
    case 0x02: OCR0B = 34; break;
    case 0x03: break;// ...More of the same.
    case 0x05: state = 0; break;  // Initialise for the next animation. (Could be a function parameter, whatever).
    }
}

Also note:

It's very common in the uC world to use a timer to do nothing else than increment a global counter variable at a regular rate.

That counter var can than be used for all kind of timing / stopwatch tasks / delay's / etc.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Personally, I would eliminate the _delay_ms altogether and use hardware timers with an interrupt. Hardware timers can be reset, stopped, or changed in the main loop while the timer is running. Meaning you can stop or reset the timing when the necessary UART event occurs.

 

A _delay_ms is a dead-lock loop so it can't be stopped unless you break it up into smaller delays as was recommended above (and even then, you can't break from those either).

 

I emphatically do not recommend changing the built-in delay_ms function. That is absolutely the wrong way to solve a problem like this.

My digital portfolio: www.jamisonjerving.com

My game company: www.polygonbyte.com

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

I'm sorry if the code isn't well optimized and such, but I'm downgrading back to basics just to understand how the system work, then I can implement more effective solutions. So my code stands here (as clawson suggested):

#include .
#include .

volatile uint8_t UART_recvd;

int main(void)
{
    ....

    while(1)
    {
		if(UART_recvd == '0')
		{
			OCR0A = 255;
			OCR1A = 0;
			OCR1B = 0;
			_delay_ms(1000);
		}
		OCR0A = 0;
		OCR1A = 0;
		OCR1B = 0;
		_delay_ms(100);
}
ISR(USART_RX_vect) {
	UART_recvd = UDR;
}

What I'm getting is the same problem: I'm sending in the '0', it displays that short animation ONCE and then turnes off because the UART_recvd is no longer '0'. I hope I'm not missing something stupid, why does it change the value?

 

I need:

To constatly enter the "if(UART_recvd == '0')" state UNTIL, I send in another char. If the interrupt only reads the value once, then surely it should always be '0'...

Last Edited: Fri. Jan 13, 2017 - 03:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm almost tempted to invite all of y'all over for a game.  I can build a widget that beeps, and when it beeps, you push the button.  After you have pushed the button, an LED will illuminate, but delayed by 5ms, 10ms, and 15ms.  Let's run forty or fifty trials, and properly double-blinded, let's see if you can tell the difference...  smiley

 

S.

 

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

PS - Welcome to AVRfreaks.  We can often be overwhelming.  S.

 

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

Set a flag indicating you are in the state and then create a way to exit that state.

#include .
#include .

volatile uint8_t UART_recvd;

bool changeColor;

int main(void)
{
    changeColor = false;

    while(1)
    {
		if(!changeColor && UART_recvd == '0')
		{
			changeColor = true;
		}

		if(changeColor && UART_recvd == '1')
		{
			changeColor = false;
		}

		if(changeColor)
		{
			OCR0A = 255;
			OCR1A = 0;
			OCR1B = 0;
			_delay_ms(1000);
		}

		OCR0A = 0;
		OCR1A = 0;
		OCR1B = 0;
		_delay_ms(100);
}

ISR(USART_RX_vect)
{
	UART_recvd = UDR;
}

This will set the changeColor flag when the UART receives the character '0' and clear the flag when it receives the character '1'

 

[Edit] Added color highlights to my code modifications

My digital portfolio: www.jamisonjerving.com

My game company: www.polygonbyte.com

Last Edited: Fri. Jan 13, 2017 - 04:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The button beeps again when contact is made, so there's no mechanical issues.  From when contact is made and the contact beep begins is timed.  S.

 

 

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

I lose count of how many times I say this but you don't write good software sat in front of a C editor and just jotting down any line of C that happens to pop into your head. Good design starts with a bit of thought and often a sheet of paper and a pen/pencil. You sketch out what it is you want to achieve in the final design and then add more and more detail until you are writing lines of C to fill in the steps you have determined you need.

 

If you do it the other way (as this thread shows) and write some C but it does not perform as you require you spend more and more time with a hammer and a roll of sticking plaster thinking "how can I bash this and glue bits back together so it actually does what I started out to achieve?". It is a very tortuous process (see this thread!)

 

So go back and read the ideas that have been suggested. Try not get too hung up on trying to make the original design work then map out a strategy and some building blocks for how you want to approach this with your new found knowledge.

 

The one "good bit" in your original design was that initial choice that the UART stuff would be handled by an interrupt. This means it can pick up a newly delivered character at any moment without it being missed. On the whole interrupts are a lot like this - they allow you to arrange for things to happen when they must happen - not wait for something else to finish before you can get on. Indeed a lot of micro programs have an empty (or almost empty) while(1) loop in main() with most of the stuff being handled in interrupts.

 

The one thing to bear in mind with such an approach is a limitation (but equally a simplification) of the AVR design in that while one interrupt is being handled it blocks any other being serviced so when you write AVR interrupts you should always approach it with a mindset that says "how quick can I get in and out doing the least possible here to not block other parts of the code".

 

You should pay attention to what Paul was saying in #22 as well (and there's excellent development of this idea by Kartman in the tutorial forum too). It's true in micros that some jobs just do "take a lot of time". But if you are effectively doing the plate spinning/ball juggling thing where you are trying to keep multiple things apparently happening all at once then each of the "long jobs" may need to be split up into smaller steps with some kind of "memory" as to how far through the process each task has got. It can then run for a bit, do "part 1" then return control to let something else have a go, then when it comes back a "state" variable tells it that part 1 is already done and this time part 2 needs to be done. In a small way the 1,000 steps of 1ms above is a bit like this (except that it wasn't really relinqushing control in the suggested for loop).

 

Anyway, bottom line, stop flogging a dead horse. If the original design wasn't a great idea let it go and move on. Many times the second attempt (or even more) at implementing something turns out a lot better than the first as you have a chance to learn from your mistakes.

 

Oh and finally no real MCU programs use _delay_ms() not even _delay_ms(1). Usually a micro has several jobs to attend to and there is no point it sitting in a corner twiddling its thumbs for no apparent reason. This is why temporal scheduling is better approached using timers and "ticks".

 

The ultimate evidence of that is the device you are currently using to read this text from the internet. Whether it's a PC, tablet or phone it's likely running Windows or Linux so some variant of those (OSX, Android, Chrome etc) and in those operating systems the apperance that multiple things are happening at once (listening for phone calls, playing alarms, playing UB40, etc) are occurring because lots of long jobs are being broken up into small steps of activity and it's all moving along on the back of some kind of timer. An MCU is just a very simple implementation of the same idea and may not (usually doesn't) have to be as fancy as an all singing all dancing Operating System but usually multiple jobs are going on and the CPU time is being shared around amongst them even if that only really means a handful of ISR()s or something.

Last Edited: Fri. Jan 13, 2017 - 04:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With all due respect, clawson, I'm not sure you saw a word I said.  Not a bad rant, though, although in error in a few places, first and foremost being that the very OP admitted at the start that he was a beginner (begginer, c.f., but hey).

 

clawson wrote:

I lose count of how many times I say this but you don't write good software sat in front of a C editor and just jotting down any line of C that happens to pop into your head. Good design starts with a bit of thought and often a sheet of paper and a pen/pencil. You sketch out what it is you want to achieve in the final design and then add more and more detail until you are writing lines of C to fill in the steps you have determined you need.

 

It is how one learns.

clawson wrote:

 

If you do it the other way (as this thread shows) and write some C but it does not perform as you require you spend more and more time with a hammer and a roll of sticking plaster thinking "how can I bash this and glue bits back together so it actually does what I started out to achieve?". It is a very tortuous process (see this thread!)

 

So go back and read the ideas that have been suggested. Try not get too hung up on trying to make the original design work then map out a strategy and some building blocks for how you want to approach this with your new found knowledge.

 

Did you actually read any of the ideas I suggested?  To a newbie?  Was it so wrong that I tried to help?

clawson wrote:

 

The one "good bit" in your original design was that initial choice that the UART stuff would be handled by an interrupt. This means it can pick up a newly delivered character at any moment without it being missed. On the whole interrupts are a lot like this - they allow you to arrange for things to happen when they must happen - not wait for something else to finish before you can get on. Indeed a lot of micro programs have an empty (or almost empty) while(1) loop in main() with most of the stuff being handled in interrupts.

 

The one thing to bear in mind with such an approach is a limitation (but equally a simplification) of the AVR design in that while one interrupt is being handled it blocks any other being serviced so when you write AVR interrupts you should always approach it with a mindset that says "how quick can I get in and out doing the least possible here to not block other parts of the code".

 

Have you got everything I post entirely on /ignore?

clawson wrote:

 

 

You should pay attention to what Paul was saying in #22 as well (and there's excellent development of this idea by Kartman in the tutorial forum too). It's true in micros that some jobs just do "take a lot of time". But if you are effectively doing the plate spinning/ball juggling thing where you are trying to keep multiple things apparently happening all at once then each of the "long jobs" may need to be split up into smaller steps with some kind of "memory" as to how far through the process each task has got. It can then run for a bit, do "part 1" then return control to let something else have a go, then when it comes back a "state" variable tells it that part 1 is already done and this time part 2 needs to be done. In a small way the 1,000 steps of 1ms above is a bit like this (except that it wasn't really relinqushing control in the suggested for loop).

 

Anyway, bottom line, stop flogging a dead horse. If the original design wasn't a great idea let it go and move on. Many times the second attempt (or even more) at implementing something turns out a lot better than the first as you have a chance to learn from your mistakes.

 

For what it wanted to do, it wasn't that bad.

clawson wrote:

 

 

Oh and finally no real MCU programs use _delay_ms() not even _delay_ms(1). Usually a micro has several jobs to attend to and there is no point it sitting in a corner twiddling its thumbs for no apparent reason. This is why temporal scheduling is better approached using timers and "ticks".

 

The ultimate evidence of that is the device you are currently using to read this text from the internet. Whether it's a PC, tablet or phone it's likely running Windows or Linux so some variant of those (OSX, Android, Chrome etc) and in those operating systems the apperance that multiple things are happening at once (listening for phone calls, playing alarms, playing UB40, etc) are occurring because lots of long jobs are being broken up into small steps of activity and it's all moving along on the back of some kind of timer. An MCU is just a very simple implementation of the same idea and may not (usually doesn't) have to be as fancy as an all singing all dancing Operating System but usually multiple jobs are going on and the CPU time is being shared around amongst them even if that only really means a handful of ISR()s or something.

 

With all due respect,  I feel completely ignored.

 

S.

 

Fuck me for trying.  S.

Last Edited: Fri. Jan 13, 2017 - 04:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Scrounge, I was not talking to you. I was talking to the OP. Please try to keep up!

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

I was keeping up.  He said at the outset that he was a beginner.  Or a begginer?  Did you miss that bit?

S.

 

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

And here I thought I was gently encouraging the guy to make it work better with a few helpful suggestions instead of a giant rant.  Apparently I should rant more.  S.

 

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

I'm not sure what your problem is - I have pretty much ignored your posts in this thread so I don't really care very much what you do/don't think.

 

I was giving the original poster what I believe, after 35+ years of doing this stuff,  "good advice" that his general approach to solving a programming problem is wrong. He's made that classic error of just writing some C with the first idea that popped into his head and then flogging it to death trying to mould any subsequent suggested solutions around an originally flawed design. Good software design is done in the head not the C editor. It pays to spend a bit of time up front planning things out before committing to specific code as it pays (huge) dividends in the end. That was my point from me to him.

 

Now you are completely at liberty to disagree with that if you like (though I would suggest it goes against most modern software engineering principles: agile, unit testing etc) but I wasn't talking to you. I was giving MY advice to the OP as to how to continue here and flogging a dead horse ain't it. He is, of course, at complete liberty to ignore me too. I don't care, I was just trying to help by pointing out how he might avoid wasting time.

 

Seeing as you have now made the point I will go back and read your posts to see if you did have anything useful to say.

 

EDIT: OK, done that - I don't see actually see how anything I've said is at odds with anything you have said - so we are all agreeing aren't we? So I now REALLY don't understand your reason for attacking me?!?

Last Edited: Fri. Jan 13, 2017 - 04:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll wait.

 

S.

 

Oddly enough I whine over and over again about being ignored while posting and clawson wonders what my problem is.  Perhaps my problem is being ignored while posting.  What a funky thought.  S.

 

Last Edited: Fri. Jan 13, 2017 - 04:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

EDIT added while you were waiting.

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

See another edit.

 

Generally speaking, I think you know your s**t and are very helpful, but I also think you write faster than you read.

 

S.

 

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

clawson wrote:

I was giving the original poster what I believe, after 35+ years of doing this stuff,  "good advice" that his general approach to solving a programming problem is wrong. He's made that classic error of just writing some C with the first idea that popped into his head and then flogging it to death trying to mould any subsequent suggested solutions around an originally flawed design. Good software design is done in the head not the C editor. It pays to spend a bit of time up front planning things out before committing to specific code as it pays (huge) dividends in the end. That was my point from me to him.

 

Now you are completely at liberty to disagree with that if you like (though I would suggest it goes against most modern software engineering principles: agile, unit testing etc) but I wasn't talking to you.

 

I am going to disagree with that.  The OP person, male or female, openly stated at the beginning that they were a beginner.  And they didnt spell it correctly, which made me fear that English wasn't their first language.  And we were talking to each other, whether you paid me any attention or not, in order to improve their code.

 

I wouldn't ignore you - your code is typically pretty good advice.  I would criticize your manner for showing it, and if you want me to, I'll tell you about it (somehow I think not).

 

In short, I am not disagreeing with anything you said in. re. code optimization.  I am disagreeing with you in how you presented it.  That too is a matter of opinion.

 

S.

 

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

Anyhow, I think we've gone a little off the rails here and the mods should chuck this thing.  S.

 

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

Okay guys, I got the code working. I appreciate all the help, but you have to understand that learning from simpler code examples is much much more helpful for me as a beginner. Clawson thanks for your insight on my programming approaches, I'll see what I can do.

 

Scroungre wrote:

PS - Welcome to AVRfreaks.  We can often be overwhelming.  S.

 

Yeah I can already see that. Sorry for the the misspell, English is indeed my second. Once again, thanks for any insights at all.

Looking forward to future chit chats