Skip to content

Embedded Programming

Tasks for the week

Group assignment:

  • Compare the performance and development workflows for other architectures
  • Document your work to the group work page and reflect on your individual page what you learned

Individual assignment:

  • Browse through the datasheet for your microcontroller
  • Program a microcontroller development board to interact and communicate

Foreword

Do you remember the fairy tale “Anoush in Wonderland” that I told you at the beginning of our acquaintance? So… Anoush began to go further and further into the forest of discoveries of the Fab Academy. It began to get dark and she became a little scared, because she did not know what lay ahead of her. And ahead were so far unfamiliar giants - knowledge of Electronics and Programming. But in order to overcome the path in that forest, she needed at least a small ray of the sun to light her way. In the dark forest, she did not lose hope, because she trusted the Sorcerers (Instructors) who always appeared at the right time and helped her in her difficult path of discovery and self-improvement. Suddenly, out of nowhere, Onik Babajanyan appeared, who gave her Arvuino Uno and told her how to get a ray of light on this “strange” device. And Anuosh was able to boldly continue her journey in the kingdom of new knowledge.

Group Assignment

During our group assignment, we compared the boards ATMega328P and STM32.

To gain a deeper understanding of the microcontroller’s performance and development workflows, we conducted a straightforward yet insightful test. The test involved instructing the microcontroller to define a digital output and alternate its state between High and Low without any delay functions. By doing so, we were able to generate a pulse train on the defined pin, producing a maximum possible frequency for that specific scenario. This enabled us to compare and evaluate the microcontroller’s performance and determine any areas for improvement.

You can find the results of our tests on the Group Assignment page.

Research

Cy Twombly, Untitled (Bacchus) 2008 . Tate . © Cy Twombly Foundation

That’s what was in my head about microcontrollers and boards. Of course, they were always so attractively unusual for me, but I considered them as a work of art, like this Twombly painting, for example.

My research began with studying datasheets of microcontrollers․ In particular, 2 microcontrollers from those that are present in our Fab Lab attracted my attention: Atmega328p and STM32. Then I asked chatGPT to compare these 2 microcontrollers. Here is the text:

“STM32 and Atmega328p are both microcontrollers that belong to different families and have different features. Here are some of the main differences between them:

  1. Architecture: STM32 is based on the ARM Cortex-M architecture, while Atmega328p is based on the AVR architecture.

  2. Performance: STM32 has higher clock speed and more processing power than Atmega328p. STM32 can run at up to 180 MHz, while Atmega328p runs at up to 20 MHz.

  3. Memory: STM32 has more flash memory and RAM compared to Atmega328p. STM32 typically has between 16 KB to 2 MB of flash memory, while Atmega328p has only 32 KB of flash memory.

  4. Peripherals: STM32 has more advanced peripherals and features, such as a more advanced timer system, more communication interfaces (like USB, CAN, Ethernet, etc.), and more advanced analog-to-digital converters (ADCs) compared to Atmega328p.

  5. Development tools: STM32 typically requires more advanced development tools and programming environments compared to Atmega328p. STM32 development boards are generally more expensive than Atmega328p boards.

  6. Power Consumption: Atmega328p consumes less power than STM32, making it more suitable for battery-powered applications that require low power consumption.

Overall, STM32 and Atmega328p are both capable microcontrollers that can be used for a wide range of applications. The choice between the two depends on the specific requirements of the project, including processing power, memory, peripherals, power consumption, development tools, ease of use, pin count, and cost.”

From the datasheet, I scooped up a pinout card for two microcontrollers, which we will need during programming.

Useful softwares

To program microcontrollers, we need the appropriate software.

Arduino Uno: Let there be light!

Basic

The core component of the Arduino Uno development board is the Atmega328p microcontroller. While delving into the datasheet, I discovered that The Atmega328 microcontroller is a popular and versatile integrated circuit used in various applications. It combines a powerful AVR 8-bit RISC architecture with many features and peripherals.

Let’s explain a few important parameters:

  • Flash Program Memory: The Atmega328 microcontroller has 32KB of flash program memory. This is where you store the program code that controls the microcontroller’s operation. It can be reprogrammed multiple times to update or change the program.

  • EEPROM: The microcontroller has 1KB of EEPROM (Electrically Erasable Programmable Read-Only Memory). It provides non-volatile memory storage, which means the data stored in EEPROM remains even when the power is turned off. You can use it to store important data that needs to be retained between power cycles.

  • SRAM: The Atmega328 has 2KB of SRAM (Static Random Access Memory). SRAM is a type of memory that the microcontroller uses to store temporary data while the program is running. It is fast and can be read from or written to quickly. However, it is volatile memory, meaning the data is lost when the power is turned off.

  • ADC (Analog-to-Digital Converter): The microcontroller includes an ADC that can convert analog signals from sensors or other devices into digital values. This is useful when you need to measure analog quantities, such as temperature or light intensity, and convert them into digital values that can be processed by the microcontroller.

  • Communication Interfaces: The Atmega328 supports various communication interfaces, such as USART, SPI, and I2C. These interfaces allow the microcontroller to communicate with other devices, such as sensors, displays, or memory chips. It enables the microcontroller to send and receive data, enabling interaction with the external world.

  • Power Consumption: The microcontroller is designed to be power-efficient. It has different power modes to minimize power consumption when the device is not actively performing tasks. In active mode, it consumes 1.5mA of current at 3V and 4MHz. In power-down mode, it consumes just 1µA of current at 3V, which is significantly lower when the microcontroller is not in use, helping to conserve energy.

These parameters provide an overview of the important aspects of the Atmega328 microcontroller, including memory, communication capabilities, and power efficiency. Understanding these parameters can help in designing and programming applications using this microcontroller.

The Software

You can find full masterclass about Arduino in this video.

The Arduino software has a lot of hints and examples that we can use to try out the different features of the board. To have the code for Blinking the light let’s try:

Files -> Examples -> Basics -> Blink

Arduino gave me a code:

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

To make this code work, we need to change a few settings.

To better understand the logic of writing code, this video helped me a lot.

In order to understand which pinout to choose, I looked at the datasheet.

Right in front of the LED light is pinout 13, which we will use by changing the code:

// the setup function runs once when you press reset or power the board
  void setup() {
// initialize digital pin 13 as an output.
  pinMode(13,OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13,HIGH); // turn the LED on (HIGH is the voltage level)
delay(800);              // wait for 0.8 second
  digitalWrite(13,LOW);  // turn the LED off by making the voltage LOW
delay(100);              // wait for 0.1 second
}

This is my first attempt at working with a development board and I was so happy with the result that my heart was beating like crazy. In the code, I changed the numbers showing the time so that the rhythm of the light matched the rhythm of my heartbeat.

Blinking light video in Arduino Uno

STM32F3-Discovery: for more fun!

Basic

The “heart” of STM32F3-Discovery is STM32F303. The datasheet highlights:

Core: The STM32 microcontroller uses the ARM Cortex-M4 processor, which operates at a frequency of 72 MHz. It can perform mathematical calculations quickly and efficiently, with a performance of up to 90 DMIPS (Dhrystone Million Instructions Per Second).

Memories: The microcontroller offers up to 512KB of Flash memory for storing the program code. It also has 64KB of SRAM for temporary data storage. Additionally, there is a routine booster of 16KB of SRAM available for faster instruction and data access.

Power and Reset Management: The microcontroller operates within a voltage range of 2.0V to 3.6V. It supports various power management modes such as Sleep, Stop, and Standby to minimize power consumption. In low-power modes, the microcontroller can consume as little as 1µA of current at 3V.

Clock Management: The microcontroller can be clocked using a crystal oscillator ranging from 4 to 32 MHz. It also includes a 32kHz oscillator for the Real-Time Clock (RTC) with calibration. Additionally, there is an internal 8MHz RC oscillator with a PLL (Phase-Locked Loop) option to increase the clock frequency.

I/Os and Interconnect: The microcontroller provides up to 115 fast I/O pins that can be used for connecting with external devices. Some of these pins are 5V-tolerant, meaning they can handle voltage levels up to 5V without damaging the microcontroller.

Analog Features: The microcontroller includes four Analog-to-Digital Converters (ADCs) capable of converting analog signals to digital values. These ADCs offer selectable resolutions of 12/10/8/6 bits and a conversion range of 0 to 3.6V. It also features two 12-bit Digital-to-Analog Converters (DACs) for converting digital values back to analog signals.

Timers and Timed Functions: The microcontroller has up to 14 timers, including a 32-bit timer, two 16-bit timers, and three 16-bit advanced-control timers. These timers support various functionalities such as input capture, output compare, pulse width modulation (PWM), and quadrature encoder input.

Communication Interfaces: The microcontroller supports multiple communication interfaces, including CAN, I2C, USART/UART, SPI, USB 2.0, and an infrared transmitter. These interfaces allow the microcontroller to communicate with different devices and exchange data.

Blinking the light

To launch the software STM32 CubeIDE, we select the directory in which our projects will be stored.

When we create a new project, we need to select the appropriate board that we will use, since this software can work with different boards.

We have 3 options how to find the board we need - by commercial number - search by name - choose from a long list of proposed boards.

In this case, when working with the STM32 microcontroller, the pinout can be seen and set up directly in the software.

After we use Device Configuration Tool Code Generation tool from toolbar to have the “base” for writing the code:

With the help of our instructor Onik Babajanyan I wrote the first lines of the code and I realized that on this board I want to do something interesting: I want all the LED lights to blink alternately in circles. Onik explained to me the logic of how to make the lights blink faster after each lap.

Now I had to carefully find the pin numbers indicated next to the lights, because the numbers did not go in sequence. Then I added to the beginning of the code for the lights to flash for one second each.

/* USER CODE BEGIN 2 */
   int time = 1000;
   /* USER CODE END 2 */

and at the end of the code I added:

time = time - 100;
if (time < 0)
{
time = 1000;
}

So on each circle the lights will flash 0.1 second faster and start blinking again every one second after the blinking time is zero. Well, I hope you understand what I mean…

I compared the code with the pin definitions provided in the datasheet.

  /* USER CODE BEGIN 2 */
  int time = 1000;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin,SET);
      HAL_Delay (time);
      HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin,RESET);
      HAL_Delay (time);
      HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin,SET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin,RESET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD7_GPIO_Port, LD7_Pin,SET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD7_GPIO_Port, LD7_Pin,RESET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD9_GPIO_Port, LD9_Pin,SET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD9_GPIO_Port, LD9_Pin,RESET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD10_GPIO_Port, LD10_Pin,SET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD10_GPIO_Port, LD10_Pin,RESET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD8_GPIO_Port, LD8_Pin,SET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD8_GPIO_Port, LD8_Pin,RESET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD6_GPIO_Port, LD6_Pin,SET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD6_GPIO_Port, LD6_Pin,RESET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin,SET);
      HAL_Delay(time);
      HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin,RESET);
      HAL_Delay(time);
        time = time - 100;
        if (time < 0)
        {
        time = 1000;
        }

 /* USER CODE END WHILE */

I really love the result:

Lights video in STM32

Breadboаrd

Another “weird” invention that I discovered this week is Brеadboard. Wikipedia tells us that “A breadboard, solderless breadboard, or protoboard is a construction base used to build semi-permanent prototypes of electronic circuits. Unlike a perfboard or stripboard, breadboards do not require soldering or destruction of tracks and are hence reusable. For this reason, breadboards are also popular with students and in technological education.

In order to connect an external LED to a microcontroller, we need to learn more about this simple but very useful device. I found a short video on YouTube that answered many questions about Breadboards.

Programming

First I create a New Project, I name it and start to set up the pinouts.

I choose the pin PD0 as an output and label it as LD20.

When I went to the code, I ran into an error:

Error in final launch sequence: Error in initializing ST-LINK device. You can find the solution here starting from 3th minute.

Here is the code I wrote using the label of the pin PD0.

while (1)
  {
      HAL_GPIO_WritePin(LD20_GPIO_Port, LD20_Pin,SET);
      HAL_Delay (500);
      HAL_GPIO_WritePin(LD20_GPIO_Port, LD20_Pin,RESET);
      HAL_Delay (200);

Blinking external LED

Connect an I2C Lcd Display to Arduino Uno

The Inter-Integrated Circuit (I2C) Protocol is a protocol intended to allow multiple “peripheral” digital integrated circuits (“chips”) to communicate with one or more “controller” chips. Like the Serial Peripheral Interface (SPI), it is only intended for short distance communications within a single device. More about I2C here.

To connect the LCD we need to download the library Liquid Cristal I2C:

Tools -> Manage Libraries

Here we find all libraries to install.

For the code I asked my new friend chatGPT to generate the code to show the text on LCD monitor.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Set the address of the LCD
const uint8_t LCD_ADDR = 0x27;

// Set the number of columns and rows on the LCD
const uint8_t LCD_COLS = 16;
const uint8_t LCD_ROWS = 2;

// Create an instance of the LiquidCrystal_I2C class
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);

void setup() {
  // Initialize the I2C communication
  Wire.begin();

  // Initialize the LCD
  lcd.init();

  // Turn on the backlight
  lcd.backlight();

  // Print a message to the LCD
  lcd.print("Hello, World!");
}

void loop() {
  // Do nothing
}

I started to play with the code. Тo display text in 2 rows (my text was longer), I changed the code:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup()
{
  // Initialize the LCD
  lcd.init();

  // Turn on the backlight
  lcd.backlight();

  // Print a message to the LCD
  lcd.setCursor(0,0);
  lcd.print("Anoush in");
1  lcd.setCursor(0,1);
  lcd.print("Wonderland!");
}

void loop() {
  // put your main code here, to run repeatedly:

}

Ticker text

I wanted to get a running line on my LCD. To do this, I again turned to the GPT chat..

To have the ticker on my monitor:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup()
{
  // Initialize the LCD
  lcd.init();

  // Turn on the backlight
  lcd.backlight();

  // Set the cursor to the beginning of the second row
  lcd.setCursor(0, 1);

  // Print the initial message
  lcd.print("I am flying... yeaaah!");
}

void loop()
{
  // Move the cursor to the beginning of the second row
  lcd.setCursor(0, 1);

  // Scroll the text to the left
  for (int i = 0; i < 16; i++) {
    lcd.scrollDisplayLeft();
    delay(300);
  }

}

Ticker on LCD

Serial Monitor

The Serial Monitor is an essential tool when creating projects with Arduino. It can be used as a debugging tool, testing out concepts or to communicate directly with the Arduino board. More about Serial Monitor tool - here.

Using the Serial Monitor, I will be able to write text in the window that pops up and see that text itself on the LCD screen. In order to get the ticker, I again asked the chat to give me the code.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup()
{
  // Initialize the LCD
  lcd.init();

  // Turn on the backlight
  lcd.backlight();

  // Initialize Serial communication
  Serial.begin(9600);

  // Set the initial message
  lcd.setCursor(0, 1);
  lcd.print("Type a message:");
}

void loop()
{
  if (Serial.available()) {
    // Read the input from the Serial Monitor
    String input = Serial.readString();

    // Clear the LCD display
    lcd.clear();

    // Set the cursor to the beginning of the second row
    lcd.setCursor(0, 1);

    // Print the input as a ticker text on the LCD
    int inputLength = input.length();
    for (int i = 0; i <= inputLength + 16; i++) {
      lcd.clear();
      lcd.setCursor(0, 1);
      lcd.print(input.substring(i % inputLength));
      lcd.print(" ");
      lcd.print(input.substring(0, i % inputLength));
      delay(250);
    }

    // Set the initial message again
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("Type a message:");
  }
}

To open the Serial Monitor we go Tool -> Serial Monitor

The Serial Monitor is located at the bottom of the editor:

Here’s what I got:

Programming: Different languages

Programming languages are used to communicate instructions to computers and develop software applications. Each programming language has its own syntax, rules, and features that distinguish it from others. The Arduino software primarily uses a modified version of C++ programming language, known as Arduino C++ or Arduino Language.

Here are some key differences between Arduino C++, Java, and Python:

  • Syntax:

Arduino C++: Arduino C++ is based on the C++ programming language but includes additional libraries and functions specific to Arduino boards. It has a C-like syntax with additional simplifications and abstractions for easier hardware interaction.

Java: Java has a syntax similar to C++, but it is designed to be platform-independent and focuses on object-oriented programming.

Python: Python has a simpler and more expressive syntax compared to C++ and Java. It uses indentation and whitespace for code blocks instead of curly braces.

  • Memory Management:

Arduino C++: Arduino C++ provides low-level memory access, allowing you to directly manipulate registers and memory addresses, which is useful for embedded systems programming.

Java: Java has automatic memory management through garbage collection, relieving developers from manual memory allocation and deallocation.

Python: Python also has automatic memory management, handling memory allocation and deallocation behind the scenes.

  • Libraries and Ecosystem:

Arduino C++: Arduino C++ comes with a rich set of libraries and functions specifically tailored for interacting with Arduino hardware components, making it easier to control sensors, actuators, and other peripherals.

Java: Java has a vast ecosystem with numerous libraries for various purposes, including web development, enterprise applications, and scientific computing.

Python: Python boasts a wide range of libraries and frameworks for different domains, such as data analysis, machine learning, web development, and more.

Here are examples of blinking an LED using Arduino C++, Java, and Python, along with an explanation of the differences:

Arduino C++ (Blinking LED):

int ledPin = 13; // Define pin number

void setup() {
  pinMode(ledPin, OUTPUT); // Set pin as output
}

void loop() {
  digitalWrite(ledPin, HIGH); // Turn on the LED
  delay(1000); // Wait for 1 second
  digitalWrite(ledPin, LOW); // Turn off the LED
  delay(1000); // Wait for 1 second
}

Explanation: In Arduino C++, the code configures pin 13 as an output and uses digitalWrite to set it high (turning on the LED) and low (turning off the LED) with a delay of 1 second between each state change.

Java (Blinking LED):

import java.util.concurrent.TimeUnit;

public class BlinkingLED {
    public static void main(String[] args) throws InterruptedException {
        int ledPin = 13; // Define pin number

        while (true) {
            System.out.println("LED ON"); // Turn on the LED
            TimeUnit.SECONDS.sleep(1); // Wait for 1 second
            System.out.println("LED OFF"); // Turn off the LED
            TimeUnit.SECONDS.sleep(1); // Wait for 1 second
        }
    }
}

Explanation: In Java, the code uses a while loop to continuously toggle the LED state by printing “LED ON” and “LED OFF” to the console. It uses the TimeUnit.SECONDS.sleep() method to introduce a 1-second delay between each state change.

Python (Blinking LED):

import time

led_pin = 13  # Define pin number

while True:
    print("LED ON")  # Turn on the LED
    time.sleep(1)  # Wait for 1 second
    print("LED OFF")  # Turn off the LED
    time.sleep(1)  # Wait for 1 second

Explanation: In Python, the code also uses a while loop to repeatedly toggle the LED state by printing “LED ON” and “LED OFF” to the console. It uses the time.sleep() function to introduce a 1-second delay between each state change.

In all three examples, the purpose is to blink an LED. The key differences lie in the syntax and the specific libraries or functions used for delays. Arduino C++ leverages Arduino-specific functions like pinMode and digitalWrite, while Java and Python use standard language constructs (System.out.println and print) along with language-agnostic functions (TimeUnit.SECONDS.sleep and time.sleep). Additionally, the syntax and structure of the code vary slightly between the languages due to their unique conventions and styles.

Toolchain

A toolchain refers to a collection of software tools that are used together to perform various tasks in the software development process. It typically includes compilers, linkers, assemblers, and other utilities that facilitate the translation of source code into executable programs for a specific target platform or architecture. Here’s an overview of the components and functionality of a typical toolchain:

  1. Compiler: is a fundamental component of a toolchain. It takes the source code written in a programming language (such as C or C++) and translates it into machine code or an intermediate representation. It analyzes the code’s syntax and semantics, performs optimizations, and generates the corresponding object files for each source file.

  2. Linker: combines object files generated by the compiler, along with any necessary libraries, into a single executable or shared library. It resolves symbol references between different object files, performs address allocation, and ensures that the final binary is correctly linked and ready for execution.

  3. Assembler: is responsible for converting assembly language code (low-level human-readable code) into machine code. It translates assembly instructions into their binary representations and generates object files that can be further processed by the linker.

  4. Libraries: contain pre-compiled code that provides reusable functionality for software development. They can be static libraries (.a files) or dynamic/shared libraries (.so or .dll files) and are linked with the application during the build process.

  5. Build Tools: automate the process of compiling, linking, and managing dependencies of software projects.They provide functionality like dependency resolution, incremental compilation, build configuration management, and task automation.

  6. Debugger: is a tool that assists developers in finding and fixing issues in their code. It allows for step-by-step code execution, breakpoints, variable inspection, and other debugging features to analyze the program’s behavior during runtime.

  7. Integrated Development Environment (IDE): is a comprehensive software tool that integrates multiple components of a toolchain into a single user-friendly interface. IDEs often provide features like code editors, compilers, linkers, debuggers, project management, and other tools to streamline the development workflow.

Toolchains enable the translation and transformation of code from human-readable source files into executable binaries. They play a vital role in managing the complexities of the compilation and linking processes, facilitating efficient and reliable software development.

Conclusion

This week has been a special discovery for me. I got acquainted with different microcontrollers and boards and realized that this is a wonderful toy for those who like to discover a new worlds, who are inquisitive. I can’t wait to create my own board because it’s the base for innovation.

By the end of the week, our instructor Babken Chugaszyan showed us a video of how these PCBs are created by machines - robots. I looked at it as a real miracle created by a human.


Last update: June 26, 2023