0% found this document useful (0 votes)
5 views

Lecture 4__Basic AVR Programming in C

This lecture covers basic AVR programming in C, focusing on AVR microcontroller architecture, C data types, and coding practices for various operations. Key topics include memory types, programming advantages of C over assembly, and methods for creating time delays. The lecture also provides examples of C programs for I/O operations, data manipulation, and memory management specific to AVR microcontrollers.

Uploaded by

hocbai1211
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

Lecture 4__Basic AVR Programming in C

This lecture covers basic AVR programming in C, focusing on AVR microcontroller architecture, C data types, and coding practices for various operations. Key topics include memory types, programming advantages of C over assembly, and methods for creating time delays. The lecture also provides examples of C programs for I/O operations, data manipulation, and memory management specific to AVR microcontrollers.

Uploaded by

hocbai1211
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 97

International University

MICRO-PROCESSING
SYSTEM
Lecture 4:
Basic AVR Programming in C

Vo Minh Thanh, M.Eng


School Of Electrical Engineering
1
OBJECTIVES
Upon completion of this Lecture, you will be able to:
• Understand AVR Microcontroller Architecture
• Examine C data types for the AVR
• Code C programs for time delay and 1/0 operations
• Code C programs for 1/0 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
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

3. 32KB Flash Program Memory


– Used to store program code
– Memory contents retained when
power is off (non-volatile)
– Fast to read; slow to write
– Can only write entire “blocks” of
memory at a time
– organized in 16-bit words
(16KWords)

20112012-I Module 2/5


ATMega32 Programmer Model:
Memory
• AVR microcontrollers are Harvard architecture.
This means, that in this architecture are separate
memory types (program memory and data
memory) connected with distinct buses. Such
memory architecture allows processor to access
program memory and data memory at the same
time. This increases performance of MCU
comparing to CISC architecture, where CPU
uses same bus for accessing program memory
and data memory.
• Each memory type has its own address space:

Type Flash RAM EEPROM


F_END Size, kB RAMEND Size, kB E_END Size, kB
Atmega8 $0FFF 8 $045F 1 $1FF 0.5
Atmega32 $3FFF 32 $085F 2 $3FF 1
Atmega64 $7FFF 64 $10FF 4 $7FF 2
Atmega128 $FFFF 128 $10FF 4 $FFF 4
ATMega32 Programmer Model: Program
Memory
Flash Memory Layout
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
8
C language

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

Data Type Size in bits Data Range/Usage


unsigned char 8 0 to 255
char 8 -128 to +127
unsigned int 16 0 to 65,535
int 16 -32,768 to +32,767
unsigned long 32 0 to 4,294,967,295
long 32 -2,147,483,648 to +2,147,483,648
float 32 ±1.175e-38 to ±3.402e38
double 32 ±1.175e-38 to ±3.402e3

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

while(1); //needed if running on a


trainer 30
Example 7-3
Write an AVR C program to toggle all the bits of Port B 200
times.
Solution:
//toggle PB 200 times
#include <avr/io.h> //standard AVR header
int main(void) //the code starts from here
{
DDRB = 0xFF; //PORTB is output
PORTB = 0xAA; //PORTB is 10101010
unsigned char z;

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;

DDRB = 0xFF; //PORTB is output

for(z = 0;z <= 8; z++)


PORTB = mynum[z];

while(1); //stay here forever


return 0;
} 33
Unsigned int
• The unsigned int is a 16-bit data type that takes a
value in the range of 0 to 65,535 (0000-FFFFH).
• In the AVR, unsigned int is used to define 16-bit
variables such as memory addresses. It is also used to
set counter values of more than 256.
• Because the AVR is an 8-bit microcontroller and the int
data type takes two bytes of RAM, we must not use
the int data type unless we have to. Because registers
and memory accesses are in 8-bit chunks, the misuse
of int variables will result in larger hex files, slower
execution of program, and more memory usage.
• For AVR programming, we do not use signed int in
places where unsigned char will do the job.
• Remember that the C compiler uses signed int as the
default unless we specify the keyword unsigned.
34
Signed int and Other data types
• Signed int
Signed int is a 16-bit data type that uses the most
significant bit (D15 of 015-DO) to represent the- or+
value. As a result, we have only 15 bits for the
magnitude of the number, or values from -32,768 to
+32,767.

• Other data types


The unsigned intis limited to values 0–65,535 (0000–
FFFFH). The AVR C compiler supports long data types, if
we want values greater than 16-bit. Also, to deal with
fractional numbers, most AVR C compilers support float
and double data types. See Examples 7-5 and 7-6.
35
Example 7-5
Write an AVR C program to toggle all bits of Port B 50,000 times.
Solution:
#include <avr/io.h> //standard AVR header
int main(void)
{
unsigned int z;

DDRB = 0xFF; //PORTE is output

for(z=0; z<50000; z++)


{
PORTB = 0x55;
PORTB = 0xAA;
}

while(1); //stay here forever


return 0;
} 36
Example 7-6
Write an AVR C program to toggle all bits of Port B 100,000
times.
Solution:
//toggle PB 100,000 times
#include <avr/io.h> //standard AVR header
int main(void)
{ unsigned long z; //long is used because it should
//store more than 65535.
DDRB = 0xFF; //PORTE is output
for(z=0;z<100000;z++)
{
PORTB = 0x55;
PORTB = 0xAA;
}
while(1); //stay here forever
return 0; 37
Time delay
• There are three ways to create a time delay in AVR C
1. Using a simple for loop
2. Using predefined C functions
3. Using AVR timers
• In creating a time delay using a for loop, we must be mindful of two
factors that can affect the accuracy of the delay:
1. The crystal frequency connected to the XTAL1-XTAL2 input
pins is the most important factor in the time delay calculation.
2. The second factor that affects the time delay is the compiler
used to compile the C program. In the case of C programs, it is the C
compiler that converts the C statements and functions to Assembly
language instructions. As a result, different compilers produce
different code. In other words, if we compile a given C program with
different compilers, each compiler produces different hex code.
• For the above reasons, when we use a loop to write time delays for C,
we must use the oscilloscope to measure the exact duration. See
Example 7-7.
38
Example 7-7
Write an AVR C program to toggle all the bits of Port B continuously with a
100 ms delay. Assume that the system is ATmega 32 with XTAL = 8 MHz.
Solution:
#include <avr/io.h>
void delay100ms(void);

int main(void) void delay100ms(void)


{ {
DDRB = 0xFF; unsigned int i ;
while (1) for(i=0; i<42150; i++);
{ }
PORTB = 0xAA; //try different numbers on your
delay100ms(); //compiler and examine the
PORTB = 0x55; result.
delay100ms();
}
return 0;
} 39
Time delay (cont.d)
• Another way of generating time delay is to use predefined
functions such as _delay_ ms() and _delay_ us() defined in
delay.h in WinAVR or delay_ ms() and delay_ us() defined in
delay.h in Code Vision.
• The only drawback of using these functions is the
portability problem. Because different compilers do not use the
same name for delay functions, you have to change every place
in which the delay functions are used, if you want to compile your
program on another compiler. To overcome this problem,
programmers use macro or wrapper function.
• Wrapper functions do nothing more than call the predefined delay
function. If you use wrapper functions and decide to change your
compiler, instead of changing all instances of predefined delay
functions, you simply change the wrapper function.
• Look at Example 7-8. Notice that calling a wrapper function may
take some microseconds.
• The use of the AVR timer to create time delays will be discussed
in Chapter 9. 40
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);
}
return 0; 41
}
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

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.

• Bit size I/0


The I/0 ports of ATmega32 are bit-accessible. But some AVR
C compilers do not support this feature, and the others do
not have a standard way of using it.
For example, the following line of code can be used in Code
Vision to set the first pin of Port B to one:
PORTB.0 = 1;
but it cannot be used in other compilers such as WinAVR.
To write portable code that can be compiled on different
compilers, we must use AND and OR bit-wise operations to
access a single bit of a given register.
44
Example 7-9
LEDs are connected to pins of Port B. Write an AVR C program that
shows the count from 0 to FFH (0000 0000 to 1111 1111 in binary)
on the LEDs.
Solution:
#include <avr/io.h> //standard AVR header

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);

// Set and Clear a Bit: using shift operators  more


readable
PORTB |= (1 << (PB3));
PORTB &= ~(1 << (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

The following shows some examples using the C bit-wise


operators:
1. 0x35 & 0x0F = 0x05 /*ANDing */
2. 0x04 | 0x68 = 0x6C /* ORing */
3. 0x54 ^ 0x78 = 0x2C /* XORing */
4. ~0x55 = 0xAA /* Inverting 55H */
Examples 7-12 through 7-20 show how the bit-wise operators
are used in C. Run these programs on your simulator and 53
54
55
56
57
58
59
Example 7 -17 (cont.d)

60
61
62
63
Compound assignment operators in C

• To reduce coding (typing) we can use compound


statements for bit-wise operators in C. See Table
7-3 and Example 7-21.

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

• Many compilers have some predefined functions


to convert data types. In Table 7-6 you can see
some of them. To use these functions, the
stdlib.h file should be included. Notice that these
functions may vary in different compilers

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

You might also like