Programmieren für Fortgeschrittene - Assembler - Teil 2 - AZ-Delivery

After the basics of assembly language were explained in the first part, standard test programs are implemented in this part and the process is explained in detail.

A microcontroller with ATmega328P is required to implement the tutorials easily. Here we recommend a AZ-ATmega328 board with USB-C connection.

Assembly language

To begin with, you should familiarize yourself with the most important commands and the structure of the assembly language.

Commands

This is an overview of the most important Atmel AVR assembler commands:

Data & Register Operators

ldi reg, data

Loads the specified number into the register

mov reg1, reg2

Copies the content of register2 to register1

in reg, IOreg

Loads the byte of an IO register into a register

out IOreg, reg

Loads the byte of a register into the IO register

clr reg

Sets all bits of the register to '0'

ser reg

Sets all bits of the register to '1'

sbi reg, bitPos

Sets the bit of the specified position in the register to the value '1'

cbi reg, bitPos

Sets the bit of the specified position in the register to the value '0'

inc reg

Increases the numerical value of the content in the register by 1 (DEC)

dec reg

Decreases the numerical value of the content in the register by 1 (DEC)

add reg1, reg2

Adds register2 to register1

sub reg1, reg2

Subtracts register2 from register1

 

Comparison operators

cpi reg, data

Compares a register with a constant value and stores the result in the zero flag of the status register

cp reg, reg

Compares two registers and stores the result in the zero flag of the status register

brne label

If the zero flag of the status register is set to false ('0') by a comparison, the specified label is executed

breq label

If the zero flag of the status register is set to true ('1') by a comparison, the specified label is executed

 

Program structure Operators

rcall label

Calls a label with the option of returning to this point

ret

Can be called to return to the saved position of the program counter (PC) after a call

rjmp label

Jump to a label

rcall, rjmp vs. call, jmp

The prefix r stands for relative. The rough difference is that relative jumps or calls use less memory, but only have a limited range in the memory. The call/jmp instruction, on the other hand, has access to the entire memory.

 

 

A complete overview of the commands can be found, for example, in the following Wikipedia article: Atmel AVR instruction set.

Instructions to the assembler

.equ

This can be used to declare a constant value in the program.

Comparable to const byte in the Arduino IDE.

 

.def

This command can be used to define a name for a register.

Comparable to #define in the Arduino IDE

 

.include

This is used to include external files in the code.
Comparable to #include in the Arduino IDE.

 

.org

This instruction is specifically in AVR Assembler to position the code at certain locations in the flash memory.
This is used, for example, at the beginning of every assembler program to start the program after the reset (0x0000) or to jump to the RESET label.

 

Label

Labels have already been mentioned several times in the command overview. These are sections in the code that can be jumped to using the program structure operators.
These are created in the code as follows:

labelname:

   Command ; Comment

   Command ; Comment

   Command ; Comment

Register

In the ATmega328P there are some peripheral registers, for example for the GPIO ports, the interfaces such as SPI or UART, interrupts or timers. These are described in more detail in the examples.

An important register for understanding how the comparisons work is the status register SREG. This consists of several bits, also called flags here, which are used for operators such as the zero flag during comparison.

You can find a detailed overview of the status register in the ATmega328P data sheet on page 11.

Examples

In order to apply the theoretical knowledge you have acquired in practice, the following is the well-known Blink program that you have probably also loaded onto your UNO as the first program in the Arduino IDE.

LED Blink

This program is intended to make an LED flash.

The assembler program should follow the following sequence:
set the digital pin to 5V (HIGH)

Wait one second

Set digital pin to 0V (LOW)

Wait one second

 

Switch the IO pins on and off:

Figure 1: Complete pinout Source

 

In order to be able to control an IO pin of the microcontroller, the IO designation of the chip must first be determined with the help of the pinout.
For the Blink example, we recommend the onboard LED 'L', which is connected to D13. According to the pinout, this is PB5 of the ATmega328P. From this designation it can be determined that it is the 5th register bit of the IO register Port B is involved.

With the sbi and cbi command, this port can be controlled very easily:

sbi PORTB, 5 ; Switch on

 

Before the IO can be controlled, the pin function must be defined. The DDR register (DataDirectionRegister).

The IO can be set to output mode with the following command:

sbi DDRB, 5

 

Wait

There is no direct wait function in the assembly language, such as the delay() in the Arduino IDE.
One possible approach would be to block the program flow with instructions that consume a processor clock cycle and increase the content of a register accordingly in the meantime. As the microcontroller has a timer, it is more elegant to use this.

The timer increments its contents with each clock pulse, since the UNO uses a 16 MHz oscillator, this would be 16,000,000 increments per second. To reduce this, there is the so-called prescaler, which can be defined via register TCCR0B. If the maximum prescaler (f/1024) is set here, this corresponds to 15,625 clocks per second.

ldi r16,  0b00000101

out TCCR0B, r16


see data sheet p.87

 

Timer0 is only 8 bits long, which corresponds to a maximum numerical value of 255. This causes the timer to 'overflow' at clock pulse 256. With this overflow, the value is set to zero and an interrupt is triggered, which points to the memory address 0x0020 (data sheet p.49).

From this memory address, the .org command can then be used to jump to a code section in which this number of overflows is to be saved.

With a waiting time of one second, this number of overflows is approximately 61.

.org 0x0020

rjmp TIMER0OVF

 

delay:

   clr overflows

   sec_count:

     cpi overflows,61

   brne sec_count

   ret

 

TIMER0OVF:

   inc overflows

   reti


The interrupt function must still be activated in the final program. This can simply be done at the beginning with the command

ldi r16, 0b00000001

sts TIMSK0, r16

 

be            ; global interrupts (SREG)

 

can be executed.

 

Final code

Finally, the code sections are put together to create the final Blink program.
Insert these lines into a Blink.asm file.
Alternatively, the file can also be here can be downloaded here.

 

.include "./m328Pdef.inc"

.def temp = r16       ; Reg for temporary buffering
.def overflows = r17  ; number of overflows

.org 0x0000
rjmp RESET

.org 0x0020
rjmp TIMER0OVF


RESET:
  
ldi temp,  0b00000101  Prescaler
  
out TCCR0B, temp

  
ldi temp, 0b00000001   Interrupt
  
sts TIMSK0, temp

  
be            ; enable global interrupts

  
clr temp
  
out TCNT0, temp        ; initialize the Timer/Counter to 0

  
sbi DDRB, 5            ; set PD4 to output


blink:
  
sbi PORTB, 5    ON
  
rcall delay
  
cbi PORTB, 5    OFF
  
rcall delay

  
rjmp blink
 
delay:
  
clr overflows
   sec_count:
    
cpi overflows,30
  
brne sec_count
  
ret                    >30 return = 0.5sec

TIMER0OVF:
  
inc overflows
  
reti

 

Finally, load the compiled .hex file onto the UNO. You can find the procedure in the first part of the blog series.

 

In this blog post, the basics of assembler programming were deepened and finally applied in the Blink example.
Further examples will be implemented in the following part.

 

Have fun rebuilding :)

Projekte für fortgeschrittene

2 commenti

Bastian Brumbi

Bastian Brumbi

Hallo Bernd Buffen,
Sie haben recht cp ist für den Vergleich zweier Register, wogegen cpi ein Register mit einem Konstanten Wert vergleicht.
In der aktuellen Version ist dies nun korrigiert.
Vielen Dank für den Hinweis.
Grüße,
Bastian

Bernd

Bernd

Hallo,

ist die Beschreibung von CP und CPI im Artikel nicht vertauscht ?
-———
Vergleichsoperatoren:
cp reg, data – Vergleicht ein Register mit einem Konstantwert und Speichert das Ergebnis im Zeroflag des Status Registers
cpi reg, reg -Vergleicht zwei Register und Speichert das Ergebnis im Zeroflag des Status Registers

Gruß
Bernd Buffen

Lascia un commento

Tutti i commenti vengono moderati prima della pubblicazione