Multiplication table in VMLab / AVR - avr

I am trying to figure out this question. I posted my code below. It doesn't work properly. it seems like its not multiplying the 2 least significant nibbles. I don't know AVR very well.
Write AVR that generates a multiplication table for SRAM addresses 0x0100 to 0x01FF. The value at each address is the product of the two least significant nibbles of the address. For example, at address 0x0123, the multiplicand is 3 and the multiplier is 2. calculate the product (6 in this case) and store it at address 0x0123. The answer should be about 10-12 lines of code with a loop
.include "C:\VMLAB\include\m168def.inc"
ldi r27, 0x01
ldi r26, 0x00
ldi r30, 0xff
main:
mov r16, r26
andi r16,0x0f
mov r17,r27
andi r17,0xf0
swap r17
mul r17, r16
st x+, r16
dec r30
brne main

According to the manual, the MUL instruction stores its result in R0 and R1. So you need to read R0 after MUL, not R16.

You have three errors in your code:
mul stores results to R1:R0 pair registers
if use X as the index register and content mark symbolically as ABCD then CD is located in XL register, not in XH (r27). AB is constant (01)
you loops only 255 times not 256
ldi xh, 0x01
ldi xl, 0x00
ldi r30, 0x00
main:
mov r16, xl ;put D to R16
andi r16,0x0f
mov r17, xl ;put C to R17
andi r17,0xf0
swap r17
mul r17, r16 ;multiply C x D
st x+, r0 ;store result low byte to memory
dec r30 ;repeat 256 times
brne main

Related

Atmel Studio does not start debugging

I select my device in the debugger, as shown in the attached screenshot. When I click on 'Start debugging', the debugging does not start. Using the simulator as a debugger, the debugging starts even without adding breakpoints. What is the issue?
Debugger
After clicking on 'Start debugging'
This is my code:
start:
; To read all 10 serial bytes from the signature row, following steps must be performed:
; 1. Load the Z-pointer with the signature byte address given
; 2. Set the SIGRD bit in the SPMCSR register
; 3. Set the SPMEN bit in the SPMCSR register
; 4. Load from Z register to general register
; 5. General register now contains the serial byte
; Read Serial Number Byte 0
ldi ZH, 0x00
ldi ZL, 0x0E // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R0, Z
; Read Serial Number Byte 1
ldi ZH, 0x00
ldi ZL, 0x0F // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R1, Z
; Read Serial Number Byte 2
ldi ZH, 0x00
ldi ZL, 0x10 // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R2, Z
; Read Serial Number Byte 3
ldi ZH, 0x00
ldi ZL, 0x11 // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R3, Z
; Read Serial Number Byte 4
ldi ZH, 0x00
ldi ZL, 0x12 // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R4, Z
; Read Serial Number Byte 5
ldi ZH, 0x00
ldi ZL, 0x13 // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R5, Z
; Read Serial Number Byte 6
ldi ZH, 0x00
ldi ZL, 0x14 // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R6, Z
; Read Serial Number Byte 7
ldi ZH, 0x00
ldi ZL, 0x15 // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R7, Z
; Read Serial Number Byte 8
ldi ZH, 0x00
ldi ZL, 0x16 // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R8, Z
; Read Serial Number Byte 9
ldi ZH, 0x00
ldi ZL, 0x17 // Set address of the Signature row to be read
; Load SPMCSR bits into R16, then write to SPMCSR
ldi R16, 0x21 ; (1<<SIGRD)|(1<<SPMEN)
out SPMCSR, R16
LPM R9, Z
`

How to setup two different led delays for a specific sequence?

I am working on a program that is suppose to create a certain blink sequence on my arduino board (atmega328p). The pattern that I am trying to create is,
ON for 1/2 second
OFF for 1/2 second
ON for 1/2 second
Off for one full second
Repeat this sequence.
I approached the problem by creating two different delays one for the 1/2 sec and other for the 1 sec, and then I call them.
If I only have one delay the light will work with that pattern but once I put both delays in the loop together the light does not even follow the pattern. I apologize if this is a easy question, I don't know if I am approaching this right.
Here is my code:
#include "config.h"
.section .data
dummy: .byte 0 ; dummy global variable
.section .text
.global main
.extern delay
.org 0x0000
main:
; clear the SREG register
eor r1, r1 ; cheap zero
out _(SREG), r1 ; clear flag register
; set up the stack
ldi r28, (RAMEND & 0x00ff)
ldi r29, (RAMEND >> 8)
out _(SPH), r29
out _(SPL), r28
; initialize the CPU clock to run at full speed
ldi r24, 0x80
sts CLKPR, r24 ; allow access to clock setup
sts CLKPR, r1 ; run at full speed
; set up the LED port
sbi LED_DIR, LED_PIN ; set LED pin to output
cbi LED_PORT, LED_PIN ; start with the LED off
; enter the blink loop
1: rcall toggle
rcall delay
rcall delay2
rjmp 1b
toggle:
in r24, LED_PORT ; get current bits
ldi r25, (1 << LED_PIN) ; LED is pin 5
eor r24, r25 ; flip the bit
out LED_PORT, r24 ; write the bits back
ret
delay: ; 1/2 sec delay loop
ldi r21, 41
ldi r22, 150
ldi r23, 127
1: dec r23
brne 1b
dec r22
brne 1b
dec r21
brne 1b
ret
delay2: ; 1 sec delay loop
ldi r18, 82
ldi r19, 43
ldi r20, 0
2: dec r20
brne 2b
dec r19
brne 2b
dec r18
brne 2b
ret

How to do analogRead() in AVR assembly language?

If I need to be specific: I'm asking about ATmega328P chip. The analog pins are under PortC on this chip.
I have learnt that digitalWrite can be done using out, and digitalRead using in.
But how can I do analogRead ?? Please explain. I'm new to this.
EXTRA: It would be helpful if you show analogWrite too (In the sense of PWM).
You can read the source code of analogRead from the Arduino environment:
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring_analog.c
The important thing is to find all the places where it reads or writes from a special function register (SFR) like ADMUX, and then make sure you do the same thing in your assembly code.
You should also look at the ATmega328P datasheet, which defines all of those SFRs, as a way to double check that you are doing the correct thing.
If you have further trouble, I recommend asking a new question where you show some code and get specific about exactly what part of analogRead is confusing to you.
This is for the future visitors who stumble upon here...
As mentioned by Rev1.0, Arduino C does make things too easy for you. A lot of complicated things are going on under the hood when you write a simple statement analogRead(). But it's not that complicated once you understand it. You should definitely read up on ADCs.
As mentioned by David Grayson, you should definitely take a look at the source code of analogRead(). Here is the datasheet of ATmega328P and the instruction set manual for ATmega328P to help you understand what is going on.
You can read this and this to get some idea on how to exactly write the code.
Now, here is what I came up with for my use-case in my project.
The bold-face words are there to tell you that this code was NOT written for a general use-case. Copy-Pasting this will most probably not work.
You see the amuont of links in this post? Read all of them. Below is only for using as a reference in case you get stuck and it might help.
adcInit:
ldi r16, 0b01100000 ; Voltage Reference: AVcc with external capacitor at AREF pin
sts ADMUX, r16 ; Enable ADC Left Adjust Result
; Analog Channel: ADC0
ldi r16, 0b10000101 ; Enable ADC
sts ADCSRA, r16 ; ADC Prescaling Factor: 32
ret
adcRead:
ldi r16, 0b01000000 ; Set ADSC flag to Trigger ADC Conversion process
lds r17, ADCSRA ;
or r17, r16 ;
sts ADCSRA, r17 ;
ret
adcWait:
lds r17, ADCSRA ; Observe the ADIF flag, it gets set by hardware when ADC conversion completes
sbrs r17, 4 ;
jmp adcWait ; Keep checking until the flag is set by hardware
ldi r16, 0b00010000 ; Set the flag again to signal 'ready-to-be-cleared' by hardware
lds r17, ADCSRA ;
or r17, r16 ;
sts ADCSRA, r17 ;
ret
It is used like this:
call adcInit
mainLoop:
call adcRead
call adcWait
lds r18, ADCL ; Must read ADCL first, and ADCH after that
lds r19, ADCH
After a long time struggle with me, I survey the datasheet of ATmega 328P and many google surfing articles, the simple and workable code is completed as below.
; UNO_asmADCapp.asm
; revised by bsliao: 2020/5/12 下午 03:39:20, TEST OK 2020/05/13, 11:33
; Reference:
; https://stackoverflow.com/questions/38972805/
; [1] how-to-code-an-adc-for-an-atmega328p-in-assembly
; Author : Dario, Created: 8/14/2016 7:34:43 AM
; [2] https://robotics.ee.uwa.edu.au/courses/des/labprep/
; LabPrep%205%20-%20Timers%20and%20ADC%20in%20ATMEL.pdf
; [3] https://www.avrfreaks.net/forum/adc-converter-assembly-using-atmega328p-mcu
; AD0 --- uno A0
; value ADCH (b9 b8) ADCL (b7- b0) <Internal> --- PB1(uno d9) PB0 (d8), PD7-PD0 (uno D7 -D0)
#define F_CPU 16000000UL
.def temp =r16
; Replace with your application code
.include "./m328Pdef.inc"
.org 0x000
rjmp start
; .org 0x002A
; rjmp ADC_conversion_complete_Handler
start:
eor r1, r1
out SREG, r1
ldi temp, HIGH(RAMEND)
out SPH, r16
ldi temp, LOW(RAMEND)
out SPL, r16
setup:
ldi temp, 0xFF ; set r16 = 1111 1111
out ddrb, temp ; set all d pins as output
out ddrd, temp ; set all b pins as output
configADC0:
;------initialize ADC0 ------- Set ADMUX and ADCSRA:
;REF1 REFS0 ALLAR - (MUX3 MUX2 MUX1 MUX0 )=(0000)
;Aref=5.0 V is used, default right-adjust result, analog in at AIN0 (ADC0)
LDI temp, 0x00
STS ADMUX, temp
;ADcENable, (ADPS2 ADPS1 ADPS0 )=(000) : division factor=128 16Mhz/128: ADC0 is applied.
LDI temp, (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
STS ADCSRA, temp
andi temp, 0b11011111
STS ADCSRA, temp
; the first conversion
LDS temp,ADCSRA
ori temp, (1<<ADSC);
STS ADCSRA, temp
LOOP:
; start the next single conversion on ADCn, here n=0
LDS temp,ADCSRA
ori temp, (1<<ADSC);
STS ADCSRA, temp
adc_read_loop:
// while (bit_is_set(ADCSRA, ADSC));
lds temp,ADCSRA
sbrc temp,ADSC ;after ADC0 conversion over, the bit ADSC in the ADCSRA is set to zero and the bit ADIF is set to one.
rjmp adc_read_loop
read_ADC_value:
lds r24,ADCL
lds r25,ADCH
display_ADC_value:
andi r25, 0x03
out PORTB, r25 ; LEDs active high, PORTB most significant byte
com r24 ; LEDs active low
out PORTD, r24 ; PORTD less significant byte
call one_sec_delay
rjmp LOOP
one_sec_delay:
ldi r20, 20
ldi r21, 255
ldi r22, 255
delay: dec r22
brne delay
dec r21
brne delay
dec r20
brne delay
ret

Summation in AVR Assembly

I want to implement a routine that calculates the sum of all natural numbers from 1 to n. n is a variable stored in RAM. The result has to be stored in a two-byte variable in RAM, too. I'm very new in assembly programming so I'm having a hard time trying to figure out the algorithm to achieve this. So far, I've done this:
.DSEG
.ORG 0x100
n: .BYTE l_n
result: .BYTE l_result
.CSEG
.ORG 0x100
SUM:
LDI XL, n ;the direction of n is stored in XL
LD R16, X ;now r16=n
LDI XL, LOW(result)
LDI XH, HIGH(result) ;X points to result
CLC ;in case C is full with trash
LDI R17, 0x0 ;R17 = 0
LDI R18, 0x1 ;R18 = 1
CALL LOOP
LDI R16,0
LDI R17,0
ADC R16, R17 ;if C is on when the loop finishes, then it has to be summed as well
ST X, R16
RET ;returns to the program that called the routine
I did the initialization of R17 and R18 because I thought that the subroutine LOOP should do something like increasing this numbers one by one until doing it n times. The thing that is complicating me the most is the fact that the result has two bytes, while each number being summed consists of just one byte. I don't know how to deal with this. Any help will be appreciated.
what you need is
ADD R18,R24 //sumL += nL
ADC R19,R25 //sumH += nH + Carry
and for 2 bytes variable the max sum will be 65535 so for
1+2+3+...+n=n*(n+1)/2 <= 65535 then N <= 361 = 0x0169
1+2+3+...+361=361*362/2=65341
and code will looks like this:
//CPU: ATmega128A
.include "m128Adef.inc"
.DSEG
//.ORG 0x100
n: .BYTE 2 // define 2 bytes var
result: .BYTE 2 // define 2 bytes var
.CSEG
.ORG 0
RJMP boot
n0: .DW 0x0169 //init value for n=361 (max value for 2 byte result)
//in: N=R24:R25
//out: Sum=R18:R19
//calc sum 1 to n (n >=1 and n <=361)
//1+2+3+...+n=n*(n+1)/2 <= 65535 => n<=361= 0x0169
Sum1toN:
LDI R18,0x00 //sumL=0
LDI R19,0x00 //sumH=0
Lsum:
ADD R18,R24 //sumL + = nL
ADC R19,R25 //sumH += nH + C
SBIW R24,0x01 //n--
BRNE Lsum // n >0 ?
RET
boot:
CLR R1
OUT SREG,R1 //Clear all
//init stack pointer
LDI R28,LOW(RAMEND) //LDI R28,0xFF
LDI R29,HIGH(RAMEND) //LDI R28,0x10
OUT SPH,R29
OUT SPL,R28
//init
LDI ZL,LOW(n0<<1)
LDI ZH,HIGH(n0<<1)
LDI XL,LOW(n)
LDI XH,HIGH(n)
LDI R24,2
LDI R25,0
init:
LPM R0,Z+
ST X+,R0
SBIW R24,1
BRNE init
//calc:
LDS R24,n // LDS R24,0x0100
LDS R25,n+1 // LDS R25,0x0101
RCALL Sum1toN
STS result,R18
STS result+1,R19
main:
RJMP main

AVR Studio - AVR Simulator 2 Carry Flag Issue

I've just started an assembly line programming class and I'm having an issue with a problem, I'm adding 240 to 49 and I know it will overflow, my goal is to make Register 1 equal to 1 when these numbers overflow. I know that the carry flag is set when I add them but I'm unsure of how to use this flag to make r1 equal to 1.
This program should calculate:
; R0 = R16 + R17 + R18
;
;--*1 Do not change anything between here and the line starting with *--
.cseg
ldi r16, 0x30
ldi r17, 0x31
ldi r18, 0x32
;*--1 Do not change anything above this line to the --*
;***
; Your code goes here:
;
add r0, r16
add r0, r17
add r0, r18
;****
;--*2 Do not change anything between here and the line starting with *--
done: jmp done
;*--2 Do not change anything above this line to the --*
I'm sure there are smarter ways, but you can use brcs, branch if carry set:
add r0, r16
add r0, r17
add r0, r18
brcs carry ; Branch if carry set
carry: ldi r1, 0x1 ; Branch destination

Resources