Файл: C Programming for microcontrollers (Joe Pardue, 2005).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.06.2025
Просмотров: 2457
Скачиваний: 2
Chapter 4: C Types, Operators, and Expressions
Shift |
<< >> |
Left to right |
Relational (inequality) |
< <= > >= |
Left to right |
Relational (equality) |
== != |
Left to right |
Bitwise AND |
& |
Left to right |
Bitwise XOR |
^ |
Left to right |
Bitwise OR |
| |
Left to right |
Logical AND |
&& |
Left to right |
Logical OR |
|| |
Left to right |
Conditional |
?: |
Right to left |
Assignment |
= *= /= %= += -= <<= >>= &= |= |
Right to left |
^= |
||
Sequential evaluation |
, |
Left to right |
64
Chapter 4: C Types, Operators, and Expressions
Projects
Port Input and Output
Figure 13 ATMega169 Block Diagram
We skimmed over a lot in Chapter 2 so that we could get some LEDs blinking. Let’s now take a more detailed look at I/O ports.
65
Chapter 4: C Types, Operators, and Expressions
When this book was written, Digi-Key listed AVRs with as few as 6 I/O pins on the ATTINY11 ($0.54) to as many as 54 on the ATMEGA169 ($8.60), the microcontroller used on the Butterfly. Most of these pins are organized into ports, collections of pins that are setup and used with port specific access and control registers. Many of the pins have more than one possible function: they can be used to input or output digital logic data or they might be used for detecting external interrupts or as input for clocks or for analog to digital conversions and so on. In this section we’ll be looking at digital I/O.
The ATMEGA169 on the Butterfly has six 8-bit and one 4-bit general purpose I/O ports shown in Figure 13 ATMega169 Block Diagram (copied from the ATMega169 data book page 3, Figure 2.) Looks mighty complex doesn’t it? Well this is a simplified block diagram of a circuit that is vastly more complex. When you see a photomicrograph of these chips they resemble aerial photos of a vast ancient city with streets laid out in a grid surrounded by a wall. The ports are the gates to the city where the ancient electrons riding their very tiny ancient donkeys enter and leave the city. I’d continue in this vein but then I’d probably win a prize in the awful metaphor competition so I’ll stop.
ATmega169 Silicon Die Curtesty of Christopher Tarnovsky from Flylogic.net
66
Chapter 4: C Types, Operators, and Expressions
Each port has three associated I/O memory locations, that act as guards determining who shall pass (guess I won’t stop):
1.Data Direction Register - DDRx – Read/Write
2.Data Register – PORTx – Read/Write
3.Port Input Pins – PINx – Read Only
For example port A has: PORTA, DDRA, and PINA.
When used for general purpose I/O the port Data Direction Register must be set to tell the micro whether a pin will be used for input or output. To use a pin for input, set the associated DDRx bit to 0; to use it as output set it to 1. For example, to use the upper 4 bits of PORTD as inputs and the lower 4 bits as output, set the bits to 00001111, which, as we’ve seen, in hex is 0x0F:
DDRD = 0x0F;
In this project we will set port B to input data from switches and port D to output +3V to drive LEDs. We use the PINB register to read the switches from port B and write the value to port D using the PORTD register.
First we set the DDRB register so that all the pins are used as inputs:
DDRB = 0x00.
Next we set the DDRD register so that all the pins are used as outputs:
DDRD = 0xFF.
Then we write an infinite loop that gets the switch data from port B using PINB and equates it to PORTD that will light the LEDs.
Open a new C/C++ file in Programmers Notepad and write the following program. Save it as PortIO.c in a new directory PortIO.
// PortIO.c #include <avr/io.h>
67
Chapter 4: C Types, Operators, and Expressions
int main (void)
{
// Init port pins
DDRB = 0x00; // set port B for input DDRD = 0xFF; // set port D for output
while(1)
{
PORTD = PINB;
}
}
Open the makefile in the Blinky directory and save it to the PortIO directory then change:
TARGET = PortIO.
Follow the Blinky example to write, compile, and download this little program. Remember to turn the Butterfly off and back on, then hold down the center joystick button before and while clicking on the ‘AVR prog…’ menu item in AVRStudio. Also remember to browse to the PortIO.hex file in AVRStudio (I often forget to change the hex file and end up programming the Butterfly with an earlier hex file). And finally after the code downloads, remember to turn the Butterfly off and back on then click the joystick to the upper position to start the program.
If everything goes as planned, the LEDs will display the state of the switches as shown below. I told you we’d have some lame examples.
68
Chapter 4: C Types, Operators, and Expressions
Figure 14: Port I/O switch input and LED output
69
Chapter 4: C Types, Operators, and Expressions
Cylon Eye Speed and Polarity Control
In this example we will use port B to input data that we will use to control the Cylon eye movement rate and the LED polarity. By polarity I mean that we will set either all the LEDs on except the sweep LED which will be off, or all the LEDs off and the sweep LED on. We will control the polarity with the switch connected to the port B pin 7, leaving the lower pins to allow us to set the speed increase factor from 0 to 127.
In this example we will use the ~ bitwise operator to invert the LEDs on port D.
Open PortIO.c in Personal Notepad and save it as CylonEyes.c in a new directory CylonEyes. Make the following changes to the main() function
// CylonEyes.c #include <avr/io.h> #include <avr/delay.h>
int main (void)
{
//declare and initialize the scroll delay_count unsigned long delay_count = 10000;
//declare a variable for the speed increase unsigned long increase = 0;
//declare a variable for the polarity
unsigned char polarity = 0;
// Init port pins
DDRB = 0x00; // set port B for input DDRD = 0xFF; // set port D for output
while(1)
{
//read the switches increase = PINB;
//set the polarity if(increase > 127)
{
increase -= 127; polarity = 1;
70
Chapter 4: C Types, Operators, and Expressions
}
else polarity = 0;
// set the delay count
delay_count = 5000 + (increase * 500);
// scroll those eyes
for(int i = 1; i <= 128; i = i*2)
{
if(polarity) PORTD = ~i; else PORTD = i; _delay_loop_2(delay_count);
}
for(int i = 128; i > 1; i -= i/2)
{
if(polarity) PORTD = ~i; else PORTD = i; _delay_loop_2(delay_count);
}
}
}
Open the makefile in the Blinky directory and change TARGET = CylonEyes then save it to the CylonEyes directory. Compile, load, and play.
Figure 15: Bit 7 high |
Figure 16: Bit 7 low |
71
Chapter 4: C Types, Operators, and Expressions
72
Chapter 5: C Control Flow
Chapter 5: C Control Flow
We specify the order in which computations are performed with control statements. We’ve already peeked at some of these concepts, now Let’s jerk open the kimono and take a good hard look.
Statements and Blocks
Expressions such as PORTD = ~i or _delay_loop_2(30000) or i -= 128 become statements when they are followed by a semicolon:
PORTD = ~i; _delay_loop_2(30000); i -= 128;
The semicolon terminates the statement.
Compound statements are made by enclosing a group of statements or declarations in block delimited by braces ‘{‘ and ‘}’. This causes the compiler to handle the block as a unit.
Tale of a bug:
I wrote the following statement:
while(QuarterSecondCount < 17600); QuarterSecondCount = 0;
Then decided that the 17600 wait count was too long so I changed it to 2200:
while(QuarterSecondCount < 2200)//17600); QuarterSecondCount = 0;
But I wanted to leave the 17600 in case I ever needed it again, so I commented it out. Do you see a problem here?
Well, what I meant to say was:
while(QuarterSecondCount < 2200); QuarterSecondCount = 0;
73
Chapter 5: C Control Flow
Which is two statements, the first waits while an interrupt increments QuarterSecondCount in the background, and once that is finished the QuarterSecondCount is set to zero. What the compiler saw was:
while(QuarterSecondCount < 2200) QuarterSecondCount = 0;
because the compiler doesn’t see the comments – the \\17600;. See the problem yet?
Well how about he equivalent statement:
while(QuarterSecondCount < 2200) QuarterSecondCount = 0;
The compiler also doesn’t know about the line break, all it sees is the last statement, which says that while QuarterSecondCount is less than 2200, set QuarterSecondCount to 0. So each time the interrupt incremented QuarterSecondCount, this statement set it back to zero.
This is the kind of bug, that after spending X amount of time locating, you carefully hide it from your boss lest she think you are stupid or careless or both. Fortunately, I am my own boss, so I’ve learned to live with my stupid and careless employee. (I fired myself once, but that just didn’t work out.)
If-Else and Else-If
We can make decisions using the if-else statement:
if (expression) statement1
else
statement2
If the expression has a non-zero result (it is true), then we do statement 1, if the expression has a 0 result (it is false) we do statement 2. We can make a list of related decisions using else if:
74
Chapter 5: C Control Flow
if (expression1) statement1
else if (expression2) statement2
else if (expression3) statement3
else
statement4
In this case each expression will be evaluated sequentially looking for the first non-zero (true) expression and if they all equal 0 (false) we do statement 4. You can omit the final else statement if you want to do nothing if all the expressions are 0 (false). We will use an example of this construction later when we write an example program for using the joystick interrupts:
if(input == KEY_PLUS)PORTD= ~0x01;
else if(input == KEY_NEXT)PORTD = ~0x02; else if(input == KEY_PREV)PORTD = ~0x04; else if(input == KEY_MINUS)PORTD = ~0x08; else if(input == KEY_ENTER)PORTD = ~0x10;
Which may be read as: if the input is equal to KEY_PLUS then set port D equal to the inverse of a byte equal to 1 (a byte of 1 is binary 00000001, the inverse is 11111110 and since we output a 0 to a pin to light and LED, this statement lights the LED). If the first line is true then the rest of the statements are skipped. If the first line isn’t true, then each line is evaluated sequentially until a true expression is found or it drops out the bottom and does nothing.
Switch
The ‘if else’ construction limits us to expressions that are either true or false. If we want to make decisions using expressions that can have any numeric result we use the switch statement that selects an expression with results equal to a specified constant.
75