Reading state of push button to turn on LED in AVR using Atmega32 - avr

I have a program that's supposed to read input values from a dip switch connected to PORTA and when button on PC0 is pressed, then it outputs the bits to the LEDs on PORTB, or if PC7 is pressed, then it shows up in PORTD. When PC3 is pressed, it's supposed to multiply both bytes for a 16-bit value stored as a high and low bytes.
The issue is that once I press the button once, the result does show up in the LEDs. However, after the first button press, the logic stops working. For example, when switching input on PORTA, it still shows up in the LEDs regardless of whether the button is pushed or not.
I cannot pinpoint where the issue is exactly and I really need help debugging this.
I'm using an atmega32, programming in Atmel studio, and simulating in Proteus.
I appreciate any help.
.cseg
.org 0x0000
; set stack pointer
ldi r29, low(ramend)
out spl, r29
ldi r29, high(ramend)
out sph, r29
start:
ser r16
out ddrb, r16 ; portb output
out ddrd, r16 ; portd output
clr r16
out ddra, r16 ;porta input
out ddrc, r16 ;portc input
ser r16
out portc, r16 ;pull-up resistor on PORTC
rjmp main
main:
sbic pinc, 0 ;skip if button is not pressed on PC0
call Load_Low ;call subroutine function to load the lower bit
sbic pinc, 7
call Load_High ;call subroutine function to load the higher bit
sbic pinc, 3
call Multiply ;call subroutine function to multiply both stored values
rjmp main
Load_High:
in r20, pina ;read bits in PINA to R20
mov r30, r20 ;store copy
out portb, r30 ;output to LEDs on PORTB
cbi portc, 0 ;clear bit
ret
Load_High:
in r20, pina
mov r31, r20
out portd, r31
cbi portc, 7
ret
Multiply:
mul r31, r30
out portd, r0
out portb, r1
cbi portc, 3
ret

In the schematics there is no pull-down resistor which could make a low logical level when the button is released. Therefore voltage is undefined, in real life voltage will be floating causing unexpected logical level change on the inputs.
Instead of adding external pull-down resistors you can connect buttons to the "GND" and engage internal pull-up resistors (setting corresponding bits of PORTC to 1 while in DDRC those bits are 0)
Also in all your routines there are strange lines without explanation:
cbi portc, 0 ;clear bit
cbi portc, 7
cbi portc, 3
those are the button inputs, why you're clearing PORTC bits each time?

Related

Multiplication table in VMLab / 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

AVR TWI (I2C) problem: Operand 1 out of range

I´m using Atmega2560 and 24c16A EEPROM for testing an I2C code. For every state transition, controller responds by changing the status register TWSR. After start condition is transmitted, EEPROM responds and then also for Device address and write instruction (SLA+R/W), status register updates fine. But when I transmit next 8 bits, instead of ACK (Data byte sent ACK), status changes to repeated start. The code is given below. I never could find the possible solution to make it work.
.include "./m2560def.inc"
.list
.cseg
.org 0x00
jmp inicio ; PC = 0x0000 RESET
inicio:
LDI R21, HIGH(RAMEND) ;Set Up Stack
OUT SPH, R21
LDI R21, LOW(RAMEND)
OUT SPL, R21
CALL I2C_INIT ;Initialize TWI(I2C)
CALL I2C_START ;Transmit START condition
LDI R27, 0b11010000 ;SLA(0b1001100) + W(0)
CALL I2C_WRITE ;Write R27 ato the I2C bus
LDI R27, 0b11110000 ;Data to be transmitted
CALL I2C_WRITE ;Write R27 ato the I2C bus
CALL I2C_STOP ;Transmit STOP condition
HERE: RJMP HERE
;----------------------------I2C_INIT-----------------------------
I2C_INIT:
LDI R21, 0
OUT TWSR, R21 ;Set prescaler bits to 0
LDI R21, 0x47 ;R21 = 0x47
OUT TWBR, R21 ;Fclk = 50 KHz (8 MHz Xtal)
LDI R21, (1<<TWEN) ;R21 = 0x04
OUT TWCR, R21 ;HEnable TWI (I2C)
RET
;----------------------------I2C_START-----------------------------
I2C_START:
LDI R21, (1<<TWINT)|1<<(TWSTA)|(1<<TWEN)
OUT TWCR, R21 ;Transmit START condition
WAIT1:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT1 ;Jump a WAIT1 if TWINT is 1
RET
;----------------------------I2C_WRITE -----------------------------
I2C_WRITE:
OUT TWDR, R27 ;Move the byte into TWRD
LDI R21, (1<<TWINT)|(1<<TWEN)
OUT TWCR, R21 ;Configure TWCR to send TWDR
WAIT3:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT3 ;Jump a WAIT3 if TWINT is 1
RET
;----------------------------I2C_STOP------------------------------
I2C_STOP:
LDI R21, (1<<TWINT)|1<<(TWSTO)|(1<<TWEN)
OUT TWCR, R21 ;Transmit STOP condition
RET
;----------------------------I2C_READ------------------------------
I2C_READ:
LDI R21, (1<<TWINT)|(1<<TWEN)
OUT TWCR, R21
WAIT2:
IN R21, TWCR ;Read Control Register TWCR into R21
SBRS R21, TWINT ;Skip the next line if TWINT is 1
RJMP WAIT2 ;Jump a WAIT2 if TWINT is 1
IN R27, TWCR ;Read received data into R21
RET
The output is
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(41,0): error: Operand 1 out of range: 0xb9
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(43,0): error: Operand 1 out of range: 0xb8
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(45,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(51,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(53,0): error: Operand 2 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(60,0): error: Operand 1 out of range: 0xbb
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(62,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(64,0): error: Operand 2 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(72,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(78,0): error: Operand 1 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(80,0): error: Operand 2 out of range: 0xbc
D:\ATMEL_AVR\I2C_TWI\I2C_TWI.asm(83,0): error: Operand 2 out of range: 0xbc
Assembly failed, 12 errors, 0 warnings
The code lines involved in these errors are:
OUT TWSR, R21
OUT TWBR, R21
OUT TWCR, R21
OUT TWCR, R21
IN R21, TWCR
OUT TWDR, R27
OUT TWCR, R21
IN R21, TWCR
OUT TWCR, R21
OUT TWCR, R21
IN R21, TWCR
IN R27, TWCR
In AVR normally all I/O registers are mapped into the memory space, starting from address 0x0020
in and out instructions could be used ONLY with the first 64 of them (with 0x20...0x5F memory addresses)
sbi, cbi, sbis and sbic has further limitation and can be used only with first 32 I/O registres (with memory address 0x20...0x3F). Please refer to the AVR Instruction Set Manual
In the datasheet to ATmega2560, section 33 "Register Summary", page 401, you can see the TWI registers have addresses 0xB8 thru 0xBD, i.e. they cannot be accessed using in and out instructions. You have to use STS and LDS.
I.e. LDS R21, TWCR instead of IN R21, TWCR etc.
Also, always be careful when accessing the first 64 registers. Make sure the corresponding names are defining their I/O and not RAM addresses. I.e. PORTA should be equal to 0x02 (IO address for in / out instruction) and not 0x22 (RAM address or PORTA, which, when accessed using in or out, equals to EEARH register).

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

Is this a hardware or Software issue?

I'm using Atmel Studio 6.1 and an Arduino Uno board with an Atmega328P microcontroller. Included below is my code and a picture of my hardware. I can't figure out if this is a hardware or software issue... All LEDs start off. When the button is pressed, it is supposed to change to a determined pattern. Pushing the button again results in a different pattern being displayed. I can't use C and I haven't been able to use real time debugging since I don't have JTAG or some other debugging supported interface. What ends up happening is that the button is pressed and I'm shorting the circuit and the power to the board gets reset. Oddly enough, the pattern changes once but then never again.
.def counter = R23
.def TimeLoopMax = R24
.def AllOnes = R16
.def DisplayPattern = R17
.def AllZeros = R18
MAIN:
LDI AllOnes, 0xFF ; assign 1 - make an output
LDI DisplayPattern, 0x00 ; start with all the LEDS ON; Holds the Light Pattern
LDI AllZeros, 0x00 ; assign 0 - make an input
LDI r19, 0x00 ; to hold the value read from PORTB0
LDI counter, 0x00 ; value for counter
LDI TimeLoopMax, 0x70
;According to the breakout board, PORTB5 is connected on spot 13 on the board
OUT DDRD, AllOnes ;set PORT D as an output
;make PORTB an input
OUT DDRB, AllZeros ;set PORT B as an input
;Start by turning all LEDS OFF
OUT PORTD, DisplayPattern
LOOP:
;read in the value from PORTB0 (ie the push button)
IN r19, PORTB0
;CPI r19, 0x05 ; might need to do a compare; compare with 5 since we're using 5V
CP r19, AllZeros
BRNE LOOP
JMP IncreasePatternCounter
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Blinking Pattern Definitions ;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;This defines a blinking sequence for Pattern 0
P0:
;P0, start at a value of 1, and shift the bit to the MSB
LDI DisplayPattern, 0x01
P0_LOOP:
OUT PORTD, DisplayPattern
LSL DisplayPattern ;Logic shift Left
;need to check butotn press every time the light switches
; since we are not using interrupts
/*IN r19, PORTB0
SBRC r19, 0 ;skip if Bit in Reg. is clear
JMP IncreasePatternCounter*/
JMP TimeWastingLoop
;This defines a blinking sequence for Pattern 1
P1:
;P1, start at a value of AA, and shift the bit to the MSB
LDI DisplayPattern, 0xAA
P1_LOOP:
OUT PORTD, DisplayPattern
LSL DisplayPattern
/*IN r19, PORTB0
SBRC r19, 0
JMP IncreasePatternCounter*/
JMP TimeWastingLoop
;This defines a blinking sequence for Pattern 1
P2:
;P1, start at a value of AA, and shift the bit to the MSB
LDI DisplayPattern, 0xFF
P2_LOOP:
OUT PORTD, DisplayPattern
LSR DisplayPattern
/*IN r19, PORTB0
SBRC r19, 0
JMP IncreasePatternCounter*/
JMP TimeWastingLoop
;This defines a blinking sequence for Pattern 1
P3:
;P1, start at a value of AA, and shift the bit to the MSB
LDI DisplayPattern, 0x00
P3_LOOP:
OUT PORTD, DisplayPattern
INC DisplayPattern
/*IN r19, PORTB0
SBRC r19, 0
JMP IncreasePatternCounter*/
JMP TimeWastingLoop
IncreasePatternCounter:
INC counter
SequenceSelect:
CPI counter, 0x01
BREQ P0;
CPI counter, 0x02
BREQ P1;
CPI counter, 0x03
BREQ P2
CPI counter, 0x04
BREQ P3
JMP P0
; Time wasting loop registers: r20,21,22
; R16 is all 1's
TimeWastingLoop:
LDI R20, 0x05
LDI R21, 0x05
LDI R22, 0x04
OutMostLoop:
CP TimeLoopMax, R20;(TLM - R20)
BREQ EndLoop
FirstInnerLoop:
/*IN r19, PORTB0
SBRC r19, 0
JMP IncreasePatternCounter*/
CP TimeLoopMax, R21; (TLM - R21)
BREQ EndOutMostLoop
SecondInnerLoop:
IN r19, PORTB0
SBRC r19, 0
JMP IncreasePatternCounter
CP TimeLoopMax, R22; (TLM - R22)
BREQ EndSecondInnerLoop
INC R22
JMP SecondInnerLoop
EndSecondInnerLoop:
CLR R22 ;reset the register for the next pass
INC R21 ; increment 1st inner loop counter
JMP FirstInnerLoop
EndOutMostLoop:
CLR R21
INC R20
JMP OutMostLoop
EndLoop:
;this is where we do the compare statements with counter
;Need to perform a compare here to see which loop we bounce back in
CPI counter, 0x01
BREQ P0_TWL ; Pattern0 Time Wasting Loop
CPI counter, 0x02
BREQ P1_TWL
CPI counter, 0x03
BREQ P2_TWL
;OUT PORTD, counter
P0_TWL:
CPI DisplayPattern, 0b00000000
BREQ BtnPressCheckP0
BRNE P0_LOOP
P1_TWL:
CPI DisplayPattern, 0b00000000
BREQ P1
BRNE P1_LOOP
P2_TWL:
CPI DisplayPattern, 0b00000000
BREQ P2
BRNE P2_LOOP
BtnPressCheckP0:
IN r19, PORTB0
SBRC r19, 0
JMP IncreasePatternCounter
JMP LOOP
Can't comment on the code portion, but the circuit around the button looks like the cause of your short circuit. This tutorial shows a good button circuit.

Resources