By Joe Pardue View In Digital Edition
This time, we are going to learn some more C syntax, a bit about libraries, and teach your Butterfly to talk. Now that you’ve gotten hooked on learning C for the AVR, I want to admit to some trepidation about how this stuff should be taught.
FIGURE 1. PC communicating with Smiley’s Workshop Learning Platform. |
Most people like my method that gets you started blinking LEDs and reading switches without fully understanding all the code you are using. I tell folks to be patient and that some of the weird stuff will eventually start to make sense. This is how I learned C. I copied lots of code, bent it to fit my needs, and then read about the stuff I didn’t understand. This worked for me, but it isn’t a formal handholding, spoon-feeding process like some folks seem to want. Frankly, if you really need that, then you should think hard about messing with microcontrollers or programming, since this is a wild and chaotic milieu through which there is no royal road. You have to do a lot of work and can count yourself lucky to get an experienced guide.
Another issue is that at the rate we are going, mixing in projects and asides like this one, it will take us nearly a year to work through the C language syntax. This is a good thing in that it gives us plenty of time and lots of microcontroller-related examples, but it’s a bad thing since most folks can learn the syntax a lot faster if they so choose. For those who wish we’d move things along a bit faster, I want to suggest that you get the shareware Pelles C compiler at www.smorgasbordet.com/pellesc/ and then purchase the venerable The C Programming Language by Kernighan and Ritchie. That way, you can zip along learning C syntax at your own rate and use this workshop to review in the context of AVR microcontrollers.
Arithmetic Operators
TABLE 1. Arithmetic Operators. | |||
Operator | Name | Examkple | Defined |
* | Multiplication | x*y | Multiply x times y |
/ | Division | x/y | Divide x by y |
% | Modulo | x%y | Provide the remainder of x divided by y |
+ | Addition | x+y | Add x and y |
- | Subtraction | x-y | Subtract y from x |
++ | Increment | x++ | Increment x after using it |
— | Decrement | —x | Decrement x before using it |
- | Negation | -x | Multiply x by –1 |
+ | Unary Plus | +x | Show x is positive (not really needed) |
Relational Operators
TABLE 2. Relational Operators. | |||
Operator | Name | Example | Defined |
> | Greater than | x>y | 1 if x is greater than y, otherwise 0 |
>= | Greater than or equal to | x>=y | 1 if x is greater than or equal to y, otherwise 0 |
< | Less than | x<y | 1 if x is less than y, otherwise 0 |
<+ | Less than or equal to | x<=y | 1 if x is less than or equal to y, otherwise 0 |
== | Equal to | x==y | 1 if x equals y, otherwise 0 |
!= | Not equal to | x!=y | 1 if x is not equal to y, otherwise 0 |
Operators seem like ordinary arithmetic or algebra symbols, and they mostly are. But they are different often enough that you need to pay attention when operations don’t act like you think they should. An example of the kind of confusion you can run into is when you use the ‘=’ assignment operator and the ‘==’ ‘is equal to’ operator:
x = y;<br />
if(x==y) _delay_loop_2(30000);
The first statement assigns x the value of y. The second statement calls the _delay_loop_2(30000) function if x ‘is equal to’ y. What about:
if(x=y) _delay_loop_2(30000); //BAD STATEMENT
This will set x equal to y and then call the _delay_loop_2(30000) function. The ‘if’ is checking to see if the statement is true, meaning that it is not equal to 0. In our case, the delay will always run unless y is 0, then it will never run. Either way, it isn’t what you thought you were testing. The WinAVR compiler will think something is strange and issue this warning:
Warning: suggest parentheses around assignment used as truth value
which will scroll by so fast you won’t see it, so you’ll assume the compile was good. It is a very easy mistake to make, and you will feel really dumb after an hour of debugging, looking for something obscure, only to find a lousy missing ‘=’ character. I do this all the time.
Assignment Operators
TABLE 3. Assignment Operators. | |||
Operator | Name | Example | Defined |
= | Assignment | x=y | Put the value of y into x |
+= | Add | x += y | Compound assignment provides a short cut way to write an expression, for example: x += y; is the same as x = x + y; x /= y; is the same as x = x/y; |
-+ | Subtract | x -= y | |
*= | Multiply | x *= y | |
/= | Divide | x /= y | |
%= | Modulo | x %= y | |
<<= | Left Shift | x <<= y | |
>>= | Right Shift | x <<= y | |
&= | Bitwise AND | x &= y | |
^= | Bitwise XOR | x ^= y | |
|= | Bitwise OR | x |= y |
The assignment operators provide a kind of shorthand technique for arithmetic operations. The following statements are equivalent:
myByte = myByte + yourByte;
Same as:
myByte += yourByte;
Conditional Expressions
You will frequently need to make decisions based on external conditions. For example, if the temperature is above 150° F turn the fan on, otherwise turn the fan off. You could write this as:
if( temp > 150 )<br />
turnFan(ON);<br />
else<br />
turnFan(OFF);
Or, you could use the C conditional operator ?: as below:
temp > 150 ? turnFan(ON) : turnFan(OFF);
The operation has the form expresson1 ? expression2 : expression3, and follows the rule that if expression1 is true (non-zero value), then use expression2, otherwise use expression3. This operator seems a little gee-wiz-impress-your-friends and not as clear as the if-else expression, but you’ll see this a lot so get used to it.
Precedence and Order of Evaluation
When a statement has a sequence of operators such as:
x = 50 + 10 / 2 – 20 * 4;
the compiler follows an order of calculation based on operator precedence. But what the compiler does may not be what you intended. Calculate the value of x. Did you get 40? If you performed the calculations sequentially beginning at the left, you get:
x = 50 + 10 / 2 – 20 * 4<br />
x = 60 / 2 – 20 * 4<br />
x = 30 – 20 * 4<br />
x = 10 * 4<br />
x = 40
So the answer is 40, right? Wrong, according to C, it is –25. The compiler does the division and multiplication first, then the addition and subtraction:
x = 50 + 10 / 2 – 20 * 4<br />
x = 50 + 10 / 2 – 80<br />
x = 50 + 5 – 80<br />
x = 55 – 80<br />
x = -25
Some C gurus will memorize the precedence and associativity table and actually write statements like x = 50 + 10 / 2 – 20 * 4. Such clever programmers are dangerous and should be avoided when possible. The Germans have a word for clever: kluge. And in programming, ‘kluge’ is a well-deserved insult.
Don’t be clever, be clear. Clever programming is difficult to read and understand. If the clever programmer gets run over by a truck (hopefully), his code will be inherited by some poor guy who will have to figure things out. DO NOT memorize the Table of Operator Precedence and Associativity in C (which I refuse to even show). DO use ’(‘ and ‘)’ to make your program clear!
Which is clearer:
x = 50 + 10 / 2 – 20 * 4;
or:
x = 50 + (10 / 2) – (20 * 4);
The second adds nothing for the compiler, but tells the reader what you intended. What if you really meant to have the operations performed in the order listed? Then you would write:
x = ((((50 + 10) / 2) – 20) * 4);
which would make x = 40. The parentheses can get mighty confusing, but not nearly as confusing as their absence.
Libraries: avr-libc library
Software libraries are repositories of functions that have been precompiled and stored as object modules that the compiler/linker can find and put into the code when you want to use them. These library functions are defined in a header ile (filename ends with an .h suffix), usually with the same name as the library and have some documentation that tells you how to use the function (avr-libc-user-manual.pdf which you can find at the unlikely location C:\WinAVR-20071221\doc\avrlibc\ if you followed instructions and installed WinAVR in the default location). You saw an example of a header file in PortIO.c from Workshop 3:
// PortIO.c<br />
#include <avr/io.h>
Also, we are talking a lot about functions here and haven’t really addressed what a function is yet other than the cursory preview given in Workshop 2. We won’t get into details for a few Workshops, but briefly, a function encapsulates a computation; it may return a value and it may require input parameters.
The really great thing about library functions is that you don’t have to know how they do their job and you never have to look at the code that does it. In object-oriented programming, this concept is called encapsulation and carries with it the idea that the less you can get your hands on, the less you are likely to screw up. Now as insulting as that may seem, it is nonetheless a very good software engineering principle. If it works and you can’t get at it, then you can’t break it.
Let’s apply this by using the library libsmws4.a to get the Butterfly shouting some math at the PC. This library will do things in the background that will allow you to send and receive data over the UART without having to know a thing about how it works.
We will also use two standard C libraries: stdio and stdlib. From stdio, we will use the standard C function printf() and a special AVR modified function printfP(PSTR()) which allows us to store strings in Flash memory which we have a lot of rather than RAM (which we have much less of). From stdlib, we will use atoi() to convert an ASCII string to an integer. Look at these functions in the avrlibc manual and try not to freak out too much over the complexity since our job here is to learn enough over time so that the manual will make sense, eventually, more or less.
Most microcontrollers are buried deep in some device where they run in merry isolation from the rest of the world. Their programs are burned into them and never change. But there are many instances when we might want to communicate with a microcontroller, this being one of them. The Butterfly uses a joystick and an LCD, which is fine for its built-in menu based applications. For anything more complex — like changing the microcontroller software — nothing beats using the PC’s serial communications port to communicate with the microcontroller.
What we need is a method to send commands and data from the PC and receive responses from the Butterfly. In this section, we will develop a generic command interpreter skeleton that we will reuse in later programs. In this project, we will use this skeleton to build a demonstration that lets the PC ask the Butterfly to do some simple math. Hey, bet you never thought you’d be training a Butterfly to add, subtract, multiply, and divide!
Writing MathCommunicator.c
Let me repeat: The MathCommunicator files we are about to use have many things in them that are well beyond our C training at this point, so just use them and don’t think too hard about it yet. We will revisit each function in later Workshops as we increase our knowledge.
Before creating the MathCommunicator project in AVRStudio, read the Smiley’s Workshop 4 – Supplement: Adding Libraries to Projects pdf file that you can get from Nuts & Volts ([url=http://www.nutsvolts.com]http://www.nutsvolts.com[/url], at the bottom of this page in the downloads section) or Smiley Micros Workshop4.zip download that also contains the AVRStudio project.
#include <avr/io.h><br />
#include <stdio.h><br />
#include <stdlib.h>
// include the special library<br />
// for Smiley’s Workshop 4<br />
#include “smws4.h”
// define the parseCommand<br />
// fuction<br />
void parseCommand(char *, uint8_t);
int main(void)<br />
{<br />
char b = 0;<br />
char s[6];<br />
uint8_t count = 0;
// this is in libsmws4<br />
initialization();
// the _P and PSTR weirdness<br />
// allows you to store a<br />
// string in flash rather than<br />
// wasting space in SRAM<br />
printf_P(PSTR(“Math Communicator at your service!\n”));
while(1)<br />
{<br />
while(b != ‘=’)<br />
{<br />
// this is in smws4.a<br />
b = (char)receiveByte();<br />
s[count++] = b;<br />
}
parseCommand(s,count);<br />
b = 0;<br />
count = 0;<br />
}
return 0;<br />
}
void parseCommand(char *s,uint8_t cnt)<br />
{<br />
int a,b;<br />
char t[3];
t[0] = s[0];<br />
t[1] = s[1];<br />
t[2] = 0;<br />
a = atoi(t); <br />
t[0] = s[3];<br />
t[1] = s[4];<br />
t[2] = 0;<br />
b = atoi(t);
switch (s[2])<br />
{<br />
case ‘+’:<br />
printf(“%d + %d = %d\n”,a,b,a+b);<br />
break;<br />
case ‘-’:<br />
printf(“%d - %d = %d\n”,a,b,a-b);<br />
break;<br />
case ‘/’:<br />
printf(“%d / %d = %d\n”,a,b,a/b);<br />
break;<br />
case ‘*’:<br />
printf(“%d * %d = %d\n”,a,b,a*b);<br />
break;<br />
case ‘%’:<br />
printf(“%d %% %d = %d\n”,a,b,a%b);<br />
break;<br />
default:<br />
{<br />
printf_P(PSTR(“Say what?\n”));<br />
break;<br />
}<br />
}
}
The parseCommand(char *s,uint8_t cnt)
is really going to challenge my ‘be patient, you’ll learn what this all means later’ admonition. I considered just sticking it in the library so you wouldn’t be blinded by it, but this will all make sense eventually so, like I said, be patient.
FIGURE 2. Developer Terminal with Math Communicator XML data. |
Using MathCommunicator.c
We will demonstrate this code using the Developer Terminal that you were introduced to in Workshop 1. In order to make our life simpler, we will restrict our math to two digit integers. We will also tell the Butterfly that the communication is finished by sending an equal sign ‘=’. We will send a digit as exactly two characters and if the digit is only one character, we will precede it with a 0; also, we will use no spaces. For example:
Add: 01+01=<br />
Subtract: 02-01=<br />
Divide: 50/05=<br />
Multiply: 25*05=<br />
Modulus: 99=
To help you with this, there is an XML data file: MathCommunicatorXMLData.xml in Workshop4.zip file that includes some examples of each math function used. In Developer Terminal, open the ‘File’ menu then select ‘Open XML Data’ and browse to the same directory as the MathCommunicator source code.
Next time, we’ll look at some more C syntax and learn that there are exactly 10 types of people in the world: those that understand binary and those that don’t. And, yes, that will make sense after you’ve read the article. NV
Joe Pardue has a BSEE and operates www.smileymicros.com from the shadows of the Great Smokey Mountains in Tennessee. He is author of Virtual Serial Port Cookbook and C Programming for Microcontrollers
WORKSHOP BOOKS & KITS |