FAB ACADEMY 2022
INSTITUTO SUPERIOR TECNOLÓGICO TÚPAC AMARU - CUSCO 
FABLAB UNIVERSIDAD  CONTINENTAL PERU

Embedded Programming

First steps in embedded systems

Embedded systems are all around us. From a video game controller, to a coffee maker, to your car dashboard and medical devices. Did you know that an average car has over 50 microcontrollers on board? I am excited to be able to jump into embedded design by leveraging some basic skills as a C programmer. In embedded systems we will learn about various complications that are not a concern in traditional OS applications, such as limited RAM or a low-end CPU. We'll also learn various techniques, such as working with a microcontroller that doesn't have a floating-point unit, working with an optimizing compiler without getting hurt, and manipulating individual bits in a hardware-friendly way. I'll try to use some techniques using an Arduino UNO, mainly because it has a very limited CPU. So you'll be able to see how to get the most out of this very basic piece of hardware.


What we should know

Here are some details you need to know before you get started. This topic is intended for programmers who want to dive into embedded design, requiring minimal familiarity with the C programming language and its basic concepts, such as binary and hexadecimal number representations, functions, data types, and so on.

We'll focus on important concepts for designing better embedded solutions, but keep in mind that we won't cover the basics of C and we won't implement an embedded project from scratch.

I will be using my first Arduino Uno kit. I will also initially be using the Arduino IDE, which can be downloaded from the Arduino website. Versions are available for various operating systems.

Apps: OS vs Embedded


Before we get to the code, it's very important to be aware of the many differences between built-in applications and the traditional operating system applications that you may be familiar with. Computer systems have many different applications such as information systems, medical devices, video games, car control, etc. As we can see, these applications have different goals and therefore different complications arise.

We are interested in two categories of computer systems. On the one hand, we have traditional desktop computers and, more recently, mobile phones. When you have a general purpose architecture, more than a megabyte of memory, the microprocessor usually has a high performance CPU, power is not a primary concern. By that I mean that the user is expected to power or recharge the battery of the devices because a design goal is to obtain the best cost-performance ratio.

On the other hand, we have embedded systems. And this is where we should pay special attention to the typical parameters. Embedded systems typically have a specific purpose, which is digital control of hardware. Embedded systems tend to have much less memory available. Typically less than two kilobytes. CPUs are not as powerful in embedded systems. So we are looking at a low-end microcontroller with a very modest CPU.

A common goal in embedded systems is to achieve the necessary performance at the lowest possible cost. We are talking about digital control applications. Say, a microwave oven, a pedometer, a car dashboard, etc. These applications can have one of two software architectures. The first is a simple main function with an endless loop, which is known as a bare metal application. And the second would be a real-time operating system, or RTOS, which is not at all a traditional operating system like Android, Windows, or Linux.

So we're talking about any digital control application that runs on its own software, other than a sophisticated operating system. As a popular engineering saying goes, "You want your designs to be fast, cheap, and good." You will have to choose two. The reality of an embedded application is quite different from that of a desktop computer. There are five important aspects in embedded systems: These aspects are memory, storage, power consumption, processing power, and hardware knowledge.


Mask in Arduino UNO


Getting started in embedded systems

From a video game controller, a coffee maker, all the way to your car dashboard and medical devices. Did you know that an average car has over 50 micro-controllers on board? My name is Eduardo Corpeno. I've been working with embedded systems, and also teaching embedded programming for over 15 years. 

I'm excited about getting you started in embedded design by leveraging your skills as a C programmer. 

In this week, I'll learn about several complications that aren't a concern in traditional operating system applications like limited Ram or a low-end CPU. So I'll learn several techniques like working with a microcontroller that doesn't have a floating point unit, working with an optimizing compiler without getting hurt and manipulating single bits in a hardware friendly way. We'll showcase all of these techniques using an Arduino UNO, mainly because it has a very limited CPU. So I'll get to see how to make the best out of this very basic piece of hardware.

Learning to write embedded software in C and deliver applications that are small, efficient, and fast. In this week, I how C programming and the Internet of Things combine in embedded applications—software that permanently resides on a device. Reviews the challenges involved in this type of programming, ranging from memory, storage, and power limitations to hardware awareness. explains how different data types can affect the performance of your app and reviews bit manipulation—an area where C shines. Neil shows how to manipulate bit-level data using bit masking and bit fields, demonstrating the strengths of both techniques with an Arduino controller. Then learning how to use the volatile and const variables to enforce data correctness and read-only access, and explore alternatives to traditional functions, which may take too long to run, and floating-point math, which consumes CPU.

Important aspects

There are five aspects that are fundamental in integrated systems. Keep an eye out for these icons as they are the means by which we can achieve our design goals. These aspects are: 

  1. Memory, 
  2. Storage, 
  3. Power consumption, 
  4. Processing power, and 
  5. Hardware knowledge.

Bit Manipulation

  • Masking

In embedded systems, being able to manipulate specific bits in data has an impact on memory because variables that use less than eight bits become possible. For example, an integers to hold numbers between one and 12 to represent the month in a date, well, this would only take four bits. Second bit manipulation is a must to deal with the hardware, especially because the in chip supporting hardware communicates with the CPU in a bit wise manner. Let's see three Bitwise operations we can perform by means of bit masking. First, we use masking to set and clear bits that is to write the logical one or a logical zero to specific bits respectively. Here's one way of doing it in C. Let's say we are working with eight bit variables and that we want to pay attention to bit number three, which is the fourth bit from the right. To write a one or zero to a specific bit, we need an eight bit mask with a one at the bit position we are interested in and zeros everywhere else. Let's say that the bit of interest is a variable called X. So writing a one, to X in that particular bit is a CCS performing a bit wise or operation between X and the mask. This makes sense because any bit Ord with a one results in one. Similarly, to clear a bit, number three in in X, we do a bit wise and operation between X and the inverted mask. This makes sense because any bit added with a zero results in zero and it's on changed otherwise. Notice that the mask always marks the bits of interest with a one. Masking is also useful to read bits. Using the same mask, we can read the state of bit number three. Performing a Bit wise and operation between X and the mask will result in either zero or the mask itself. Luckily in C, zero means false and anything other than zero is true, so this is perfectly valid. Finally, we use masking to invert specific bits. Following our example, we can invert this state of bit number three in X, simply by performing a Bitwise XR operation between X and the mask. Let's see a coding example. Here we have a convenient macro function that produces a mask for a single bit. This is done by shifting the constant one, X times to the left. This is not expensive at all because it happens at compile time, so your target CPU will never know about this shifting. 

 This eight bit port is called port B and each bit can be configured as input or as an output. Reading input pins and writing to output pins is as easy as reading or writing to a memory mapped bit called port BD. So as the comment says, we want to set pins two and five and clear pins, zero, three and seven, then if pin four is high, we want to invert pin, number one. 

These are roles, separate operations on the same port, but at different bits. So the first thing this managed port B function does, is freezing the state of the port BD register to work with a copy called temp. Then we said bits two and five with a mask created from separate masks for bits two and five. As you can see, the result is a mask with ones in bits two and five. Next, we clear bit zero, three and seven with an inverted mask. Before the inversion, this mask is produced just like the previous one, that is with a mask for B zero, a mask for bit three and a mask for a bit seven. Now in the conditional part, we check the state of bit number four. This is presumably an input pin and if it is high, the inversion is done by a bit Bit wise XR on bit number one. Finally, we commit the changes collected with temp into port BD. There are three key ideas in this video. First, masking is a safe way to only deal with the bits of interest. The rest of the bits are ignored and unchanged. Second, if you decide to use masks in your code, you are expected to make it easy to read. It's not nice to write your masks in decimal or with complex expressions because other programmers may misunderstand your code. And third, don't worry about mass calculations. These are done by the compiler, not your target CPU.

In embedded systems, being able to manipulate specific bits in data has an impact on memory because variables that use less than eight bits become possible. For example, an integers to hold numbers between one and 12 to represent the month in a date, well, this would only take four bits. Second bit manipulation is a must to deal with the hardware, especially because the in chip supporting hardware communicates with the CPU in a bit wise manner. Let's see three Bitwise operations we can perform by means of bit masking. First, we use masking to set and clear bits that is to write the logical one or a logical zero to specific bits respectively. Here's one way of doing it in C. Let's say we are working with eight bit variables and that we want to pay attention to bit number three, which is the fourth bit from the right. To write a one or zero to a specific bit, we need an eight bit mask with a one at the bit position we are interested in and zeros everywhere else. Let's say that the bit of interest is a variable called X. So writing a one, to X in that particular bit is a CCS performing a bit wise or operation between X and the mask. This makes sense because any bit Ord with a one results in one. Similarly, to clear a bit, number three in in X, we do a bit wise and operation between X and the inverted mask. This makes sense because any bit added with a zero results in zero and it's on changed otherwise. Notice that the mask always marks the bits of interest with a one. Masking is also useful to read bits. Using the same mask, we can read the state of bit number three. Performing a Bit wise and operation between X and the mask will result in either zero or the mask itself. Luckily in C, zero means false and anything other than zero is true, so this is perfectly valid. Finally, we use masking to invert specific bits. Following our example, we can invert this state of bit number three in X, simply by performing a Bitwise XR operation between X and the mask. Let's see a coding example. Here we have a convenient macro function that produces a mask for a single bit. This is done by shifting the constant one, X times to the left. This is not expensive at all because it happens at compile time, so your target CPU will never know about this shifting. 

 This eight bit port is called port B and each bit can be configured as input or as an output. Reading input pins and writing to output pins is as easy as reading or writing to a memory mapped bit called port BD. So as the comment says, we want to set pins two and five and clear pins, zero, three and seven, then if pin four is high, we want to invert pin, number one. 

These are roles, separate operations on the same port, but at different bits. So the first thing this managed port B function does, is freezing the state of the port BD register to work with a copy called temp. Then we said bits two and five with a mask created from separate masks for bits two and five. As you can see, the result is a mask with ones in bits two and five. Next, we clear bit zero, three and seven with an inverted mask. Before the inversion, this mask is produced just like the previous one, that is with a mask for B zero, a mask for bit three and a mask for a bit seven. Now in the conditional part, we check the state of bit number four. This is presumably an input pin and if it is high, the inversion is done by a bit Bit wise XR on bit number one. Finally, we commit the changes collected with temp into port BD. There are three key ideas in this video. First, masking is a safe way to only deal with the bits of interest. The rest of the bits are ignored and unchanged. Second, if you decide to use masks in your code, you are expected to make it easy to read. It's not nice to write your masks in decimal or with complex expressions because other programmers may misunderstand your code. And third, don't worry about mass calculations. These are done by the compiler, not your target CPU.