¿Cómo producir retardos de tiempo en la programación PIC18 ASM?

Soy un novato con la programación de microcontroladores. Tengo conocimientos de programación de alto nivel, pero me estoy involucrando en el desarrollo de CPU/microcontroladores de bajo nivel.

Quiero hacer este simple ejercicio de encender y apagar una serie de LED durante 1 segundo cada uno.

Probé este montaje (pensando en dos leds):

    List    P=18F4550           
    include <P18F4550.inc>          



    CONFIG FOSC = INTOSC_EC ;INTOSC_EC          ; Internal oscillator                                
    CONFIG PWRT= ON             ; Power-up Timer Enable bit
    CONFIG BOR=OFF              ; Brown-out Reset disabled in hardware and software
    CONFIG WDT=OFF              ; WDT disabled
    CONFIG MCLRE=ON             ; MCLR pin enabled
    CONFIG PBADEN=OFF           ; PORTB<4:0> pins are configured as digital I/O
    CONFIG LVP=OFF              ; Single-Supply ICSP disabled
    CONFIG DEBUG = OFF                  ; Background debugger disabled
    CONFIG XINST = OFF          ; Extended Instruction disabled
;******************************Variables***********************************
    count equ 0x00

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


    org     0x0000  
    movlw   0x62       
    movwf   OSCCON     ;Working at 4 MhZ
    clrf    TRISD      ;D port as output
    ;leds OFF so far

LGHTLOOP
    bcf PORTD,0         
    call DELAY

    bsf PORTD,0         
    CALL DELAY

    bcf PORTD,1         
    call DELAY

    bsf PORTD,1       
    CALL DELAY

bra  LIGHTLOOP

DELAY
    movlw   .1000  
    movwf   count

    LOOP2
         DECFSZ   count,F  

    bra  LOOP2

    nop

    return 

    end

Intenté simularlo en el software Proteus, teniendo esta configuración:

ingrese la descripción de la imagen aquí

De hecho, puedo ver los LED encendiéndose y apagándose, pero nada como un segundo, sin importar cuánto cambie movlw .255en DELAYla subrutina. Probé con diferentes valores más altos y obtuve el mismo comportamiento extraño.

¿En qué me equivoqué?

movlw .255Creo que contar los ciclos de instrucciones en las subrutinas debería ser más de 1 segundo. Pero aún así la simulación lo muestra más rápido. Agregar dos leds más lo hace parecer aún más rápido.

¡Sí! Solo lo pienso. Probé con 255 pero no estoy realmente recibiendo un segundo...
Sí, lo siento... traduje el código, lo estoy editando ahora.
Como sé, si quiero poner decimales, el compilador sabrá que es un número de base 10 que antecede a un punto
No sé cuántos ciclos tarda la instrucción DECFSZ, pero si cuenta 255 tics en un reloj de 4 MHz, su retraso será de 63,75 µs. Incluso si se necesitaron cinco ciclos para la instrucción, nunca vería un parpadeo.
Bueno, por eso tengo una noppor fin. Eso debería estar induciendo al retardado.
Un solo nop y un regreso agregarán un número trivial de ciclos. Su ciclo contiene solo las instrucciones DECFSZ y de bifurcación; mi punto anterior permanece.
Cuando descubras esto, mira el uso de los temporizadores. Los bucles de retardo serán muy limitantes si desea hacer algo más que parpadear los LED.

Respuestas (2)

El problema es que la decfszinstrucción funciona implícitamente en un registro de un byte (8 bits). El valor más grande que puede contener dicho registro es FF hexadecimal (decimal 255). Cuando intenta inicializar counta decimal 1000 (hexadecimal 3E8), en realidad solo está configurando ese registro en los 8 bits de orden inferior de ese valor, o E8 (decimal 232).

Para eludir esta limitación de la decfszinstrucción, la técnica habitual es crear un "nido" de dos bucles, cada uno con una variable separada. Esto le permitiría ejecutar el cuerpo del bucle interno hasta 65535 veces (0xFFFF hexadecimal).

    ; control variable for inner loop
    i equ 0

    ; control variable for outer loop
    j equ 1

    ; initialize j with the MSBs of the loop count
    movlw .1000 >> 8
    movwf j
    ; initialize i with the LSBs of the loop count
    movlw .1000 & 0xFF
    movwf i

loop:

    ; body of loop here

    nop

    ; end of inner loop
    decfsz i
    bra loop

    ; end of outer loop
    decfsz j
    bra loop

Tenga en cuenta que el ciclo interno se ejecuta la cantidad de veces ique se inicializa en la primera iteración del ciclo externo. Cada vez después de eso, se ejecuta 256 veces.

Gracias, lo revisé y funciona bien. El problema es el tiempo. Quiero que los retrasos sean de 1 segundo
De acuerdo, con un reloj de CPU de 4 MHz, debe ejecutar instrucciones que suman 1,000,000 de ciclos de máquina. Si el cuerpo del ciclo es solo un solo nop, cada iteración requerirá 4 ciclos de máquina, lo que significa que deberá ejecutar 250,000 iteraciones. Esto es demasiado para el bucle anidado doble, por lo que puede poner más instrucciones en el cuerpo del bucle o puede agregar un tercer nivel de bucle.
O use un mecanismo de retardo más apropiado, como el módulo de temporizador que actúa como un contador, con la entrada escalada desde el oscilador.
@David, ¿puede ofrecer un ejemplo?
@DaveTweed, ¿puedes explicar un poco más?

Esto es lo que necesitas:

http://www.piclist.com/cgi-bin/delay.exe

probablemente el sitio más útil jamás creado. (¡Después de este!)

Cabe señalar que, si bien esto resolverá su problema, tampoco le enseñará absolutamente nada sobre cómo programar el suyo propio, aunque si lee el código que genera, puede resolverlo.

La otra respuesta debe leerse correctamente si desea una comprensión real. Esto es solo para que las cosas funcionen.

¿Puede ofrecer un ejemplo sobre cómo usar el código generado en su propio código?
Escriba lo que necesita, en términos de segundos o ciclos de reloj, ingrese su frecuencia de reloj, asígnele un nombre único (my1SecDelay), luego presione el botón generar. El sitio al que lo llevan le proporciona el código, todo lo que necesita hacer es copiarlo y pegarlo, pero ASEGÚRESE de declarar las variables en la parte superior de su código, en ASM es algo así como: delay1 equ 0x28h. entonces ya terminaste, solo llámalo.
Para usarlo, simplemente use "call my1SecDelay" o como lo haya llamado.
Lo siento, edite: si ha declarado las var que necesita, en la parte superior tiene su código, entonces no necesita el inicio hasta el punto en que dice; 4999 (o la cantidad que sea) de ciclos. Es decir, tirar la parte "cblock d1 d2 d3 endc"