2
\$\begingroup\$

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);
        
        
    }
}
        
\$\endgroup\$
10
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Jan 4, 2022 at 0:29
  • \$\begingroup\$ I did enable the internal pullup resistors, do I still need to add external ones? \$\endgroup\$
    – Seif_1999
    Commented Jan 4, 2022 at 0:31
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Jan 4, 2022 at 0:41
  • \$\begingroup\$ 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. \$\endgroup\$
    – Seif_1999
    Commented Jan 4, 2022 at 0:45
  • \$\begingroup\$ 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. \$\endgroup\$
    – TimWescott
    Commented Jan 4, 2022 at 0:52

1 Answer 1

2
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ 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 \$\endgroup\$
    – Seif_1999
    Commented Jan 4, 2022 at 14:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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