Table of Contents

 
 Multiasm Project Logo

Project Information

This content was implemented under the following project:

Consortium Partners

 Consortium Partner's Logos

Erasmus+ Disclaimer
This project has been co-funded by the European Union.
Views and opinions expressed are, however, those of the author or authors only and do not necessarily reflect those of the European Union or the Foundation for the Development of the Education System. Neither the European Union nor the entity providing the grant can be held responsible for them.

Copyright Notice
This content was created by the MultiASM Consortium 2023–2026.
The content is copyrighted and distributed under CC BY-NC Creative Commons Licence and is free for non-commercial use.

CC BY-NC

In case of commercial use, please get in touch with MultiASM Consortium representative.

Introduction

This manual is intended to help students bootstrap into assembler programming across a variety of applications. It presents practical exercises in a hands-on lab format, often also covering toolchain configuration. Some sections present details for hardware, such as remote IoT and remote ARM laboratories. Others assume the student owns or has access to the PC and can install software.

ARM and Mobiles

ARM processors are omnipresent, ranging from simple IoT devices to laptops, notebooks, and workstations.
For this reason, we had to select one technology to use for a practical introduction and experimentation.
To present both hardware interfacing and programming, the obvious choice is the Raspberry Pi. The following chapters present laboratory details and scenarios.

Follow the links below to the lab descriptions and scenarios:

SUT's ARM laboratory

RTU's ARM laboratory

Programming in Assembler for Embedded Systems

Assembler programming for embedded systems uses an integrated solution for IoT laboratories, namely VREL NextGen Software.
Users connect to the system using a web browser and develop software in the browser, compile it and inject it into the microcontroller, all remotely. Next, they use a web camera to observe the results.
The following chapters present more data on how to use the VREL NextGen remote labs system.

Introduction to the Arduino Uno programming in Assembler

The following chapter assumes that you are familiar with basic assembler operations for AVR microcontrollers. Below, we explain the most important construction elements and assembler instructions for manipulating the Arduino Uno's (figure 2) GPIOs, based on the ATmega328P microcontroller.

Figure 2: Arduino Uno development board

GPIO and Ports

The Arduino Uno exposes a number of GPIOs that can serve as binary inputs and outputs, analogue inputs, and many of them provide advanced, hardware-accelerated functions, such as UART, SPI, I2C, PWM, and ADC. In fact, not all of the pins on the development board are such “general-purpose”: some of them provide specific features, while others do not: there is no internal multiplexer, so functions such as UART, I2C, SPI, PWM and ADC are bound to particular GPIOs and cannot be changed.

On the programming level, GPIO ports are grouped into 3 “ports” (figure 3), and it is how you can access them:

A bit in the port corresponds to a single GPIO pin, e.g. bit 5 (6th, zero-ordered) of PortB corresponds to GPIO D13 and is connected to the built-in LED.

Figure 3: Arduino ports

IO Registers

Each Port has assigned three 8-bit registers:

Instructions

There is a set of assembler instructions that operate on Ports (I/O registers), as shown in table 1.

Assembler-level operations using ports are much faster than DigitalRead, DigitalWrite, and other instructions in C++, roughly 50 times faster.
Table 1: Common GPIO-related, I/O instructions
Instruction Description
SBI Set bit in register
CBI Clear bit in register
SBIS Skif if bit in register is set (1)
SBIC Skip if bit in register is clear (0)
IN Read hardware register to the general-purpose register (R0-R31)
OUT Write the general-purpose register to the hardware register.
ANDI Masks a bit
ORI Sets a bit

A common scenario is to first set either the GPIO is input or output (using the correct DDRx register), then either set (SBI), reset (CBI), check (SBIS, SBIC), read the whole register (IN) or write the whole register (OUT).

IN and OUT instructions operate on whole, 8-bit registers rather than on single bits. Those are general-purpose instructions, covering the whole range of IO registers (0-63), beyond aforementioned DDRx, PORTx and PINx registers.

Examples

Template for the assembler code

Using plain assembler (not C++ + assembler) requires a specific construction of the application where the program is located (loaded) into memory exactly at 0x0000.

    .org 0x0000
    rjmp start
 
start:
...

It is common practice to use ``rjmp`` (relative jump), which makes is easier to place data before the start of the code. And it is a good “embedded” practice to keep it even, if it does not really jump, as in this example. Forgetting to put it may impact your programming experience later, when you decide to declare some data.

Core I/O registers and their IDs
To operate on I/O registers, the developer must either include a library with definitions or (when programming in pure assembler) declare them on their own.
Below there is a table 2 with a list of I/O registers used to control GPIO (Ports B, C and D) and their addresses:

Table 2: I/O registers and their addresses (IDs)
Name Address (I/O) Description
PINB 0x03 Input pins register (Port B)
DDRB 0x04 Data direction register (Port B)
PORTB 0x05 Output register/pull-up enable (Port B)
PINC 0x06 Input pins register (Port C)
DDRC 0x07 Data direction register (Port C)
PORTC 0x08 Output register/pull-up enable (Port C)
PIND 0x09 Input pins register (Port D)
DDRD 0x0A Data direction register (Port D)
PORTD 0x0B Output register/pull-up enable (Port D)

The easiest is to declare constants (converted to values at compile time) and insert them before the code starts (note that they do not exist in memory, so do not disturb code placement and proper execution):

; I/O registers
.equ PINB,  0x03
.equ DDRB,  0x04
.equ PORTB, 0x05
.equ PINC,  0x06
.equ DDRC,  0x07
.equ PORTC, 0x08
.equ PIND,  0x09
.equ DDRD,  0x0A
.equ PORTD, 0x0B
 
; your code starts here
    .org 0x0000
    rjmp start
 
start:
...
.equ is converted into a value and substituted in the code during compile: thus it does not exist in the final, compiled binary code.
Depending on the compiler you use, there are two standards of syntax. You can find the correct .equ PINB, 0x03 or .equ PINB = 0x03

Below are sections representing common usage scenarios for GPIO management:

USE GPIO as output
In this scenario, we use GPIO as an output. The simplest is to use the built-in LED to get instantly observable results.
The built-in LED is connected to GPIO13 (D13) and is controlled via PortB (5th bit, zero-based indexing; see figure 3). The built-in LED is enabled in the LOW (0) state and off in the HIGH (1) state on GPIO13. It is also convenient to declare a bit number representing the built-in LED position in PortB, so instead of using a number, we can use an identifier:

.equ

Use GPIO as input

Use GPIO as input with pull-up

Reading analogue values

Reading of the analogue values is not so straightforward as in the case of binary ones. Built-in ADC converter uses 10-bit resolution, has 6 channels (A0-A5, respectively). It also uses a reference voltage (configurable), typically 5V.
The low-level ADC register-based operations use the following formula to obtain an ADC value (figure 4, based on the input value Vgpio and the reference value Vref).

Figure 4: ADC value calculation based on the input voltage and reference voltage

Analogue reading uses a complex setup of ADC-related registers as presented in table 3:

Table 3: ADC-related registers used for reading the analogue values of GPIOs
Register Description
ADMUX Selects voltage reference and

SUT AVR Assembler Laboratory Node Hardware Reference

Introduction

Each laboratory node is equipped with an Arduino Uno R3 development board, based on the ATmega328P MCU. It also has two extension boards:

There are 10 laboratory nodes. They can be used independently, but for collaboration, nodes are interconnected symmetrically, with GPIOs described in the hardware reference section below.

Hardware reference

The table 4 lists all hardware components and details. Note that some elements are accessible, but their use is not supported via the remote lab, e.g., buttons and a buzzer.
The node is depicted in the figure 5 and its interface visual schematic is presented in the figure 6. The schematic presents only components used in scenarios and accessible via the VREL NextGen environment (controllable and observable via video stream), omitting unused components such as buttons, a buzzer, and a potentiometer.

Figure 5: AVR (Arduino Uno) SUT Node
Figure 6: SUT node's visual interface components schematic
Table 4: AVR (Arduino Uno) SUT Node Hardware Details
Component ID Component Hardware Details (controller) Control method GPIOs (as mapped to the Arduno Uno) Remarks
D1 LED (red) direct via GPIO binary (0→on, 1→off) GPIO13
D2 LED (red) direct via GPIO binary (0→on, 1→off) GPIO12
D3 LED (red) direct via GPIO binary (0→on, 1→off) GPIO11
D4 LED (red) direct via GPIO binary (0→on, 1→off) GPIO10 shared with interconnection with another module
LED4 4x 7-segment display indirect, via two 74HC575 registers serial load to 2 registers, daisy-chained GPIO8 - serial input of the controller
GPIO7 - shift data internally, raising edge (write next bit and shift data in serial)
GPIO4 - reset display buffer

Communication

Devices (laboratory nodes) are interconnected in pairs, so it is possible to work in groups and implement scenarios involving more than one device:

Interconnections are symmetrical, so that device 1 can send data to device 2 and vice versa (similar to serial communication). Note that analogue inputs are also involved in the interconnection interface. See image 7 for details.

Figure 7: SUT AVR nodes interconnection diagram

The in-series resistors protect the Arduino boards' outputs from excessive current when both pins are configured as outputs with opposite logic states.

The capacitors on the analogue lines filter the PWM signal, providing a stable voltage for the analogue-to-digital converter to measure.

Table 5: AVR (Arduino Uno) SUT Node Interconnections
Arduino Uno pin name AVR pin name Alternate function Comment
D2 PD2 INT0 Interrupt input
D5 PD5 T1 Timer/counter input
D6 PD6 OC0A PWM output to generate analogue voltage
D9 PB1 OC1A Digital output / Timer output
D10 PB2 OC1B Digital output / Timer output
A5 PC5 ADC5 Analogue input

Such a connection makes it possible to implement a variety of scenarios:

Nodes are interconnected in pairs: 1-2, 3-4, 5-6, 7-8, 9-10. Scenarios for data transmission between MCUs require booking and the use of correct nodes for sending and receiving messages.