Connect with us

Trouble with tribbles.. erm, timers.

Discussion in 'Microcontrollers, Programming and IoT' started by The Hammer, Feb 19, 2014.

Scroll to continue with content
  1. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Hello there!

    I've been working on some code for a 107 key mechanical keyboard with macro key functionality where each key has it's own RGB LED. This means two matrices. One is for the key presses (10 columns input, 11 rows output), and another is for the LEDs. I have been using 3 74HC595 and 3 ULN2803 combined with 3 74HC595 with a separate data line to control the matrix even though it is an 18 column by 18 row matrix (future expandability).

    Anyways, I've been writing code for it and while this isn't done yet I would appreciate some input. I know it's rather complex/long, but I've written the basics here so you guys could get the gist here and tell me about if I'm doing anything wrong with these loops I've made?

    Maybe there's a way to shorten this code as I have yet to make copies of the massively long if statements in the LED loop?

    What are some good ways to call an output on a timer, and where are some good tutorials on Atmel chips and timers?

    Before you yell at me, yes, I am aware that I've tried using pins that can't possibly work since the ATTINY861 I'm using doesn't have enough pins for the job. lol Thanks in advance! :D
    Code:
    /*
     * ATTINY861_20PU_LED_MATRIX.c
     *
     * Created: 1/25/2014 4:24:47 PM
     *  Author: The Hammer
     */ 
    
    
    #include <avr/io.h>
    #include <util/delay.h>
    // 74HC595 pins and variables
    #define rdata PA0
    #define cdata PA1
    #define sh_cp PA2
    #define st_cp PA3
    int row = 0, column = 0, time = 0, state = 0, runout = 0;
    int esc = 0, escBC[2], escBPC[2], escGPWM[1], escG[2], escBPWM[1], escB[2], escRPWM[1], escR[2];
    int tilda = 0, tildaBC[2], escBPC[2], tildaG[2], tildaB[2], tildaR[2];
    int tab = 0, tabBC[2], tabBPC[2], tabG[2], tabB[2], tabR[2];
    int caps = 0, capsBC[2], capsBPC[2], capsG[2], capsB[2], capsR[2];
    int lshift = 0, lshiftBC[2], lshiftBPC[2], lshiftG[2], lshiftB[2], lshiftR[2];
    int lctrl = 0, lctrlBC[2], lctrlBPC[2], lctrlG[2], lctrlB[2], lctrlR[2];
    int f1 = 0, f1BC[2], f1BPC[2], f1G[2], f1B[2], f1R[2];
    int one = 0, oneBC[2], oneBPC[2], oneG[2], 1B[2], oneR[2];
    int q = 0, qBC[2], qBPC[2], qG[2], qB[2], qR[2];
    int a = 0, aBC[2], aBPC[2], aG[2], aB[2], aR[2];
    // write digital "high" to pin <pn> on port <prt>
    //#define DIGIWRITE_H(prt, pn) prt |= (1<<pn)
    // write digital "low" to pin <pn> on port <prt>
    //#define DIGIWRITE_L(prt, pn) prt &= ~(1<<pn)
    
    int ledstate(int& time, int& state, int& runout)
    {
    	if (state==1)
    	{
    		PORTA |= (1<<rdata);
    		--time;
    		if (time==0||time==19)
    		{
    			runout++;
    		}
    	}
    	else
    	{
    		PORTA &= ~(1<<rdata);
    		--time;
    		if (time==0||time==19)
    		{
    			runout++;
    		}
    	}
    	return(time, state, runout);
    }
    
    int main(void)
    {
    	DDRA = (1<<PA0)|(1<<PA1)|(1<<PA2)|(1<<PA3);
    	PORTA = (0<<PA0)|(0<<PA1)|(0<<PA2)|(0<<PA3);
    	while(row==0)
    	{
    		PORTA |= (1<<0);
    		esc = PINA0;
    		if (esc==1)
    		{
    			skusb(esc);
    		}
    		tilda = PINA1;
    		if (tilda==1)
    		{
    			skusb(tilda);
    		}
    		tab = PINA2;
    		if (tab==1)
    		{
    			skusb(tab);
    		}
    		caps = PINA3;
    		if (caps==1)
    		{
    			skusb(caps);
    		}
    		lshift = PINA4;
    		if (lshift==1)
    		{
    			skusb(lshift);
    		}
    		lctrl = PINA5;
    		if (lctrl==1)
    		{
    			skusb(lctrl);
    		}
    		f1 = PINA6;
    		if (f1==1)
    		{
    			skusb(f1);
    		}
    		one = PINA7;
    		if (one==1)
    		{
    			skusb(one);
    		}
    		q = PINA8;
    		if (q==1)
    		{
    			skusb(q);
    		}
    		a = PINA9;
    		if (a==1)
    		{
    			skusb(a);
    		}
    		PORTA &= ~(1<<0);
    		row++;
    	}
    	while(column==0)
    	{
    		if (esc==1)
    		{
    			if (escBPC[0] > 0)	//if escape is pressed reset fader and set escG to escButtonPressedColor[0]
    			{
    				escG[0] = escBPC[0];
    				escG[1] = 1;
    				escG[2] = 0;
    				escGPWM[1] = 1;
    			}
    			else
    			{
    				escG[0] = 20;
    				escG[1] = 0;
    				escG[2] = 0;
    				escGPWM[1] = 1;
    			}
    		}
    		else
    		{
    			if (escG[2]==2) //if esc not pressed and LED has completed duty cycle
    			{
    				if (escGPWM[1] < 49)	//if fader timer has not run out
    				{
    					if (escBPC[0] < escBC[0])	//if escButtonPressColor is smaller than escButtonColor
    					{
    						escGPWM[0] = escBC[0] - escBPC[0];
    						escGPWM[0] /= 49;
    						escGPWM[0] *= escGPWM[1];
    						escG[0] = (escBC[0] - escGPWM[0]);
    						++escGPWM[1];
    					}
    					else if (escBPC[0] > escBC[0])	//if escBPC is bigger than escBC perform fade for escG
    					{
    						escGPWM[0] = escBPC[0] - escBC[0];
    						escGPWM[0] /= 49;
    						escGPWM[0] *= escGPWM[1];
    						escG[0] = escGPWM[0];
    						++escGPWM[1];
    					}
    					else
    					{
    						escG[0] = escBC[0];
    						escG[1] = 1;
    					}
    				}
    				else
    				{
    					escG[0] = escBC[0];
    					escG[1] = 1;
    				}
    				escG[2] = 0;
    			}
    			else if (escG[2]==1 && escG[0]==0)	//if esc not pressed and LED is done with first stage of duty cycle
    			{
    				if (escGPWM[1] > 0)
    				{
    					escG[0] = 20 - escGPWM[0];
    				}
    				else
    				{
    					escG[0] = 20 - escBC[0];
    				}
    				escG[1] = 0;
    			}
    			else if (escG[2]==0 && escG[0]==0)	//if esc not pressed and LED is ready to begin duty cycle
    			{
    				escG[0] = escBC[0];
    				escG[1] = 1;
    			}
    		}
    		ledstate (escG[0], escG[1], escG[2]);
    		PORTA |= (1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (escB[0], escB[1], escB[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (escR[0], escR[1], escR[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (tildaG[0], tildaG[1], tildaG[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (tildaB[0], tildaB[1], tildaB[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (tildaR[0], tildaR[1], tildaR[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (tabG[0], tabG[1], tabG[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (tabB[0], tabB[1], tabB[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (tabR[0], tabR[1], tabR[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (capsG[0], capsG[1], capsG[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (capsB[0], capsB[1], capsB[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (capsR[0], capsR[1], capsR[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (lshiftG[0], lshiftG[1], lshiftG[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (lshiftB[0], lshiftB[1], lshiftB[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (lshiftR[0], lshiftR[1], lshiftR[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (lctrlG[0], lctrlG[1], lctrlG[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (lctrlB[0], lctrlB[1], lctrlB[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		ledstate (lctrlR[0], lctrlR[1], lctrlR[2]);
    		PORTA &= ~(1<<cdata);
    		PORTA |= (1<<sh_cp);
    		PORTA &= ~(1<<sh_cp);
    		
    		PORTA |= (1<<st_cp);
    		PORTA &= ~(1<<st_cp);
    		column++;
    	}
     
  2. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Anybody have any good places to learn about interrupts and timers for Atmel stuff?
     
  3. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,271
    Nov 28, 2011
    I don't have time to look at the code now, but I did a write-up about multi-tasking approaches at https://www.electronicspoint.com/required-delay-before-function-execution-t254903.html#post1512974 which may be helpful.
    I don't know of any offhand, but Google will find them for you if you can feed in the proper keywords :)
    I'll post again when I've had a look at your code.
     
  4. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,271
    Nov 28, 2011
    I've had a look at your code and discovered a major problem with it. It doesn't have any comments!

    Comments are essential, not just so other people can understand what the program does and how it works, but so that YOU can understand what it does and how it works, when you come back in six months' time, wanting to change or add something.

    If you want me (and probably anyone else on the forum) to look at your code, you need to comment it properly. DO read some guidelines on good (professional) programming sites on HOW to comment your code, then implement them.

    One small point. You can make your program a lot shorter, and therefore easier to read and comprehend, if you change blocks of code like this, where there is only one statement inside a pair of squiggly brackets:
    Code:
            if (time==0||time==19)
            {
                runout++;
            }
    to
    Code:
            if (time==0||time==19)
                runout++;
     
  5. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Thanks, I'll check that out and see if it'll suite my purposes!

    Truth. I had some comments in there before, but didn't like their layout. So, I removed them before posting. I was up for 36 hours, and didn't think to add any back in when posting. lol

    I'll check out the link first, add to my code, add comments, and then get back here with this. It might take me a bit, but I shall return.. with results!:)
     
  6. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Hmmmm, reading that gave me many ideas. Thanks, but I do have a question for you on that though. As you can see I'm running multiple while loops. If my code is executing a while loop and at the end of the while loop there is one task that requires a timer to reach a certain point before it executes code that drives a pin high, then low, and makes the next while loop run will it not be performing other tasks? Hmmm... maybe a half assed code will help you understand what I mean...

    Code:
    int row = 0, col=0;
    while(row==0)
    {
        random code to be executed-row0;
        wait for timer to be Y
        {
            ++row;
        }
    while(row==1)
    {
        random code to be executed-row1;
        wait for timer to be Y
        {
            --row;
        }
    while(col==0)
    {
        random code to be executed-col0;
        wait for timer to be X
        {
            pinA3 high;
            pinA3 low;
            ++col;
        }
    }
    while(col==1)
    {
        random code to be executed-col1;
        wait for timer to be X
        {
            pinA3 high;
            pinA3 low;
            --col;
        }
    }
    What I'm hoping to accomplish is that the "random code to be executed-row0" section will obviously be handled first by the program, but while it's waiting for the timer to be Y will "random code to be executed-col0" operate, and vice versa.. if it's waiting for the timer to be X in "random code to be executed-col0"? As in does the entire program hang when it's waiting for a timer? What worries me is if Y timer completes after being waited on, and the code before X timer completes doesn't have enough time to complete before X is reached and problems will arise..:confused:
     
    Last edited: Feb 20, 2014
  7. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Also, I'm kind of confused about passing variables by reference to a function.. I wouldn't think this was so, but I read online about how if you pass a variable to a function that it is permanently referenced by that function.. so that every time the function is called it modifies all the previously referenced variables even if passed new ones by reference..

    Code:
    int addfunction(int& x)
    {
        ++x;
    }
    int main(void)
    {
        int a = 1, b = 4, c = 7;
        addfunction(a);
        addfunction(b);
        addfunction(c);
    }
    
    So, essentially when this code is run at the end a will be 4, b will be 6, and c will be 8..:confused:
     
    Last edited: Feb 20, 2014
  8. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,271
    Nov 28, 2011
    That's right. When the processor gets to a while() loop, it just executes the contents of the loop repeatedly until the while() condition becomes false, then it proceeds to the next part of the program.

    When you wrote
    Code:
    while (row == 0) {
        random code to be executed-row0;
        if (timer == Y)
              ++row;
         } //end while(row == 0)
    you could have just said
    Code:
    while (timer != Y) { // or while (timer < Y)
        random code to be executed-row0;
         } //end while(timer != Y)
    That will have the same effect. With the longer version, the while() loop will only terminate when the timer reaches Y and row gets incremented, so you can just test the timer directly instead.
    Yes, because each while() loop needs to complete before the next one will be executed. But you could restructure your code like this:
    Code:
    int row, col;
      row = col = 0;
      while (1) {
        do_row_stuff(row);
        do_col_stuff(col);
        if (timer == Y)
          row = 1 - row; // switch to other row
        if (timer == X)
          col = 1 - col; // switch to other column
        } // end while(1)
    // code never gets here
    Depending on exactly what you want to do, that might work - at least, the approach might work. The details of deciding when to switch rows and columns needs to be a bit more complicated than I've shown, because it's not reliable to test for an exact match between timer and Y or X. If the timer runs faster than the loop, you could miss the moment when the timer exactly matches the target value. If the timer runs slower, then it will match more than once, and the column or row will alternate every time through the loop during that period.
    If you're doing two things within the while() loop, they will both be done the same number of times, repeatedly, until the while() condition tests false. If you want the two functions to be able to run under different conditions, you'll need an if() test before each one, with the overall while() loop structure the same as I showed above.
    There are three errors:
    1. In line 1, the parameters for addfunction() should be int* x not int& x. x is a pointer to an integer.
    2. In line 2, to increment the variable pointed to by x, you need to say ++*x; not ++x. In other words, you want to increment the thing that's pointed to by x, not x itself; x is the address of the variable you're interested in.
    When you're using a variable as a pointer, it's a good idea to indicate this in the variable's name. I would have used intp instead of x; this reminds you that the variable is actually a pointer to another variable (of type int).
    3. In lines 6, 7 and 8, you need to pass the addresses of a, b and c to addfunction(), so you need to say addfunction(&a); (etc).

    Passing a variable by reference gives the called function the ability to access the variable itself. This is needed if the called function is going to modify the variable. If you pass a variable by value, the information that's passed to the called function is the value of that variable at the time the function was called. The called function can modify that value, but that won't affect the variable that the value came from; it will only affect the copy of that variable that is being used by the called function.
    If you make those changes, the variables will be 2, 5 and 8. Each call to addfunction() will increment one variable. You seem to want addfunction() to increment the other variables that it has seen in the past. For this, you would need to make some changes to addfunction(). You would need to allocate some static storage to build up a list of pointers to variables (add one pointer to the list each time addfunction() is called) and some code to run through the list and increment all the variables in the list.
     
  9. (*steve*)

    (*steve*) ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd Moderator

    25,497
    2,839
    Jan 21, 2010
    It doesn't work like that. Either the web site was wrong or you misunderstood something)
     
  10. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Ermagawhd, thank you for explaining that all Kris! I cannot even begin to tell you how pss'd off I was getting that C++ was the way it was.. some of the resources I've found on the web through the almighty Google *all bow* have been incomplete, poor explanations for people whom are just starting out.. I'm going to get to work using what you've said to fix my 342 lines of written code. lol :D

    Thanks!
     
  11. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Another question though.. if you make a while statement similar to what you were saying:

    Code:
    while (timer == Y)
    {
        int a = 1;
        int b = 2;
    }
    If the timer increments while the while loop is running does the code in the while statement stop? As in if I have a lot of complex code that takes a while to compute or do inside the "while (timer == Y)" and it hasn't reached the end of all the code inside that loop will it discontinue running it??

    As in if the timer is EXTREMELY fast is it possible that a can be 1, but b can not exist (or even that a doesn't exist if the timer is THAT fast)?
     
  12. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    THIS gave me the wrong idea..

    "After you create the reference, whenever you use the variable, you can just treat it as though it were a regular integer variable. But when you create it, you must initialize it with another variable, whose address it will keep around behind the scenes to allow you to use it to modify that variable. "
    http://www.cprogramming.com/tutorial/references.html
     
  13. (*steve*)

    (*steve*) ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd Moderator

    25,497
    2,839
    Jan 21, 2010
    That depends...

    It depends on (at least) four things:

    1) Optimization

    If the code is not optimised at all, the variable might be checked each time through the loop. Other optimization settings may determine that if neither Timer nor Y change in the loop that they only need to be checked once and never again. Even if Y changes, the code might decide to cache the value of timer.

    In general you don't have a lot of control over this and playing with optimization to change the behaiviour of code is very bad juju.

    2) Declaration of "timer"

    Let's say timer is an int.

    if you defne it as

    Code:
    volatile int timer;
    this tells the compiler that timer can change any time and that it should re-read it whenever it needs to ascertain its value, it can't assume it remains the same just because it can't see where it's being changed.

    This is important if you have things changed in interrupt driven code, for example.

    3) The nature of equality

    whilst only one value satisfies ==, and any deviation will cause the loop to exit, you should be aware that with floats especially equality is not what it seems.

    As an example if you add 0.1 ten times to a variable with an initial value of 0 the end value is not equal to 1. Thus you're better off looping while something is equal, or until it's not equal, or using > and < rather than ==, and possibly using constructs like (abs(value - endValue) < 0.01) to allow for floating point inaccuracy when you *really* do want equality. Alternatively, for floating values, stick to integer index variables and multiply the increment by the loop counter.

    4) Timing.

    And you ask about this explicitly

    No, the loop exit conditions are only tested at either the beginning or the end of the loop (in most cases you couldn't tell the difference).

    If you want to exit early, you can add additional tests ands use the "break" keyword.

    As a matter of fact, "a" and "b" simply do not exist for any code executing outside that loop, nor once the loop has exited. They are defined as local to the scope of the loop's body.
     
  14. (*steve*)

    (*steve*) ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd Moderator

    25,497
    2,839
    Jan 21, 2010
    This is talking about pointers (albeit an implicit use of them).

    Do you understand what it means now, or would you like it explained?
     
  15. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,271
    Nov 28, 2011
    Yeah, what Steve said :)
     
  16. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Firstly, thanks for taking the time to help me out!

    No, I won't need you to explain that tutorial Steve. I've re-read that with more understanding as far as C++ goes (thanks to you guys!) and it makes better sense, but I do believe that what he wrote was very misleading..

    As far as understanding everything else.. Um, well.. not entirely. Steve said both "It depends", "No", and then gave a not answer to my questions. Which were the exact same question, but I wasn't sure how to go about asking it. I understand about variable's and their scope, but it was a question specifically about inside that function which is why that's all I included in that code. My guess is the correct answer was "No", once a while loop starts running it's code it won't stop unless told to do so (weather that means encountering the end of the loop, or a break) or encountering a serious error.

    I was unaware that ints, variables, and timers didn't get along fine, so that lets me know to watch out for what I'm doing with timers a little more carefully.

    I'm not sure what he means by optimization entirely.. for a lot of code there's obviously more than one way to accomplish a goal, but how you get there can cost less CPU time or less space in the code and I think that must be to what you're referring?

    I'll be back in a couple days time with more questions as I've been working on my code! I'm at about 1900 lines at this point.. dealing with two matrix simultaneously is a lot of copy&paste work.. I suppose I could write a single function to go through all 11 rows, and 18 columns, but I'm not sure how much CPU time that would actually take recreating what I've done dynamically..

    I did at least add a bunch of comments to ease in my and everyone else headaches though!
     
    Last edited: Feb 21, 2014
  17. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,271
    Nov 28, 2011
    I agree it could have been explained a lot more clearly.
    The processor can only do one thing at a time. (Assuming it's a single-core device!) A while() loop operates like this:

    First, the condition (between the parentheses in the while() statement) is tested. If it's false at that time, the whole while() loop is skipped, and the processor continues to the next part of the program.

    If the condition is true at the time it's tested, the contents of the while() loop is executed. This may involve a break statement, in which case the while() loop terminates immediately and execution moves on to the next part of the program. It may involve a continue statement, in which case the rest of the code inside the loop is skipped for that time through the loop.

    When all the code inside the while() loop has been executed, or if a continue statement was executed, the processor goes back to the start of the loop and re-evaluates the condition. As before, if the condition is false at that time, the loop terminates; if it's true, the contents of the loop is executed once more. And so on.

    No, the condition is only tested at the beginning of the loop, unless you specifically write code inside the loop to check it again and break out of the loop if it has gone false.
    It's quite possible that when the (timer == Y) test is made at the start of the loop, timer is not equal to Y. In that case, the body of the loop will not be executed at all.
    It's not a general problem. It depends on the nature of 'timer'. If 'timer' is a variable that changes only when it's explicitly modified by that part of the code - for example, if it's a counter that is conditionally modified within the body of the while() loop - there's no problem.

    The problems start if 'timer' is (a) a variable that is modified by an interrupt handler, when the interrupt can come along at any time and change the value in the timer variable "behind the back" of that code, or if 'timer' is (b) a reference to a hardware register, such as the counting register of a timer peripheral in the MCU, that could change at any time (from the point of view of that code, at least).

    In those cases, 'timer' must be declared volatile, so the compiler knows that it could change without your code's knowledge. Look up 'volatile' for more details.
    Exactly. In this case, a simple optimisation could be to allocate an MCU register to hold 'timer' during that section of code. The compiler would be more likely to do this if timer is used extensively in that code, because it's more efficient to use a register that contains the value than to get that value from a variable in memory each time it's needed. In this case, the compiler could optimise your code so that it loads the value of the timer variable into an MCU register before the start of the loop, and uses the register copy every time through the loop. If it does that, the only way that 'timer' will change is if that code changes it. The actual 'timer' variable could change because of an interrupt, or a change at the hardware level, but that code would be blissfully unaware of it.

    This is the reason for the 'volatile' keyword. It tells the compiler not to make any assumptions about the variable, such as the assumption that it won't change unless explicitly modified by that code so it is safe to cache its value in a register.
    Writing lots of lines of code is not necessarily a badge of honour! Definitely not, if it involves copying and pasting. That approach leads to "spaghetti code", which takes up a lot of space and is much harder to understand.

    Start with a block of code that you're copying and pasting, and make it a function. Find the parts that you're changing each time you were pasting it, and make them function parameters. Then call the function multiple times, with different parameters.

    Another trick that could be useful in this application is to use structs (structures) to hold the related data for each object. (An object would be a row or a column.) This sets up a template for a block of memory that contains all the information that relates to one object. You can instantiate (create an instance of) the data object as many times as you need. Then, if you want to call a function to perform a specific operation on a row or column, you can pass a single parameter that identifies the object - in C, this will normally be a pointer to the data object - and the function can find all the information it needs to process that particular row or column by looking in the data object. It's faster and smaller, and it makes your program easier to understand.
    OK! Seriously though, you should be finding out about these tricks before you go writing a 10,000-line program that no one can follow, including yourself, with random bugs here and there where you forgot to change one of the pasted lines, that won't even fit in the MCU! It does require discipline, but as parents like to say when they mete out that discipline, "it's for your own good!"
     
  18. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    I suppose my problem is that this is my first journey into the world of microcontrollers. I am finding myself re-asking a lot of questions I thought I knew the answers to (mainly finding out that the answers never changed :p), because I keep thinking that the embedded nature of the device possibly makes fundamentals change.

    So, thanks for putting up with me!

    As far as making functions out of what I've copypasta'd my main concern is taking up too much time on the processor.. I'm really not sure how quickly it will get through bits of code since I've never worked with microcontrollers before. If I re-write my code could I send it to you in file form so that I'm not posting my work where it might be easily copied without my knowledge?
     
  19. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,271
    Nov 28, 2011
    I guess the fundamentals are the same as programming a computer. The obvious differences that come to mind are resources (much less memory, slower processor), no operating system (although RTOS, real-time operating systems, are available) and the built-in peripherals, which you need to access directly or through a driver you link into your program.

    I'm glad you chose an AVR over a PIC. AVRs are much less restrictive and more easily scalable.

    I know what you're saying about speed, but there are many other aspects of your program that will affect its speed to a much greater extent than the difference between inlining and function calls. For example, moving loop-invariant calculations outside a loop, or restructuring how calculations are done, can make a huge difference to speed.

    Another option would be to create a macro using #define that looks like a function call but actually expands to all the necessary code at compile time. Then you invoke the macro repeatedly, passing parameters to it, and the compiler converts that into inlined code. Effectively the compiler does the copying and pasting, with edits on each pasted copy, automatically at compile time.

    I doubt that's a good way to go, but at least it's tidy.

    I contribute on this site so that others may benefit, so I don't go to PMs or email unless I'm really interested in the project. I doubt that anyone will rip off your code or ideas. Anyone who's smart enough to get an embedded project up and running will probably have their own ideas about how to do it. But there's no guarantee and it's your call.
     
  20. The Hammer

    The Hammer

    164
    0
    Sep 17, 2013
    Yes, Atmel MCUs seem to have the best of both worlds for what I'm looking for. I'm designing a keyboard (circuit board is 95% done in Eagle already, just need to select a proper MCU, connect traces, and add through hole resistors for the LEDS) so the USB implementations I've read on some of the Atmel MCU seem like they may be of benefit, plus I'm doing a lot of stuff based off timers. The separate nature of the timers is a MUST. Doing PWM for 321 LEDs in a matrix with color mixing and fades and color overides, and 107 keys in a matrix, AND USB HID communications with all the features I'm implementing I don't see how I could have gone with PIC. I would like to try using both at one point, but for now this project is going to still take me a while to complete.

    I'd like to keep the hard design parts to myself, and those whom are helping me. Then those people get their names traced on my board. lol :cool:

    If it's popular I'll produce the keyboard for other people.
     
    Last edited: Feb 21, 2014
Ask a Question
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Electronics Point Logo
Continue to site
Quote of the day

-