Download as pdf or txt
Download as pdf or txt
You are on page 1of 50

(Revised) Rough Notes on Programming

AVR Microcontrollers in C.
Mechanical Engineering Report 2007/04
P. A. Jacobs
School of Engineering
The University of Queensland.
February 21, 2008

Preface
These notes follow on from the material that you studied in CSSE1000 Introduction
to Computer Systems. There you studied details of logic gates, binary numbers and
instruction set architectures using the Atmel AVR microcontroller family as an example.
In your present course (METR2800 Team Project I ), you need to get on to designing and
building an application which will include such a microcontroller. These notes focus on
programming an AVR microcontroller in C and provide a number of example programs
to illustrate the use of some of the AVR peripheral devices.

Session Contents

1. Introduction to the hardware and software development environment. A small but


complete application example is implemented with an ATmega88 microcontroller
on the STK500 development board.

2. An example embedded application. Elements of C programming: data types, vari-


ables, operators, expressions and statements. C quick reference sheet.

3. A more extensive application the uses serial communication and more C control
structures. Decision and control statements. Functions, scope of variables.

4. An application using the analog-to-digital converter. Pointers and addresses. Pass


by reference. Arrays and strings. The C preprocessor. Interrupts.

1
CONTENTS 2

Contents
1 Embedded computing system based on Atmel’s AVR 4

2 Example: flash a LED 7


2.1 Example peripheral: PORTD bidirectional port . . . . . . . . . . . . . . . 7
2.2 Development tools – software and hardware . . . . . . . . . . . . . . . . . 8
2.3 Preparing the firmware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4 C source code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3 Example 2: a task loop and a hardware timer 13


3.1 C source code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.2 Your own prototype board with ISP . . . . . . . . . . . . . . . . . . . . . . 15

4 Elements of a C program 17
4.1 Types of data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.2 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.3 Assignment statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5 Example 3: Serial-port communication 23


5.1 C source code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

6 Decision and flow-control statements 26


6.1 Conditional statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
6.2 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
6.3 Unconditional control statements . . . . . . . . . . . . . . . . . . . . . . . 29

7 Functions 30

8 Pointers-to and addresses-of variables 32

9 Functions revisited: pass-by-reference 33

10 Arrays 34
10.1 Pointers and arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
10.2 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

11 The C preprocessor 36

12 Example 4: Using the analog-to-digital converter 38


12.1 C source code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

13 Interrupts 42
13.1 Example 5: Using interrupts with a hardware timer. . . . . . . . . . . . . . 44
13.2 Example 6: Using interrupts to count button presses . . . . . . . . . . . . 45

14 Software architectures of embedded systems 46


14.1 Round-Robin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
14.2 Round-Robin with interrupts . . . . . . . . . . . . . . . . . . . . . . . . . 47
CONTENTS 3

A ANSI C Reference Card 49


1 EMBEDDED COMPUTING SYSTEM BASED ON ATMEL’S AVR 4

1 Embedded computing system based on Atmel’s AVR


• AVR microcontroller units (MCUs) all have the same core, i.e. same instruction set
and memory organisation.

• “flavours” are based on features and complexity

– tinyAVR: reduced feature set


– megaAVR: lots of features and peripheral devices built in
– AVR or classic AVR: in between range of features

• selection based on

– tight budget - choose one with just enough functionality


– convenience of development - choose one with “bells and whistles”

• in CSSE1000/COMP1300, you used the ATmega8515, a digital-only MCU

• for these demonstrations, we will work with the ATmega88

– has a nice selection of features (see following page), including a serial port and
an analog-to-digital converter (with several input channels)
– 28-pin narrow DIL package is convenient for prototyping and there are enough
I/O pins to play without needing very careful planning.
– pinout is shown at the start of the Atmel datasheet (book) on the ATmega88
[1]. You will be reading the pages of this book over and over...
– internal arrangement is around an 8-bit data bus
– “Harvard architecture” with separate paths and storage areas for program in-
structions and data
– We won’t worry too much about the details of the general-purpose registers,
the internal static RAM or the machine instruction set because we will let the
C compiler handle most of the details.
– However, memory layout, especially the I/O memory layout is important for
us as C programmers; the peripheral devices are controlled and accessed via
special function registers in the I/O memory space. For the ATmega88 data
memory, we have:
∗ 32 general purpose registers (0x0000–0x001F)
∗ 64 I/O registers (0x0020–0x005F)
∗ 160 extended I/O registers (0x0060–0x00FF)
∗ 1kbytes internal SRAM (0x0100–0x04FF)
1 EMBEDDED COMPUTING SYSTEM BASED ON ATMEL’S AVR 5
1 EMBEDDED COMPUTING SYSTEM BASED ON ATMEL’S AVR 6

Block diagram for the ATmega88, as scanned from the datasheet [1].

Pin layout for the ATmega88 for the 28-pin Plastic Dual Inline Package (PDIP), as
scanned from the datasheet.
2 EXAMPLE: FLASH A LED 7

2 Example: flash a LED


• The microcontroller version of the “Hello, World” program.

• We’ll look at the hardware, development environment and software required to make
this happen.

2.1 Example peripheral: PORTD bidirectional port


Schematic dragram for a digital I/O pin, as scanned from the datasheet.

• peripheral device can be used for digital input or output

• three I/O memory addresses are assigned to this port


I/O address bits name
7 6 5 4 3 2 1 0

PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0 PORTD


0x0B (0x2B)
data register
DDRD
DDD7 DDD6 DDD5 DDD4 DDD3 DDD2 DDD1 DDD0
0x0A (0x2A) data direc-
tion register

PIND7 PIND6 PIND5 PIND4 PIND3 PIND2 PIND1 PIND0 PIND


0x00 (0x29) input regis-
ter
• The names for the registers and the bits are contained in the header file supplied
with the C compiler. On my Linux computer, these definitions may be found in
/usr/avr/include/avr/iomx8.h.
2 EXAMPLE: FLASH A LED 8

• Registers DDRD and PORTD are readable and writeable with initial values for all
bits being 0. Register PIND is readable only.

• Writing a 0 to a bit in DDRD sets the corresponding pin to input (and a 1 will set
the pin to output. Note that initial value for DDRD bits being 0 implies that all
pins are initially configured for input.

• When set as an input pin, a pull-up resistor can be activated by writing a 1 to the
corresponding PORTD bit.

• Output buffers can source or sink an absolute maximum current of 40mA per I/O
pin and the whole device can cope with a total of 200mA. See section 27 of the
datasheet.

2.2 Development tools – software and hardware


• We will be using Atmel’s AVR Studio 4 as our Integrated Development Environment.

– Consists of editor, simulator, device programming software.


– We don’t have to remember so much about the individual tools because we can
usually select actions and options from the menus.
– Visit http://www.atmel.com to download a copy for home.

• We will use the WinAVR programming tools.

– Consists of the GNU C compiler and the AVR-libc standard C library and
peripherals library.
– Visit http://winavr.sourceforge.net to download a copy for home.

• Atmel’s STK500 development hardware which supports the “target” MCU by pro-
viding power, clock signal and input/output devices (such as switches, LEDs and
an RS232 port).

• To install our ATmega88 MCU in the STK500,

– use socket SCKT3200A2 (the green one)


– use the In-System Programming (ISP) header SPROG2 and connect (with the
6-pin cable) to the appropriate header (green)
– Other jumpers that should be installed:
∗ VTARGET, supply voltage to the MCU
∗ AREF, analogue reference voltage
∗ RESET, allow the master MCU (on the top of the STK500) to control the
target’s reset pin.
∗ XTAL1, use STK500 on-board clock as the main clock to the target MCU.
2 EXAMPLE: FLASH A LED 9

∗ OSCSEL, jumper 1→2 selects the on-board software-generated clock. This


is handy on the STK500 because you can adjust its frequency but remem-
ber that, when you build your own board, you must have a working clock
for the MCU to respond to in-system programming. It could be the MCU’s
internal 8MHz clock if you don’t want to build an external clock circuit.
∗ make sure that BSEL2 is not jumpered.
– Finally, connect a 10-pin jumper cable from PORTD header to the LEDs
header.

2.3 Preparing the firmware


(a) Start AVR Studio
(b) Create a new project and configure settings.
project type: AVR GCC
project name: flashleds1

create initial √
file
create folder
location: C:\avr-work
next
debug platform: AVR Simulator
device: ATmega88
Also, at this time, configure the editor (Tools→Options→Editor) to set tab width
4 and check “replace tabs with spaces”.
(c) Type in source code (from the following section)
Save as “flashleds1.c” and use Ctrl-s frequently.
(d) Build the project. (Build→Rebuild All) and check for errors.
– Behind the GUI, avr-gcc has cross-compiled the program and then linked it
with the standard library and start-up code to produce an executable program
that can be downloaded to the MCU. Alternatively, this code may be run in
the simulation environment.
– Note the messages in the lower window (labelled “Build”).
– Note the creation of an “Intel HEX” file that we will later download to the
MCU.
(e) Start the simulator (Debug→Start Debugging)
– Note that the yellow arrow points to the first line of code to be executed.
– Stepping options are shown in the toolbar (nongreyed now).
– Open the IO View entry I/O ATmega88, PORTD.
– “Step into” the code a couple of times to see the DDRD bits change.
– Hover the cursor over the “which bit” variable name to see details of this
variable’s value and location in memory. This doesn’t work for DDRD because
that is a macro name, however, one can see its address as well as its content
in the I/O View window.
2 EXAMPLE: FLASH A LED 10

(f) Download/program the executable program into the MCU.

– Tools→Program AVR...→Connect→Select “STK500 or AVRISP” and we should


get “STK500 Window” showing the state of the target MCU.
– Make sure that the power is supplied to the board and that the “status” LED
is a steady green.
– Press “Program” to download the executable program and run it.
– Note messages in the log window telling us how the download process went.
– Note the “Fuse” settings. The factory-set default settings on the ATmega88
seem to be
∗ Boot flash section size=1024 words, Boot start address=$0c00.
∗ Serial program downloading enabled (greyed on).
∗ Brown-out detection disabled.
∗ Divide clock by 8 internally.
∗ Internal RC oscillator 8MHz; Start up time: 6CK/14CL+65ms
We could try setting
∗ External clock; Start up time: 6CK/14CL+65ms
∗ Unset divide clock by 8 internally
then experiment with setting the STK500 Oscillator frequency. Values line
3.686 MHz seem odd but are good for RS232 communications.

• Close the STK500 connection window.


Save project.
Exit AVR Studio.
The MCU should continue to run independently so long as power is applied.
2 EXAMPLE: FLASH A LED 11

2.4 C source code


// f l a s h l e d s 1 . c
// Try out t h e AVR S t u d i o + AVRGCC + STK500 .
// PJ , 26−Feb −2007

// g e t t h e p r o c e s s o r − s p e c i f i c d e f i n i t i o n s f o r t h e IO memory s p a c e
#i n c l u d e <a v r / i o . h>

// macro d e f i n i t i o n t o s e l e c t b i t n , assuming 0 <= n <= 7


#d e f i n e BIT ( n ) ( 1 << ( n ) )

v o i d my delay ( u n s i g n e d c h a r c 1 s t a r t )
{
u n s i g n e d c h a r c1 , c2 ;
u n s i g n e d i n t count = 0 ;
f o r ( c1 = c 1 s t a r t ; c1 > 0 ; −−c1 ) {
f o r ( c2 = 2 5 5 ; c2 > 0 ; −−c2 ) {
count += 1 ;
}
}
// we i g n o r e t h e v a l u e o f count
return ;
}

i n t main ( v o i d )
{
unsigned char which bit ;
DDRD = 0xFF ; // a l l ou t put
which bit = 0;
w h i l e ( 1 ) { // never−e n d i n g l o o p
w h i c h b i t += 1 ;
i f ( which bit > 7 ) which bit = 0;
// Turn on a LED by w r i t i n g 0 t o i t s c o r r e s p o n d i n g MCU p i n
PORTD = ˜BIT ( w h i c h b i t ) ;
my delay ( 1 0 ) ;
}
r e t u r n 0 ; // we s h o u l d n e v e r g e t h e r e .
}

Notes on this program:


• Have used C++-style comments.

• Simple statements end with “;”.

• Compound statements are delimited by “{” and “}”.

• Execution starts at the main function.

• Nonzero values are interpreted at “true” in a Boolean context.

• Operations on individual bits in GCC.

// bit position macro


#define BIT(n) (1 << (n))

// turn on bit n, leaving the others unchanged


PORTD |= BIT(n);
2 EXAMPLE: FLASH A LED 12

// turn off bit n, leaving the others unchanged


PORTD &= ~BIT(n);

// toggle bit n
PORTD ^= BIT(n);

• Standard C integer objects (desktop computing)

– int is at least 16 bits


– sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long)

• GCC on AVR

– char is 8 bits
– can use shift operators to multiply or divide by powers of 2
– there is no hardware divide but newer MCUs have hardware multiply
– avoid the use of floating-point data and operations

• LED connection on STK500

+5V

R2
150
1

LED1

VTG
2

C
R1
B
Q1

10K
E

LEDn
3 EXAMPLE 2: A TASK LOOP AND A HARDWARE TIMER 13

3 Example 2: a task loop and a hardware timer


• The simplest arrangement for an embedded program that performs a loop of tasks
with a fixed period.

• Flowchart is somewhat trivial but does visualize the task loop clearly.

Start main

initialize hardware
setup_timer()

setup tumer reset timer wait for timer

set initial count


reset timer set register
in timer register
for prescale CK/1024

select bit
return
return timer
overflow?
No
light up LED

Yes

wait for timer


return
3 EXAMPLE 2: A TASK LOOP AND A HARDWARE TIMER 14

3.1 C source code


// flashleds2 . c
//
// The a p p l i c a t i o n doesn ’ t do much ; i t j u s t l i g h t s one LED a t a time
// a c r o s s t h e bottom o f t h e STK500 board .
// I t does , however , d e m o n s t r a t e t h e u s e o f a hardware t i m e r
// t o p r o v i d e a r e g u l a r p e r i o d f o r t h e main l o o p o f t h e a p p l i c a i o n .
//
// PJ , 27−Feb −2007 o r i g i n a l f o r AT90S4433
// 11−Feb −2008 update t o ATmega88

// g e t t h e p r o c e s s o r − s p e c i f i c d e f i n i t i o n s f o r t h e IO memory s p a c e
#i n c l u d e <a v r / i o . h>

// macro d e f i n i t i o n t o s e l e c t b i t n , assuming 0 <= n <= 7


#d e f i n e BIT ( n ) ( 1 << ( n ) )

void setup timer1 ( void )


// We w i l l u s e t h e 16− b i t t i m e r / c o u n t e r 1 w i t h o u t i n t e r r u p t s
// and we w i l l s l o w t h i n g s down by p r e s c a l i n g t h e system c l o c k .
{
// Normal p o r t o p e r a t i o n , OC1A and OC1B d i s c o n n e c t e d
// Leave b i t s COM1A1 t h r o u g h COM1B0 o f TCCR1A a s d e f a u l t z e r o v a l u e s
// p r e s c a l e CK/1024
TCCR1B |= BIT ( CS12 ) | BIT ( CS10 ) ;
return ;
}

void r e s e t t i m e r 1 ( unsigned i n t counts )


// With a c l o c k f r e q u e n c y o f 3 . 6 8 6 MHz on t h e STK500 and
// a 1024 p r e s c a l i n g f a c t o r , we e x p e c t a count o f 360 t o r e s u l t
// i n t i m e r 1 o v e r f l o w a f t e r 100 m i l l i s e c o n d s .
{
TIFR1 |= BIT (TOV1 ) ; // c l e a r t h e o v e r f l o w b i t by w r i t i n g l o g i c a l 1
TCNT1 = 0xFFFF − c o u n t s ; // s e t t h e s t a r t i n g count a s a 16− b i t v a l u e
return ;
}

void w a i t f o r t i m e r 1 ( void )
{
w h i l e ( ( TIFR1 & BIT (TOV1) ) == 0 ) /∗ do n o t h i n g ∗/ ;
return ;
}

i n t main ( v o i d )
{
unsigned char which bit ;
DDRD = 0xFF ; // a l l ou t put
setup timer1 ( ) ;
which bit = 0;
w h i l e ( 1 ) { // never−e n d i n g l o o p
r e s e t t i m e r 1 ( 3 6 0 ) ; // Now, we have 100ms t o do our work .

// The body o f t h i s l o o p doesn ’ t do much work .


// I t j u s t makes t h e n e x t LED t u r n on b e f o r e w a s t i n g time
// w a i t i n g f o r t i m e r / c o u n t e r 1 t o o v e r f l o w .
w h i c h b i t += 1 ;
i f ( which bit > 7 ) which bit = 0;
// Turn on a LED by w r i t i n g 0 t o i t s c o r r e s p o n d i n g MCU p i n
PORTD = ˜BIT ( w h i c h b i t ) ;

// H o p e f u l l y , t h e r e i s some time t o w a i t f o r t h e t i m e r t o o v e r f l o w .
wait for timer1 ();
}
return 0; // we s h o u l d n e v e r g e t h e r e .
}
3 EXAMPLE 2: A TASK LOOP AND A HARDWARE TIMER 15

3.2 Your own prototype board with ISP


• Encourage groups to make a prototype on stripboard.

• Allows software development well before printed-circuit boards have been manufac-
tured.

• Soldered joints are more reliable than spring contacts in breadboards.

• Photograph shows a partly constructed prototype board for a timer. The coloured
ribbon cable connects the 6-pin ISP header from the STK500 to a 6-pin inline header
on the prototype board. The other pairs of wires are to LEDs that will eventually
be mounted on the front panel of the device.

• There is a voltage regulator on the top part of the board for when the board is not
powered through the ISP cable.

• An external full-swing crystal oscillator is used for the clock signal.

• Current-limiting resistors are used with the peripheral connectors.

• One pair of lines (RXD and TXD) can be connected to PC’s serial port through the
level-shifting chip on the STK500.
3 EXAMPLE 2: A TASK LOOP AND A HARDWARE TIMER 16

Circuit-diagram for the prototype board, including the voltage regulator and the ISP
header.
+5V

10K

470

RESET/
C?

U2
1

470
RXD 1 28
S_RESET PCINT14/!RESET/PC6 PC5/ADC5/SCL/PCINT13
470 2 ATmega88 27
TXD PCINT16/RXD/PD0 PC4/ADC4/SDA/PCINT12
2

3 26
PCINT17/TXD/PD1 PC3/ADC3/PCINT11
470 4 25
INT0 PCINT18/INT0/PD2 PC2/ADC2/PCINT10

470 5 24
PCINT19/OC2B/INT1/PD3 PC1/ADC1/PCINT9
INT1
470 6 23
PCINT20/XCK/T0/PD4 PC0/ADC0/PCINT8
+5V PD4
7 22
VCC GND

8 21 100n +5V
100n GND AREF
20MHz 9 20
PCINT6/XTAL1/TOCS1/PB6 AVCC
15p
10 19 100n
PCINT7/XTAL2/TOSC2/PB7 PB5/SCK/PCINT5 SCK
11 18
PCINT21/OC0B/T1/PD5 PB4/MISO/PCINT4 MISO
15p
12 17
PCINT22/OC0A/AIN0/PD6 PB3/MOSI/OC2A/PCINT3 MOSI
13 16
PCINT23/AIN1/PD7 PB2/!SS/OC1B/PCINT2

14 15
PCINT0/CLKO/ICP1/PB0 PB1/OC1A/PCINT1

TITLE

FILE: REVISION:

PAGE OF DRAWN BY:

VTG

D2

U1
CONN_PLUG_PACK D1 +5V
J1_5V
1 IN OUT 3
1
1

2
GND
2 2
1

2 7805
J2_POWER_LED
10u C1 10u C2
ISP_CONNECTOR
2

1
1 MISO
2 VTG
3 SCK
470 R1

4 MOSI
5 RESET/
6 GND

TITLE

FILE: REVISION

PAGE OF DRAWN B
4 ELEMENTS OF A C PROGRAM 17

4 Elements of a C program
• We will be dealing with data (variables, register values) and instructions (program
code, functions) on what should be done with the data.
• If you want some further reading, see the book [2]. It has a good tutorial introduction
but uses a different compiler for the AVRs.
• Basic program structure

// heading comments

... data declarations, function definitions ...

int main( void ) // this function is special; execution starts here


{
... local difinitions ...
... executable statements ...
return 0;
}

For a small embedded program, it is unlikely that we should never arrive at the
return statement. For a desktop computing application, the zero will be returned
to the operating system.
• Comments

// C++ style comment


/* C style comment */

Be careful to avoid nesting the C-style comments.


• Statements
– consist of identifiers, keywords, expressions and other statements
– a single statement ends with a semicolon;
b = 10; /* assignment of integer value 10 to variable b */
if ( i > 1 ) b = a - 2;
The second statement contains a keyword, expression and another single state-
ment.
– a compound statement is delimited by braces
if ( i > 1 ) { // begin compound statement
b = a - 2;
j = j + 1;
} // end if statement
Note that there is no semicolon at the end of the compound statement. Putting
one there would introduce a null statement.
4 ELEMENTS OF A C PROGRAM 18

• An identifier is the name of a variable or function.

– must start with a letter of the alphabet or an underscore


– this can be followed by any number of letters, digits or underscores
– usually the first 32 characters are significant
– case sensitive
– recommendations
∗ identifiers starting with underscores are usually reserved for system names
∗ don’t rely on case sensitivity to distinguish names
∗ use descriptive names

• keywords (or reserved words) are names that have special meaning to the compiler

– examples: if while and for


– for a complete list, see the appendix A
– these cannot be used for variable or function names

• A variable labels a specific memory location (or register) that is set aside to hold a
particular type of data.

– Each variable is identified by its name.


– Each must be defined before use in a declaration statement that may also
initialize the value of the variable.
– General form of the variable declaration statement
type name ; // comment
– Examples:
int count; // This will accumulate the number of key presses.
int count = 0; // On startup, we know that nothing has happened.

4.1 Types of data


• Internally, all data is stored and handled as binary numbers. All other types are
interpretations of the bit patterns.

• Characters

– Examples:
char letter; // 8-bit quantity on the AVR.
letter = ’a’; // Note the use of single quotes.
– Some special characters
’\b’ backspace
’\f’ form feed
’\n’ new line
’\r’ carriage return
4 ELEMENTS OF A C PROGRAM 19

’\t’ tab
’\’’ apostrophe
’\¨’ double quote
’\\’ backslash
’\nnn’ character number nnn in the ASCII table where nnn is octal
– In AVR programming, we will use char variables to hold 8-bit data (a byte)
without necessarily interpreting the data as a character.

• strings are arrays of characters

char my_name[] = "Peter";

The square brackets signify an array. Note that we use double quotes for string
constants.
The storage in memory is ’P’,’e’,’t’,’e’,’r’,’\000’ where the trailing null
character marks the end of the string.

• int variables are used to store 16-bit data.

– unsigned int is used to represent a whole number in the range 0 .. 65535


– (signed) int has the range -32768 .. 32767
– Be careful with large values; the number system wraps around.
– Usually integers are written in base 10 (or decimal) with digits 0..9.
– When working with AVR MCUs, it is often convenient to use other bases.
base 2 (binary) for single bits
base 8 (octal) for groups of 3 bits
base 16 (hexadecimal) for groups of 4 bits
– Examples:
0b1011 binary (0b prefix)
11 decimal (no prefix or leading zero)
013 octal (leading zero)
0xB hexadecimal (0x prefix)
• float variables are used to represent numbers with a fractional part

– Can only represent a subset of real numbers, for example, 5.5, -2.31 and 9.0.
– Note the decimal point. This is what tells the compiler to use a floating-point
representation.
– floats are represented as 32-bit patters on AVR MCUs.
– Operations are done in software and are slow.
– Try to avoid using float data on AVR, however, it’s hard to beat the convenience
of representing real numbers as float data.
– Range for float numbers -3.4e+38 .. 3.4e+38
– Precision is about 6 decimal digits.
4 ELEMENTS OF A C PROGRAM 20

• boolean or logical values


– There is no dedicated type.
– Use an interpretation of integer data.
0 is equivalent to false.
Any nonzero value is interpreted as true.
– Logical expressions evaluate to 1 (for true) or 0 (for false).
• program space
Data may be stored in the flash memory of the AVR. This is separate to the normal
RAM data space.

#include <avr/pgmspace.h>
...
prog_char c;
prog_int16_t j;

There are also a number of functions in AVR libc to copy data into and out of
program space.

4.2 Expressions
Expressions are used to specify simple computations. An expression is a combination of
operands and operators that express a single value.

• Examples:

int a = 1;
int b = 2;
...
a + 2 // expresses an arithmetic value 3
a >= b // expresses a logical value 0
a = b // expresses an assignment of the value 2

• arithmetic
operators
+ addition (binary operator)
− subtraction
∗ multiplication
division (note that newer AVR MCUs have
/
hardware multiplication but not division)
% modulus (or remainder)
++, −− increment and decrement (one operand only)
• precedence
1. ()
2. negation
3. ∗, /
4. +, −
4 ELEMENTS OF A C PROGRAM 21

• relational operators
== is equal to
> is greater than
>= is greater than or euqal to
< is less than
<= is less than or equal to
! = is not equal to

• logical operators result in true (1) or false (0) values


&& and
! not
|| or
• bitwise operators
∼ ones complement
<< shift bit pattern left (with zero fill)
>> shift bit pattern right
& and
| or
∧ exclusive-or
• More examples:

char i = 0b0011;
char j = 0xB; // 0b00001011

~i has the value 0b11111100


i << 2 0b00001100
i & j 0b00000011

– The right operand for the shift operators gives the number of bit-places to
shift.
– Each left shift is equivalent to multiplying the left operator by 2.
– Right shift treats signed and unsigned quantities differently.
unsigned: shifts zero into the most-significant bit signed: the sign bit will be
replicated

• Rules of thumb for precedence


1. multiply and divide come before add and subtract
2. put parentheses around everything else

• For arithmetic operations, the operand of smaller type is coerced to the larger type.
For example, int is a larger type than char so, if we have an addition operation
involving both int and char operands, the char value will be promoted to an int
value before the operation is performed.
4 ELEMENTS OF A C PROGRAM 22

4.3 Assignment statements


• A declaration sets aside space in memory for the variable.

unsigned char z; // 8-bit quantity

• An assignment statement is is used to give the variable a value.


General form:
variable = expression ;
Example:
z = 22;

• The assignment operator means: compute the value of the expression on the right
and assign that value to the variable.

• May initialize variables in their declaration statement.


int upper limit = 10000;

• Sometimes we want a value that does not change.


const int upper limit = 10000;

• Interacting with the AVR hardware via the special function registers in I/O memory.

#include <avr/io.h>
int main( void )
{
unsigned char z;
DDRD = 0xFF; // sets all port pins as output
while (1) {
z = PINB; // read the binary value from port B
PORTD |= (z & 0xF); // set the least-significant 4 bits
// to look like those at port B
}
return;
}

The read-modifiy-write statement could be written as


PORTD = PORTD | (z & 0xF);
5 EXAMPLE 3: SERIAL-PORT COMMUNICATION 23

5 Example 3: Serial-port communication


This example also demonstrates some of the C flow-control structures. The implicit com-
munication between the blocks is via variables.

Start main

Initialize hardware,
ports, timer, RS232
total=0

reset timer

read PORTB switches

VTG

increment or decrement
or reset the total

10K

send updated total SWn 150


to PC via RS232 port
1

S1

wait for timer


2
5 EXAMPLE 3: SERIAL-PORT COMMUNICATION 24

5.1 C source code


// countupdown . c
//
// This a p p l i c a t i o n t a k e s i n p u t from t h e b u t t o n s a t t h e bottom o f
// t h e STK500 board and a c c u m u l a t e s a count o f t h e key p r e s s e s .
// SW0 == c l e a r t o t a l
// SW1 == i n c r e m e n t t o t a l
// SW2 == decrement t o t a l
// Remember t o c o n n e c t PORTB t o t h e s w i t c h e s .
//
// The t o t a l i s o ut pu t t o t h e s p a r e RS232 p o r t a s a h e x a d e c i m a l number .
// Remember t o c o n n e c t p i n s PD0 , PD1 t o t h e h e a d e r RxD, TxD−s p a r e
// with a 2−w i r e jumper c a b l e .
//
// PJ , 28−Feb −2007 o r i g i n a l f o r AT90S4433
// 12−Feb −2008 r e v i s e d f o r ATmega88

#i n c l u d e <a v r / i o . h>

// −−−−−−−−−−−−−−−−−− t i m e r −−−−−−−−−−−−−−−−−−−−−−−

void setup timer1 ( void )


// We w i l l u s e t h e 16− b i t t i m e r / c o u n t e r 1 w i t h o u t i n t e r r u p t s
// and we w i l l s l o w t h i n g s down by p r e s c a l i n g t h e system c l o c k .
{
TCCR1B |= ( 1 << CS12 ) | ( 1 << CS10 ) ; // p r e s c a l e CK/1024
return ;
}

void r e s e t t i m e r 1 ( unsigned i n t counts )


// With a c l o c k f r e q u e n c y o f 3 . 6 8 6 MHz on t h e STK500 and
// a 1024 p r e s c a l i n g f a c t o r , we e x p e c t a count o f 360 t o r e s u l t
// i n t i m e r 1 o v e r f l o w a f t e r 100 m i l l i s e c o n d s .
{
TIFR1 |= ( 1 << TOV1 ) ; // c l e a r t h e o v e r f l o w b i t by w r i t i n g l o g i c a l 1
TCNT1 = 0xFFFF − c o u n t s ; // s e t t h e s t a r t i n g count
return ;
}

void w a i t f o r t i m e r 1 ( void )
{
w h i l e ( ( TIFR1 & ( 1 << TOV1) ) == 0 ) /∗ do n o t h i n g ∗/ ;
return ;
}

// −−−−−−−−−−−−−−−−−−−−−− s e r i a l p o r t −−−−−−−−−−−−−−−−−−−−−−−−

// Assume t h a t we run t h e STK500 c l o c k a t f u l l s p e e d .


#d e f i n e FOSC 3686400
#d e f i n e BAUD 9600
#d e f i n e MYUBRR FOSC/16/BAUD − 1
// See t a b l e 18−1 o f d a t a s h e e t f o r t h e baud−r a t e r e g i s t e r f o r m u l a .

void setup uart ( void )


{
UBRR0 = MYUBRR; // a v a l u e o f 23 f o r 9600 baud with FOSC=3.686 MHz
UCSR0B |= ( 1 << TXEN0 ) ; // c o n n e c t t h e t r a n s m i t t o PD1
return ;
}

void s e n d c h a r a c t e r ( unsigned char c )


{
// Note t h e n u l l s t a t e m e n t s i n t h e b o d i e s o f t h e f o l l o w i n g l o o p s .
w h i l e ( (UCSR0A & ( 1 << UDRE0) ) == 0 ) /∗ w a i t u n t i l empty ∗/ ;
UDR0 = c ; // send t h e c h a r a c t e r
w h i l e ( (UCSR0A & ( 1 << TXC0) ) == 0 ) /∗ w a i t u n t i l c o m p l e t e ∗/ ;
}

unsigned char hex repr ( unsigned char n)


5 EXAMPLE 3: SERIAL-PORT COMMUNICATION 25

// Return a h e x a d e c i m a l c h a r a c t e r r e p r e s e n t i n g t h e b i n a r y v a l u e .
{
n &= 0x0F ; // keep o n l y 4 b i t s , j u s t i n c a s e t h e u s e r has s e n t more
i f ( n < 10 ) {
return n+ ’0 ’;
} else {
r e t u r n ( n−10)+ ’A ’ ;
}
}

v o i d send number ( u n s i g n e d i n t n )
// Send a 4− d i g i t h e x a d e c i m a l r e p r e s e n t a t i o n o f t h e number
// t o t h e s e r i a l p o r t , s t a r t i n g with t h e most s i g n i f i c a n t d i g i t .
{
u n s i g n e d c h a r nybble , i ;
f o r ( i = 0 ; i < 4 ; i++ ) {
n y b b l e = ( n & 0 xF000 ) >> 1 2 ; // g e t most s i g n i f i c a n t d i g i t
s e n d c h a r a c t e r ( h ex r ep r ( nybble ) ) ;
n = n << 4 ; // move r e m a i n i n g b i t s l e f t
}
}

// −−−−−−−−−−−−−−−−−−− t h e main program / l o o p −−−−−−−−−−−−−−−−−−−

i n t main ( v o i d )
{
unsigned char t o t a l , o l d t o t a l , k e y b i t s ;
// PORTB d e f a u l t s t o i n p u t ; t h i s i s where t h e s w i t c h e s a r e c o n n e c t e d .
setup timer1 ( ) ;
setup uart ( ) ;
total = 0;
w h i l e ( 1 ) { // never−e n d i n g l o o p
r e s e t t i m e r 1 ( 3 6 0 ) ; // Now, we have 100ms t o do our work .

// Attend t o a l l o f our c h o r e s . . .
old total = total ;
k e y b i t s = PINB & 0 b00000111 ; // o n l y l o o k a t SW2, SW1 and SW0
// Note t h a t , when p r e s s e d , t h e s w i t c h e s p u l l t h e MCU p i n s low .
i f ( ( k e y b i t s & 0 b001 ) == 0 ) {
total = 0;
} e l s e i f ( ( k e y b i t s & 0 b010 ) == 0 ) {
t o t a l ++;
} e l s e i f ( ( k e y b i t s & 0 b100 ) == 0 ) {
t o t a l −−;
}
i f ( o l d t o t a l != t o t a l ) {
// Only send v a l u e when t h a t has been a change .
send number ( t o t a l ) ;
s e n d c h a r a c t e r ( ’ \ n ’ ) ; // new l i n e
s e n d c h a r a c t e r ( ’ \ r ’ ) ; // c a r r i a g e r e t u r n
}
// H o p e f u l l y , t h e r e i s some time t o w a i t f o r t h e t i m e r t o o v e r f l o w .
wait for timer1 ();
}
return 0; // we s h o u l d n e v e r g e t h e r e .
}
6 DECISION AND FLOW-CONTROL STATEMENTS 26

6 Decision and flow-control statements


Some of these notes were adapted from texts [3] and [4].

6.1 Conditional statements


• simplest form

false true
expression?
if ( expression ) statement

statement

The statement may be a single or a compound statement.

• else clause for a case where the condition is false

false true
if ( expression ) expression?

statement-1
else
statement-2 statement-1
statement-2

• Even if statemenst 1 and 2 are single statements, it is a good idea to use braces and
indentation to show the structure unambiguously.

• We can nest if statements to get more than two execution paths.

• Unless otherwise indicated by the use of braces, an else clause belongs to the
innermost if statement that does not yet have an associated else clause. (page 52,
[3])

• For many execution paths, use a switch statement.


switch ( expression ) {
case const-expr-1 : statements-1
case const-expr-2 : statements-2
case const-expr-3 : statements-3
default : statements
}
The expression is evaluated and is tested for a match with a number of integer-valued
6 DECISION AND FLOW-CONTROL STATEMENTS 27

constants (the const-expr values) If a match is found, execution starts at that case.
All of the statements from that point until the end of the switch construct will be
executed or until a break statement is encountered. Using a break statement is the
normal way to stop “fall-through” between case clauses. However, making use of
fall-through, we may have several cases for one particular set of statements. The
optional default case is executed if none of the others are satisfied.

6.2 Loops
• pretested loop

false

while ( expression ) statement expression?

true

statement

• post-tested loop

do statement while ( expression );


statement

true
expression?

false
6 DECISION AND FLOW-CONTROL STATEMENTS 28

• for statement

init_expr;

false
cond_expr?

true
for ( init expr ; cond expr ; final expr )
statement statement

final_expr;

– init expr is usually an assignment expression.


– The statement is executed if cond expr is nonzero (true). Note that cond expr
does not have to be a boolean expression.
– final expr is evaluated each time, after the statement is executed. It is usually
an increment expression.
– Note the semicolons separating the expressions.
– An example of a never-ending loop with a null statement as the body.
for (;;) ;
6 DECISION AND FLOW-CONTROL STATEMENTS 29

6.3 Unconditional control statements


• break;

– Flow of control jumps to the statement immediately following the body of the
statement containing the break statement.
– Use for exceptional situations where a loop must be abandoned.
– Also used to stop execution “falling through” from one case to the next in a
switch statement.

• continue;

– Execution starts the next iteration of the innermost enclosing do, for or while
loop.
– Useful for skipping the body of a loop in an exceptional situation.

• goto label ;

– May be useful in exceptional situations, for example, when breaking out of


multiply-nested loops.
– The label needs to be part of a full statement (which could be a null statement).
– Execution can only jump to a target within a single function body.
– Example:
Start:
i = 0; // this is part of the labelled statement
...
if ( i > 200 ) goto Start;
7 FUNCTIONS 30

7 Functions
• Functions are a form of procedural abstraction, so we don’t have to deal with too
much detail at any one point in time.

• The idea is to encapsulate an operation or procedure, give it a name, and subse-


quently use this operation in other parts of the program simply by using its name.

• There is a special function called “main” which is entered when your program starts
running (possibly after power-on or some other reset condition for the AVR MCUs).

• All other functions are called directly or indirectly from main except interrupt rou-
tines which are called directly by the MCU hardware. (More on that in Section
13.)

• type of the function

– Functions return a value of their declared type whenever they are used.
– The value returned may be ifnored (implicitly discarded). If you wish to always
do this, it is probably best to declare the type of the function to be void to
indicate that no value is returned.

• definition of the function

– declares the name and type of the function and the argument list.
– provides the body of the function as a compound statement.

• formal parameters (or arguments)


are the names used inside the function to refer to its arguments.

• actual parameters (or arguments)


are the expressions or values used as arguments when the function is actually called.

• If we wish to refer to the function before defining it, we need to declare it to the
compiler by specifying its prototype.
In the function prototype, we don’t need to specify the formal parameter names,
just their type, however, specifying the names improves readability of your code.

• function body

– The body of a function is always a compound statement.


– New variables may be declared within this compound statement (or block).
These will be local to the function and not be visible to code outside the
function.
– If any new variable has the same name as any global variable, the local variable
will hide the global variable.
– Formal parameters are effectively local variables.
7 FUNCTIONS 31

• passing arguments (pass-by-value mechanism)

– When a function is called, any arguments that are provided by the caller are
simply treated as expressions.
– The value of each expression is used to initialize the corresponding formal
parameter.
– The formal parameters behave the same way as other local variables within
the function. Any changes to them are not seen outside the function.

• scope of variables

– The scope of a variable is the area of the program code in which the variable
is valid (or is visible).
– A global variable is one that is declared outside any function and is valid
anywhere (within the source code module).
– A local variable has a scope that is limited to the block in which it is declared
and cannot be accessed outside of that block. (A block is a section of code
enclosed in curly braces { ... }).
8 POINTERS-TO AND ADDRESSES-OF VARIABLES 32

8 Pointers-to and addresses-of variables


unsigned int i, j; // defines 16-bit variables
unsigned int *p; // defines a pointer variable

The variable p will contain the address of an unsigned int variable (as opposed to the
value of the int).
Conceptual model of the data memory.

address ? p

int value ? j

int value ? i

The memory locations will have numerical addresses as seen in the AVR datasheet. The
names of the variables are shown to the right of the memory locations that they label.
The question marks in the memory locations indicate undefined values (presently).
// assign some values
i = 1;
j = 3;
p = &i;

&i p

3 j

1 i

p now points to the variable i because p contains the address (numerical location in data
memory) of the variable i.
& is the address-of operator.
9 FUNCTIONS REVISITED: PASS-BY-REFERENCE 33

To change the value of a variable, given its address, we use the ∗ (prefix) operator. It is
equivalent to “what is pointer to” and is sometimes called the “dereference” operator.

*p = 4;
j = (*p);

&i p

4 j

4 i

The expression ∗p evaluates to 4. Also, we may have more than one pointer variable
pointing to any particular memory location.

9 Functions revisited: pass-by-reference


Sometimes we would like a mechanism to affect the variables used as actual parameters
to a function. Even though values only are passed through to the formal arguments of a
function, we may pass the address of any variable that we want to affect from within the
function.

void double_it( int *v )


{
(*v) = (*v) * 2;
return;
}

int main( void )


{
int i = 4;
...
double_it( &i );
...
}
10 ARRAYS 34

10 Arrays
• An array is a collection of variables with identical type and a single name.

• General definition of a single-dimensional array:


type variable[size];

• Example:
int data list[3];

int value ? data_list[2]

int value ? data_list[1]


Increasing address
int value ? data_list[0]

Although the size may be an expression, it must be evaluated at compile time. Note
also that the array elements start at index 0 and are numbered up to n − 1 where
n is the number of elements. We can consider this index as an offset from the first
element in the array.

• Initialize each element:

int i;
for ( i = 0; i < 3; i += 1 ) {
data_list[i] = i + 1;
}

3 data_list[2]

2 data_list[1]
Increasing address
1 data_list[0]
10 ARRAYS 35

• Use the same notation to access the elements:

int sum = 0;
for ( i = 0; i < 3; i += 1 ) {
sum += data_list[i];
}

10.1 Pointers and arrays


• The name of the array itself is a pointer to the first element (at index 0).

• Example:

int data_list[3];
int *p;
...
p = data_list;
// or
p = &data_list[0];
...
*p = 5;

&data_list[0] p

int value ? data_list[2]

int value ? data_list[1]

5 data_list[0]

• We could use “pointer arithmetic” to step through the array.

for ( p = data_list; p <= &data_list[2]; ++p ) *p = 0;


11 THE C PREPROCESSOR 36

10.2 Strings
• Strings are created out of arrays of characters with the last character being the
special null character.

• Example:

char name[10];
...
name[0] = ’P’; name[1] = ’e’; name[2] = ’t’;
name[3] = ’e’; name[4] = ’r’; name[5] = ’\000’;

• Alternatively:

#include <string.h>
...
strcpy(name, "Peter");

11 The C preprocessor
• The preprocessor is essentially a specialized text editor which changes your source
code before passing it (the transformed source code) onto the compiler.

• All preprocessor directives (also known as commands or macros) begin with a sharp
“#” character in the first column and terminate at the end of the line unless they
are continues by having a backslash “\” character as the very last character on the
line.

• Be careful that the syntax of the preprocessor is different to the C language. You
cannot use C constructs as preprocessor directives.

• include files

– The include directive allows the program to use source code from another file.
– Examples
#include <avr/io.h>
#include "my_defs.h"
The angle brackets tell the preprocessor to look in the system area for the
include file. The double quotes tell the preprocessor start looking in the current
area.
– We can include any file we like but, usually, the file will contain function
prototypes and parameter definitions.

• replacement directive

– General form:
#define name substitute-text
11 THE C PREPROCESSOR 37

– Examples:
#define BIT(n) (1 << (n))
...
#define NDIM 10
int x[NDIM];
...
for ( i = 0; i < NDIM; ++i ) x[i] = 0;
The convention is to use captial letters for the name of the definition. Also,
when parameters are passed to a definition, use parentheses substitute text in
order to be safe. The definition may be used in all sorts of places.
– We can also remove a definition:
#undef NDIM
– Definitions remain in force until they are removed or until the end of the source
code file is reached.
– The AVR libc writers have provided definitions for all of the useful I/O regis-
ters, for example:
#include <avr/io.h>
...
DDRD = 0xFF;

• conditional compilation
We may have multiple versions of the source code in one file.

#define DEBUG
...
#ifdef DEBUG
...
code for the debugging version
...
#else
...
code for the production version
...
#endif
12 EXAMPLE 4: USING THE ANALOG-TO-DIGITAL CONVERTER 38

12 Example 4: Using the analog-to-digital converter


• This example demonstrates the construction of a program from several modules.
Add source files to the project “tree-view” in the top-left of the IDE.

– adc demo.c
– my adc.c
– my serial.c
– my timer.c

• The header files are automatically put under the heading “External Dependencies”
when you next build the project.

12.1 C source code


main module
// adc demo . c
//
// This a p p l i c a t i o n t a k e s i n p u t from c h a n n e l 0 o f t h e ADC and
// s e n d s t h o s e v a l u e s t o t h e s e r i a l p o r t ( when t h e y change ) .
//
// PJ , 02−Mar−2007 v e r s i o n f o r AT90S4433
// 12−Feb −2008 r e v i s e d f o r ATmega88
//
// Also , d i v i d e d t h e code i n t o s e v e r a l s o u r c e f i l e s .
// The names a r e a b i t u g l y but a r e r e a s o n a b l y c e r t a i n not t o be
// t h e same a s t h e l i b r a r i e s s u p p l i e d by t h e c o m p i l e r vendor .

#i n c l u d e ” my timer . h”
#i n c l u d e ” m y s e r i a l p o r t . h”
#i n c l u d e ” my adc . h”

i n t main ( v o i d )
{
unsigned i n t adc value , o l d v a l u e ;
setup timer0 ( ) ;
setup uart ( ) ;
setup adc ( ) ;
adc value = 0;
w h i l e ( 1 ) { // never−e n d i n g l o o p
r e s e t t i m e r 0 ( 1 8 0 ) ; // Now, we have 50ms t o do our work .

// Attend t o a l l o f our c h o r e s . . .
old value = adc value ;
adc value = get adc value ( 0 ) ;
i f ( o l d v a l u e != a d c v a l u e ) {
// Only send v a l u e when t h a t has been a change .
send number ( a d c v a l u e ) ;
s e n d c h a r a c t e r ( ’ \ n ’ ) ; // new l i n e
s e n d c h a r a c t e r ( ’ \ r ’ ) ; // c a r r i a g e r e t u r n
}
// H o p e f u l l y , t h e r e i s some time t o w a i t f o r t h e t i m e r t o o v e r f l o w .
wait for timer0 ();
}
return 0; // we s h o u l d n e v e r g e t h e r e .
}
12 EXAMPLE 4: USING THE ANALOG-TO-DIGITAL CONVERTER 39

ADC module
// my adc . c
// F u n c t i o n s t o a c c e s s t h e a n a l o g u e −to−d i g i t a l c o n v e r t e r .
//
// PJ , 12−Feb −2008

#i n c l u d e <a v r / i o . h>

void setup adc ( void )


{
ADCSRA &= ˜ ( 1 << ADATE) ; // s e l e c t s i n g l e sample mode
ADCSRA |= ( 1 << ADEN) ; // e n a b l e ADC hardware
// S e t p r e s c a l e r d i v i s i o n f a c t o r t o 8 .
ADCSRA |= ( 1 << ADPS0 ) ;
ADCSRA |= ( 1 << ADPS1 ) ;
ADCSRA &= ˜ ( 1 << ADPS2 ) ;
return ;
}

unsigned i n t g e t a d c v a l u e ( unsigned char c h a n n e l i d )


{
unsigned char low byte , high byte ;
i f ( channel id > 5 ) channel id = 5;
ADMUX = c h a n n e l i d ;
ADCSRA |= ( 1 << ADSC ) ; // s t a r t c o n v e r s i o n
w h i l e ( (ADCSRA & ( 1 << ADIF ) ) == 0 ) /∗ w a i t ∗/ ;
l o w b y t e = ADCL; // D a t a s h e e t s a y s t o r e a d ADCL f i r s t .
h i g h b y t e = ADCH;
ADCSRA |= ( 1 << ADIF ) ; // c l e a r t h e c o n v e r s i o n −c o m p l e t e f l a g
r e t u r n ( i n t ) h i g h b y t e ∗ 256 + l o w b y t e ;
}
12 EXAMPLE 4: USING THE ANALOG-TO-DIGITAL CONVERTER 40

Timer module
// my timer . c
// F u n c t i o n s t o s e t and w a i t f o r hardware t i m e r s .
//
// PJ , 12−Feb −2008 , updated f o r ATmega88

#i n c l u d e <a v r / i o . h>

void setup timer0 ( void )


// We w i l l u s e t h e 8− b i t t i m e r / c o u n t e r 0 w i t h o u t i n t e r r u p t s
// and we w i l l s l o w t h i n g s down by p r e s c a l i n g t h e system c l o c k .
{
TCCR0B |= ( 1 << CS02 ) | ( 1 << CS00 ) ; // p r e s c a l e CK/1024
return ;
}

void r e s e t t i m e r 0 ( unsigned char counts )


// With a c l o c k f r e q u e n c y o f 3 . 6 8 6 MHz on t h e STK500 and
// a 1024 p r e s c a l i n g f a c t o r , we e x p e c t a count o f 36 t o r e s u l t
// i n t i m e r 1 o v e r f l o w a f t e r 10 m i l l i s e c o n d s .
{
TIFR0 |= ( 1 << TOV0 ) ; // c l e a r t h e o v e r f l o w b i t by w r i t i n g l o g i c a l 1
TCNT0 = 0xFF − c o u n t s ; // s e t t h e s t a r t i n g count
return ;
}

void w a i t f o r t i m e r 0 ( void )
{
w h i l e ( ( TIFR0 & ( 1 << TOV0) ) == 0 ) /∗ do n o t h i n g ∗/ ;
return ;
}

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

void setup timer1 ( void )


// We w i l l u s e t h e 16− b i t t i m e r / c o u n t e r 1 w i t h o u t i n t e r r u p t s
// and we w i l l s l o w t h i n g s down by p r e s c a l i n g t h e system c l o c k .
{
TCCR1B |= ( 1 << CS12 ) | ( 1 << CS10 ) ; // p r e s c a l e CK/1024
return ;
}

void r e s e t t i m e r 1 ( unsigned i n t counts )


// With a c l o c k f r e q u e n c y o f 3 . 6 8 6 MHz on t h e STK500 and
// a 1024 p r e s c a l i n g f a c t o r , we e x p e c t a count o f 360 t o r e s u l t
// i n t i m e r 1 o v e r f l o w a f t e r 100 m i l l i s e c o n d s .
{
TIFR1 |= ( 1 << TOV1 ) ; // c l e a r t h e o v e r f l o w b i t by w r i t i n g l o g i c a l 1
TCNT1 = 0xFFFF − c o u n t s ; // s e t t h e s t a r t i n g count
return ;
}

void w a i t f o r t i m e r 1 ( void )
{
w h i l e ( ( TIFR1 & ( 1 << TOV1) ) == 0 ) /∗ do n o t h i n g ∗/ ;
return ;
}
12 EXAMPLE 4: USING THE ANALOG-TO-DIGITAL CONVERTER 41

Serial port module


// m y s e r i a l p o r t . c
// F u n c t i o n s t o send c h a r a c t e r s v i a t h e hardware USART.
//
// PJ , 12−Feb −2008

#i n c l u d e <a v r / i o . h>

// Assume t h a t we run t h e STK500 c l o c k a t f u l l s p e e d .


#d e f i n e FOSC 3686400
#d e f i n e BAUD 9600
#d e f i n e MYUBRR FOSC/16/BAUD − 1
// See t a b l e 18−1 o f d a t a s h e e t f o r t h e baud−r a t e r e g i s t e r f o r m u l a .

void setup uart ( void )


{
UBRR0 = MYUBRR; // a v a l u e o f 23 f o r 9600 baud with FOSC=3.686 MHz
UCSR0B |= ( 1 << TXEN0 ) ; // c o n n e c t t h e t r a n s m i t t o PD1
return ;
}

void s e n d c h a r a c t e r ( unsigned char c )


{
// Note t h e n u l l s t a t e m e n t s i n t h e b o d i e s o f t h e f o l l o w i n g l o o p s .
w h i l e ( (UCSR0A & ( 1 << UDRE0) ) == 0 ) /∗ w a i t u n t i l empty ∗/ ;
UDR0 = c ; // send t h e c h a r a c t e r
w h i l e ( (UCSR0A & ( 1 << TXC0) ) == 0 ) /∗ w a i t u n t i l c o m p l e t e ∗/ ;
}

unsigned char hex repr ( unsigned char n)


// Return a h e x a d e c i m a l c h a r a c t e r r e p r e s e n t i n g t h e b i n a r y v a l u e .
{
n &= 0x0F ; // keep o n l y 4 b i t s , j u s t i n c a s e t h e u s e r has s e n t more
i f ( n < 10 ) {
return n+ ’0 ’;
} else {
r e t u r n ( n−10)+ ’A ’ ;
}
}

v o i d send number ( u n s i g n e d i n t n )
// Send a 4− d i g i t h e x a d e c i m a l r e p r e s e n t a t i o n o f t h e number
// t o t h e s e r i a l p o r t , s t a r t i n g with t h e most s i g n i f i c a n t d i g i t .
{
u n s i g n e d c h a r nybble , i ;
f o r ( i = 0 ; i < 4 ; i++ ) {
n y b b l e = ( n & 0 xF000 ) >> 1 2 ; // g e t most s i g n i f i c a n t d i g i t
s e n d c h a r a c t e r ( h ex r ep r ( nybble ) ) ;
n = n << 4 ; // move r e m a i n i n g b i t s l e f t
}
}
13 INTERRUPTS 42

13 Interrupts
• The hardware may signal that it needs servicing via an interrupt request 1 .

• If the processor has its interrupts enabled, it suspends whatever it is doing and
executes the corresponding interrupt service routine before resuming work on the
task code.

• AVR MCUs have a number of interrupt sources, each enabled by one enable bit
and each with an entry in the interrupt vector table in the lowest part of program
memory. Entries in the table are typically jump instructions.

• When an interrupt occurs, the Global Interrupt Enable bit is cleared and all inter-
rupts are disabled. The program counter is vectored to the actual interrupt vector in
order to jump to the interrupt handling routine. Hardware clears the corresponding
flag that generated the interrupt.

• See section 6.26 in the AVR-libc reference manual for details of setting up an inter-
rupt handler. For example:

#include <avr/interrupt.h>
...
ISR(TIMER1_OVF_vect)
{
// Our interrupt handling code here
// for dealing with the timer overflow signal.
}

We are provided with a macro to define the ISR and a table of interrupt vector
names for our particular processor.

• Interrupt service routines (ISRs) must save the context of the task code and restore
that context when they are finished. The C compiler usually handles that detail for
us by writing suitable code at the start and at the end of our code in the ISR.

• Usually, it is neither desirable nor possible to do all of the work in an interrupt


routine so the routine needs to signal the task code to do follow-up processing. To
do this communication, the ISR shares one or more variables.

• Once one or more variables are shared, we have to be careful because, while in the
task code, the interrupt routins may change the data at any instant. This is the
classic data-sharing problem.

• It may be difficult to identify when programming in C because single statements in


C may be translated to several assembler instructions and the interrupt event may
occur within these assembler statements.
1
Some of these notes from Simon’s text [5].
13 INTERRUPTS 43

• One cure for the data-sharing problem is to disable interrupts in “critical sections”
of task code. (These are sections of code that must not be interrupted for the system
to work properly.)

• Be sure to use the volatile keyword to indicate that the content of a variable may
change because of interrupts or other things that the compiler doesn’t know about.

static volatile int my_flag;

• Interrupt latency is the amount of time it take a processor to respond to an interrupt.


It is determined by:

1 The longest period of time during which the interrupt is disabled.


2 The period of time that it takes to execute routines for higher-priority inter-
rupts.
3 The length of time that it takes the MCU to stop what it is doing (in the
task code), do the necessary book-keeping and start executing code in the ISR.
Look this up in the AVR datasheet.
4 The length of time it takes the ISR to save the context of its just-left task and
do the work that constitutes the “response” to the interrupt signal.

Item 2 leads to the desire to make interrupt routines short.


13 INTERRUPTS 44

13.1 Example 5: Using interrupts with a hardware timer.


// flashleds3 . c
//
// The a p p l i c a t i o n doesn ’ t do much ; i t j u s t l i g h t s one LED a t a time
// a c r o s s t h e bottom o f t h e STK500 board .
// I t does , however , d e m o n s t r a t e t h e u s e o f a hardware t i m e r
// with i n t e r r u p t t o p r o v i d e a r e g u l a r p e r i o d f o r t h e d i s p l a y update .
//
// PJ , 28−Aug−2007
// 21−Feb −2008 updated f o r ATmega88

// g e t t h e p r o c e s s o r − s p e c i f i c definitions
#i n c l u d e <a v r / i o . h>
#i n c l u d e <a v r / i n t e r r u p t . h>

// We w i l l u s e t h e 16− b i t t i m e r / c o u n t e r 1 with o v e r f l o w i n t e r r u p t
// and we w i l l s l o w t h i n g s down by p r e s c a l i n g t h e system c l o c k .
// With a c l o c k f r e q u e n c y o f 3 . 6 8 6 MHz on t h e STK500 and
// a 1024 p r e s c a l i n g f a c t o r , we e x p e c t a count o f 360 t o r e s u l t
// i n t i m e r 1 o v e r f l o w a f t e r 100 m i l l i s e c o n d s .
unsigned i n t s t a r t c o u n t = 360;

// We s l s o need some o t h e r data t h a t i s p r e s e r v e d between u p d a t e s


// o f t h e d i s p l a y .
unsigned char which bit ;

void setup timer1 ( void )


{
// Normal p o r t o p e r a t i o n , OC1A and OC1B d i s c o n n e c t e d
// Leave b i t s COM1A1 t h r o u g h COM1B0 o f TCCR1A a s d e f a u l t z e r o v a l u e s
// p r e s c a l e CK/1024
TCCR1B |= ( 1 << CS12 ) | ( 1 << CS10 ) ;
TIFR1 |= ( 1 << TOV1 ) ; // c l e a r t h e o v e r f l o w f l a g
TIMSK1 |= ( 1 << TOIE1 ) ; // e n a b l e o v e r f l o w i n t e r r u p t
return ;
}

ISR ( TIMER1 OVF vect )


// This r o u t i n e i s e x e c t u t e d on t h e Timer 1 o v e r f l o w i n t e r r u p t .
// Note t h a t i t i s not c a l l e d d i r e c t l y by any o f our o t h e r code .
// A l s o n o t e t h a t t h e body o f t h i s s e r v i c e r o u t i n e s h o u l d not
// do much work b e c a u s e t h e i n t e r r u p t s a r e c u r r e n t l y t u r n e d o f f .
{
// o v e r f l o w b i t i s a l r e a d y c l e a r e d by hardware
TCNT1 = 0xFFFF − s t a r t c o u n t ; // r e s e t t h e c o u n t e r
// D e c i d e t h e n e x t LED t o t u r n on then make i t happen and l e a v e
w h i c h b i t += 1 ;
i f ( which bit > 7 ) which bit = 0;
// Turn on a LED by w r i t i n g 0 t o i t s c o r r e s p o n d i n g MCU p i n
PORTD = ˜ ( 1 << w h i c h b i t ) ;
}

i n t main ( v o i d )
{
DDRD = 0xFF ; // a l l ou tp ut s o t h a t we can t u r n on t h e LEDs
setup timer1 ( ) ;
TCNT1 = 0xFFFF − s t a r t c o u n t ; // r e s e t t h e c o u n t e r
which bit = 0;
sei ();

// At t h i s p o i n t , we can g e t one with whatever work n e e d s t o be done


// and t h e Timer1 i n t e r r u p t w i l l r e g u l a r l y be s e r v i c e d by t h e ISR .

w h i l e ( 1 ) /∗ do n o t h i n g ∗/ ;
r e t u r n 0 ; // we s h o u l d n e v e r g e t h e r e .
}
13 INTERRUPTS 45

13.2 Example 6: Using interrupts to count button presses


// button−i n t e r r u p t . c
//
// D emon str atio n o f u s i n g e x t e r n a l i n t e r r u p t s on t h e AVR.
// Port B i s o ut pu t with t h e 10− p i n jumper c o n n e c t e d t o LEDs on STK500 .
// I t shows t h e c u r r e n t count o f b ut to n p r e s s e s .
// Note t h a t o n l y LED0 . . LED5 a r e c o n n e c t e d f o r ATmega88 .
// Port D i s i n p u t with PD2=INT0 ( count up ) and PD3=INT1 ( count down ) .
// Use 10− p i n jumper t o c o n n e c t t o b u t t o n s on STK500 .
//
// PJ , August 2007 v e r s i o n f o r AT90S4433
// 21−Feb −2008 v e r s i o n f o r ATmega88
//
#i n c l u d e <a v r / i o . h>
#i n c l u d e <a v r / i n t e r r u p t . h>

u n s i g n e d c h a r count = 0 ;

ISR ( INT0 vect ) {


count++;
PORTB = ˜ count ;
}

ISR ( INT1 vect ) {


count −−;
PORTB = ˜ count ;
}

i n t main ( ) {
// S e t i n p u t and ou tp ut
DDRD &= ˜ ( 1 << PD2 ) ; // INT0 a s i n p u t
DDRD &= ˜ ( 1 << PD3 ) ; // INT1 a s i n p u t
DDRB = 0xFF ; // A l l o ut pu t
PORTB = 0xFF ; // t u r n o f f LEDs on STK500

// Enable s p e c i f i c e x t e r n a l i n t e r r u p t s
EIMSK |= ( 1 << INT0 ) ;
EIMSK |= ( 1 << INT1 ) ;
// S e t up i n t e r r u p t s e n s e c o n t r o l s o t h a t f a l l i n g e d g e s
// on INT0 and INT1 g e n e r a t e i n t e r r u p t r e q u e s t s .
EICRA |= ( 1 << ISC01 ) ;
EICRA |= ( 1 << ISC11 ) ;

sei ();

// A l l o f t h e i n t e r e s t i n g code i s now i n t h e ISRs s o


// we j u s t w a i t around f o r t h e hardware t o make r e q u e s t s .
while (1) ;

return 0;
}
14 SOFTWARE ARCHITECTURES OF EMBEDDED SYSTEMS 46

14 Software architectures of embedded systems


• It’s all about system response. For the source of these note, see section 5 of Simon’s
text [5].
• How we structure our software depends on how much the MCU needs to do and
how strict the time requirements are.
• Options discussed by Simon [5] are:
– Round-Robin
– Round-Robin with interrupts
– Function-queue scheduling
– Real-time operating system

14.1 Round-Robin
• This is the simplest architecture (and was used in the early examples).
• No interrupts
• No shared-data issues
• Structure of task code:

int main( void )


{
// initialize hardware
while ( 1 ) {
if ( device A needs service ) {
// service device A; handle data transfer to or from device
}
if ( device B needs service ) {
// service device B; handle data transfer to or from device
}
// ... more devices ...
} // end Round-Robin Loop
return 0;
} // end main

• Provided that we have slack in our processing needs, we can make the Round-Robin
loop regular by adding a wait-for-timer-tick at the end of the loop.
• If any device needs a response time less than the time needed to do all of the tasks,
the system won’t work properly.
• If any task does a large amount of processing, the system response will be poor.
• We may be able to improve response time for one device by testing the device more
than once in the loop: ABACADA...
14 SOFTWARE ARCHITECTURES OF EMBEDDED SYSTEMS 47

• The system is fragile. After changing the task sequence to meet response needs, one
change or addition can mess it up again.

14.2 Round-Robin with interrupts


• Interrupt routines deal with the urgent needs of any devices and set flags.

• The main (task) loop polls the flags and does follow-up processing requested by the
interrupts.

• Structure of the code:

char flag_dev_A = 0;
char flag_dev_B = 0;
...
void ISR( device_A )
{
// service device A
flag_dev_A = 1; // to signal follow-up processing
}
void ISR( device_B )
{
// service device B
flag_dev_B = 1; // to signal follow-up processing
}
...
int main( void )
{
while ( 1 ) {
if ( flag_dev_A ) {
flag_dev_A = 0;
// Do follow-up processing for device A.
}
if ( flag_dev_B ) {
flag_dev_B = 0;
// Do follow-up processing for device B.
}
...
} // end Round-Robin Loop
return 0;
} // end main

• All processing that is in the interrupt routines gets higher priority than the task-loop
code.

• This ability to set priority for a piece of code is an advantage but it comes with the
cost of having to deal with shared-data.
REFERENCES 48

References
[1] Atmel. Atmega48/88/168. Datasheet doc2545, http://www.atmel.com/.

[2] Richard F. Barnett, Larry O’Cull, and Sarah Cox. Embedded C Programming and the
Atmel AVR. Clifton Park, Delmar, New York, 2003.

[3] Steven R. Lerman. Problem solving and computation for scientists and engineers : an
introduction using C. Prentice-Hall, Englewood Cliffs, New Jersey, 1993.

[4] B. W. Kernighan and D. M. Richie. The C Programming Language. Prentice Hall


PTR, Upper Saddle River, New Jersey, 1988.

[5] David E. Simon. An embedded software primer. Addison-Wesley, 1999.


A
C Reference Card (ANSI) Constants Flow of Control
suffix: long, unsigned, float 65536L, -1U, 3.0F statement terminator ;
Program Structure/Functions exponential form 4.2e1 block delimiters { }
type fnc(type 1 , . . . ); function prototype prefix: octal, hexadecimal 0, 0x or 0X exit from switch, while, do, for break;
type name; variable declaration Example. 031 is 25, 0x31 is 49 decimal next iteration of while, do, for continue;
int main(void) { main routine character constant (char, octal, hex) 'a', '\ooo', '\xhh' go to goto label;
declarations local variable declarations newline, cr, tab, backspace \n, \r, \t, \b label label: statement
statements special characters \\, \?, \', \" return value from function return expr
} string constant (ends with '\0') "abc. . . de" Flow Constructions
type fnc(arg 1 , . . . ) { function definition if statement if (expr 1 ) statement 1
declarations local variable declarations Pointers, Arrays & Structures else if (expr 2 ) statement 2
statements declare pointer to type type *name; else statement 3
return value; declare function returning pointer to type type *f(); while statement while (expr )
} declare pointer to function returning type type (*pf)(); statement
/* */ comments generic pointer type void * for statement for (expr 1 ; expr 2 ; expr 3 )
int main(int argc, char *argv[]) main with args null pointer constant NULL statement
exit(arg); terminate execution do statement do statement
object pointed to by pointer *pointer
while(expr );
address of object name &name
C Preprocessor array name[dim] switch statement switch (expr ) {
case const 1 : statement 1 break;
include library file #include <filename> multi-dim array name[dim 1 ][dim 2 ]. . . case const 2 : statement 2 break;
include user file #include "filename" Structures default: statement
replacement text #define name text struct tag { structure template }
A ANSI C REFERENCE CARD

replacement macro #define name(var ) text declarations declaration of members


Example. #define max(A,B) ((A)>(B) ? (A) : (B)) }; ANSI Standard Libraries
undefine #undef name create structure struct tag name <assert.h> <ctype.h> <errno.h> <float.h> <limits.h>
quoted string in replace # member of structure from template name.member <locale.h> <math.h> <setjmp.h> <signal.h> <stdarg.h>
Example. #define msg(A) printf("%s = %d", #A, (A)) member of pointed-to structure pointer -> member <stddef.h> <stdio.h> <stdlib.h> <string.h> <time.h>
concatenate args and rescan ## Example. (*p).x and p->x are the same
conditional execution #if, #else, #elif, #endif single object, multiple possible types union
Character Class Tests <ctype.h>
is name defined, not defined? #ifdef, #ifndef bit field with b bits unsigned member : b; alphanumeric? isalnum(c)
name defined? defined(name)
ANSI C Reference Card

alphabetic? isalpha(c)
line continuation char \ Operators (grouped by precedence) control character? iscntrl(c)
Data Types/Declarations struct member operator name.member decimal digit? isdigit(c)
struct member through pointer pointer ->member printing character (not incl space)? isgraph(c)
character (1 byte) char lower case letter? islower(c)
integer int increment, decrement ++, --
printing character (incl space)? isprint(c)
real number (single, double precision) float, double plus, minus, logical not, bitwise not +, -, !, ~
printing char except space, letter, digit? ispunct(c)
short (16 bit integer) short indirection via pointer, address of object *pointer , &name
space, formfeed, newline, cr, tab, vtab? isspace(c)
long (32 bit integer) long cast expression to type (type) expr
upper case letter? isupper(c)
double long (64 bit integer) long long size of an object sizeof
hexadecimal digit? isxdigit(c)
positive or negative signed multiply, divide, modulus (remainder) *, /, % convert to lower case tolower(c)
non-negative modulo 2m unsigned add, subtract +, - convert to upper case toupper(c)
pointer to int, float,. . . int*, float*,. . .
left, right shift [bit ops] <<, >> String Operations <string.h>
enumeration constant enum tag {name 1 =value 1 ,. . . };
constant (read-only) value type const name; relational comparisons >, >=, <, <= s is a string; cs, ct are constant strings
declare external variable extern equality comparisons ==, != length of s strlen(s)
internal to source file static and [bit op] & copy ct to s strcpy(s,ct)
local persistent between calls static concatenate ct after s strcat(s,ct)
no value void exclusive or [bit op] ^
compare cs to ct strcmp(cs,ct)
structure struct tag {. . . }; or (inclusive) [bit op] | only first n chars strncmp(cs,ct,n)
create new name for data type typedef type name; logical and && pointer to first c in cs strchr(cs,c)
size of an object (type is size_t) sizeof object pointer to last c in cs strrchr(cs,c)
logical or ||
size of a data type (type is size_t) sizeof(type) copy n chars from ct to s memcpy(s,ct,n)
conditional expression expr 1 ? expr 2 : expr 3
Initialization copy n chars from ct to s (may overlap) memmove(s,ct,n)
assignment operators +=, -=, *=, . . . compare n chars of cs with ct memcmp(cs,ct,n)
initialize variable type name=value; expression evaluation separator , pointer to first c in first n chars of cs memchr(cs,c,n)
initialize array type name[]={value 1 ,. . . }; Unary operators, conditional expression and assignment oper- put c into first n chars of s memset(s,c,n)
initialize char string char name[]="string"; ators group right to left; all others group left to right.
°
c 2007 Joseph H. Silverman Permissions on back. v2.2
49
C Reference Card (ANSI) Standard Utility Functions <stdlib.h> Mathematical Functions <math.h>
absolute value of int n abs(n) Arguments and returned values are double
Input/Output <stdio.h> absolute value of long n labs(n) trig functions sin(x), cos(x), tan(x)
Standard I/O quotient and remainder of ints n,d div(n,d) inverse trig functions asin(x), acos(x), atan(x)
standard input stream stdin returns structure with div_t.quot and div_t.rem arctan(y/x) atan2(y,x)
standard output stream stdout quotient and remainder of longs n,d ldiv(n,d) hyperbolic trig functions sinh(x), cosh(x), tanh(x)
standard error stream stderr returns structure with ldiv_t.quot and ldiv_t.rem exponentials & logs exp(x), log(x), log10(x)
end of file (type is int) EOF pseudo-random integer [0,RAND_MAX] rand() exponentials & logs (2 power) ldexp(x,n), frexp(x,&e)
get a character getchar() set random seed to n srand(n) division & remainder modf(x,ip), fmod(x,y)
print a character putchar(chr ) terminate program execution exit(status) powers pow(x,y), sqrt(x)
print formatted data printf("format",arg 1 ,. . . ) pass string s to system for execution system(s) rounding ceil(x), floor(x), fabs(x)
print to string s sprintf(s,"format",arg 1 ,. . . ) Conversions
convert string s to double atof(s) Integer Type Limits <limits.h>
read formatted data scanf("format",&name 1 ,. . . )
read from string s sscanf(s,"format",&name 1 ,. . . ) convert string s to integer atoi(s) The numbers given in parentheses are typical values for the
print string s puts(s) convert string s to long atol(s) constants on a 32-bit Unix system, followed by minimum re-
File I/O convert prefix of s to double strtod(s,&endp) quired values (if significantly different).
declare file pointer FILE *fp; convert prefix of s (base b) to long strtol(s,&endp,b) CHAR_BIT bits in char (8)
pointer to named file fopen("name","mode") same, but unsigned long strtoul(s,&endp,b) CHAR_MAX max value of char (SCHAR_MAX or UCHAR_MAX)
modes: r (read), w (write), a (append), b (binary) Storage Allocation CHAR_MIN min value of char (SCHAR MIN or 0)
A ANSI C REFERENCE CARD

get a character getc(fp) allocate storage malloc(size), calloc(nobj,size) SCHAR_MAX max signed char (+127)
write a character putc(chr ,fp) change size of storage newptr = realloc(ptr,size); SCHAR_MIN min signed char (−128)
write to file fprintf(fp,"format",arg 1 ,. . . ) deallocate storage free(ptr); SHRT_MAX max value of short (+32,767)
read from file fscanf(fp,"format",arg 1 ,. . . ) Array Functions SHRT_MIN min value of short (−32,768)
read and store n elts to *ptr fread(*ptr,eltsize,n,fp) search array for key bsearch(key,array,n,size,cmpf) INT_MAX max value of int (+2,147,483,647) (+32,767)
write n elts from *ptr to file fwrite(*ptr,eltsize,n,fp) sort array ascending order qsort(array,n,size,cmpf) INT_MIN min value of int (−2,147,483,648) (−32,767)
close file fclose(fp) LONG_MAX max value of long (+2,147,483,647)
Time and Date Functions <time.h> LONG_MIN min value of long (−2,147,483,648)
non-zero if error ferror(fp)
non-zero if already reached EOF feof(fp) processor time used by program clock() UCHAR_MAX max unsigned char (255)
read line to string s (< max chars) fgets(s,max,fp) Example. clock()/CLOCKS_PER_SEC is time in seconds USHRT_MAX max unsigned short (65,535)
write string s fputs(s,fp) current calendar time time() UINT_MAX max unsigned int (4,294,967,295) (65,535)
Codes for Formatted I/O: "%-+ 0w.pmc" time2 -time1 in seconds (double) difftime(time2 ,time1 ) ULONG_MAX max unsigned long (4,294,967,295)
- left justify arithmetic types representing times clock_t,time_t
structure type for calendar time comps struct tm Float Type Limits <float.h>
+ print with sign
space print space if no sign tm_sec seconds after minute The numbers given in parentheses are typical values for the
0 pad with leading zeros tm_min minutes after hour constants on a 32-bit Unix system.
w min field width tm_hour hours since midnight FLT_RADIX radix of exponent rep (2)
p precision tm_mday day of month FLT_ROUNDS floating point rounding mode
m conversion character: tm_mon months since January FLT_DIG decimal digits of precision (6)
h short, l long, L long double tm_year years since 1900 FLT_EPSILON smallest x so 1.0f + x 6= 1.0f (1.1E − 7)
c conversion character: tm_wday days since Sunday FLT_MANT_DIG number of digits in mantissa
d,i integer u unsigned tm_yday days since January 1 FLT_MAX maximum float number (3.4E38)
c single char s char string tm_isdst Daylight Savings Time flag FLT_MAX_EXP maximum exponent
f double (printf) e,E exponential convert local time to calendar time mktime(tp) FLT_MIN minimum float number (1.2E − 38)
f float (scanf) lf double (scanf) convert time in tp to string asctime(tp) FLT_MIN_EXP minimum exponent
o octal x,X hexadecimal convert calendar time in tp to local time ctime(tp) DBL_DIG decimal digits of precision (15)
p pointer n number of chars written convert calendar time to GMT gmtime(tp) DBL_EPSILON smallest x so 1.0 + x 6= 1.0 (2.2E − 16)
g,G same as f or e,E depending on exponent convert calendar time to local time localtime(tp) DBL_MANT_DIG number of digits in mantissa
format date and time info strftime(s,smax,"format",tp) DBL_MAX max double number (1.8E308)
Variable Argument Lists <stdarg.h> tp is a pointer to a structure of type tm DBL_MAX_EXP maximum exponent
declaration of pointer to arguments va_list ap; DBL_MIN min double number (2.2E − 308)
initialization of argument pointer va_start(ap,lastarg); DBL_MIN_EXP minimum exponent
lastarg is last named parameter of the function
access next unnamed arg, update pointer va_arg(ap,type)
call before exiting function va_end(ap); January 2007 v2.2. Copyright °
c 2007 Joseph H. Silverman
Permission is granted to make and distribute copies of this card pro-
vided the copyright notice and this permission notice are preserved on
all copies.
Send comments and corrections to J.H. Silverman, Math. Dept., Brown
50

Univ., Providence, RI 02912 USA. hjhs@math.brown.edui

You might also like