This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:multiasm:exercisesbook:arduinouno [2026/03/26 23:03] – [Instructions] pczekalski | en:multiasm:exercisesbook:arduinouno [2026/03/29 17:24] (current) – [Examples] pczekalski | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== 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 {{ref> | ||
| + | |||
| + | <figure arduinouno> | ||
| + | {{: | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | ===== 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 " | ||
| + | |||
| + | On the programming level, GPIO ports are grouped into 3 " | ||
| + | * PortB, with GPIOs from D8 to D13, | ||
| + | * PortC, with GPIOs from port A0 to A5, | ||
| + | * PortD, with GPIOs from D0 to D7. | ||
| + | |||
| + | 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 arduinoports> | ||
| + | {{: | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | ===== IO Registers ===== | ||
| + | Each Port has assigned three 8-bit registers: | ||
| + | * DDRx (Data Direction Register): there are 3 of those registers, one per Port (B, C, D): DDRB, DDRC and DDRD. This registers configures GPIO as Input (0) or Output (1). Configuration is done "per bit", so it is equivalent to controlling each GPIO individually. | ||
| + | * PORTx (Port Data Register): there are also 3 of those registers: PORTB, PORTC and PORTD. The operation depends on the value of the specific bit in the corresponding DDR register; either pin is configured as input or output: | ||
| + | * If a specific GPIO pin (represented as a bit in the related DDRx register) is set as output, then PORTx bit directly affects the GPIO output: 1 is HIGH (+5V), while 0 is LOW (0V). | ||
| + | * If a specific GPIO pin is set to input, PORTx value controls the internal pull-up resistor: 1 enables pull-up, 0 disables it. | ||
| + | * PINx (Pin Value Register) represents the current input state of the GPIO. | ||
| + | |||
| + | ==== Instructions ==== | ||
| + | There is a set of assembler instructions that operate on Ports (I/O registers), as shown in table {{ref> | ||
| + | < | ||
| + | |||
| + | <table assemblergpioinstructions> | ||
| + | < | ||
| + | ^ Instruction | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | </ | ||
| + | |||
| + | A common scenario is to first set either the GPIO is input or output (using the correct DDRx register), then either set ('' | ||
| + | <note tip>'' | ||
| + | |||
| + | ==== 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. | ||
| + | |||
| + | <code asm> | ||
| + | |||
| + | .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 " | ||
| + | |||
| + | ** 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 {{ref> | ||
| + | <table ioregisterids> | ||
| + | < | ||
| + | ^ Name ^ Address (I/O) ^ Description ^ | ||
| + | | PINB | 0x03 | Input pins register (Port B) | | ||
| + | | DDRB | 0x04 | Data direction register (Port B) | | ||
| + | | PORTB | 0x05 | Output register/ | ||
| + | | PINC | 0x06 | Input pins register (Port C) | | ||
| + | | DDRC | 0x07 | Data direction register (Port C) | | ||
| + | | PORTC | 0x08 | Output register/ | ||
| + | | PIND | 0x09 | Input pins register (Port D) | | ||
| + | | DDRD | 0x0A | Data direction register (Port D) | | ||
| + | | PORTD | 0x0B | Output register/ | ||
| + | </ | ||
| + | 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): | ||
| + | <code asm> | ||
| + | ; 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: | ||
| + | ... | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | < | ||
| + | |||
| + | 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 {{ref> | ||
| + | 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: | ||
| + | |||
| + | <code asm> | ||
| + | .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), | ||
| + | The low-level ADC register-based operations use the following formula to obtain an ADC value (figure {{ref> | ||
| + | |||
| + | <figure avreq1> | ||
| + | {{: | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | Analogue reading uses a complex setup of ADC-related registers as presented in table {{ref> | ||
| + | |||
| + | <table tabadcregisters> | ||
| + | < | ||
| + | ^ Register | ||
| + | | '' | ||
| + | | | | | ||
| + | | | | | ||
| + | | | | | ||
| + | </ | ||