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

MPS - Ch07 - AVR - C Programming

This document discusses programming the AVR microcontroller in C language. It covers reasons for using C over assembly, C data types for AVR including unsigned char, char, unsigned int, and others. It also discusses compiling C code, I/O operations, logic operations, and other common programming tasks in C.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
112 views

MPS - Ch07 - AVR - C Programming

This document discusses programming the AVR microcontroller in C language. It covers reasons for using C over assembly, C data types for AVR including unsigned char, char, unsigned int, and others. It also discusses compiling C code, I/O operations, logic operations, and other common programming tasks in C.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 92

Chapter 7

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

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

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

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


return 0;
}
25
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;
} 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;

DDRB = 0xFF; //PORTB is output

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


PORTB = mynum[z];

while(1); //stay here forever


return 0;
} 28
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.
29
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.
30
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;
}
// Notice that the maximum value for unsigned int is 65,535
31
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;
} 32
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.
33
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
//compiler and examine the result.
delay100ms();
PORTB = 0x55;
delay100ms();
}
return 0;
} 34
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.

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.

• 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.
39
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;
}

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

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

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 examine the results.
48
49
50
51
52
53
54
Example 7 -17 (cont.d)

55
56
57
58
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.

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

• For example, 11111101 or FDH is 253 in decimal. The


following is one version of an algorithm for conversion of
hex (binary) to decimal:

79
80
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

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

You might also like