0% found this document useful (0 votes)
20 views48 pages

P01 C Language Tutorial ENG

The document is a primer on the C programming language, particularly its application in embedded systems. It covers the fundamentals of C, including data types, structures, pointers, and function parameters, along with practical examples and programming tasks. Additionally, it highlights the reasons for C's popularity in embedded systems, such as compiler availability and efficient resource management.

Uploaded by

Elmehdi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views48 pages

P01 C Language Tutorial ENG

The document is a primer on the C programming language, particularly its application in embedded systems. It covers the fundamentals of C, including data types, structures, pointers, and function parameters, along with practical examples and programming tasks. Additionally, it highlights the reasons for C's popularity in embedded systems, such as compiler availability and efficient resource management.

Uploaded by

Elmehdi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

C Language Primer

Fundamentals of Digital and Microprocessor Systems


(KTU course T170B151)

Ž. Nakutis, 2020
C language use in embedded systems programming
(2017 Embedded Market Survey)

2 (Ž.Nakutis, ©)
Why C is so popular in Embedded
Systems

 Compilers are available nearly for all 8, 16 and 32


microprocessors

 C ensures optimal mix between low level progammin


constructions (drivers) and high level programming (main
program) features

3 (Ž.Nakutis, ©)
Reference for self-studying

 See moodle course:


 http://www.loirak.com/prog/ctutor.php
 http://www.tutorialspoint.com/cprogramming/
 http://www.cprogramming.com/tutorial/c-tutorial.html
 The C book
 C Programming (Steve Summit)

4 (Ž.Nakutis, ©)
The first program: Toogle LED
// This is pseudo code, not to run on any processor
#include <processor.h>

void Delay(const unsigned int uCount) //Function to provide delay


{
unsigned int i=0;
for(; i < uCount; i++)
{
}
}
int main()
{
Port2DIR.1=0; //configuring as output pin
while(1)
{
Port2.1=1; // Make pin high
Delay(1000);
Port2.1=0; // Make pin low
Delay(1000);
* C pseudocode: not
} for particular
} processor or
scematics !
5 (Ž.Nakutis, ©)
What is in Programming Language
Specification?
 Syntax: program structure and sentences grammar
 Punctuation (semicolon)
 Keywords(if, int, switch, etc.)
 Function declaration rules (brackets)
 Semantics: Meaning of language elements. A sentence might be
free of error from syntax point of view, but the meaning and the
expected result might depend on ...
For example, ++ operator means add one:
 If applied to integer type variable, increments value by one.
 If applied to pointer (address), then increments address by number of bytes
equal to the size of variable a pointer points to
 If applied to float type variable, then complier error will be recognized
because this operation does not has a sense

6 (Ž.Nakutis,s ©)
Simple C program structure
Program
resset

func. F1

Initialization functions

Func. main
func. F2 Library
func. B1

ISR

Library
func. B2

7 (Ž.Nakutis, ©)
Types of high level programming
languages
 Compiled:
 C / C++ / Java (Arduino scripts are also compiled)
 The entire program is converted to machine code,
 Program execution is faster, but needs more memory to
store intermediate object code, compilation takes longer
 Interpreted:
 Phyton / JavaScript / MATLAB
 Interpreter converts code line by line during execution
 Program execution is slower, does not save object code
https://www.guru99.com/difference-compiler-vs-interpreter.html
In embedded systems compiled program dominate (hardware
resources are limited (no need to save interpreter), real time
processing
8
is important) (Ž.Nakutis, ©)
Basic data types in C language
Data types Number of bytes Range of numbers
char 1 128  127
unsigned char 1 0  255
short 2 32768  32767
unsigned short 2 0  65535
int 2 (8 and 16 bit architecture) 32768  32767 (8 and 16 bit architecture)
4 (32 bit architecture) -2-31  (231-1)
unsigned int 2 (8 and16 bitų architecture) 0  65535 (8 and 16 bit architecture)
4 (32 bit architecture) 0  (232-1)
unsigned long 4 0  (232-1) = 0  4 294 967 295
long long 8 0  (264-1)
float 4 1.175494E-38  3.402823E+38
double 8 2.2250738585072009 × 10−308  ≈
1.7976931348623157 × 10308
9 https://en.wikipedia.org/wiki/C_data_types (Ž.Nakutis, ©)
Portable types <stdint.h>
<stdint.h> fragment
/* exact-width signed integer types */
typedef signed char int8_t;
typedef signed short int int16_t;
/* iškvietimo pavyzdys */
typedef signed int int32_t;
#include <stdint.h>
typedef signed __INT64 int64_t; uint8_t var1;
int32_t var3 = -100;
/* exact-width unsigned integer types */
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned __INT64 uint64_t;

10 (Ž.Nakutis, ©)
C types are very important (examples)
unsigned char a, b, c, d;
a = 200;
b = 100;
// What values will be assigned to c and d?
c = a + b;
d = b / a;

11 (Ž.Nakutis, ©)
Collection of variables
 Arrays – hold data of the same type
 One dimensional
 Multi dimensional
 Structures – can hold data of different type
 Bit fields

12 (Ž.Nakutis, ©)
Arrays
// array declaration example
int array[10];
char string[]=“Hello”;
unsigned char bufer[20];
int array2[5][3];

// using arrays examples


array[5]= -10;
array2[0][2]=array [0];
strcpy(bufer,string);

13 (Ž.Nakutis, ©)
Structures
struct
{ unsigned int amplify_factor;
unsigned char volume;
char attribute;
} settings; // structure declaration

// using structure
settings. amplify_factor =100;
settings. attribute =‘a’; // ASCII code of ‘a’ (97 decimal) will be
assigned

14 (Ž.Nakutis, ©)
Bit fields
typedef struct
{ unsigned char D0_1:2;
unsigned char D2_6:5;
unsigned char D7_8:2;
unsigned char DHigh:1;
} TMyBits;
TMyBits var;

// bit fields usage


var.D0_1=3;
var.D2_6=0x1F;
15 (Ž.Nakutis, ©)
User defined data types
typedef struct
{ unsigned int Year;
unsigned char Hour;
unsigned char Minute;
unsigned char Flag;
} TTime;
// variables declaration
TTime Reminder1, Reminder2, Alarms[10];

// Using array of structures:


Aliarms[1]. Year =2019;
16 (Ž.Nakutis, ©)
Pointers
 A pointer is a variable that points at, or refers to, another variable.
 If we have a pointer variable of type ``pointer to int,`` it might point to the int variable
i, or to the third cell of the int array a.
 Given a pointer variable, we can ask questions like, ``What's the value of the variable
that this pointer points to? (http://c-faq.com/%7Escs/cclass/notes/sx10.html )

int* pConfig;
unsigned char array[10];
char string[ ]=“Hello”;

// using pointers
*pConfig=3;
array= string; // not an error, but incorrect
strcpy(array, string); //how strcpy is declared?
17 https://www.tutorialspoint.com/cprogramming/c_pointers.htm
(Ž.Nakutis, ©)
Pointer operators * and &

 Operator * – Dereference Operator or Indirection


Operator
 Operatorius & – Address Of Operator

18 (Ž.Nakutis, ©)
Pointer operator * and & using
examples
int* pA;
int B; // static variable

struct {
int Field1;
float Field2;
} MyStruct;

int main()
{
pA= &B; // pointer pA refers to B

*pA=3; // indirect operator, value assignement


B=5; // value assignment to variable

pA=&(MyStruct.Field1); // pointer to structure field Field1


}

19 (Ž.Nakutis, ©)
Variables and functions visibility scope
char A, s; // global variables
char sum(char X1, char X2) // function declaration
{
char Y; // local variable within function sum

Y=X1+X2;
return Y;
}

int main (void)


{
char B; // local variable within function main
B=2;
A=3;
s=sum(A,B); // function sum call (lt. Iškvietimas)
s=sum(10,A); // second function sum call
// Y=sum(B,B); // is this an error???
}

20 (Ž.Nakutis, ©)
C compiler directives
#include <math.h>
#include “my.h”

// using define symbolic names makes program:


// 1. easier to read
// 2. easier to modify (only one place modification)
#define NumberPI 3.14
#define ARRAY_SIZE 50

float Array[ARRAY_SIZE]; // use symbolic name to declare array


int i, A;
int main()
{
for(i=0;i<ARRAY_SIZE;i++)
Array[i]=sin(2*NumberPI*i/ARRAY_SIZE);

if (A<ARRAY_SIZE) // use symbolic definition instead of numbers


A=ARRAY_SIZE;
}

21 (Ž.Nakutis, ©)
Header files (*.h) example
stm32f4xx_gpio.h
/**
* @brief GPIO Configuration Mode enumeration
*/
typedef enum
{
GPIO_Mode_IN = 0x00, /*!< GPIO Input Mode */
GPIO_Mode_OUT = 0x01, /*!< GPIO Output Mode */
GPIO_Mode_AF = 0x02, /*!< GPIO Alternate function Mode */
GPIO_Mode_AN = 0x03 /*!< GPIO Analog Mode */
}GPIOMode_TypeDef;
#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_IN) || ((MODE) == GPIO_Mode_OUT) || \
((MODE) == GPIO_Mode_AF)|| ((MODE) == GPIO_Mode_AN))

/** @defgroup GPIO_pins_define


* @{
*/
#define GPIO_Pin_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /* Pin 4 selected */

/* GPIO Read and Write functions **********************************************/


uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

22 (Ž.Nakutis, ©)
Passing parameters to functions
 By value:
 Values of passed parameters can not be modified because
function operates on their copies
 By pointer
 Function can modified values of passed parameters and can
return them to the calling program
Variable Function parameter declaration
Value Pointer
func(int param); func(int* param);
Call example: Call example:
int y; // static func(y); func(&y);
Call example: Call example:
int* x; // pointer func(*x); func(x);

23 (Ž.Nakutis, ©)
Task (function check)
1. Create a function named check in C. Two arrays of
arbitrary length has to be passed as parameters, for
example, user name and password. The function
must return „1“ if passed parameters match
corresponding internal arrays, otherwise the
function must return „0“.
2. Declare function prototype in a header file.
3. Present an example of function call from a C
module.

24 (Ž.Nakutis, ©)
Solution: function definition in C module
my_library.c
#define USER_NAME_LEN 4
#define USER_PSW_LEN 5
char UserName[USER_NAME_LEN]="John";
char UserPsw[USER_PSW_LEN]="Abc52";

bool check(char* user, int user_len, char* psw, int psw_len)


{
bool result=true;
int i;
if (user_len != USER_NAME_LEN || psw_len != USER_PSW_LEN)
return false;

for (i=0;i<USER_NAME_LEN;i++)
{ if (user[i] != UserName[i])
{
result=false;
}
} // for

if (result==false) return result;

for (i=0;i<USER_PSW_LEN;i++)
{ if (psw[i] != UserPsw[i])
{
result=false;
}
} // for
return result;
}
25 (Ž.Nakutis, ©)
Solution : function declaration in header file
my_library.h)
#ifndef __MY_LIBRARY_H__
#define __MY_LIBRARY_H__

/*
Parameteres:
user - user name string (array of characters)
user_len - length of user array
psw - password string
psw_len - length of psw array
Return:
TRUE – when user and psw match preset values, otherwise- FALSE
*/
bool check(char* user, int user_len, char* psw, int psw_len);

#endif

26 (Ž.Nakutis, ©)
Solution : function call in C module

#include <my_library.h>

bool MyResult;

int main(void)
{
// Function call
MyResult = check("John",4,"Abc53",5);
// MyResult will become false because passwords do not match
}

27 (Ž.Nakutis, ©)
Task (function EventsLog)
1. Define a function called EventsLog.
2. The function has two input parameters:
 Array Events of arbitrary length containing elements of type
Ttime;
 Single parameter Alarm of type Ttime.
3. The function must assign „1“ to every element of Events
parameter’s field Flag if values of the corresponding
element‘s time field matches time filed (Year, Month, Day) of
parameter Alarm.
typedef struct
{ unsigned int Year;
unsigned char Month;
unsigned char Day;
unsigned char Flag;
} Ttime; 28
(Ž.Nakutis, ©)
Solution: function definition
my_events.h
typedef struct
{ unsigned int Year;
unsigned char Month;
unsigned char Day;
unsigned char Flag;
} Ttime;

my_events.c
#include “my_events.h”
/* Function declaration
Parameters:
Ttime* Events - array of input events
int NumEvents - size of array Events
Ttime* Alarm -
return : none*/
void EventsLog(Ttime* Events, int NumEvents, Ttime* Alarm)
{
int i;
for (i=0;i<NumEvents,i++)
if (Events[i].Year==Alarm->Year && // alternative *(Events+ i).Year == (*Alarm).Year &&
Events[i].Month==Alarm->Month &&
Events[i].Day==Alarm->Day)
Events[i].Flag=1;
else
Events[i].Flag=0;
}

29 (Ž.Nakutis, ©)
Solution : function call
#include “my_events.h”

#define NUMBER_OF_EVENTS 5
Ttime MyAlarm;
Ttime MyEvents[NUMBER_OF_EVENTS];

int main (void)


{

MyAlarm.Year=2018;
MyAlarm.Month=2;
MyAlarm.Day=20;

// Function EventsLog call


EventsLog(MyEvents, NUMBER_OF_EVENTS, &MyAlarm);
}

30 (Ž.Nakutis, ©)
Bit logic operations
Bit logic operations are used for bit manipulation: set to
„1“, , reset to „0“ or toggle. The result is always a multibit
variable, whose every bit is calculated according to logic
operation from corresponding bits of operands.

Operator Is used for Example


AND Bit reset a = a & 0xF0;
a &= 0xF0;
OR Bit set a = a | 1;
a |= 1;
XOR Bit toggle a = a ^ 0xFF;
a ^= 0xFF;
Inversion Invert all bits a=~0xF0;
31 (Ž.Nakutis, ©)
Bit logic operations (examples)

unsigned char a;
a = 0x55; //X1=0x55
X1 = ~ a; //X1=0xAA
// logic AND
X2 = a & 0xF0; // 0xF0=1111 0000, X2=0x50
// logic OR
X3 = a | 0x03; // 0x03=0000 0011, X3=0x53
// logic XOR,
X4 = a ^ 0x01; // 0x01=0000 0001, X4=0x52
32 (Ž.Nakutis, ©)
Program flow control

 Conditional sentences (if else, switch)

 Cycles (for, while, until)

 Blocks ({})

33 (Ž.Nakutis, ©)
for and if examples

unsigned char mas[10], min;


int main ()
{
min=0xFF;
for (i=0;i<10;i++)
{
if (min>mas[i])
{ min=mas[i];
} else { }
} // for cycle end
} // main function end

34 (Ž.Nakutis, ©)
Multiple conditions sentence switch
char ProgramState;

switch (ProgramState)
{
case 0: {
// Program block 0
ProgramState=1;
} break;
case 1: {
// Program block 1
ProgramState=2;
} break;
case 2: {
// Program block 2
ProgramState=0;
} break;
default: // Program block default
}
35 (Ž.Nakutis, ©)
Interrupts and their sources
 Interrupts enable microprocessor to handle asynchronous
external events (external to MCU, e.g. byte reception via
UART interface, logic level change at GPIO, etc.)
 Interrupts enable to synchronize program flow with the status
of peripherals, e.g. timer overflow

 Sources of interrupts:
 General purpose input output (GPIO) ports
 Internal timers
 Internal ADC (Analog-to-Digital Converter)
 Communication interfaces (UART, SPI, I2C)
 Direct Memory Access (DMA) channel
 Brown-out detector module (observation of supply voltage level)
36 (Ž.Nakutis, ©)
Libraries
 Collection of functions (drivers of peripherals, digital
signal processing, communication stacks, etc.)
 Library header files must be included in the program
code using directive #include „library_name.h“
 Sometimes they can be provided in open C code, but
other time in already compiled format (not readable by
humans)
 Building entire project libraries are linked together with
other project modules and this way executable image is
generated. Executable image (file) can by loaded to the
memory of embedded systems (MCU program memory)

37 (Ž.Nakutis, ©)
Examples of libraries
 Peripheral driver libraries (ST Microelectronics call them
HAL – Hardware Abstraction Level libraries)
 Standard C libraries (math.h, stdio.h, string.h, etc.)
 Digital signal processing libraries (digital filters, FTT,
matrix multiplication, etc.)
 Communication stacks (TCP/IP, UART, Modbus, wireless
communication stacks)
 Motor control libraries
 Fixed point numbers and operations library

38 (Ž.Nakutis, ©)
Const and volatile type
qualifiers
 const and volatile – are independent qualifiers. It is an error
to think they are opposite qualifiers. Sometimes it is
meaningful to use them together.
 const denotes, that the program can not modify the variable,
for example

const int a=10; // variable a can not be assigned any new value,
however, it can be initialized
int main()
{
a=20;
}

References:
1. Introduction to the Volatile Keyword http://www.embedded.com/story/OEG20010615S0107
2. Const and volatile http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html
39 (Ž.Nakutis, ©)
Const qualifier and pointers
 char *const X; // constant pointer (X value can not be
modified) to object, whose value can be modified (*X can be
modified by the program)
 char const *X; // pointer to constant object; the pointer
itself (X value) can be modified, but the object it points to can
not be modified const char pi=3.14;
const char e=2.71;
const char* p1;
const char* p2;
int main(void)
{
p1=&pi;
p1=&e;
p2=p1;
}
40 (Ž.Nakutis, ©)
Volatile qualifier – attribute often used
in embedded systems programs
Declaration examples:
volatile int a; // volatile variable
volatile int * pb; // pointer to volatile variable of type int

The volatile qualifier tells to compiler, that the value of variable may change
at any time independent from neighboring program code execution
The volatile keyword indicates that the value in a memory location can be
altered in ways unknown to the compiler or have other unknown side effects
(e.g. modification via a signal interrupt, hardware register, or memory mapped
I/O) even though nothing in the program code modifies the contents.

In practice the above applies to three types of variables:


 Memory-mapped peripheral registers, for example, new value at the port
input appears not because of SW operation, but due to external HW
 Global variables, that may be modified by interrupt service routines
 Global variables in programs with concurrent (parallel) multi-threaded
application
41 (Ž.Nakutis, ©)
Peripheral registers and volatile
qualifier
Incorrect code – optimizing compiler reads *ptr once and many
times compares it to 0. A programmer wanted many times
reading and each time after reading to compare to 0.
int* ptr = (int*) 0x1234;

// Wait for register to become non-zero


while (*ptr == 0); // compiler will interpret the code
like read once, check condition ones

// Continue program
Correct code:
int volatile* ptr = (int volatile*) 0x1234;
// Wait for register to become non-zero
while (*ptr == 0); // compiler will interpret the code
like read in every cycle and check in every cycle

// Continue program
42 (Ž.Nakutis, ©)
Volatile qualifier used to declare
structures for peripheral devices
access
#define DEVADDR 0x20001005 // base address of peripheral device
int devno=1; // peripheral device channel number

struct devregs {
unsigned short csr; /* control & status */
unsigned short data; /* data port */
};

volatile struct devregs *const dvp=DEVADDR+devno*sizeof(struct


devregs);
/* structure type variable is volatile (all its fields are
volatile), but pointer dvp is constant and corresponds to the
address of peripheral device*/

if (dvp->data == 0x01) { … } /* process data */


43 (Ž.Nakutis, ©)
Interrupts and volatile qualifier
Incorrect code – optimizing compiler does not know, that ext_rcvd value
may be changed in interrupt service routine, and therefore !ext_rcvd is
always TRUE and program will never exit while cycle.
int ext_rcvd = FALSE;
// correct declaration would be
// int volatile etx_rcvd = FALSE;
void main()
{
while (!ext_rcvd) // Wait
{
// process received data
}
}
interrupt void rx_isr(void)
{
if (ETX == rx_char)
{
ext_rcvd = TRUE;
}
}
44 (Ž.Nakutis, ©)
Volatile qualifier and pointers
char *volatile X;
char* (volatile X);
// volatile pointer (X value may change independent from main program code)
char volatile *X;
(char volatile) *X;
// pointer to volatile object (*X value may change independent of code)

volatile int* p1; // pointer to volatile int


int* p2;

int X1,X2;

X1= (*p1) * (*p1); // twise reads *p1, if *p1 changes X1 not


necessary square of *p1
X2= (*p2) * (*p2); // always square of *p1

45 (Ž.Nakutis, ©)
Incorrect usage (or not using) of
volatile qualifier may be suspected
if:
1. Program behavior was correct until optimizing
compiler was disabled

2. Program behavior was correct until interrupts were


disabled

3. Strange peripheral device driver behaviour

4. Everything was OK in single threaded


implementation, but crashed after running the
second thread
46 (Ž.Nakutis, ©)
Why not to use volatile for every
variable?

In this case we would prevent optimizing compiler from


transforming code into effective executable. Sometimes
intermediate variables indeed reasonable to remove, though
program description looks easier to read when they are
present.

47 (Ž.Nakutis, ©)
static qualifier
static int globalX=0; Features:
// used only in this C 1. Visible only inside one C module (C
module (file)
module is file with *.c extension)
void MyFunction(void) 2. Copy of variable is not created in stack,
{ if variable with static qualifier is
//local variables declared inside a function. The value is
static int staticX=0;
int localX=0;
preserved (not reinitialized) between
calls.
staticX++; int main()
localX++; {
globalX++;
return; MyFunction(); // after return globalX=1;
} // inside staticX=1; localX=1;
MyFunction(); // after return globalX=2;
Static can be applied to // inside staticX=2; localX=1;
MyFunction(); // after return globalX=3;
function also. Then the
// inside staticX=3; localX=1;
function becomes local. }
48 (Ž.Nakutis, ©)

You might also like