MPS - Ch07 - AVR - C Programming
MPS - Ch07 - AVR - C Programming
AVR Programming in C
Refs:
1. The AVR microcontroller and embedded systems: using Assembly
and C, Muhammad Ali Mazidi, 2011 Pearson Education
2. Slides of Dr. Tomas Fryza, Brno University of Technology
1
OBJECTIVES
Upon completion of this chapter, you will be able to:
• Examine C data types for the AVR
• Code C programs for time delay and I/O operations
• Code C programs for I/O bit manipulation
• Code C programs for logic and arithmetic operations
• Code C programs for ASCII and BCD data conversion
• Code C programs for binary (hex) to decimal conversion
• Code C programs for data serialization
• Code C programs for EEPROM access
2
Outline
1. Data types and time delays in C
2. I/O programming in C
3. Logic operations in C
4. Data conversion programs in C
5. Data serialization in C
6. Memory allocation in C
3
Why program the AVR in C?
The following are some of the major reasons for
writing programs in C instead of Assembly:
1. It is easier and less time consuming to write in C than
in Assembly.
2. C is easier to modify and update.
3. You can use code available in function libraries.
4. C code is portable to other microcontrollers with little
or no modification
4
C language
5
Compilation of C language source code
Download to MCU
Linux Windows
counter.hex
6
Software development tools
7
C compilers
• Several third-party companies develop C compilers for
the AVR microcontroller.
• Our goal is not to recommend one over another, but to
provide you with the fundamentals of C programming for
the AVR.
• You can use the compiler of your choice for the chapter
examples and programs.
• For this course we have chosen WinAVR compiler to
integrate with AVR Studio.
8
Source Code Compilation
9
Assembling the application
10
Intel HEX Format Description
11
Basic Principles in C Language Programming
12
Structure of C Code
13
Function Prototype
14
Tips on Making Programs Readable
15
Variables
16
Arithmetic and Binary Operations
17
Condition
18
Loops
19
Outline
1. Data types and time delays in C
2. I/O programming in C
3. Logic operations in C
4. Data conversion programs in C
5. Data serialization in C
6. Memory allocation in C
20
C data types for the AVR C
• One of the goals of AVR programmers is to create
smaller hex files, so it is worthwhile to re-examine C
data types.
• A good understanding of C data types for the AVR can
help programmers to create smaller hex files.
• In this section we focus on the specific C data types that
are most common and widely used in AVR C compilers.
• Table 7 -1 shows data types and sizes, but these may
vary from one compiler to another.
21
Table 7-1. Some Data Types Widely Used by C Compilers
22
Unsigned char
• The unsigned char is an 8-bit data type that takes a value in the
range of 0-255 (00-FFH). It is one of the most widely used
data types for the 8-bit AVR.
• In declaring variables, we must pay careful attention to the size
of the data and try to use unsigned char instead of int if possible
for saving more memory space.
• Remember that C compilers use the signed char as the default
unless we put the keyword unsigned in front of the char (see
Example 7-1). We can also use the unsigned char data type for
a string of ASCII characters, including extended ASCII
characters. Example 7-2 shows a string of ASCII characters. See
Example 7-3 for toggling a port 200 times. 23
Example 7-1
Write an AVR C program to send values 00-FF to Port B.
Solution:
#include <avr/io.h> //standard AVR header
int main(void)
{
unsigned char z;
DDRB = 0xFF; //PORTB is output
for (z = 0; z <= 255; z++)
PORTB = z;
return 0;
}
//Notice that the program never exits the for loop because if you
//increment an unsigned char variable when it is 0xFF, it will
//become zero. => Correct z <= 255 to z < 255
24
Example 7-2
Write an AVR C program to send hex values for ASCII characters of 0,
1, 2, 3, 4, 5, A, B, C, and D to Port B.
Solution:
#include <avr/io.h> //standard AVR header
int main(void) //the code starts from here
{
unsigned char myList[]= "012345ABCD";
unsigned char z ;
DDRB = 0xFF; //PORTB is output
for( z = 0; z < 10; z++) //repeat 10 times and increment z
PORTB = myList[z] ; //send the character to PORTB
for(z = 0; z < 200; z++) //run the next line 200 times
PORTB = ~PORTB; //toggle PORTB
while(1);
return 0;
} 26
Signed char
• The signed char is an 8-bit data type that uses the most
significant bit (D7 of D7-DO) to represent the - or + value.
As a result, we have only 7 bits for the magnitude of the
signed number, giving us values from -128 to+ 127.
• In situations where + and - are needed to represent a
given quantity such as temperature, the use of the signed
char data type is necessary (see Example 7-4).
• Again, notice that if we do not use the keyword
unsigned, the default is the signed value. For that
reason we should stick with the unsigned char unless the
data needs to be represented as signed numbers.
27
Example 7-4
Write an AVR C program to send values of -4 to +4 to Port B.
Solution:
#include <avr/io.h> //standard AVR header
int main(void)
{
char mynum[]= {-4,-3,-2,-1,0,+1,+2,+3,+4};
unsigned char z;
35
Example 7-8
Write an AVR C program to toggle all the pins of Port C continuously with a 10 ms
delay. Use a predefined delay function in Win AVR.
Solution:
#define F_CPU 8000000UL // XTAL = 8MHZ = 8000000Hz
#include <util/delay.h> // _delay_ms(d) and _delay_us(d)
#include <avr/io.h> Note:
int main(void) The maximal possible delay with _delay_ms() is 262.14 ms
{ The maximal possible delay with _delay_us() is 768 us
void delay_ms(int d) { //delay in milliseconds
_delay_ms(d); }
DDRB = 0xFF; //PORTB is output
while (1) {
PORTB = 0xAA;
delay_ms(10);
PORTB = 0x55;
delay_ms(10);
}
36
return 0;
Outline
1. Data types and time delays in C
2. I/O programming in C
3. Logic operations in C
4. Data conversion programs in C
5. Data serialization in C
6. Memory allocation in C
37
C to Configure Ports
• Input/Output Port: assign an appropriate value to DDRx
Exs:
1) PORTA as input (write 0’s to DDRA)
DDRA = 0; // 0x00
2) PORTB as output (write 1’s to DDRB)
DDRB = 0xFF; // 0b11111111
• Input port with/without internal pull-up resistors:
– Write a one to PORTx.n will turn on internal pull-up resistor
– Write a zero to PORTx.n will turn off internal pull-up resistor
Ex: PORTB confihured as Input port with the internal pull-up
resistors on
DDRB = 0x00; // Input port
PORTB = 0xFF; // turn on the pull-up resistors
38
• Byte size I/0
To access a PORT register as a byte, we use the PORTx label where
x indicates the name of the port. We access the data direction
registers in the same way, using DDRx to indicate the data
direction of port x. To access a PIN register as a byte, we use the
PINx label where x indicates the name of the port. See Examples 7-
9, 7-10, and 7-11.
int main(void)
{
DDRB = 0xFF; //Port B is output
while (1)
{
PORTB = PORTB + 1;
}
return 0;
}
40
Example 7-10
Write an AVR C program to get a byte of data from Port B, and then send it
to Port C.
Solution:
#include <avr/io.h> //standard AVR header
int main(void)
{
unsigned char temp;
DDRB = 0x00; //Port B is input
DDRC = 0xFF; //Port C is output
while(1)
{
temp = PINB ; // or PORTC = PINB
PORTC = temp ;
}
return 0;
} 41
Example 7-11
Write an AVR C program to get a byte of data from Port C. If it is less than
100, send it to Port B; otherwise, send it to Port D.
Solution:
#include <avr/io.h>
while(1)
int main(void) {
{ temp = PINC; //read from PINC
unsigned char temp; if (temp < 100)
DDRC = 0; // PORTC: Input PORTB = temp;
DDRB = 0xFF; // PORTB: Output else
PORTD = temp;
DDRD = 0xFF; // PORTD: Output }
return 0;
}
42
Special Functions of GCC and AVR Libc
#include <avr/io.h>
// These following functions are defined in “avr/sfr_defs.h”
// This file was included in “avr/io.h”
// _BV Converts a bit number into a Byte Value (BV).
#define _BV(bit) (1 << (bit))
#define bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))
#define bit_is_clear(sfr, bit) (!(_SFR_BYTE(sfr) & _BV(bit)))
#define loop_until_bit_is_set(sfr, bit) do { } while
(bit_is_clear(sfr, bit))
#define loop_until_bit_is_clear(sfr, bit) do { } while
(bit_is_set(sfr, bit))
43
Special Functions of GCC and AVR Libc
44
Special functions - Examples
• Set/Clear a Bit
// Set and Clear a Bit: using _BV
PORTB |= _BV(PB3);
PORTB &= ~_BV(PB3);
• Check a bit
// If PD3 is 0 then
if (bit_is_clear(PIND,3)) ...
// If PD3 is 1 then
if (bit_is_set(PIND,3))
45
Outline
1. Data types and time delays in C
2. I/O programming in C
3. Logic operations in C
4. Data conversion programs in C
5. Data serialization in C
6. Memory allocation in C
46
• One of the most important and powerful features of the C
language is its ability to perform bit manipulation.
• Bit-wise operators in C
While every C programmer is familiar with the logical operators
AND (&&), OR (||), and NOT (!), many C programmers are less
familiar with the bitwise operators AND(&), OR (|), EX-OR(^),
inverter(~), shift right(>>), and shift left (<<).
These bit-wise operators are widely used in software engineering
for embedded systems and control; consequently, their
understanding and mastery are critical in microcontroller-based
system design and interfacing. See Table 7-2.
47
Table 7-2: Bit-wise Logic Operators for C
AND OR XOR NOT
A B
A&B A|B A^B Y= ~B
0 0 0 0 0 1
0 1 0 1 1 0
1 0 0 1 1 1
1 1 1 1 0 0
55
56
57
58
Compound assignment operators in C
59
60
Example 7-21 (cont.d)
61
Bit-wise shift operation in C
62
Bit-wise shift operation and bit manipulation
• Reexamine the last 10 examples. To do bit-wise I/0 operation in C,
we need numbers like 0b00100000 in which there are seven
zeroes and one one. Only the position of the one varies in different
programs.
• To leave the generation of ones and zeros to the compiler and
improve the code clarity, we use shift operations.
• For example, instead of writing “0b00100000" we can write
“0b00000001 << 5" or we can write simply “1 <<5".
• Sometimes we need numbers like 0b11101111. To generate such a
number, we do the shifting first and then invert it. For example, to
generate 0b11101111 we can write !(1<<4). See Example 7-22.
• Notice that to generate more complicated numbers, we can OR
two simpler numbers. For example, to generate a number that has
a one in position D7 and another one in position D4, we can OR a
number that has only a one in position D7 with a number that has
only a one in position D4. So we can simply write
(1 <<7)|(1 <<4). 63
64
65
66
67
Outline
1. Data types and time delays in C
2. I/O programming in C
3. Logic operations in C
4. Data conversion programs in C
5. Data serialization in C
6. Memory allocation in C
68
ASCII numbers
69
Packed BCD to ASCII conversion
70
ASCII to packed BCD conversion
• To convert ASCII to packed BCD, you first convert it to
unpacked BCD (to get rid of the 3), and then combine the
numbers to make packed BCD. For example, 4 and 7 on the
keyboard give 34H and 37H, respectively. The goal is to
produce 47H or "0100 0111", which is packed BCD.
71
72
// w = 0x34 & 0x0F = 0x04
// w = 0x04 << 4 = 0x40
// z = 0x37 & 0x0F = 0x07
// bcdbyte = 0x40 | 0x07 = 0x47
73
Checksum byte in ROM
• To ensure data integrity, the checksum process uses
what is called a checksum byte. The checksum byte is
an extra byte that is tagged to the end of a series of
bytes of data. To calculate the checksum byte of a series
of bytes of data, the following steps can be taken:
1. Add the bytes together and drop the carries.
2. Take the 2's complement of the total sum. This is
the checksum byte, which becomes the last byte
of the series.
• To perform the checksum operation, add all the bytes,
including the checksum byte. The result must be zero. If
it is not zero, one or more bytes of data have been
changed (corrupted). See Examples 7-28 through 7-30.
74
75
76
// G means Good
// B means Bad
77
Binary (hex) to decimal and ASCII conversion in C
• The printf function is part of the standard I/0 library in C and
can do many things including converting data from binary
(hex) to decimal, or vice versa. But printf takes a lot of
memory space and increases your hex file substantially. For
this reason, in systems based on the AVR microcontroller, it is
better to know how to write our own conversion function
instead of using printf.
• One of the most widely used conversions is binary to decimal
conversion (used for ADC, RTC, ....). In order to display binary
data, we need to convert it to decimal and then to ASCII.
Because the hexadecimal format is a convenient way of
representing binary data, we refer to the binary data as hex.
The binary data 00-FFH converted to decimal will give us 000
to 255. One way to do that is to divide it by 10 and keep the
remainder, as was shown in Chapters 5 and 6.
78
Binary (hex) to decimal and ASCII conversion in C
79
80
Data Type Conversion Functions in C
81
Outline
1. Data types and time delays in C
2. I/O programming in C
3. Logic operations in C
4. Data conversion programs in C
5. Data serialization in C
6. Memory allocation in C
82
Data serialization in C
Serializing data is a way of sending a byte of data one bit at a
time through a single pin of a microcontroller. There are two
ways to transfer a byte of data serially:
1. Using the serial port. In using the serial port, the programmer
has very limited control over the sequence of data transfer. The
details of serial port data transfer are discussed in Chapter 11.
2. The second method of serializing data is to transfer data one bit
a time and control the sequence of data and spaces between them.
In many new generations of devices such as LCD, ADC, and
EEPROM, the serial versions are becoming popular because they
take up less space on a printed circuit board. Although we can use
standards such as PC, SPI, and CAN, not all devices support such
standards. For this reason we need to be familiar with data
serialization using the C language. 83
84
85
86
87
Outline
1. Data types and time delays in C
2. I/O programming in C
3. Logic operations in C
4. Data conversion programs in C
5. Data serialization in C
6. Memory allocation in C
88
Flash, RAM, and EEPROM data space in the AVR (1/2)
In the AVR we have three spaces in which to store data. They are as
follows:
1. The 64K bytes of SRAM space with address range 0000-FFFFH. As
we have seen in previous chapters, many AVR chips have much
less than 64K bytes for the SRAM. We also have seen how we can
read (from) or write (into) this RAM space directly or indirectly. We
store temporary variables in SRAM since the SRAM is the scratch
pad.
2. The 2M words (4M bytes) of code (program) space with addresses
of 000000-1FFFFFH. This 2M words of on-chip Flash ROM space
is used primarily for storing programs (opcodes) and therefore is
directly under control of the program counter (PC). As we have
seen in the previous chapters, many AVR chips have much less
than 2M words of on-chip program ROM (see Table 7-7). We have
also seen how to access the program space for the purpose of
data storage (see Chapter 6).
89
Flash, RAM, and EEPROM data space in the AVR (2/2)
3. EEPROM. As we mentioned before, EEPROM can save data when the
power is off. That is why we use EEPROM to save variables that should
not be lost when the power is off. For example, the temperature set
point of a cooling system should be changed by users and cannot be
stored in program space. Also, it should be saved when the power is off,
so we place it in EEPROM. Also, when there is not enough code space,
we can place permanent variables in EEPROM to save some code space.
• To define a variable in EEPROM, you can put the eeprom directive in
front of it:
flash unsigned char mynum[] = "Hello"; //use Flash code space
eeprom unsigned char = 7; //use EEPROM space
90
91
92