Файл: C Programming for microcontrollers (Joe Pardue, 2005).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.06.2025
Просмотров: 2456
Скачиваний: 2
Chapter 4: C Types, Operators, and Expressions
‘a’ = 0x61 = 01100001 ~0x20 = 11011111
--------------------
AND = 01000001 = 0x41 = ‘A’
This is a lot harder for us than ordinary subtraction, but much easier for the machine.
While using &= and/or |= is acceptable, the Butterfly code generally does this a little differently, not to make your life harder, but to make the code a little clearer, though it won’t seem that way at first. When we create masks to set or clear bits, we will name the bits so for instance the first bit in port D is named PD0 and we can guess that the eighth bit is named PD7. That’s simple, but it gets hairy when we give complicated names to all the bits in the dozens of registers. For instance in the Timer0 register: TCCR0A, Timer/Counter Control Register A we have the following bits named (page 90 ATMEGA169 databook):
Bit 7 = FOC0A – Force Output Compare A
Bit 6 = WGM00 – Waveform Generation Mode 0
Bit 5 = COM0A1 – Compare Match Output Mode 1
Bit 4 = COM0A0 – Compare Match Output Mode 0
Bit 3 = WGM01 – Waveform Generation Mode 1
Bit 2 = CS02 – Clock Select Bit 2
Bit 1 = CS01 – Clock Select Bit 1
Bit 0 = CS00 – Clock Select Bit 0
Bits 0, 1, and 2 the Clock Select Bits are defined as:
57
Chapter 4: C Types, Operators, and Expressions
Figure 12: from page 92 of the ATMega169 data book
Let’s initialize the timer with:
// Set Fast PWM mode and CLK/256 prescaler TCCR0A |= (1<<WGM01)|(1<<WGM00)|(4<<CS00);
We use the left shift operator ’<<’ to shift the number before the operand to the numeric position in the byte specified by the number following the operand. In the case of (1<<WGM01) we shift a 1 to the left by WGM01 bit positions, and we see from iom169.h:
/* TCCR0A */ |
7 |
#define FOC0A |
|
#define WGM00 |
6 |
#define COM0A1 |
5 |
#define COM0A0 |
4 |
#define WGM01 |
3 |
#define CS02 |
2 |
#define CS01 |
1 |
#define CS00 |
0 |
WGM01 = 3, so (1<<WGM01) is the same as (1<<3) and means to shift 0000001 three places left to 00001000. Now look at The TCCR0A register and notice where the WGM01 bit is located. Ah ha! Like I said, we have a way of dealing with a bit by a name.
58
Chapter 4: C Types, Operators, and Expressions
But wait, wouldn’t that mean that (4<<CS00) means we are setting the CS00 bit to 4? But a bit can only be 0 or 1 so how the heck do we set a bit to 4?. Well, or course we don’t. The CS00 = 0, so we are left shifting the number 4 by 0 meaning we aren’t doing any shifting of the 4, we are just ORing it with the other two values:
TCCR0A |= (1<<WGM01)|(1<<WGM00)|(4<<CS00);
Since 4 = 00000100, we will be setting the CS02 bit, not the CS00 bit. So why didn’t we say (1<<CS02) instead of (4<<CS00)? And the answer is ‘because’. Actually the answer is that the lower three bytes of the TCC0RA register can be considered a three bit field for a number used to select the clock. The number 4 selects the clock/256 prescaler (see the Clock Select Bit table in Figure 12 above). Now we can see that (5<<CS00) would mean set the clock to clk/1024 and so forth. We will often think in terms of multi-bit fields.
Our goal was to ‘Set Fast PWM mode, CLK/256 prescaler’ (this will be explained later in the timer section) so we want to set bits 6, 3, and 2 (01001100 = 0x4C) without affecting the other bits. If we OR it like before we would:
TCC0RA |= 0x4C;
Which is:
TCC0RA = xxxxxxxx = we don’t know, or need too. 0x4C = 01001100
------------------------
OR = x1xx11xx = our bits are set the rest are not
changed.
The only problem is what does it mean to setup the timer with 0x4C? When you see TCC0RA |= 0x4C; you don’t know what it is doing and you have to derive the binary and look in the data book to figure it out. But using:
TCCR0A |= (1<<WGM01)|(1<<WGM00)|(4<<CS00);
The (1<<WGM01)|(1<<WGM00)|(4<<CS00) is the same as 0x4C except that we can read that we are setting both the Waveform Generation bits and we are setting
59
Chapter 4: C Types, Operators, and Expressions
the clock prescaler to 4, we may still have to use the data book to look at the Waveform generator and Clock Select tables, but it is still clearer isn’t it?
Which gives you a better chance at knowing what is going on?
TCC0RA |= 0x4C;
Versus:
TCCR0A |= (1<<WGM01)|(1<<WGM00)|(4<<CS00);
Heck, I don’t know, but it is how the guys in Norway do it so we’ll give them the benefit of the doubt and do it the Norway way and be able to steal all that cool Butterfly code.
Testing Bits
Now we have our timer setup, but suppose there is a function that needs to know how the Waveform Generator is set so that it can choose among several alternative actions? We can test a bit by using the AND operator, but not assigning any values. For example:
Waveform Generator Modes:
WGM01 |
WGM00 |
Mode |
0 |
0 |
Normal |
0 |
1 |
PWM, phase correct |
1 |
0 |
CTC |
1 |
1 |
Fast PWM |
if( !(TCC0RA & WGM01) && !(TCC0RA & WGM00) )
{
// do this only if in the normal mode
}
else if( !(TCC0RA & WGM01) && (TCC0RA & WGM00) )
{
// do this only if in the PWM, phase correct mode
}
else if( (TCC0RA & WGM01) && !(TCC0RA & WGM00) )
{
// do this only if in the CTC mode
}
60
Chapter 4: C Types, Operators, and Expressions
else if( (TCC0RA & WGM01) && (TCC0RA & WGM00) )
{
// do this only if in the Fast PWM mode
}
The (TCC0RA & WGM01) test will be 1, true, only if the WGM01 bit is 1, likewise for the (TCC0RA & WGM00) statement. The !(TCC0RA & WGM01), adding the ‘!’ or NOT to the statement means that it is true only if the innards of the () are false. The ‘if’ statement will only be true if both the first and (logical AND = &&) the second are true. So we’ve used two bitwise ANDs and a logical AND in this statement.
AND I hope it is clear. It isn’t, so get out the pencil and paper computer and work through it till it is. Seriously, when I was editing and reread this section I had a ‘good grief’ moment. But this is critical since we will be doing lots of clearing and setting control register bits. And it is as simple as I can make it, so do carefully walk through the example, pencil and paper in hand and work each example.
Assignment Operators and Expressions
Table 6: Assignment Operators
Operator |
Name |
Example |
Defined |
= |
Assignment |
x=y |
Put the value of y into x |
+= |
Compound |
x += y |
This provides a short cut way to write and |
-= |
assignment |
expression, the example: |
|
*= |
x += y; is the same as |
||
/= |
x = x + y; |
||
%= |
|||
<<= |
|||
>>= |
|||
&= |
|||
^= |
|||
|= |
|||
61
Chapter 4: C Types, Operators, and Expressions
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, if it is under 100° F, turn the fan off. You could write this as:
if( temp > 150) Fan(ON);
else
Fan(OFF);
Or you could use the C conditional operator ?: (
Table 3) as below:
temp > 150 ? Fan(ON) : Fan(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 expression 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 (Table 7). 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 as listed you get:
x = 50 + 10 / 2 – 20 * 4 x = 60 / 2 – 20 * 4
x = 30 – 20 * 4 x = 10 * 4
x = 40
62
Chapter 4: C Types, Operators, and Expressions
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 x = 50 + 10 / 2 – 80
x = 50 + 5 – 80 x = 55 – 80
x = -25
Some C gurus will memorize the precedence and associatively 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 Associatively in C. 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 was intended. But 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.
Table 7: Operator Precedence and Associativity in C
Operator Type |
Operators |
Associativity |
Expression |
() [] . -> |
Left to right |
Unary |
- + ~ ! * & ++ -- sizeof(type) |
Right to left |
Multiplicative |
* / % |
Left to right |
Additive |
+ - |
Left to right |
63