# Manual ARM Microcontroller Course E.T.S.V. Scintilla | Programming C

## 4 Programming C

Due to its low level description, C is a relatively simple language to get started with: it features only a minimum of possible operations, so there is not that much to learn.

4.1

Data Types

Every operation needs at least one variable. There are two types of variables: integer type variables (for integer numbers) and floating point variables (for non-integer numbers). In ARM microcontrollers it is strongly advised to use unsigned integer type variable with a size of 32 bits or less standard. Longer data types or floating point variables take more time to perform operations on and thus decrease performance and increase power consumption. Keep in mind that an ARM has 32 bit registers: using data types smaller than this will not save memory, but will just leave the rest of the bits in the register unused.

4.1.1

Integer Data types

The integer type variables can be found in table

1 . This table shows that there

is no Boolean type variable. To be able to process operations which require a true/false input, any other integer data type can be used. Giving the value “0” as the argument of a function will execute the same as a logic “false” and any other value will be processed as a logic “true”. The code snippet in Snippet

will thus switch on an LED. The LED will switch on for every value of x, as long as it not 0.

In microcontrollers it is very common to use an integer as an array of bits.

For example a 16 bits unsigned integer (uint16_t) can not only be used to store a 16 bit number, but could also be used to store 16 bits. For this we have to know how unsigned integers are stored. This type of integers is stored as a binary number. If we consider the decimal number “1234”, this can be stored in binary as “0b0000010011010010”. The “0b” is a marking which determines that a number is given in binary and the zeros at the beginning are added to show this is a 16 bit number. Microcontrollers often have setting registers where one bit e.g. determines if a hardware peripheral is switched on. To alter this specific bit one can use bitwise operators, which will be discussed later in this manual.

Apart from the binary “0b” notation, hexadecimal notation is very common as well. Instead of base 10 (decimal) or base 2 (binary) we now use base 16.

This notation can be considered to be a short form of binary notation which groups four bits in one digit. The star of a hexadecimal number is indicated with “0x”. In hexadecimal notation one can now count 0, 1, 2, 3, 4, 5, 6, 7, 8,

9, A, B, C, D, E, F. In this base 16, 0x0 is the same as 0b0000, 0x5 is the same as 0b0101 and 0xE is the same as 0b1110. The longer decimal number 1234 can be converted to hexadecimal by separating the binary number in groups of four digits and rewriting these groups to hexadecimal. “0b0000 0100 1101 0010” in hexadecimal notation will become “0x04D2”. Hexadecimal numbers have the advantages of both binary and decimal notation: it is grouped per bit, but the notation is still short.

12

Snippet 2 Example of an integer as a Boolean uint16_t x = 1615;

**if**

( x ){ setLED ();

}

**else**

{ resetLED ();

}

Table 1 Integer data types in C

Name Syntax Range Size (bits)

Boolean

Signed character

Unsigned character

Signed integer

Unsigned integer

Signed long integer

Unsigned long integer

Signed long long integer

Non-existent There are no Booleans in standard C 1 int8_t

−128 to 127

8 uint8_t int16_t

0 to 255

−32, 768 to 32, 767

8

16 uint16_t int32_t uint32_t int64_t

Unsigned long long integer uint64_t

0 to 65, 535

−2

31 to 2

31

− 1

0 to 2

32

− 1

−2

63 to 2

0 to 2

64

63

− 1

− 1

16

32

32

64

64

4.1.2

Floating point data types

Apart from the integer data types, non-integer or “floating point” data types can be used as well. Floating point operations are generally harder for a microcontroller to process. For example, it is harder to perform 1.23456 · 10

1

9.87654 · 10

−2

+ than to perform 12345678 + 98765432. This implies that floating point operations take longer to calculate and thus decrease performance. It can be concluded that it is unwise to use floating point numbers when this is not necessary. The possible floating point types can be found in Table

long double indicated here is specified according to the “IEEE 754 quadrupleprecision binary floating-point format”, but implementations of the long double may differ per system.

Table 2 Floating point data types in C

Name Syntax Sign bit Exponent bits Fraction bits Size (bits)

Floating point

Double floating point float double

Long double floating point long double 1

1

1

8

11

15

23

52

112

32

64

128

4.1.3

Arrays

An array is an indexed lists of a certain data type. These arrays can e.g. be used to store lists of variables, as we will do later to map an output sample number to an output value. As in every properly thought out programming language, the first entry in the array is numbered “0”. As an example, the example code

13

in code snippet

generates a list of squares of the first 8 integer numbers and returns the value of the fifth square. This should be 25.

Snippet 3 Example code for reading and writing of an array

//generate a list of 8 signed numbers of 16 bits named "y" uint16_t y [8];

//fill the list with the squares of the index numbers

**for**

( uint16_t i =0; i <8; i ++){ y [ i ] = i * i ;

}

//return the value of the fifth square

**return**

y [5];

4.1.4

Structs

C is no object oriented language, but has a feature which comes close to object oriented storing of variables. This can be done by defining a so called “struct” type variable. A struct can be considered to be an object with several variables in it. Structs are used often where there is a clear repetition in data sets. As an example, a microcontroller has several sets of input and output pins (or “general purpose input and output (GPIO) ports”, more about this later). Each GPIO port has (amongst others) a setting for pin modes. Data could be organized in a very convenient way if we could make an object “GPIO port” with as one of its internal variables a value for the pin mode for that port. In code snippet

the type definition (similar to a class) for the GPIO ports is given, a struct

(similar to an object) “GPIOA” is created, and one of its variables is changed and returned.

Snippet 4 Example of defining and working with a struct

//give a type definition for the GPIO structs

**typedef **struct

{ uint32_t MODER ; uint32_t OTYPER ;

//GPIO port mode register

//GPIO port output type register uint32_t OSPEEDR ; //GPIO port output speed register uint32_t PUPDR ; //GPIO port pull-up/pull-down register uint32_t IDR ; uint32_t ODR ; uint32_t BSRR ;

//GPIO port input data register

//GPIO port output data register

//GPIO port bit set/reset register uint32_t uint32_t

LCKR

AFR

} GPIO_TypeDef ;

;

[2];

//GPIO port configuration lock register

//GPIO alternate function registers

//initialize GPIO port "GPIOA"

GPIO_TypeDef GPIOA ;

//Set the variable "MODER" in the struct "GPIOA" to "0x0001"

GPIOA -> MODER = 0 x0001 ;

//return the variable "MODER" of struct "GPIOA"

**return**

GPIOA -> MODER ;

14

4.1.5

Enumerated type

An enumerated type is a limited list of keywords, using symbolic names to make a program clearer to the programmer. This data type will be useful when you want to implement a state machine, as in code snippet

can be used directly in code.

Snippet 5 Example of a state machine using enumerated type

//define the enumerated type States with three possible values

**typedef **enum

{ startState , waitState , processState

} States ;

//declare and initialize mystate to startState.

States mystate = startState ;

4.2

Operators

To perform operations on variables, operators can be used. These operators can be categorized into 4 main categories:

1. Mathematical (arithmetic) operators

2. Comparison operators

3. Logical operators

4. Bitwise operators

This section gives a brief overview of these operators.

4.2.1

Standard Operators

A list of standard mathematical operators in C can be found at: http://en.

wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Arithmetic_operators

A list of standard comparison operators can be found at: http://en.wikipedia.

org/wiki/Operators_in_C_and_C%2B%2B#Comparison_operators.2Frelational_

operators

4.2.2

Logical Operators

Logical operator do operations on word. This means That a variable is processed as a logical “false” if its value is 0 and is processed as a logical “true” if it has any other value. A list of the possible logical operators is given in Table

Table

shows how the operation

0 b1100 && 0 b0110 is performed.

These operators base their input on a whole word. There are also operations which perform logical operations based in each individual bit in a word. These are called bitwise operators

15

Table 3 List of logical operators on words

Name Syntax Application

Logical NOT !

a

Logical OR a || b

Returns the logical inverse of a

Returns “true” if a, b or both are true

Logical AND a && b Returns “true” if a and b are both true

Table 4 Processing of a logical operation on a word a 1 1 0 0 b 0 1 1 0

→

→

TRUE

TRUE && return value: TRUE

4.2.3

Bitwise operators

A bitwise operator performs the logical operation not per word, but per bit. A list of possible bitwise operators is given in table

Table

shows how the operation

0 b1100 & 0 b0110 is performed. This shows that the logical and operation is performed for every column and not for the whole word. This will later prove very useful for reading, setting and clearing specific bits.

4.2.4

Compound assignment operators

It is very common to perform bitwise operation where a certain variable is both one of the arguments, as well as the location to store the result of the operation. For this a shortened form called a “compound assignment operator” can be used. This allows to for shorter code over which the programmer has a better overview. For example the logical operations in code snippet

perform exactly the same operation. A full list of these operators can be found at http://en.wikipedia.org/wiki/Operators_in_C_and_

C%2B%2B#Compound_assignment_operators

Snippet 6 Example of compound statements

//initializing the variables uint8_t x = 0 b01010101 ; uint8_t y = 0 b00001111 ;

//performing a bitwise operation on variable x x = x & y ;

//performing the same operation again using a compound assignment operator

X &= y ;

An example which uses a lot of these bitwise operators is the resetting of a specific bit. It might sound simple to set a single “1” to a “0”, but takes quite some steps to clear bit 5 as is Table

7 . To perform this operation, the code in

code snippet

is used. This code is explained step by step in table

16

Table 5 List of bitwise operators

Name Syntax Application

Bitwise NOT

Bitwise OR

~ a a | b

Bitwise AND a & b

Bitwise exclusive OR a ^ b

Bitwise left shift

Bitwise right shift

Flips all bits in a

ORs the first bit of a with the first bit of b, etc.

ANDs the first bit of a with the first bit of b, etc.

XORs the first bit of a with the first bit of b, etc.

a << b Shifts the bits in a b places to the left a >> b Shifts the bits in a b places to the right

Table 6 Processing of a bitwise operator a b

1

0

1

1

0

1

0

0 return value: 0 1 0 0

&

Snippet 7 Example of the clearing of a single bit in a register

//initializing the variables uint8_t x = 0 b10101010 ; uint8_t bitToClear = 5;

//clearing the bit to clear x &= ~(1<< bitToClear );

Table 7 Example of clearing of a single bit

Bit number 7 6 5 4 3 2 1 0

Current value of x 1 0 1 0 1 0 1 0

Desired value of x 1 0 0 0 1 0 1 0

Table 8 Step by step explanation of the code in snippet

# Description

5

6

7

3

4

1

2

Original statement

Rewriting &= to the full form

Substituting x and bitToClear

Rewriting 1 to binary

Substituted code x &= ~(1 << bitToClear ); x = x & ~(1 << bitToClear ); x = 0 b10101010 & ~(1 << 5); x = 0 b10101010 & ~(0 b00000001 << 5);

Performing the bitshift between brackets x = 0 b10101010 & ~(0 b00100000 );

Performing the bitwise NOT operation x = 0 b10101010 & 0 b11011111 ;

Performing the AND operation x = 0 b10001010 ;

17

4.3

Statements in C

To build logical blocks with these operators, statements are added to determine when and how to perform the logical operations. This can be done using control statements. This section will cover some basic control statements.

4.3.1

Conditional Statements

There are two types of conditional statements: if statements and switch cases.

In this manual we assume you are familiar with if statements. The syntax for a C if statement is as given in code snippet

8 , both with or without the “else”

statement.

Snippet 8 Example of an if statement with and without an else clause int16_t x = -3; int16_t y ;

//set y as the absolute value of x

**if**

( x > 0){ y = x ;

}

**else**

{ y = x ;

}

//set x to its absolute value

**if**

( x < 0){ x = x ;

}

Alternative to the if statement, a switch statement can be used. The given argument determines to which line in the statement the program will jump. In code snippet

an example of a morning routine is given. If you wake up on time, there is time to take a shower and have breakfast, if there is little time left, you will skip some steps and if your wake up very early or too late you will go back to sleep.

The same routine could be realized using if statements, but in many cases the switch statement is more insightful and convenient. Switch statements are extremely useful to implement state machines and execute some code depending on a state variable.

Snippet 9 Example of a switch statement

//variable for time until your lecture starts in quarters of an hour int16_t timeToLecture = 2;

//choose what to skip depending on how much time you have

**switch**

( timeToLecture ){

**case**

4:

//just do the same routine as when you had 3 quarters of an hour

**case**

3:

//if you have 3 quarters of an hour start by taking a shower takeAShower ();

//then continue with the next step of your morning routine

**case**

2:

//if you have 2 quarters of an hour have some breakfast

18

haveBreakfast ();

//then continue with the next step of your morning routine

**case**

1:

//leave directly if there is only on 1 quarter of an hour left leaveForLecture ();

//and end your morning routine (jump out of switch statement)

**default**

:

**break**

();

//in the case that the time until the lecture is more than 4 quarters of an hour or less than 1 (0 or negative), go back to sleep.

goBackToSleep ();

//end of the default routine

**break**

;

}

4.3.2

Iteration Statements

C knows two types of iterations: for loops and while loops. It is assumed that the reader knows how to work with these loops. The syntax for these loops is given in code snippet

and code snippet

Snippet 10 Example of a for loop

//perform a piece of code 10 times

**for**

( uint16_t i =0; i <10; i ++){

//write the code to loop here

}

Snippet 11 Example of a while loop

//blink an LED as long as a button is pushed

**while**

( readButton ()){ blinkLED ();

}

19

4.4

Functions

If code is to be used multiple places, it is advised to make functions of these blocks of code. This manual assumes you know what functions are. The C syntax for functions is as given in code snippet

function inside the main () scope and the code in the function block will be executed. The declaration of a function needs to be before the first call of the function. To do that you can use function prototypes, where you specify the name, return type and number and types of the arguments.

Snippet 12 Example of a function

//function prototype of the function multiply int16_t multiply ( int16_t , int16_t ); int main (){

//define some variables int16_t a = 4; int16_t b = 5;

//calculate the value for c using the multiply function int16_t c = multiply ( a , b );

}

//definition of the function multiply int16_t multiply ( int16_t x , int16_t y ){

**return**

x * y ;

}

20

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project