Upload
lytu
View
223
Download
3
Embed Size (px)
Citation preview
TKT-3500
Microcontroller
systems
Lec 2b – Programming with C
Teemu Laukkarinen
Department of Computer Systems
Tampere University of Technology
Fall 2011
Copyright Tampere University of Technology Department of Computer Systems
#2/55
Sources
Original slides by Erno Salminen B.W. Kernigham, D.M. Ritchie, The C
programming language, Prenctice Hall PTR, 2nd ed. 1988.
B.W. Kernigham, Programming in C: A Tutorial, [online] http://www.lysator.liu.se/c/bwk-tutor.html
S. Holmes, C Programming, [online] http://www2.its.strath.ac.uk/courses/c/
Wikipedia
Thanks also to Heikki Orsila, Mauri Kuorilehto, and Teemu Laukkarinen
Copyright Tampere University of Technology Department of Computer Systems
#3/55
Contents
Introduction
Functions and basic data types
Pointers, arrays, strings, structures
Part 2:
Placement in memory
Macros
Interrupts handlers
Compilation and linking
Call conventions
Copyright Tampere University of Technology Department of Computer Systems
#4/55
C programming language
General-purpose, compiled, block structured,
procedural, imperative computer
programming language
Based on B language
Developed in 1972
by Dennis Ritchie at the Bell Telephone
Laboratories for use with the Unix operating
system
See also:
http://www.lysator.liu.se/c/bwk-tutor.html
http://www2.its.strath.ac.uk/courses/c/
Copyright Tampere University of Technology Department of Computer Systems
#6/55
D
Erlang
Smalltalk
[DedaSys LLC , Programming Language Popularity,
http://www.langpop.com/widget/]
Popularity of programing languages
Results based on searches in Yahoo, Craigs list, Amazon, Freshmeat,
Google code, and Del.icio.us
Copyright Tampere University of Technology Department of Computer Systems
#7/55
The first C program
#include <stdio.h>
void main(int argc, char *argv[])
{
/* Comments are added like this */
printf(“Hello world\n");
}
Every C program contains a function named main printf is a library function which will format and print output on
the terminal (not necessarily available on embedded systems)
#include <stdio.h> allows its usage
Curly brackets group statements together e.g. in function or loop
grouping is known as a compound statement or a block
C is case sensitive A lower and upper case letter (z and Z) are two separate letters
However, white spaces are removed (if not in quotes)
Copyright Tampere University of Technology Department of Computer Systems
#8/55
Code blocks
Inside functions, C only allows presenting new
variables in the beginning of code blocks
This is different than in C++
This is not true of the new C99 standard, though
For the backwards compatibility sake, put them to the
beginning
void example_f(void)
{
int x; /* good */
x = 0;
int y; /* bad */
if (x == 0) {
int z; /* good, a new code block inside if () */
Copyright Tampere University of Technology Department of Computer Systems
#9/55
Functions
Sometimes called subroutines or procedures
Encapsulates a block of instructions that can be used (called) more than once Easier design, maintenance, and debugging
Takes usually fixed number of input parameters
Return at most one value
int add (int a, int b) {
int c = a + b;
return c;
}
Copyright Tampere University of Technology Department of Computer Systems
#10/55
Local and global variables
Function may define local variables c in prev example
Other functions cannot access them – limited scope
However, they can be accessed via pointers
Functions may have locals with identical names without conflicts
Mem is allocated for them dynamically from the stackduring function call and released upon return
On the other hand, global variables are visible to one or several modules Declared outside any function
Initialized to zero before entering main()
A global variable is constantly in the same memory address
Copyright Tampere University of Technology Department of Computer Systems
#11/55
Control statements
Conditional execution:
if-then-else: basic branching
switch-case: multi-way decision, often 1 out of N
continue: starts next loop iteration
break: exits from switch or loop
goto: jump to certain address/label
Loops:
for: repeat for known num of iterations
while: repeat as long as condition holds
do-while: as while, but interate at least once
Copyright Tampere University of Technology Department of Computer Systems
#12/55
Control statements – True/false
If, switch, for, while, and do-while perform a
test
Body is executed only if test return logical ”true”
if (a == 5) {}; /* equality */
if (a != 0) {}; /* inequality */
for (a=0; a<5; a++){} /* a is 0,1,2,3,4*/
while (a) {... a--; ..} /* until a==0 */
Note! Zero is considered ”false” whereas all other
values are ”true”
Value 5 is true but its inverse, ~5, is also true
Copyright Tampere University of Technology Department of Computer Systems
#13/55
Flowcharts
Flowcharts are simple graphical representations of program steps Visualize the actions and conditions
Especially good for assembly programming
Not too suitable for interrupts
ConditionT
Case #1
F
Condition
T
Case #2
F
...T
...
F
ConditionT
Case #N
F
Selection (switch)
ConditionF
Loop Task
T
Repetition (do..while)
ConditionT
Loop Task
F
Selection (while)
First task
Next task
Task sequence
ConditionT F
Task #1 Task #2
Selection (if..then.else)
Copyright Tampere University of Technology Department of Computer Systems
#15/55
Data types
Data type defines allowed operations and
defines size of allocated memory
Data type is given when defining variables: int freespace;
when defining function’s return value: int main()
when defining function’s parameters: int foo (int a)
Value range of a data type depends on the used processor and compiler! int may be 8,16, 32, 36 bits, or any other size
Creates many problems when porting programs
4 basic data types: char, int, float, anddouble
Copyright Tampere University of Technology Department of Computer Systems
#16/55
Data types - integer
int – integer number
int answer = 42;
int b = answer / 5; /* == 8 */
int c = answer / 50 * 2;/* == 0*/
Most common data type in C
Reflects natural size of integers on the host
machine
E.g. 16 bits in PIC, 32 bits in ARM
Size modifiers presented later
”Natural size” aims to fast execution
Copyright Tampere University of Technology Department of Computer Systems
#17/55
Data types – char
char – character
Pronounced [ka-rak-tər], not [za-rak-ter]
A small integer, at least 8 bits 28=256 distinct values, enough to store western
alphabets (ASCII)
Type is not restricted to alphabets by any means!
char initial = „E‟, char grade = 4;
Standard does not specify signedness Use signed char or unsigned char when
sign is relevant
In most CPUs, smallest unit of memory access is 1 byte which usually equals 1 char
Copyright Tampere University of Technology Department of Computer Systems
#18/55
Data types – float, double
float – floating point (single precision) float temperature = 37.2;
double – floating point (double precision)
Larger value range
Floating-point ALU takes large silicon area Floating point types are usually not available as
native types on embedded systems
a) Adopt fixed-point arithmetic
b) Emulated with SW, when performance is not important Simpler than fixed point programming
Takes tens to hundreds of cycles per FP operation
Copyright Tampere University of Technology Department of Computer Systems
#19/55
Data types – void, enum
void – not really a data type
Used to denote that function does not return any value, void foo() {… return;}
Used also with pointers
enum – enumeration
Used for integer constants
enum command_id {
send = 1;
receive = 2;
abort = 4;
}
Compilers may support non-standard types also E.g. with microcontrollers!
Copyright Tampere University of Technology Department of Computer Systems
#20/55
Data type modifiers
Modifiers signed and unsigned
Define how the bit representation of a number is
interpreted and handled. Very useful.
char’s type depends on compiler, but modifier forces the
range of 8-bit char [-128, 127] or [0, 255]
int is signed by default
Modifiers short and long
Used storage space (=value range) :
char ≤ short int ≤ int ≤ long int
float ≤ double ≤ long double
The standard does not require that any of these sizes are
necessarily different
Sometimes these affect, but not always! Avoid.
Copyright Tampere University of Technology Department of Computer Systems
#21/55
Libraries stdint.h and limits.h
With #include <stdint.h> you can use
portable data types
int8_t is always 8 bits
int16_t, int32_t, int64_t similarly
intptr_t always the size of address space
allows arithmetic and bit operations on
addresses, for example to make zeros of LSBs
int may be too small for that on some platforms
(usually 64-bit systems)
With #include <limits.h> you can see
minimum/maximum values of data types
Copyright Tampere University of Technology Department of Computer Systems
#22/55
Sizeof()
Built-in operator returns the amount of
memory (number of characters/bytes) needed
for
a type
a variable
Hence sizeof(char) == 1
Needed, e.g., in dynamic memory allocation with malloc()
int a;
sizeof(int) == 2;
sizeof (a) == 2;Copyright Tampere University of Technology Department of Computer Systems
#23/55
Data type qualifiers - const
const - constant
The const qualifier is used to tell C that the
variable value can not change after
initialization
const float pi = 3.14159;
pi = 3.14; /* Aargh! Error*/
Also ensures that function does not modify its
parameters
int foo (const int *ptr) {...}
Especially with pointer parameters
Copyright Tampere University of Technology Department of Computer Systems
#24/55
Data type qualifiers – const/#define
Another way is the preprocessor command #define
which has the advantage that it does not use any storage
#define pi 3.14159
All occurrences of string pi are replaced with
immediate value 3.14159 before compilation
Simple string replacement may cause problems unless you
use parentheses
#define kekki pi + 1
... float a = 2 * kekki; /* 2 * pi + 1 */
Note, with π you should really use #include
<math.h> and then use constaint M_PI
Copyright Tampere University of Technology Department of Computer Systems
#25/55
Data type qualifiers - volatile
volatile - subject to rapid or unexpected change
Tells the compiler that the value of a variable is volatile, which means
Access to it will always generate a load or store operation on the underlying memory space
Compiler will not generate a code that tries to remember the old value in a register (read:
compiler will not try to optimize use of that variable; read2: compiler will not assume anything
about the value of that variable)
Used in peripheral accesses
volatile int *hw_data_reg = 0xFF50;
while (*hw_data_reg == 0) {...
Also used on variables modified in interrupts and read on normal code (very very
dangerous…)
Note, volatile is a potential sign of bug
Wherever you use volatile, you are potentially making an atomicity, mutual exclusion or
synchronization error, since the access to a volatile variable is not atomic operation!
Inline assembler, processor or compiler specific knowledge is often needed in these cases
DO NOT use on variables shared between threads (without synchronization,
however, correct synchronization makes volatile useless, self-study more
http://kernel.org/doc/Documentation/volatile-considered-harmful.txt )
Note 2, forgetting volatile from variables changed by the HW will also cause
serious and hard to find bugs, which appear randomly
Copyright Tampere University of Technology Department of Computer Systems
#26/55
Data type storage classes - extern
Storage class provides information about variable’s location and visibility
extern – external variable or function
When using global variables, extern directive can be put to a “header” file to announce existence of a given global variable to other code modules File foo.h presents the integer to other modules:
#ifndef _FOO_H_
#define _FOO_H_
extern int myinteger;
#endif
File foo.c declares the variable, i.e. reserves space for it:#include “foo.h”
int myinteger;
File bar.c can use myinteger:#include “foo.h”
... myinteger += 1;
Copyright Tampere University of Technology Department of Computer Systems
#27/55
Data type storage classes – static, register
static variables As a local variable, static variable retains its value even after the
control is transferred to the calling function
It is really a global variable with limited visibility Dangerous! Use global variables instead.
void count_usages(void) {
static int callcount = 0; /* initial value zero */
callcount++;
printf(“foo has been called %d times\n”, callcount);
}
When used as a global variable, a static variable is only visible inside the code module
register
Compiler tries to keep such variable in the CPU’s register for faster access
Do not use unless you’re very certain. Nothing is guaranteed.
Copyright Tampere University of Technology Department of Computer Systems
#29/55
Purpose
Bit manipulation differs from regular artihmetics behavior of which depends on the data types (char, int...)
One may manipulate individual bits of any data type Set certain bit(s) to 1
Clear certain bit(s) to 0
Complement certain bit(s)
These are logical operations
Remember: ALU = arithemetic-logic unit
Useful for handling control registers
reading status registers
using general purpose input/output pins
Combine larger data values from 8 bit pieces Typical on embedded systems: 32bit or so variables are transferred on
8bit pieces through a serial interface
Copyright Tampere University of Technology Department of Computer Systems
#30/55
Reese, chap4_logctrl.pdf, slide 5-
Copyright Tampere University of Technology Department of Computer Systems
#34/55
Combine two 8bit to 16bit (and vise
versa)
uint16_t target = 0;
uint8_t byte_hi = 0xFF;
uint8_t byte_low = 0xAA;
target = byte_hi; // target == 0x00FF
target <<= 8; // target == 0xFF00
target |= byte_low; // target == 0xFFAA
// NOTE: some compilers can be stupid (or wise?)
// and require to work that operands are the same
// size, then
target |= (uint16_t) byte_low;
// or even
target |= (0x00FF & (uint16_t) byte_low);
uint16_t source = 0xFFAA;
uint8_t byte_hi;
uint8_t byte_low;
byte_hi = (target >>8); // byte_hi == 0xFF
byte_low = target; // byte_low == 0xAA
// works on most compilers, might require type
// conversion to compile without warnings
#35/55
Pointers, arrays, strings, structures
Copyright Tampere University of Technology Department of Computer Systems
#36/55
Pointers
A pointer in C is the address of something address of a variable or function
Whereas variable name is an alias of memory address
The unary operator `&' is used to produce the address of an object, if it has one.
1000x54
0x55 0x54
memoryaddress
0x56 ...
variable
a
b
int a, *b;
a = 100;
b = &a;
...
b has the address of variable a
NOTE: DO NOT INITIALIZE POINTERS
ANR VARIABLES ON SAME TIME!
Copyright Tampere University of Technology Department of Computer Systems
#37/55
Pointers (2)
Revised example
int a, *b, c;
a = 100;
b = &a;
c = *b;
b is now a pointer
*b means the value pointed to by b
This in-direct access is known as dereferencing
Works both ways
*b = 7;
1000x54
0x55 0x54
memoryaddress
0x56 100
variable
a
*b
c
...
1000x54
0x55 0x54
0x56 100
a
*b
c
...
7
Copyright Tampere University of Technology Department of Computer Systems
#38/55
Pointers (3)
Most frequent uses of pointers in C are 1. For computing addresses of data
2. Passing an address of a memory area to a function
3. Passing an address to a variable whose value is changed in a function that is called
4. Accessing memory-mapped registers of HW
Pointers can also be declared as void pointers
They can't be dereferenced without explicit casting
int x;
float f;
void *p = &x; // p points to integer x
*(int*)p = 2; // dereferencing p
p = &r; // p points to float r
*(float*)p = 1.1;
Copyright Tampere University of Technology Department of Computer Systems
#39/55
Arrays
Arrays are often visible as pointer-like objects in C
Denoted with brackets []
Example: Compute a sum of value in a table until a
zero value is reached:
int table[] = {1, 2, 3, 0};
int i;
int sum = 0;
for (i = 0; table[i] != 0; i++){
sum += table[i];
printf(“sum is %d\n”, sum);
}
Copyright Tampere University of Technology Department of Computer Systems
#40/55
Arrays (2)
Arrays are defined by 1. type (mandatory), and
2. optional size, or
3. optional initializerint table[5]; /* non-initialized array of 5 int */
int values[] = {1, 2, 3}; /* array of 3 initialized int */
int *p = values; /* p points to the 1st int of array */
/* same as int *p = &values[0] */
values[1] = 4; /* update to {1,4,3} */
p[1] = 5; /* update to {1,5,3} */
p += 1; /* or p++, p points now to 2nd int */
*p = 6; /* update to {1,6,3} */
/* Do not mix ptr arithmetics and indexing*/
Copyright Tampere University of Technology Department of Computer Systems
#41/55
Arrays (3)
NOTE! p++ increases actual value of p by sizeof(dereferenced type)
Similarly with indexing
Note that there’s no implicit index checking! Possible buffer overflow
int *iptrchar *cptr
0xde
0xad
0xbe
0xef
7 0...
bit index
0x01
0x23
0x45
0x67...
...
0x0
0x4
iptr[1]
iptr[0]cptr = (char*) iptr
cptr[1]
cptr[2]
byte addr
(Pointers *iptr and *ctpr are actually in
the same mem as arrays. They are
drawn separately for clarity.)
memory
Copyright Tampere University of Technology Department of Computer Systems
#42/55
Arrays (4)
Global arrays are initialized to zero by default
Local arrays are unitialized without explicit initialization
sizeof(table) is the size of memory
occupied by the table (five integers in bytes)
memset(table, 0, sizeof(table));
fills the table with zeroes
Warning: sizeof(p) is now just the space
occupied by the pointer
E.g. sizeof(p) == 4 on 32-bit architectures
Copyright Tampere University of Technology Department of Computer Systems
#43/55
Strings
Strings are a common special case of arrays
Strings are char arrays that are terminated with a
zero
Strings can be explicitly initialized with double quotes “”:
char text[] = “hello, world!”;
printf(“%s\n”, text);
Total length of text[] is actually 14 characters =13
alphabets and terminating zero
h e l l o , w o r l d ! 0x0mem contents
0x5
00
0x5
04
0x5
08
0x5
0b
mem addr
Symbol text means simply the start
address 0x500
Copyright Tampere University of Technology Department of Computer Systems
#44/55
Strings (2)
C has a number of string function, use #include <string.h> to use them
strcpy() – copy a string
strcat() – concatenate two stringschar text[256] = “Hello, “;
strcat(text, “world!”);
printf(“%s\n”, text); /* Prints “Hello, world!” */
strlen() – return the length of a string
strlen(“foo”) == 3
To find more about any standard C function, login to proffa and type: man
function_name -> e.g. man strcpyCopyright Tampere University of Technology Department of Computer Systems
#45/55
Strings (3)
As a special case:
char *text = “hello, world!”
is a string constant
The content of this string may not be written to
However, string constants can be used anywhere, so
we can easily copy given strings to any char arrays:char *text = malloc(32); /* allocate 32 bytes */
if (text == NULL)
return; /* allocation failed */
strcpy(text, “hello, world!”); /* init text with constant */
text[0] = „H‟; /* capitalize first letter */
printf(“%s\n”, text);
NULL is used to denote a zero valued pointer to
distinguish it from a zero valued integer
Copyright Tampere University of Technology Department of Computer Systems
#46/55
Structures
A structure is a collection of one or more
variables, possibly of different types, grouped
together into a single convenient package
struct vector {
float x;
float y;
float z;
}; /* Note, definition terminated with ; */
struct vector origo = {0, 0, 0};
Copyright Tampere University of Technology Department of Computer Systems
#47/55
Structures (2)
Structures
can be passed as arguments to functions, or
can be returned from a function, and
the semantics are that of memory copying, unless pointers are passed
Structures can be initialized like arrays, but
they are more convenient to use:
struct user {
char *name;
int age;
};
Copyright Tampere University of Technology Department of Computer Systems
#48/55
Structures (3)
struct user admin = {“Aulis
Kaakko”, 35};
Note, any modern C compiler allowsstruct user admin = {.name = “Aulis Kaakko”, .age = 35};
One need not know all elements or their order
Omitted elements are reserved as zeros in mem struct user admin = {.name = “Bubi”}; /* age becomes 0*/
printf(“User name/age: %s/%d\n”,
admin.name, admin.age);
Copyright Tampere University of Technology Department of Computer Systems
#49/55
Structures (4)
Arrays of structures are of course possible:
struct vector axes[3] =
{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
sizeof(axes) == 36 /* == 3 * 3 * 4 */
Elements of the structure
are placed in memory in the same order as they
are presented in the structure
but the space between the elements is
compiler/arch dependent
Copyright Tampere University of Technology Department of Computer Systems
#50/55
Structures (5)
Natural alignment of microprocessors often
causes padding between elements,
e.g. a char can be accessed from any address,
but an integer can only be accessed from addresses divisible by sizeof(int))
Space occupied by a structure is not the sum
of its element sizes:
sizeof(struct {char c; int d;}) == 8,
on many 32-bit archs
d
c
something else
...
struct
8 7..031
Copyright Tampere University of Technology Department of Computer Systems
#51/55
Structures (6)
When you have a structure in the embedded world (and even in the desktop world):
IF YOU DO NOT KNOW THE SIZE OF THE STRUCTURE, PASS IT AS A POINTER TO FUNCTIONS
Otherwise the structure is reserved from the (software) stack and it may consume huge amount of stack memory
struct huge {
uint8_t stupid_table[1024];
};
void huge_stack_consumption(struct huge take_all_you_can_parameter); // NO!
void stack_consumption_of_a_pointer(struct huge* small_consumption); // YES!
void stack_consumption_of_a_pointer(struct huge const* small_consumption); // EVEN BETTER!
// NOTE: here we denoted that stuff behind small_consumption pointer should not be modified
// following will enforce only pointer to be unaltered (which is stupid on function parameters)
void foobar(int *const ptr); // cannot say: ”ptr = 0;”, but can say ”*ptr = 0;”
// and last, this will not allow modifing the struct nor the pointer
void foobar(int const * const ptr); // cannot say: ”ptr = 0; *ptr = 0”
#52/55
PIC-specific considerations
Copyright Tampere University of Technology Department of Computer Systems
#56/55
However after power off RAM contents lost
-> Use EEPROM: safer but more time consuming and space constrainedCopyright Tampere University of Technology Department of Computer Systems
#57/55
Common Wisdom
"C makes it easy to shoot yourself in the foot.
C++ makes it harder, but when you do, it blows
away your whole leg.“ --- Bjarne Stroustrup,
creator of C++
C++:han on todella ”onnistunut” oliokieli (Luennoijan
mielipide )
Everyone knows that debugging is twice as hard
as writing a program in the first place. So if
you're as clever as you can be when you write it,
how will you ever debug it? --- Brian Kernighan
Controlling complexity is the essence of
computer programming. --- Brian Kernighan