Lecture 4__Basic AVR Programming in C
Lecture 4__Basic AVR Programming in C
MICRO-PROCESSING
SYSTEM
Lecture 4:
Basic AVR Programming in C
2
AVR Architecture
ATMega32 Architecture
• Native data size is 8 bits (1
byte).
• Uses 16-bit data addressing
allowing it to address 216 =
65536 unique addresses.
• Has three separate on-chip
memories
• 2KB SRAM: 8 bits wide
used to store data
• 1KB EEPROM: 8 bits
wide used for persistent
data storage
• 32KB Flash: 16 bits
wide used to store
program code
• I/O ports A-D
• Digital input/output
• Analog input
• Serial/Parallel
• Pulse accumulator
ATMega32 Programmer Model:
Memory
1. 2KB SRAM
– For temporary data storage
– Memory is lost when power is shut
off (volatile)
– Fast read and write
2. 1KB EEPROM
– For persistent data storage
– Memory contents are retained when
power is off (non-volatile)
– Fast read; slow write
– Can write individual bytes
9
Compilation of C language source code
Download to MCU
Linux Windows
counter.hex
10
Software development tools
11
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 book we have chosen AVR GCC compiler
to integrate with AVR Studio. At the time of the
writing of this book AVR GCC and AVR Studio are
available as a free download from the Web.
12
Programming Language
13
Source Code Compilation
14
Assembling the application
15
Intel HEX Format Description
16
Basic Principles in C Language
Programming
17
Structure of C Code
18
Function Prototype
19
Tips on Making Programs Readable
20
Variables
21
Arithmetic and Binary Operations
22
Condition
23
Loops
24
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
25
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.
26
Table 7-1. Some Data Types Widely Used by C Compilers
27
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 28
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.
29
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;
31
}
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.
32
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;
42
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
43
• 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;
}
45
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;
} 46
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
unsigned char temp; PINC
DDRC = 0; // PORTC: Input if (temp < 100)
DDRB = 0xFF; // PORTB: Output PORTB = temp;
DDRD = 0xFF; // PORTD: Output else
PORTD = temp;
}
return 0;
}
47
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))
48
Special Functions of GCC and AVR Libc
49
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)) 50
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
51
• 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
52
interfacing. See Table 7-2.
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
60
61
62
63
Compound assignment operators in C
64
65
Example 7-21 (cont.d)
66
Bit-wise shift operation in C
67
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 68
69
70
71
72
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
73
ASCII numbers
74
Packed BCD to ASCII conversion
75
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.
76
77
// w = 0x34 & 0x0F = 0x04
// w = 0x04 << 4 = 0x40
// z = 0x37 & 0x0F = 0x07
// bcdbyte = 0x40 | 0x07 = 0x47
78
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 79
80
81
// G means Good
// B means Bad
82
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
83
the remainder, as was shown in Chapters 5 and 6.
Binary (hex) to decimal and ASCII conversion
in C
• For example, 11111101 or FDH is 253 in decimal.
The following is one version of an algorithm for
conversion of hex (binary) to decimal:
84
85
Data Type Conversion Functions in C
86
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
87
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 reason88
89
90
91
92
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
93
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
94
purpose of data storage (see Chapter 6).
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
95
96
97