Страница 1 из 1

вопрос, корректно из подпрограммы rjmp-ом выпрыгивать?

Добавлено: Пн апр 28, 2014 00:27:53
sergo80zxc
вопрос господа гуру, можно из подпрограммы sled: rjmp-ом выпрыгивать?


Спойлер

Код: Выделить всё

; программа реализует следующие действия. при нажатии кнопки ( INT0) первый раз, загорается светодиод на ноге D0
; далее бегущий огонек бежит от С0 по С5  и так 2 цикла, далее бегущий огонек бежит в обратном порядке 2 цикла
; далее моргают все 6 светодиодов порта С 2 раза. При нажатии кнопки второй раз  Порт С перестает моргать вообще
; и D0 тоже погашен. прога работает
; 
; 865 байт  имя программы в авр студии   ---- zxc1  -----

;****************************************************************************************
.cseg
.include "m8Adef.inc"

;***********************************************************************

.org   $000 rjmp   RESET            ;Reset Handler
.org   $001 rjmp   EXT_INT0         ;rjmp EXT_INT0 ; IRQ0 Handler
;.org   $002 reti                       ;rjmp EXT_INT1 ; IRQ1 Handler
;.org   $003 reti                       ;rjmp TIM2_COMP ; Timer2 Compare Handler
;.org   $004 reti                       ;rjmp TIM2_OVF ; Timer2 Overflow Handler
;.org   $005 reti                       ;rjmp TIM1_CAPT ; Timer1 Capture Handler
;.org   $006 reti                       ;rjmp TIM1_COMPA ; Timer1 CompareA Handler
;.org   $007 reti                       ;rjmp TIM1_COMPB ; Timer1 CompareB Handler
;.org   $008 reti                       ;rjmp TIM1_OVF ; Timer1 Overflow Handler
;.org   $009 reti                       ;rjmp TIM0_OVF ; Timer0 Overflow Handler
;.org   $00a reti                       ;rjmp SPI_STC ; SPI Transfer Complete Handler
;.org   $00b reti                       ;rjmp USART_RXC ; USART RX Complete Handler
;.org   $00c reti                       ;rjmp USART_UDRE ; UDR Empty Handler
;.org   $00d reti                       ;rjmp USART_TXC ; USART TX Complete Handler
;.org   $00e reti                       ;rjmp ADC ; ADC Conversion Complete Handler
;.org   $00f reti                       ;rjmp EE_RDY ; EEPROM Ready Handler
;.org   $010 reti                       ;rjmp ANA_COMP ; Analog Comparator Handler
;.org   $011 reti                       ;rjmp TWSI ; Two-wire Serial Interface Handler
;.org   $012 reti                       ;rjmp SPM_RDY ; Store Program Memory Ready Handler

;***********************************************************************

RESET: 
     ldi r16,low(RAMEND)    ;загрузка указателя стека
     out SPL,r16
     ldi r16,high(RAMEND) 
     out SPH,r16

     ldi r16, 0b00000001       
     out DDRD, r16          ; D0 выход     ,    D1-D7 входы
     ldi r16, 0b11111110
	 out PORTD, r16         ; D1-D7  подтяжка резистором внутреним



     ldi r16, 0b11111111
     out DDRC, r16          ; С на вых


     
     ldi r16, 0b00000000       
     out MCUCR, r16         ; регистр настроийки типа прерывания (INT0, сработают по логическому нулю)
     ldi r16, 0b01000000       
     out GICR, r16          ; разрешение  прерываний  INT0,   (регистр управления прерыв.)     
     ldi r16, 0b01000000
     out GIFR, r16          ; разрешение  прерываний  INT0,   (регист маски прерывания) 

sei                         ; разрешаете прерывания

;***********************************************************************
start: 
     sbis PIND,0            ; считываем состояние ножки порта D , если горит, то пропускаем следующюю команду      
     rjmp start
     rjmp main
;*****************************************************************************************************************

main:  
rcall sled
       ldi r16, 0b00000001
	   rcall zaderjka

       ldi r16, 0b00000010
       rcall zaderjka

	   ldi r16, 0b00000100
       rcall zaderjka

	   ldi r16, 0b00001000
       rcall zaderjka

	   ldi r16, 0b00010000
       rcall zaderjka

	   ldi r16, 0b00100000
       rcall zaderjka

	   ldi r16, 0b00000000
       rcall zaderjka

;********************************************************************************************************************8

main1:
rcall sled
       ldi r16, 0b00000001
	   rcall zaderjka

       ldi r16, 0b00000010
       rcall zaderjka

	   ldi r16, 0b00000100
       rcall zaderjka

	   ldi r16, 0b00001000
       rcall zaderjka

	   ldi r16, 0b00010000
       rcall zaderjka

	   ldi r16, 0b00100000
       rcall zaderjka

	   ldi r16, 0b00000000
       rcall zaderjka
;************************************************************************************************************************


main2:
rcall sled
       ldi r16, 0b00100000
	   rcall zaderjka

       ldi r16, 0b00010000
       rcall zaderjka

	   ldi r16, 0b00001000
       rcall zaderjka

	   ldi r16, 0b00000100
       rcall zaderjka

	   ldi r16, 0b00000010
       rcall zaderjka

	   ldi r16, 0b00000001
       rcall zaderjka

	   ldi r16, 0b00000000
       rcall zaderjka

;**************************************************************************************************************************
main3:
rcall sled
       ldi r16, 0b00100000
	   rcall zaderjka

       ldi r16, 0b00010000
       rcall zaderjka

	   ldi r16, 0b00001000
       rcall zaderjka

	   ldi r16, 0b00000100
       rcall zaderjka

	   ldi r16, 0b00000010
       rcall zaderjka

	   ldi r16, 0b00000001
       rcall zaderjka

	   ldi r16, 0b00000000
       rcall zaderjka

;*************************************************************************************************************************
main4:
rcall sled
       ldi r16, 0b11111111
	   out PORTC, r16
	   rcall zaderjka1

	   ldi r16, 0b00000000
       out PORTC, r16
       rcall zaderjka1

rcall sled
       ldi r16, 0b11111111
	   out PORTC, r16
	   rcall zaderjka1

       ldi r16, 0b00000000
       out PORTC, r16
       rcall zaderjka1

rjmp main

;***********************************************************************************************************************

zaderjka: 
          out PORTC, r16
         
          ldi r16,0            ;задержка примерно 50 милисекунд
          ldi r17,100
         
Loop1:    dec r16
          brne Loop1

          dec r17
          brne Loop1
    ret

;*************************************************************************************************************************

zaderjka1:
          ldi r16,0            ;задержка примерно 130 милисекунд
          ldi r17,0         

Loop2:    dec r16
          brne Loop2

          dec r17
          brne Loop2
    ret

;***************************************************************************************************************************

EXT_INT0:
      cli                       ; запретить прерывания
         push r16               ; сохраняем используемые регистры  r16 (этих пушей может быть много)
	     push r17               ; сохраняем используемые регистры  r17 (этих пушей может быть много)
         in r17, SREG           ; сохраням флаги
         push r17               ; и запихиваем их в стек

         sbis PIND,0            ; считываем состояние ножки порта D , если горит, то пропускаем следующюю команду
         rjmp KUKA              ; перепрыгмваем на команду поджога светодиода
         rcall KUKA1            ; вызываем подпрограмму гашения светодиода

         rcall drebezg          ; антидребезг (задержка 130 милисекунд)

 
         pop r17                ; извлекаем из стека флаги в r17 (обратный порядок при извлечении из стека)
         out SREG, r17          ; отправляем их обратно в регистр флагов
         pop r17                ; восстанавливаем регистр r17
	     pop r16                ; восстанавливаем регистр r16
      sei                       ; разрешить прерывания
reti                            ; возвращаемся из прерывания

;************************************************************************************************************************

KUKA1:
         cbi PORTD, 0           ; погасить D0
ret

;********************************************************************************************************************** 

KUKA:
         sbi PORTD, 0           ; зажигаем D0 

         rcall drebezg          ; антидребезг (задержка 130 милисекунд)

         pop r17                ; извлекаем из стека флаги в r17 (обратный порядок при извлечении из стека)
         out SREG, r17          ; отправляем их обратно в регистр флагов
         pop r17                ; восстанавливаем регистр r17
	     pop r16                ; восстанавливаем регистр r16
      sei                       ; разрешить прерывания
reti                            ; возвращаемся из прерывания

;*********************************************************************************************************************

drebezg:
          ldi r16,0             ;задержка примерно 130 милисекунд
          ldi r17,0

Loop7:    dec r16
          brne Loop7

          dec r17
          brne Loop7
ret

;*********************************************************************************************************************

sled:
         sbis PIND,0            ; считываем состояние ножки порта D , если горит, то пропускаем следующюю команду
         rjmp start             ; на безделье
ret
         
;**********************************************************************************************************************

Re: вопрос, корректно из подпрограммы rjmp-ом выпрыгивать?

Добавлено: Пн апр 28, 2014 07:14:40
Alkul
sergo80zxc писал(а):вопрос господа гуру, можно из подпрограммы sled: rjmp-ом выпрыгивать?
Имеется в виду вот эта "чудесатая" запись? :))

Код: Выделить всё

sled:
         sbis PIND,0            ; считываем состояние ножки порта D , если горит, то пропускаем следующюю команду
         rjmp start             ; на безделье
ret
Конечно, нет. Вы входите в подпрограмму sled по команде rcall. Вы вообще в курсе, какие действия МК выполняет при этом?
Вначале на стек помещается адрес следующей за rcall sled команды (т.н. "адрес возврата"), потом выполняется переход на указанную метку. По команде ret вначале со стека снимается адрес возврата, потом выполняется переход по данному адресу. То есть, вызываем подпрограмму - два байта на стек кладем, выходим из подпрограммы - два байта со стека снимаем.
Самое "лучшее" что может получиться при использовании Вашего варианта - при каждом вызове rcall sled и выходе по rjmp указатель стека будет смещаться на 2 байта (так как при вызове два байта на стек кладется, а при возврате не снимается) и довольно скоро стек "заедет" на участок данных в ОЗУ, что повлечет сбои в программе, а потом и её крах. Но это только в случае, если программа довольно линейна. Если же в программе широко используются подпрограммы, крах наступит значительно быстрей.

В принципе, выход по rjmp из подпрограммы возможен, но при этом нужно обязательно "снять" со стека адрес возврата. Вот так:

Код: Выделить всё

sled:
   sbic PIND,0      ;считать состояние вывода порта, если не горит, то пропустить выход по ret и сделать выход по rjmp
   ret              ;горит, делаем выход по ret
   pop R16          ;иначе не горит, снимаем со стека
   pop R16          ;два байта адреса возврата
   rjmp start       ;и переходим на метку start
Если же выходить по rjmp из обработчика прерывания (вместо выхода по reti), то нужно дополнительно озаботиться разрешением прерываний, а также сбросом флагов (например, при выходе из обработчика прерывания по переполнению таймер-счетчика нужно сбросить бит переполнения).

Re: вопрос, корректно из подпрограммы rjmp-ом выпрыгивать?

Добавлено: Пн апр 28, 2014 16:01:35
sergo80zxc
Alkul то , что нужно) все понял, спасибо)) респект и уважуха :oops:
ниже исправлен код все верно? в таком виде нет косяков? не считая отсутствия оптимизации кода)
Спойлер

Код: Выделить всё

; программа реализует следующие действия. при нажатии кнопки ( INT0) первый раз, загорается светодиод на ноге D0
; далее бегущий огонек бежит от С0 по С5  и так 2 цикла, далее бегущий огонек бежит в обратном порядке 2 цикла
; далее моргают все 6 светодиодов порта С 2 раза. При нажатии кнопки второй раз  Порт С перестает моргать вообще
; и D0 тоже погашен. прога работает
; 
; 865 байт  имя программы в авр студии   ---- zxc1  -----

;****************************************************************************************
.cseg
.include "m8Adef.inc"

;***********************************************************************

.org   $000 rjmp   RESET            ;Reset Handler
.org   $001 rjmp   EXT_INT0         ;rjmp EXT_INT0 ; IRQ0 Handler
;.org   $002 reti                       ;rjmp EXT_INT1 ; IRQ1 Handler
;.org   $003 reti                       ;rjmp TIM2_COMP ; Timer2 Compare Handler
;.org   $004 reti                       ;rjmp TIM2_OVF ; Timer2 Overflow Handler
;.org   $005 reti                       ;rjmp TIM1_CAPT ; Timer1 Capture Handler
;.org   $006 reti                       ;rjmp TIM1_COMPA ; Timer1 CompareA Handler
;.org   $007 reti                       ;rjmp TIM1_COMPB ; Timer1 CompareB Handler
;.org   $008 reti                       ;rjmp TIM1_OVF ; Timer1 Overflow Handler
;.org   $009 reti                       ;rjmp TIM0_OVF ; Timer0 Overflow Handler
;.org   $00a reti                       ;rjmp SPI_STC ; SPI Transfer Complete Handler
;.org   $00b reti                       ;rjmp USART_RXC ; USART RX Complete Handler
;.org   $00c reti                       ;rjmp USART_UDRE ; UDR Empty Handler
;.org   $00d reti                       ;rjmp USART_TXC ; USART TX Complete Handler
;.org   $00e reti                       ;rjmp ADC ; ADC Conversion Complete Handler
;.org   $00f reti                       ;rjmp EE_RDY ; EEPROM Ready Handler
;.org   $010 reti                       ;rjmp ANA_COMP ; Analog Comparator Handler
;.org   $011 reti                       ;rjmp TWSI ; Two-wire Serial Interface Handler
;.org   $012 reti                       ;rjmp SPM_RDY ; Store Program Memory Ready Handler

;***********************************************************************

RESET: 
     ldi r16,low(RAMEND)    ;загрузка указателя стека
     out SPL,r16
     ldi r16,high(RAMEND) 
     out SPH,r16

     ldi r16, 0b00000001       
     out DDRD, r16          ; D0 выход     ,    D1-D7 входы
     ldi r16, 0b11111110
	 out PORTD, r16         ; D1-D7  подтяжка резистором внутреним



     ldi r16, 0b11111111
     out DDRC, r16          ; С на вых


     
     ldi r16, 0b00000000       
     out MCUCR, r16         ; регистр настроийки типа прерывания (INT0, сработают по логическому нулю)
     ldi r16, 0b01000000       
     out GICR, r16          ; разрешение  прерываний  INT0,   (регистр управления прерыв.)     
     ldi r16, 0b01000000
     out GIFR, r16          ; разрешение  прерываний  INT0,   (регист маски прерывания) 

sei                         ; разрешаете прерывания

;***********************************************************************
start: 
     sbis PIND,0            ; считываем состояние ножки порта D , если горит, то пропускаем следующюю команду      
     rjmp start             ; зацикливаемся
     rjmp main              ; валим дрыгать ножками
;*****************************************************************************************************************

main:  
rcall sled                   ; проверяем не горит ли D0 если не горит перестанем моргать
       ldi r16, 0b00000001   
	   rcall zaderjka        ; подпрограма задержки и зажигания светодиода

       ldi r16, 0b00000010
       rcall zaderjka        ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000100
       rcall zaderjka         ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00001000
       rcall zaderjka          ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00010000
       rcall zaderjka         ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00100000
       rcall zaderjka          ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000000
       rcall zaderjka         

;********************************************************************************************************************8

main1:                        ; проверяем не горит ли D0 если не горит перестанем моргать
rcall sled
       ldi r16, 0b00000001
	   rcall zaderjka         ; подпрограма задержки и зажигания светодиода

       ldi r16, 0b00000010
       rcall zaderjka         ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000100
       rcall zaderjka         ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00001000
       rcall zaderjka         ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00010000
       rcall zaderjka         ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00100000
       rcall zaderjka         ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000000
       rcall zaderjka
;************************************************************************************************************************


main2:
rcall sled                          ; проверяем не горит ли D0 если не горит перестанем моргать
       ldi r16, 0b00100000
	   rcall zaderjka             ; подпрограма задержки и зажигания светодиода

       ldi r16, 0b00010000
       rcall zaderjka            ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00001000
       rcall zaderjka            ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000100
       rcall zaderjka              ; подпрограма задержки и зажигания светодиода       

	   ldi r16, 0b00000010
       rcall zaderjka             ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000001
       rcall zaderjka            ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000000
       rcall zaderjka

;**************************************************************************************************************************
main3:
rcall sled                       ; проверяем не горит ли D0 если не горит перестанем моргать
       ldi r16, 0b00100000
	   rcall zaderjka           ; подпрограма задержки и зажигания светодиода

       ldi r16, 0b00010000
       rcall zaderjka           ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00001000
       rcall zaderjka          ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000100
       rcall zaderjka          ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000010
       rcall zaderjka           ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000001
       rcall zaderjka           ; подпрограма задержки и зажигания светодиода

	   ldi r16, 0b00000000
       rcall zaderjka           ; подпрограма задержки и зажигания светодиода

;*************************************************************************************************************************
main4:
rcall sled                     ; проверяем не горит ли D0 если не горит перестанем моргать
       ldi r16, 0b11111111
	   out PORTC, r16
	   rcall zaderjka1         ; подпрограма задержки

	   ldi r16, 0b00000000
       out PORTC, r16
       rcall zaderjka1         ; подпрограма задержки

rcall sled
       ldi r16, 0b11111111
	   out PORTC, r16
	   rcall zaderjka1         ; подпрограма задержки

       ldi r16, 0b00000000
       out PORTC, r16
       rcall zaderjka1         ; подпрограма задержки

rjmp main

;***********************************************************************************************************************

zaderjka: 
          out PORTC, r16
         
          ldi r16,0            ;задержка примерно 50 милисекунд
          ldi r17,100
         
Loop1:    dec r16
          brne Loop1

          dec r17
          brne Loop1
    ret

;*************************************************************************************************************************

zaderjka1:
          ldi r16,0            ;задержка примерно 130 милисекунд
          ldi r17,0         

Loop2:    dec r16
          brne Loop2

          dec r17
          brne Loop2
    ret

;***************************************************************************************************************************

EXT_INT0:
      cli                       ; запретить прерывания
         push r16               ; сохраняем используемые регистры  r16 (этих пушей может быть много)
	     push r17               ; сохраняем используемые регистры  r17 (этих пушей может быть много)
         in r17, SREG           ; сохраням флаги
         push r17               ; и запихиваем их в стек

         sbis PIND,0            ; считываем состояние ножки порта D , если горит, то пропускаем следующюю команду
         rjmp KUKA              ; перепрыгмваем на команду поджога светодиода
         rcall KUKA1            ; вызываем подпрограмму гашения светодиода

         rcall drebezg          ; антидребезг (задержка 130 милисекунд)

 
         pop r17                ; извлекаем из стека флаги в r17 (обратный порядок при извлечении из стека)
         out SREG, r17          ; отправляем их обратно в регистр флагов
         pop r17                ; восстанавливаем регистр r17
	     pop r16                ; восстанавливаем регистр r16
      sei                       ; разрешить прерывания
reti                            ; возвращаемся из прерывания

;************************************************************************************************************************

KUKA1:
         cbi PORTD, 0           ; погасить D0
ret

;********************************************************************************************************************** 

KUKA:
         sbi PORTD, 0           ; зажигаем D0 

         rcall drebezg          ; антидребезг (задержка 130 милисекунд)

         pop r17                ; извлекаем из стека флаги в r17 (обратный порядок при извлечении из стека)
         out SREG, r17          ; отправляем их обратно в регистр флагов
         pop r17                ; восстанавливаем регистр r17
	     pop r16                ; восстанавливаем регистр r16
      sei                       ; разрешить прерывания
reti                            ; возвращаемся из прерывания

;*********************************************************************************************************************

drebezg:
          ldi r16,0             ;задержка примерно 130 милисекунд
          ldi r17,0

Loop7:    dec r16
          brne Loop7

          dec r17
          brne Loop7
ret

;*********************************************************************************************************************

sled:
   sbic PIND,0      ;считать состояние вывода порта, если не горит, то пропустить следующюю команду и сделать выход по rjmp
   ret              ;горит, делаем выход по ret
   pop R16          ;иначе не горит, снимаем со стека
   pop R16          ;два байта адреса возврата записаные подпрограмой rcall
   rjmp start       ;и переходим на метку start
         
;**********************************************************************************************************************

Re: вопрос, корректно из подпрограммы rjmp-ом выпрыгивать?

Добавлено: Пн апр 28, 2014 20:12:07
Alkul
sergo80zxc писал(а):ниже исправлен код все верно? в таком виде нет косяков?
Да вроде нет. Должно работать. А насчет оптимизации - это да, она отсутствует как класс :)
sergo80zxc писал(а):Alkul то , что нужно) все понял, спасибо)) респект и уважуха
Можно поблагодарить, добавив плюсик в карму
:))