Файл: C Programming for microcontrollers (Joe Pardue, 2005).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.06.2025
Просмотров: 2455
Скачиваний: 2
Chapter 3: A Brief Introduction to C – What Makes Blinky Blink?
language was developed primarily to formalize these and related concepts and force their use.
The Main() Thing
All C programs must have a ‘main’ function that contains the code that is first run when the program begins.
int main (void)
{
// Do something
}
Blinky has:
int main (void)
{
// set PORTD for output DDRD= 0xFF;
while(1)
{
for(int i = 1; i <= 128; i = i*2)
{
PORTD = ~i; _delay_loop_2(30000);
}
for(int i = 128; i > 1; i -= i/2)
{
PORTD = ~i; _delay_loop_2(30000);
}
}
}
42
Chapter 3: A Brief Introduction to C – What Makes Blinky Blink?
In this function we leave C for a moment and look at things that are specific to the AVR microcontroller. The line:
DDRD = 0xFF;
Sets the microcontroller Data Direction Register D to equal 255. This tells the microcontroller that Port D pins, which are hooked up to our LEDs, are to be used to output voltage states (which we use to turn the LEDs on and off). We use the hexadecimal version, 0xFF, of 255 here because it is easier to understand what’s happening. You disagree? Well, by the time you finish this text, you’ll be using hexadecimal numbers like a pro and understand they do make working with microcontrollers easier, but for now, just humor me.
The program tests the while(1) and finding it true, proceeds to the ‘for’ statement, which is also true and passes to the line:
PORTD = ~i;
Which causes the microcontroller to set the Port D pins to light up the LEDs with the value of ~i. The ‘~’ inverts the value of i , we’ll learn more about this later.
Say what? Okay, ‘i’ starts off equal to 1, which in binary is 00000001 (like hexadecimal, you’ll grow to love binary). This provides +3v on the rightmost LED, lighting it up and leaves the other LEDs unlit at 0v.
The first ‘for’ loop runs eight times, each time moving the lit LED to the left, then it exits. In the next ‘for’ loop the -= operator subtracts i/2 from i and sets i equal to the results causing the LED to move to the right. When it is finished the loop runs again… for how long? Right… forever. Or at least until either the universe ends or you unplug the Butterfly.
NOTE: the Butterfly LCD dances like crazy with each LED pass, because some of the Port D pins are also tied to the LCD. It’s a bug in our design, but in the world of marketing it would be called a free bonus feature available exclusively to you for an unheard of low price if you act immediately. Will it harm the LCD? Probably not, but I don’t know for sure, so don’t leave Blinky running overnight.
43
Chapter 3: A Brief Introduction to C – What Makes Blinky Blink?
That’s enough for a quickie introduction. We skimmed over a lot that you’ll see in detail later. You now know just enough to be dangerous and I hope the learning process hasn’t caused your forehead to do too much damage too your keyboard.
44
Chapter 4: C Types, Operators, and Expressions
Chapter 4: C Types, Operators, and
Expressions
Data Types and Sizes
Seen on a shirt at a Robothon event:
There are exactly 10 types of people in the world.
Those who understand binary numbers and those who don’t.
If this doesn’t make sense to you now, it will in a minute.
Bits
The first computers were people with quill pens who spent their lives calculating tables of things like cannonball trajectories to help soldiers more accurately slaughter their enemies. Later mechanical computers, with brass gears and cams, were developed to make the slaughter cheaper, quicker, and easier. Then one day a genius figured that you could do all this computing even easier if you used switches. Switches can be off or on, and the fundamental datum is the ‘bit’ with exactly two ‘binary’, states. We variously refer to these states as ‘0 and 1’ or ‘on and off’ or ‘true and false’. It’s the latter that allows us to use bits to automate Boolean logic and thus the modern binary logic computer entered the world and now slaughter is so cheap, quick and easy to compute that anybody can do it. Maybe this is skimming the topic a bit (har!) but a full explanation would begin with the first sentence of Genesis and only hit its stride about the time Albert Turing offed himself as his unjust reward for saving the free world, and while fascinating, it won’t get us blinking LEDs any quicker, so Let’s move on.
Each of our LEDs is connected to a microcontroller pin that can have two voltage states: ground or +3v, which can be manipulated as a data bit.
Bytes
The AVR and many other microcontrollers physically handle data in 8-bit units called bytes, a data type that can have 256 states, 0 thru 255. This is shown in the following sequence of states, (leaving out 9 thru 247, see Appendix 5 to see them all, and be sure to take a magnifying glass):
45
Chapter 4: C Types, Operators, and Expressions
00000000 = 0 |
1111000 = 248 |
|
00000001 = 1 |
1111001 = 249 |
|
00000010 = 2 |
1111010 = 250 |
|
00000011 = 3 |
(9 thru 247) |
1111011 = 251 |
00000100 = 4 |
1111100 = 252 |
|
00000101 = 5 |
1111101 = 253 |
|
00000110 = 6 |
1111110 = 254 |
|
00000111 = 7 |
1111111 = 255 |
|
00001000 = 8 |
Look at our Cylon eye and notice that we have 8 LEDs with one lit at a time scrolling back and forth. What you are seeing is 8 of the 256 possible states being presented in a sequence that fools us into thinking we are seeing a back and forth scrolling motion. If the presentation sequence were random, we’d just see the light blinking on and off chaotically. Using binary numbers where the lit LED is represented by 1 shown next to the hexadecimal and decimal equivalent, what we are seeing is:
00000001 = 0x01 = 1
00000010 = 0x02 = 2
00000100 = 0x04 = 4
00001000 = 0x08 = 8
00010000 = 0x10 = 16
00100000 = 0x20 = 32
01000000 = 0x40 = 64
10000000 = 0x80 = 128
01000000 = 0x40 = 64
00100000 = 0x20 = 32
00010000 = 0x10 = 16
00001000 = 0x08 = 8
00000100 = 0x04 = 4
00000010 = 0x02 = 2
00000001 = 0x01 = 1
In microcontroller applications, we will often be dealing with the states of bytesized ports, like Port D. A port is a place where ships come and go, or in the case
46
Chapter 4: C Types, Operators, and Expressions
of a microcontroller it is a place where outside voltages (0v or 3v) can be read or set.
We use binary and hexadecimal numbers for ports because it is cumbersome and non-intuitive to think of port data as decimal numbers, Quick, what will 66 look like on our LEDs? Quick, what will 01000010 look like on our LEDs? Since 01000010 = 66, you see my point? And I bet you get the joke at the beginning of this section.
The hexadecimal system is another commonly seen number system used in microcontrollers. It has a base of 16, that is 16 states per digit:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F.
Since we use numbers to the base 10 because we have ten digits, fingers if you count the thumb as a finger, to count with. It might help to imagine an alien with 16 fingers, or better yet: 4 hands with three fingers and on thumb on each. In C, a hexadecimal number is preceded by 0x. The hex byte representation of the decimal number 129 is 0x81. The decimal and binary equivalents of the hex numbers are:
0 = 0000 = 0x0
1 = 0001 = 0x1
2 = 0010 = 0x2
3 = 0011 = 0x3
4 = 0100 = 0x4
5 = 0101 = 0x5
6 = 0110 = 0x6
7 = 0111 = 0x7
8 = 1000 = 0x8
9 = 1001 = 0x9
10 = 1010 = 0xA
11 = 1011= 0xB
12 = 1100 = 0xC
13 = 1101 = 0xD
14 = 1110 = 0xE
15 = 1111 = 0xF
47
Chapter 4: C Types, Operators, and Expressions
It is very common for new users of hex numbers to make the mistake of saying, ‘Well there are 16 hex integers, so 0xF, the last one, is 16.’ We make this mistake because we think of counting beginning with 1, but for most computer use you’ll see counting beginning with 0. 0 is the first integer and 15 is the 16th integer. When you count like a computer your first digit (left thumb?) is 0 not 1. If a computer had those alien hands to count on, the first thumb would be 0 and the last would be 15 (0xF if it was speaking hex instead of dec). Try to keep this in mind because it will bite you later.
Experienced microcontroller programmers memorize the binary equivalent of hex digits and find hex numbers very useful. For instance, given 0xA9, what would the LEDs (or the voltage states of an 8-bit register) look like? If you memorize the table, you come up with 0xA = 1010 and 0x9 = 1001, so the LEDs (voltage states) will look like: 10101001. As pointed out earlier, ask the same question in decimal, what will 169 look like on the LEDs and good luck, on doing that in your head. Look at Appendix 5 to see all the byte states in decimal, hexadecimal, and binary. Finally, all jokes equating byte to bite are prohibited.
char
The name of this data type is short for character, and is typically used to represent a character in the ASCII character set (Appendix 4 – ASCII Table). Originally, there were 127 ASCII characters used by Teletype machines to transmit and receive data. You will note that in Figure 1, you see Dennis Ritchie, who wrote C, standing next to Ken Thompson, who wrote UNIX, working on a Teletype machine. Clunky as they were (the Teletype, not Ritchie and Thompson), Teletypes were light years ahead of entering data by individual switches representing each bit of data. Teletypes send and receive characters so a lot of C, especially the standard library, is character oriented. The number of bits in a char is machine dependent, but in all machines I’ve encountered including the AVR, a char is an 8-bit byte which can have 256 bit states. The computer uses this byte of data as representing a signed value from –128 to + 127.
The ASCII code was extended to include characters for 128 to 255 primarily to do weird European characters, math symbols, and character graphics on early PCs.
48
Chapter 4: C Types, Operators, and Expressions
unsigned
If the modifier unsigned is used in the definition of a char variable: ‘unsigned char’, the value is from 0 to 255. Many C compilers will have ‘byte’ or ‘Byte’ defined as equaling ‘unsigned char’. The ‘byte’ keyword is not part of C, but it is very convenient, since in microcontrollers we usually use a lot of numbers, but not a lot of ‘char’acters.
int
On AVR microcontrollers int declares a 16 bit data variable as having values from –32768 to +32767. A variable declared with ‘unsigned int’ will have a value from 0 to 65535.
The long and short of it
Everybody else makes that dumb joke at this point, so why be different?
You can declare variables as ‘short int’ and ‘long int’. For C the size is machine dependent, but on many systems a short int is the same as an int, 16 bits, while a long int is 32 bits.
Variable Names
The changeable data you are processing is stored in bytes of RAM, Random Access Memory, at specific addresses. Variables are names that provide an alias for the address being used. We’ll look at the gory details in the ‘Variables External, Static, and Register’ section of.
Constants
Constants are data that cannot be changed by the program and are usually stored in ROM, Read Only Memory. We could just type in the constant value wherever needed, but that will get old quick, so we alias the value with a name. We usually do this in a header file or at the start of the software module, which adds the advantage that if we ever want to change the constant we can do it once in the definition instead of at each occurrence in the code. By convention, constant names are all caps. For example we might want to use pi in calculation (pi containts a decimal so we use the float data type) so we define as follows:
#define PI 3.1415926
49
Chapter 4: C Types, Operators, and Expressions
We can then use PI anywhere in our software and the compiler will automatically substitute the numerical value for it:
float pieCircumference = 0.0; float piePanRadius = 0.0;
pieCircumference = PI * (piePanRadius^2);
Declarations
A declaration is a text statement that declares to the complier how your words are to be used. When you declare ‘unsigned char counter = 0’ you are telling the compiler that when it encounters the word ‘counter’ to consider it as data stored at some specific location with the alias name ‘counter’ that can have values from 0 to 255, but in this case initially has a value of 0.
Arithmetic Operators
Operators seem like ordinary arithmetic or algebra symbols, and they mostly are. But they are different from arithmetic or algebra often enough that you need to pay attention when operations don’t act like you think they should. The compiler might just be doing what you told it to do, rather than what you wanted it to do. An example of the kind of confusion you can run into when you use the ‘=’ assignment operator and the ‘==’ ‘is equal to’ operator:
x = y;
if(x==y) _delay_loop_2(30000);
The first statement assigns x to the value of y. The second 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’ becomes meaningless because the condition, x=y, is always true, so the delay will always run. The WinAVR compiler will think something is strange and issue this warning:
Warning: suggest parentheses around assignment used as truth value
50