Introduction
This document briefly goes through some important concepts in C that are commonly used in our code. This does not cover everything in C programming and is written with the assumption that the reader has a basic knowledge of programming (eg variables, loops, conditional statements, OOP).
For a more comprehensive tutorial visit this link.
Table of Contents
Table of Contents |
---|
#define
What is #define?
In embedded systems, we use #define
to define macros. A macro is a block of code which is given a name. An example is
...
Here the macro, 15
, is given the name NUM_GPIO_PINS
. This is a preprocessor directive, meaning that before the code is compiled, the preprocessor will replace NUM_GPIO_PINS
to 15
every time it appears. Even though we could simply just hardcode 15 into our code, when we use #define
we make our code much more readable.
#define vs Variables
The alternative to using #define
is to use variables. We could alternatively do the same thing with what we did above and use the keyword const
to make sure the variable value does not change so it acts similar to #define
.
...
However, we normally using #define since we have a limited amount of memory in MCUs and do not want to waste space.
typedef struct
If you have programmed with OOP, you should be familiar with classes and objects. If not that's okay too. In C, we have structs which act similarly. Basically, a struct is a data type used to group a list of variables under a certain name. For example, in the struct below we have a dog with variables name and age.
Code Block | ||
---|---|---|
| ||
struct Dog { string name; int age; } Dog; |
To access create a variable of type Dog and access it, we can do
...
In our code, you’ll notice that we put the keyword typedef
in front of the struct
. This is so every time we create a variable of type Dog, we don’t have to put struct
in front of it and we can access it as shown below.
Code Block | ||
---|---|---|
| ||
Dog d = { .name = "Winchester", .age = 42 }; |
typedef enum
An enum (enumeration) in C allows us to easily assign variables with consecutive values. See the example below. Again, we use the typedef so we don’t need to write enum in front of the type name when trying to create a variable of type GpioPins.
Code Block | ||
---|---|---|
| ||
typedef enum { GPIO_PIN_0 = 0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, NUM_GPIO_PINS } GpioPins; |
...
See? This is okay, but it is much nicer to use enums in this case.
Decimal, Binary, and Hex
Decimal, binary, and hexadecimal are simply different ways of writing numbers. We are normally used to using decimal in our daily lives. Binary, on the other hand, is used since it more closely resembles how the computer calculates numbers. Hexadecimal is used as a more friendly way of representing binary values.
Below is a table of numbers 0 - 15 represent in decimal, binary, and hex.
...
Decimal
Decimal is what we are used to seeing which uses 10 as a base. I think this should be trivial since we always see this. However, to calculate this value we do the following.
...
Notice how we take each value x, from the nth column, and multiply it by the base to the nth exponent. This will help us with calculations for binary values as well.
Binary
In binary, numbers are represented by 1s and 0s. Binary uses base 2, let us break this down.
...
sum = (1 x 2^0) + (1 x 2^1) + (0 x 2^2) + (1 x 2^3)
= 1 + 2 + 0 + 8
= 11
Hexadecimal
In hex, numbers are represented from 0-9 and A-F. Hex uses base 16. You can see the values of A-F in the chart above. We can again, break this down and convert hexadecimal to decimal. For hex, you will also notice they generally have a prefix “0x…” before it. This just indicates that it is in hex.
...
sum = (12 x 16^0) + (4 x 16^1)
= 12 + 64
= 76
Bitwise Operations
Below is a table to summarize bitwise operators. Please view this link to see examples of how bitwise operators work and how we can manipulate numbers.
https://www.programiz.com/c-programming/bitwise-operators
...
Writing to Registers
When writing drivers for certain MCUs, one important skill is being able to find the correct register from a datasheet and write to it.
...
We can check if the 0th bit is activated as well
Code Block | ||
---|---|---|
| ||
if (register & (1 << 0)) { // do something } |