2

I posted a kind of similar question recently, but it was not that organized, so here I am trying again :) I wrote this short assembly code to blink an LED at different intervals depending on the state a 2-pin dip switch. The code is working perfectly in simulation but in real world the controller is just stuck at mode1 and randomly visits other modes for a couple of cycles then go back to mode1 even through mode1 is supposed to blink nothing and just keep checking for changes on the DIP switch. Also I say strange behaviors as it works with C code with no problems, so I guess maybe it is not a hardware issue? please advice. here is my code:

.include "m32def.inc"

    .cseg                       ; indicates that what follows is code
    .org 0x00

    .def delayMultiplier = r25
    .def Mode    = r31



    ldi r21,LOW(RAMEND)     ; initialize 
    out SPL,r21             ; stack pointer ;do not need to save r21
    ldi r21,HIGH(RAMEND)    ; to RAMEND
    out SPH,r21             ; "             ;do not need to save r21



    ser r16
    out DDRD, r16

    clr r16
    out PORTD, r16

    out DDRB, r16
    
  
    sbi PORTB, 0
    sbi PORTB, 1

start:
    
    clr r16
    out PORTD, r16
   

    in Mode, PINB           ; Read PortB actual values in Mode
    ldi r30, 0
    cp Mode, r30            ; Subtract 0 from mode to compare with 0
    breq mode3              ; if Sw == 00 jump to mode 3
        
    ldi r30, 1
    cp Mode, r30
    breq mode2              ; if Sw == 01 jump to mode 2

    ldi r30, 2
    cp Mode, r30 
    breq mode1              ; if Sw == 10 jump to mode 1

    ldi r30, 3
    cp Mode, r30
    breq start              ; if Sw == 11 jump to mode 0


mode1:
    
    sbi PORTD, 7
    ldi delayMultiplier, 25
    rcall delay
    cbi PORTD, 7
    ldi delayMultiplier, 25
    rcall delay
    rjmp start

mode2:
    
    sbi PORTD, 7
    ldi delayMultiplier, 100
    rcall delay
    cbi PORTD, 7
    ldi delayMultiplier, 100
    rcall delay
    rjmp start

mode3:
    
    sbi PORTD, 7
    ldi delayMultiplier, 250
    rcall delay
    cbi PORTD, 7
    ldi delayMultiplier, 250
    rcall delay
    rjmp start






.include "delay.asm"

here is delay sub routine:

.equ l1      = 20000


 delay:
      push r27
      push r26
      push r25
      

      
    
 loop2:
    
      ldi r26, LOW(l1)
      ldi r27, HIGH(l1)
      
loop1:
      sbiw r26, 1
      brne loop1

      dec delayMultiplier
      brne loop2

      
      pop r25
      pop r26
      pop r27


      ret

and here is the schematic

enter image description here

Please be gentle I am kind of a beginner :)

UPDATE: I tried using a mask to read only bits 0, 1 from PINB and it worked for a while, but suddenly every thing stopped working and I went down to trying to blink an LED and it just do not work, here is the code:

.include "m32def.inc"               ;load the ATmega32 directory which include rigsters' & bits' named and addresses


    
    .cseg                               ; indicates that what follows is code
    .org 0x00                           ; indicates orgin address in flash to start  
    
            ldi r21,LOW(RAMEND)         ; initialize 
            out SPL,r21                 ; stack pointer ;do not need to save r21
            ldi r21,HIGH(RAMEND)        ; to RAMEND
            out SPH,r21                 ; "             ;do not need to save r21
            
            

            ser r16                     ; 
            out DDRD, r16               ; Set PORTD as an ouput

            start:

            sbi PORTD, 7                ; turn on LED on PORTD pin7
            
            ldi delayMultiplier, 100    ; do nothing for 1 sec
            rcall delay

            
            cbi PORTD, 7                ; turn off LED on PORTD pin7

            ldi delayMultiplier, 100    ; do nothing for 1sec
            rcall delay

            
            rjmp start                  ; start over

            
            .include "delay.asm"
        
        

I tried light another LED at other parts of the code to try and debug and found out that it stops working at the "ret" instruction in the first "delay" call, even though it works in simulation. and this C code (I suppose it does the same) thing works without a problem

#include <avr/io.h>
#define F_CPU 8000000L
#include <util/delay.h>

int main(void)
{
    
    DDRD = 0xff;
   
    while (1) 
    {
    
        PORTD = (1<<7);
        _delay_ms(1000);
        PORTD = 0;
        _delay_ms(1000);
        
        
    }
}
        
Seif_1999
  • 37
  • 6
  • You probably need pull-up resistors on the switch inputs - 10K or so from U2 pin 1 and 2 to +5 V. - to ensure that the inputs are High when the switches are off. – Peter Bennett Jan 04 '22 at 00:29
  • I did enable the internal pullup resistors, do I still need to add external ones? – Seif_1999 Jan 04 '22 at 00:31
  • Probably not, but they wouldn't hurt. A comment in your code where you enable the internal pull-ups would be nice for those of us who are not too familiar with ATmega assembly language. – Peter Bennett Jan 04 '22 at 00:41
  • Yes, your absolutely right, I do write comments but I got frustrated and totally forget about it, sorry. Also I tried adding external pullup but sadly it did not make a difference. – Seif_1999 Jan 04 '22 at 00:45
  • I'm not familiar with that core -- are you sure that r31 is really general purpose, and isn't being used by anything else? You might try setting 'Mode' to a constant and seeing if it still jumps around -- that'll separate the hardware domain from the software domain pretty quick. – TimWescott Jan 04 '22 at 00:52
  • Unfortunately yes r31 is general purpose, but any way I will try to change it as well as changing "mode" to constant. I will give it a go now – Seif_1999 Jan 04 '22 at 00:58
  • Setting "Mode" to constant did solve the problem. I am very confused right now, as I used a multimeter to measure the actual values of PORTB pins and it was equivalent to (0000 0011), but setting "Mode" to 3 as a constant made it stop jumping around. do you have any advice regarding this? @TimWescott – Seif_1999 Jan 04 '22 at 01:04
  • A multimeter has a (quite high) resistive impendance, that's why you measure "0". Without the multimeter, open pins can be read as any value. You might want to use pull-downs on the unused pins, or mask the switch's pins. – the busybee Jan 04 '22 at 08:25
  • Thank you, I will give it a try and post the results. – Seif_1999 Jan 04 '22 at 14:09
  • Have you tried to pull up entry PORTB? Or use a mask to read from the port PB0 and PB1 only? – G36 Jan 04 '22 at 14:29

1 Answers1

2

You did not post the C code but most likely the C code works because it does not do the exact same things than the assembly code, or the C code does not work either if it does the exact same things.

The assembly code reads the whole PINB register and acts on all PINB bits, including unused bits that are floating. And if any those floating bits are read high, the assembly code does not recognize this and jumps nowhere special so code just falls through to execute Mode1.

So imagine what the code does when PINB reads 0x80 instead of 0x00. It won't be any of the 0x00, 0x01, 0x02, or 0x00 choises the code recognizes.

Justme
  • 127,425
  • 3
  • 97
  • 261
  • Thanks for your advice, will try reading the specific bits 0 & 1 and maybe also enable the internal pullup for all the port, if it still did not work, I will add the C code to the question. Thank you – Seif_1999 Jan 04 '22 at 14:07