0

I am new to using an Arduino and I am trying to program my it to implement a flashing pattern with two LEDs. When one LED is off, the other must be on. The program needs to be done in assembly. I have a working program in c++ but I need to convert it to assembly. I have an Arduino UNO ATmega 328p

Here is my c++ program:

 const int led = 13;
 const int led2 = 12;

 void setup() {
    pinMode(led, OUTPUT);
    pinMode(led2, OUTPUT);
 }

 void loop() {
   digitalWrite(led, HIGH);    
   delay(1000);       
   digitalWrite(led, LOW);    
   digitalWrite(led2, HIGH);
   delay(1000);
   digitalWrite(led2, LOW);
 }
4
  • 3
    Why does it "need" to be done in assembly? School project?
    – Duncan C
    Commented Aug 11, 2019 at 20:28
  • 4
    And what are you looking for us to do for you? This sounds more like a call for somebody to do the work for you than a question. You'll need to research the different registers that manage the various GPIO lines, as well as the instruction set for your model of Arduino.
    – Duncan C
    Commented Aug 11, 2019 at 20:30
  • 1
    Did you try a web-search? Guess not :) instructables.com/id/… Start in Step 3 and modify. This can also be done by writing assembly in C. The Arduino core has a few functions that are written in this style, github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/… Commented Aug 11, 2019 at 21:42
  • Of course I searched it, I came here as a last resort
    – user58745
    Commented Aug 12, 2019 at 12:58

4 Answers 4

2

In the case, that this is a school assignment and you actually have to program directly in assembler, you probably don't want to directly convert your C++ sketch. It will generate way more assembler code, than is really necessary for alternatively blinking two LEDs (for example digitalWrite() contains not only the actual writing to the corresponding pin registers, but much more code), and it will be obvious, that you didn't write it yourself.

In this case you have to go the harder way. You should start by looking into existing assembler blink programs. Via google I found this series at github gist, which also has a blink example. You can derive some of the program from there. Also there is a list of assembler command with descriptions.

In the end everything, that is done on a microcontroller, comes down to reading and writing Special Function Registers (SFR). The pins are organized in ports with 8 (or sometimes less) pins. Every port is then represented by one byte in the corresponding SFR (1 bit per pin). For changing the direction of a pin between output and input, the DDRX registers are used (where X is the character of the port used). For actually setting the output state (or enabling/disabling the internal pullup resistor) the PORTX registers are used. The current state (LOW or HIGH; while input or output) can be read from the PINX registers. For these things, the datasheet of the microcontroller (here the Atmega328P) is a must read (meaning: not completely, but enough to feel comfortable getting needed information from it).

Learn how to set single bits in a byte (in a SFR). You can learn it from the linked example, or from tutorials over the internet. Then write code to write the correct value to the corresponding DDRX bits to set the LED pins to output. Then you can use this to write a value to an output pin.

For blinking you need a timer. If you google for something like "arduino assembler blink" you will also find an example, which configures the timer for blinking. Basically it involves setting the Timers SFRs to the wanted values. If you don't want to do this, you might get away with executing a high number of "nop" operations (which does nothing). You would have to write a loop, that will execute a high number of times. ( very "nop" will need 1 clock cycle - credits to Ghanima for searching that up). The clock runs on 16MHz, so you can do the math to calculate, how long 1 "nop" needs, and then calculate, how many of them you need).

2
  • It's one cycle per NOP which is for the AVR architecture the most sensible thing to do.
    – Ghanima
    Commented Aug 12, 2019 at 18:50
  • 1
    @Ghanima Thanks, I corrected the number in my answer
    – chrisl
    Commented Aug 12, 2019 at 21:04
2

There are also a couple of ways of including assembler code right inside your Arduino IDE.

By including the assembler in the Arduino IDE, the assembler source is assembled and linked into your project and uploaded to your Arduino from within the IDE - just like any other program.

This makes it easy to get started with Assembler while still retaining the familiarity of the Arduino IDE. Why? Because since you can mix C / C++ code with the Assembler code this means that you do not have to write a complete assembler program that manages all of the hardware initialisation nitty gritty - you can start small and expand your assembler as and when you you feel comfortable or need to do so. Like the example below, you can start with just two lines of assembler that fit into a larger program if you wanted to.

Following are the instructions for creating a mixed C / Assembler program in Arduino. I also give an example of how to pass parameters and get a result from an Assembler function.

To create a mixed C + Assembler program create a new project in the Arduino IDE (call it whatever you like) and enter the following code into the IDE:

extern "C" {
  void myInit();
  void myLoop();
  int myAdd(int, int);
}

void setup() {
  Serial.begin(9600);
  myInit();

  // This is just a dummy example showing inline assembler using the asm "directive"
  // Refer to this tutorial, or similar, for details of inline assembler in Arduino:
  // https://ucexperiment.wordpress.com/2016/03/11/arduino-inline-assembly-tutorial-5-2/
  asm(
    "ldi   r26,42"
  );
}

void loop() {
  myLoop();

  int ans = myAdd(240, 25);
  delay(ans);
  Serial.print("Answer: ");
  Serial.println(ans);
  delay(1000);
}

Next click the little down arrow in the top right corner of the IDE as per the following diagram and select "New Tab":

Arduino IDE - Add New Tab

Enter myFunctions.S as the name of the new tab as shown below. Note that the name does not really matter, but, it must end with the extension .S and the "S" must be a capital "S" not a lowercase "s":

Arduino IDE - Name the New Tab

Paste the following code into the myFunctions.S tab.

; Example assembler file that blinks an LED.
; and adds two numbers together.

#define __SFR_OFFSET 0

#include "avr/io.h"

// Declare the three function entry points as "globals"
.global myInit
.global myLoop
.global myAdd

myInit:
  sbi   DDRB,5      ; Set PB5 (the built in LED on Arduino Uno) as output
  ret


myLoop: 
  ldi   r20,250     ; Set the delay duration in ms. Maximum value is 255.
  call  myDelay_ms
  sbi   PORTB,5     ; Set PB5 HIGH
  ldi   r20,250     ; Delay for another 250 ms. 
  call  myDelay_ms
  cbi   PORTB,5     ; Set PB5 LOW
  ret

; These symbols are for internal to this assembler file's use only.
; They are private because they are not listed as a global symbol
; Also, they do not conform to the subroutine calling conventions used
; by the Arduino IDE's compiler. i.e. they are not compatible with
; being called directly from C. However, since we are calling them from
; our own assembler, we can (almost) make up any calling convention we like.
myDelay_ms:
  ; Delay about r20*1ms. Destroys r20, r30, and r31.
  ; One millisecond is about 16000 cycles at 16MHz.
  ; The basic loop takes about 5 cycles, so we need about 16,000,000 / 5 = 3000 loops.
  ; NB: most instructions are 8 bit, so we load the 16 bit counter using
  ;     two 8 bit loads.
  ldi   r31, 3000>>8    ; high(3000)
  ldi   r30, 3000&255   ; low(3000)
delaylp:
  sbiw    r30, 1        ; Decrement our 1 ms counter (this instruction operates on a 16 bit value in R31:R30).
  brne    delaylp       ; If R30 is non zero, loop back
  subi    r20, 1        ; Otherwise we have passed 1 ms so, decrement the high order byte of our counter.
  brne    myDelay_ms    ; If this is non zero, loop back.
  ret                   ; Otherwise, we have counted down from R20 * 3,000 - so return.


; Function to add two integers and return an integer.
; For few parameter functions, the Arduino IDE uses registers built into the
; Microcontroller (i.e. the CPU) to exchange data between subroutine caller and callee.
; Return values are as follows:
;   - single Byte (e.g. char, byte) R24 only
;   - double Byte (e.g. int)        R25:R24 (R25 is the high order byte)
;   - quad Byte   (e.g. long)       R25:R24:R23:R22 (R25 is the high order byte)
; Parameters are passed in similar ways starting with R25 and working down, but exactly
; what is where will depend upon the parameter types.
; Refer to this tutorial for more details or the AVR compiler documentation from ATMEL
;   https://ucexperiment.wordpress.com/2016/04/02/arduino-inline-assembly-tutorial-12-functions/
; In our case, a is passed in R25:R24 and b is passed in R23:R22
myAdd:
  add     r24, r22      ; Add the low order bytes
  adc     r25, r23      ; Add the high order bytes PLUS any value carried from the previous addition.
  ret                   ; The result is in R25:R24 which is where it needs to be to pass back to the C code.

Upload it to your Uno, start the serial monitor and you should see some sum's being ouput (well one sum continuously being output). Feel free to change the values in the main loop to see different results.

I've put loads of comments and a few references into the code to get you started on the path to finding more information.

One other reference that will be useful if you are going to mess with assembler on the UNO is the ATMega-328P reference manual/datasheet (all 662 pages of it) which you can obtain from Microchip's ATmega328p reference page. There are a whole bunch more useful resources there that will likely come in handy from time to time.

1

It "needs" to be done in assembly as an assignment, right? Otherwise you would "want to learn" assembly.

What I suggest you do is change the C program to use port manipulation, for example:

 void setup() {
   DDRB |= 0b00100000;  // D13 to output mode
 }

 void loop() {
   PORTB |= 0b00100000;  // turn on D13
   delay(2000);       
   PORTB &= ~0b00100000;  // turn off D13
   delay(2000);
 }

Now at least you are flashing D13 without using the rather complex digitalWrite and pinMode library functions. The delay is another issue I'll leave you to investigate however my page about timers might help you.

Now you can disassemble the generated code to see what the compiler generated in assembler and use that to help you learn how to do it.

For example, when I compiled the above code and turned on "verbose compiling" I found the location of the generated .elf file.

Running avr-objdump on that file (like this):

avr-objdump -S /tmp/build436d41bc5c0da39afe99cd9c05ac272a.tmp/sketch_aug13a.ino.elf > temp.txt

Gives me the following code for "loop":

00000094 <loop>:
  94:   2d 9a           sbi 0x05, 5 ; 5
  96:   60 ed           ldi r22, 0xD0   ; 208
  98:   77 e0           ldi r23, 0x07   ; 7
  9a:   80 e0           ldi r24, 0x00   ; 0
  9c:   90 e0           ldi r25, 0x00   ; 0
  9e:   0e 94 c5 00     call    0x18a   ; 0x18a <delay>
  a2:   2d 98           cbi 0x05, 5 ; 5
  a4:   60 ed           ldi r22, 0xD0   ; 208
  a6:   77 e0           ldi r23, 0x07   ; 7
  a8:   80 e0           ldi r24, 0x00   ; 0
  aa:   90 e0           ldi r25, 0x00   ; 0
  ac:   0c 94 c5 00     jmp 0x18a   ; 0x18a <delay>

What we basically see there is one "sbi" instruction to turn the LED on and one "cbi" instruction to turn it off.

As for the rest, well I don't want to do your homework for you. Have fun learning how to program in assembler!

0

Try this method to convert your sketch

https://sourceforge.net/projects/arduino-to-assembly-converter/

2
  • 1
    I tried that but when I run it there are multiple errors
    – user58745
    Commented Aug 12, 2019 at 12:58
  • 1
    Also you should check out chrisl's comments about being "way to much generated code" and "obvious you did not do it yourself". Unless you are aiming for an F on your project, automated conversion of the C code is not the answer. Of course it is entirely your choice as to what grade you wish to work for.
    – GMc
    Commented Aug 12, 2019 at 21:57

Not the answer you're looking for? Browse other questions tagged or ask your own question.