# Retardo básico en un PIC



## jairlobato (Ene 26, 2008)

Hola muy buen dia a todos, estoy aqui de nuevo por una duda muy básica: quiero hacer un retardo en un pic 16f84 o 16f877a el cual al encenderlo espere durante 5 segundos en alto y a continuación me envie un bajo permanente asta que se le quite la alimentación esto es con motivo de hacer un sumobot para una competencia, y les prometo que envio los detalles de como quedó por completo el proyecto para que todos lo puedan ver y lo prueben si gustan gracias a todos por su ayuda de antemano.


----------



## mabauti (Ene 26, 2008)

Aqui te va una rutina de 5 segundos


```
; Delay = 5 seconds
; Clock frequency = 4 MHz

; Actual delay = 5 seconds = 5000000 cycles
; Error = 0 %

	cblock
	d1
	d2
	d3
	endc

Delay
			;4999993 cycles
	movlw	0x2C
	movwf	d1
	movlw	0xE7
	movwf	d2
	movlw	0x0B
	movwf	d3
Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	$+2
	decfsz	d3, f
	goto	Delay_0

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return
```

o el link http://www.golovchenko.org/cgi-bin/delay


----------



## ELIUSM (Ene 27, 2008)

Hola cumpa!
Aqui en esta página hay un programita que está hecho para hacer retardos de cualquier cantidad de tiempo, con resolución de milisegundos. Es tal como se soñaría: tu le dices al programa cuánto tiempo quieres y este HACE el código, el cual copias y pegas en MPLAB.

http://www.pablin.com.ar/electron/download/picdel.zip

Saludoos!


----------



## jairlobato (Ene 27, 2008)

muchas gracias a los dos seguro que me servirá que esten bien, ha una cosa mas, estuve investigando y me encontre conla directiva INCLUDE<RETARDOS.INC> que tan de confianza podrá ser? se supone que ahi vienen retardos predeterminados listos para usar


----------



## mabauti (Ene 27, 2008)

es buena, el unico detalle es que esta limitada a una frecuencia de XTAL de 4Mhz


----------



## Manson (Jun 11, 2008)

Si yo pongo el include Retardos.INC, que mas debo hacer para tener el de 10uSg de retardo? no he tenio oportunidad de estudiar mucho los includes.


----------



## alexrevolt (Nov 3, 2008)

abre la libreria usando mplab y mira el nombre de los retardos y usa call para llamarlos:
por ejemplo

call    Retardo_10ms ;llama al retardo de 10 mili segundos


----------



## dcp1985 (Ago 3, 2010)

Hola, sabeis si esta disponible el Piddel_sp para windows 7??
esque no me va esa version.

Muchas gracias, Un saludo!!


----------



## toralejo (Mar 27, 2011)

Alguna formula para aprender a hacer el retardo usando tecnologia de papel y lapiz???
Muchas gracias me seria de mucha ayuda ya q en la universidad no me permiten usar el picdelay


----------



## xizuth (Ago 16, 2012)

Este pequeño programa lo desarrollado en java, para generar un codigo de retardo.
para PIC 
Ahi les dejo los lick, de cualquier servidor lo pueden bajar

http://www.mediafire.com/?kqzz7gwx9pb4w69

http://www.4shared.com/file/F3jzSXHb/Generador_de_retardos.html


----------



## cesar767_7 (Abr 19, 2014)

Alguien sabe que algoritmo utilizan en esta pagina http://www.piclist.com/techref/piclist/codegen/delay.htm para poder generar el resultado mas optimo¿?


----------



## chclau (Abr 19, 2014)

Porque se sabe la cantidad de ciclos que toma cada instruccion, y si les das tambien el dato del reloj saben cuanto dura el ciclo, y de ahi se calcula el retardo.


----------



## papirrin (Abr 19, 2014)

Yo apostaria que se basa en la descomposición en factores primos XD
el numero a descomponer obviamente es el resultado del tiempo y la frecuencia del oscilacior.
¿para que necesitas saber?


----------



## Miembro eliminado 356005 (Abr 19, 2014)

La explicación matemática la tienes aquí, mientras que la obtención del código está al final de aquí.

¡Ojo! No es exactamente el mismo código que genera, pero sí casi el mismo. La diferencia está en la posición de los goto $+1 dentro de los bucles anidados.

El cálculo se basa en dividir el número de ciclos de instrucción, por lo que tarda en ejecutarse cada bucle anidado. El límite, para tres bucles anidados, veo que es de 117 millones de ciclos (menos de 2 minutos, a 4 Mhz). El generador tiene capacidad para 4 bucles anidados, así que se pueden hacer esperas aún más largas.


----------



## cesar767_7 (Abr 19, 2014)

Muchas gracias amigos por responder. 
Joaquin la verdad lo estaba viendo ya desde mas antes toda esa teoria pero esta complico... pero muchas gracias por el dato... lo seguiré estudiando y haber si alguien sabe algo mas sencillo que toda esa teoria!!! muchas gracias


----------



## Miembro eliminado 356005 (May 3, 2014)

Esto...

Acabo de descubrir que ese generador funciona bien hasta los 458763 ciclos, pero a partir de ahí, falla.

Para 458764, tengo un resultado de 1785 ciclos demás.

Para 4 000 000 de ciclos, el error se eleva a 12376.

¿Alguien más puede verificarlo?

He hecho las pruebas con los siguientes valores:

* Frecuencia: 4 Mhz
* Cálculo a partir de ciclos (no segundos)
* No generar código para subrutina.

Adjunto hoja de cálculo donde estoy haciendo las cuentas.


----------



## Scooter (May 4, 2014)

Usad los timers es una locura un retardo de mas de 100~200 ciclos. El sistema está muerto y enterrado.


----------



## Ardogan (May 4, 2014)

Scooter dijo:


> Usad los timers es una locura un retardo de mas de 100~200 ciclos. El sistema está muerto y enterrado.



Y gastando energía... no es importante para hobby, pero para diseñar un producto con pilas a vender miles de unidades (o más) ese tipo de cosas puede redundar en que duplicas la cantidad de pilas de desecho como consecuencia de tu diseño (es el equivalente a un auto que consume el doble de gasolina para recorrer la misma distancia).

Tener a un micro con timers ejecutando instrucciones en vacío para hacer retardos  es lo más cercano a usarlo de pisa-papeles


----------



## cesar767_7 (May 4, 2014)

Bueno tienen mucha razón que utilizar los timer del pic es la mejor opción, que estar perdiendo el tiempo con los retardos. Pero lo que yo propongo y trato de investigar es con fines educativos. Espero que entiendan al punto al que voy.


----------



## Scooter (May 4, 2014)

Ruego no te ofendas por lo siguiente, voy a ser muy vehemente y tan solo refleja MI forma de pensar, no es un dogma ni pretendo estar en posesión de la verdad absoluta:
¿Fines educativos?¿Vas a educar en los malos usos y malos vicios?
Bajo mi punto de vista si es didáctico razón de mas para *NO* enseñar malos hábitos.
Según lo veo yo en un sistema "de verdad", "profesional", "serio" o como se le quiera llamar. SIEMPRE, SIEMPRE, SIEMPRE, SIEMPRE, SIEMPRE  hay algo que hacer, NO SE PUEDE, o no se debe dejar dummie, zombie, o echando una siestecita, un sistema varios segundos. Eso es una eternidad en la que pueden pasar mil cosas, hay que atender al paro de emergencia, informar del proceso, admitir datos de configuración y un largo etcétera.
Los bucles vacíos para unos pocos muy muy pocos ms y cuantos menos mejor.
Yo nunca he hecho bucles vacíos ni de 256µs. Solo decrementar un registro de 8 bits y unos 20~100 ciclos de 1MHz. Mas me parece una aberración enorme.


----------



## papirrin (May 4, 2014)

yo creo que depende de lo que se necesite hacer.

por ejemplo ese algritmo lo utilice para medir el tiempo de descarga de un capacitor, o sea el timer de 16bits que nada mas trae uno el pic lo utilice para medir el tiempo de descarga, y carge el capacitor con tiempo 100uS con ese retardo


----------



## Scooter (May 4, 2014)

100µS Eso puede quedar a tu elección como hacerlo.
¡¡Cuatro millones de ciclos!! eso ya...

_"Cuando no tienes nada que hacer"_* es una frase sin sentido, siempre hay algo que hacer, si no ahora a la semana que viene se me ocurrirá que el medidor de condensadores de resultados intermedios, o la curva de carga o algo útil que además de la capacidad de información adicional del estado del condensador, por ejemplo.

*Una de las frases célebres de mi profesora de programación. (es lo que tiene no haber hecho nunca nada de verdad)


----------



## papirrin (May 4, 2014)

> ¡¡Cuatro millones de ciclos!! eso ya...



bueno en realidad fueron mas porque el pic "iba" a 48MHz


----------



## Scooter (May 4, 2014)

Pues entonces significa que con uno de 500kHz bastaba.

Cada uno es libre de hacer lo que quiera, me pongo muy vehemente porque me parece una salvajada. Es como si el profesor de la autoescuela enseña a pisar fijo el acelerador e ir regulando la velocidad deseada pisando el embrague, el resultado parece el mismo; vas a x km/h pero no es muy recomendable.

Por cierto 100us a 48MHz me salen 4800 ciclos que para mi gusto ya es un pasión pero no son cuatro millones.


----------



## cesar767_7 (May 4, 2014)

Lo primero es lo primero, no puedes no puedes comerte el postre sin haber pasado por el almuerzo... nadie niega que tenemos muchos temas que tratar, pero si quieres aprender a manejar un pic tenemos que aprender desde lo mas basicO.


----------



## Scooter (May 4, 2014)

Si cada día que pasa me voy dando cuenta de que el raro soy yo.
TODOS mis profesores me han enseñado a hacer bucles vacíos y yo NUNCA los he usado para NADA*. Bajo mi punto de vista tiempo perdido que empleé en estudiar cosas inútiles y cálculos absurdos y modos erróneos de base.
También tengo compañeros que siguen enseñando a hacer empalmes por arrollamiento de hilo cuando en España hace CUARENTA AÑOS que están PROHIBIDOS. Es que _"les viene bien"_ , no sabemos muy bien a quien le viene bien pero bueno. Algunos que los enseñan no habían nacido y ya estaban prohibidos.

El otro día un compañero mecánico estaba empezando con arduino y a los dos o tres días de empezar, no mas, ya me buscó con_ "oye es que cuando hago un delay() ya no hace nada, no va ni el botón de parar, ¿Como puedo hacer que mientras tanto haga...?"_
Está claro que el marciano soy yo porque en arduino hay varios delay de varios colores y sabores pero para manejar el timer hay que saltarse el arduino e ir al C puro del avr. A lo mejor para eso ya usarán un doble núcleo de 4GHz o varios hilos de multitarea o algo así, no se.
Menos mal que no doy clase de informática que si no me iba a crear aún mas fama de raro.


*Bueno ya he dicho que algunos pocos retardos muy cortos, para esperar que el ADC saque el resultado o algo así.


----------



## cesar767_7 (May 4, 2014)

JoaquinFerrero dijo:


> Esto...
> 
> Acabo de descubrir que ese generador funciona bien hasta los 458763 ciclos, pero a partir de ahí, falla.



bueno no es tan preciso como pense!!!
He estado buscando alguna forma de hacerlo por mi propia cuenta pero me sale muy tedioso.


----------



## papirrin (May 4, 2014)

hijoles no se ni como decirlo,pero parece que estan super extraviados con eso de los retardos a nivel ensamblador...

para empezar cuando se usan esos retardos es para hacer "algo" que amerita presicion en donde cada ciclo cuenta, al principio pregunte que para que se queria saber ese tipo de calculo y no obtuve respuesta, si es solo por aprender lo que puedo decir es que no importa el algoritmo que hallan utilizado mientras la suma de todas las instrucciones den el total del retraso que se requiera, pueden poner 3000000 millones de nop o utilizar cualquier otro algoritmo la presicion la dara el cristal. 

y alparecer chclau si tiene idea...



> Porque se sabe la cantidad de ciclos que toma cada instruccion, y si les das tambien el dato del reloj saben cuanto dura el ciclo, y de ahi se calcula el retardo.


----------



## Miembro eliminado 356005 (May 4, 2014)

Meter el PIC en un bucle infinito en el que vamos mirando las entradas de los actuadores y reaccionando a lo que llega, es igual de inútil que un bucle vacío de espera, desde luego.

Lo ideal es poner el PIC en modo SLEEP, y que se despierte cuando ocurra algo. Con 14 interrupciones distintas, es fácil, ¿no?

Pero... el tema es que a veces es necesario esperar un determinado tiempo.

Último ejemplo en el que he participado: un ascensor llega a un piso. Debe transcurrir un par de cientos de milisegundos para que la cabina se estabilice antes de que los motores de las puertas empiecen a moverse. Para los usuarios es imperceptible, pero la maquinaria te lo exige.

Yo no conocía nada de los PIC hace tres meses, y cuando veía los delay() me ponía enfermo, o los bucles vacíos esperando que un botón se levantara... ¡vaya forma de gastar los vatios!

Lo primero que pensé es: "bueno, pues uso los temporizadores, que para eso pueden servir muy bien". El caso es que van fenomenal... hasta que me di cuenta de que en la mitad de los casos, el programa principal está esperando en un bucle vacío a que el temporizador termine. Así que... como práctica está bien, pero en algunos casos, un simple bucle de espera es más que suficiente.

Ahora lo entiendo mejor: depende de la aplicación, y que son sistemas baratos que pueden estar perfectamente en un bucle sin hacer nada (no son CPU de un ordenador). Eso sí,  siempre que pueda les mandaré a _SLEEPear_.

¡Qué daño hacen los libros y manuales en los que aparece el famoso bucle infinito rodeando al _switch/case_!


----------



## cesar767_7 (May 4, 2014)

bueno estas en lo cierto compañero... son temas basicos que no son insulsos aprenderlos.
Yo quiero hallar el mejor algoritmo para tener el minimo error en mis variables que tenga que elegir, o  mejor aun tener un programa a la mano donde solo me de los valores de las variables.


----------



## papirrin (May 4, 2014)

miren estoy estudiando un codigo para utilizar un monitor VGA y este es un segmento del codigo:

```
blank_line:
        incf    CurPos, f               ; 4
        ; Prepare TMR1 to put H_SYNC low;
        bcf     T1CON, TMR1ON           ; 5 (*)
        movlw   0xFE                    ; 6   10-274 = -264 = 0xFEEE
        movwf   TMR1H                   ; 7
        movlw   0xFD                    ; 8
        movwf   TMR1L                   ; 9
        bsf     T1CON, TMR1ON           ; 10 (*)
        bcf     PIR1, TMR1IF
;       bsf     PIE1, TMR1IE
        
        movlw   -0x4D
        addwf   CurPos, w
        btfss   status, c
        bra     not_v_sync              ;1, 2
        
v_sync:

        bcf     pin_V_SYNC              ;2      
        bnz     i_reset_curpos          ;3 (4)
        lfsr    2, TextData             ;4, 5
        clrf    CurLineH                ;6   Y_SIZE rows
        clrf    CurLineL                ;7   16 lines per row
        movlw   high(FONT_TABLE)        ;8
        movwf   TBLPTRH                 ;9
        bra     I_V_EXIT                ;10,11
i_reset_curpos:
[COLOR="Red"]        nop                             ;5
        nop                             ;6[/COLOR]
        movlw   .2                      ;7
        movwf   CurPos                  ;8
[COLOR="red"]        nop                             ;9[/COLOR]
        bra     i_v_exit                ;10, 11
not_v_sync:     
        bsf     pin_V_SYNC              ;3
I_V_EXIT:
```

los numeros que estan a la derecha los puso el programador para llevar el tiempo,porque eltiempo en eso es vital, y si ven los "NOP" son retardos para ir ajustando el tiempo.

es una claro ejemplo del porque se utilizan tiempos sin que haga "nada"


----------



## Scooter (May 4, 2014)

Como siempre digo, lo mejor de tener una opinión es que se puede cambiar; se puede aprender que lo que uno cree no es correcto.

Ese tipo de aplicaciones tan tan críticas si que pueden requerir una atención completa; si atiendes una interrupción o "si hace viento" te saldrá un pixel mas ancho y la imagen se verá mal.
Creo que convendrás conmigo que generar imagen por soft son aplicaciones bastante "límite", normalmente se emplea algo de hard ya sea una fpga o un simple registro de desplazamiento para descargar la CPU, de lo contrario solo puedes hacer "lo demás" en los momentos de retrazado de la señal etc lo que limita bastante. Si es una aplicación poco exigente en "todo lo demás" es viable por economía  el hacerlo por soft, de lo contrario tendrás bastantes problemas.

A lo que me refiero es hacer timers de varios segundos, para hacer un reloj por ejemplo a base de ciclos vacíos.
En tu caso todo el tiempo "perdido" no lo es, es "empleado" en generar la imagen, el sistema está muerto, pero estás generando la imagen y a cambio gastas 0 en hardware. Esperar por esperar sin usar el timer que está allí es lo que veo absurdo.


----------



## Miembro eliminado 356005 (May 4, 2014)

cesar767_7 dijo:


> bueno no es tan preciso como pensé!!!
> He estado buscando alguna forma de hacerlo por mi propia cuenta pero me sale muy tedioso.


He enviado un correo al autor original, a ver si me responde.

El fallo creo que está en la forma que tienen de calcular el valor inicial de los bucles. En concreto, si el número de ciclos a generar es múltiplo de 65536*7.


----------



## papirrin (May 4, 2014)

A ver... si tiene un fallo el algoritmo y cada ciclo por ejemplo es de 1uS que importa?, agarramos un analizador logico o un osiloscopio y vemos el tiempo REAL, agregamos unos nop  mas o quitamos un ciclo y agregamos unos nop mas... se entiende... no vdad?....si no se entendio necesitan unas clases de ensamblador XD

me podran decir que no tienen un analizador logico o un osciloscopio(de calidad), si no lo tienen no necesitan de un tiempo tan exacto


----------



## Miembro eliminado 356005 (May 6, 2014)

Buenas noticias: el equivocado era yo.

El propio Nikolai Golovchenko me ha respondido, indicándome la fórmula que usan para verificar el número de ciclos consumidos.

La he aplicado a las fórmulas que tenía en la hoja de cálculo, y lo he verificado en el MPLAB X IDE, con la herramienta Stopwatch, que mide el número de ciclos usados, y sí, la salida del generador es correcta.

Aún no tengo claro una de las fórmulas que intervienen en el proceso, pero la forma de obtención de los valores d1, d2, d3... es así:


```
ciclos := número_de_ciclos_que_deseamos_generar - 12
loop := int(ciclos / 7)
d1 := 1 + (int(loop/1)     % 256) = 1 + loop % 256
d2 := 1 + (int(loop/256)   % 256)
d3 := 1 + (int(loop/65536) % 256)
ciclos_restantes_al_final := ciclos - loop * 7
```
Esto, en parte, es lo que está en la página, en forma de macro para el ensamblador.


----------



## Miembro eliminado 356005 (May 18, 2014)

Buenas...

Después de varios días de investigación, ya queda claro el funcionamiento de este tipo de retardos.

(Sigue una explicación de cómo funcionan, luego una muy breve explicación de cómo calcular los parámetros, y el anuncio de un programa en Perl, más unas hojas de cálculo, que ayudan a la creación de vuestros propios retardos)

*Funcionamiento*
Para explicarlo, veamos un ejemplo:


```
; Delay = 400ms
; Clock frequency = 4mhz

; Actual delay = 0.4 seconds = 400000 cycles
; Error = 0.00 %

        cblock 0x70
        d1
        d2
        d3
        endc

                        ;399999 cycles
        movlw   0x36
        movwf   d1
        movlw   0xE0
        movwf   d2
        movlw   0x01
        movwf   d3
Delay_0
        decfsz  d1, f
        goto    $+2
        decfsz  d2, f
        goto    $+2
        decfsz  d3, f
        goto    Delay_0

                        ;1 cycles
        nop
```
La teoría de funcionamiento es la siguiente: el retraso se produce por la ejecución de bucles anidados que, sencillamente, consumen ciclos de instrucción. La cuestión es encontrar la combinación adecuada de ciclos anidados para que se aproxime lo más posible al retardo o espera que deseamos.

Los bucles anidados consumen una serie de ciclos fijos, y otros variables. Un bucle, esencialmente, consume 3 ciclos cada vez que se ejecuta la instrucción de decrementado (1 ciclo) más el goto que le sigue (2 ciclos), y 2 ciclos si al hacer el decremento (1 ciclo) el contador pasa de tener un valor '1' a '0', por lo que entonces el procesador 'salta' la instrucción siguiente (el goto), consumiendo 1 ciclo más.

Simplificando, podemos decir que, si un bucle comienza con un determinado valor en el contador 'd1', realizará (d1-1) vueltas consumiendo 3 ciclos, más una última vuelta que consumirá 2 ciclos.

Ahora bien... 3 y 2 ciclos son cifras pequeñas, que para bucles cortos están bien, pero si queremos esperas más largas, nos obligará a hacer demasiados bucles.

La solución que adoptan los autores del Generador de Golovchenko es la de aumentar la cantidad de ciclos de instrucción consumidos por los bucles más internos: cuanto más niveles internos hay, más ciclos deben consumir.

En el ejemplo, vemos que el bucle de 'd1', consume 7 ciclos de forma normal (1 ciclo para decrementar, y 2 ciclos por cada uno de los 3 goto que se ejecutan en cascada). Y en la última vuelta de 'd1', consume los 2 ciclos de siempre para llegar a la instrucción de decrementado de 'd2'.

Y con 'd2' pasa lo mismo: consume 5 ciclos (1 del decremento y 4 ciclos por los 2 goto que le siguen). Para 'd3', tenemos el caso sencillo de 3 ciclos por vuelta (1 decremento más 2 de un goto).

A estos ciclos consumidos por los bucles hay que sumar los 2 ciclos * número de bucles anidados, correspondientes a las instrucciones mov* que hay antes de los bucles, donde se realiza la carga de los contadores.

Un detalle... Supongamos que 'd1' -como en el ejemplo-, comienza con el valor 0x36. Entonces el bucle más interno consumirá (0x36-1) vueltas, a razón de 7 ciclos por vuelta, más una última vuelta, con 2 ciclos más. En ese momento, 'd1' queda con el valor '0'. A continuación, entramos en el decremento de 'd2', que pasará de 0xE0 a 0F. Y saltará a ejecutar de nuevo el bucle interno de 'd1'. Como 'd1' valía '0', tenemos entonces que el bucle más interno se ejecutará (256-1) veces consumiendo 7 ciclos cada vez, hasta que en la última vuelta, cuando pase de '1' a '0', consumirá 2 ciclos más.

Entonces, tenemos que los primeros valores que damos a los contadores, son un 'ajuste fino' de los bucles, sabiendo que una vez que se agoten, en la siguiente vuelta se ejecutará un 'giro completo' de 256-1 vueltas de ese mismo bucle. Esto podríamos llamarlo el 'núcleo duro' del retardo. Y ese 'núcleo duro' se ejecutará tantas veces como indique el bucle anidado más externo a él (menos en la primera vuelta, claro).

Puede parecer que es muy complicado, entonces, saber cómo calcular cuántos bucles anidados y qué valores poner al inicio, en los contadores.

*Simplificación*
Pero, resulta que no lo es tanto. El efecto de varios goto en cascada, permite simplificar el cálculo, ya que los ciclos consumidos por los bucles más externos, en las últimas vueltas, se compensan con los ciclos consumidos con los goto, de tal manera, que al final todo depende del número de ciclos consumido por el bucle más interno.

Ejemplo: Para 3 bucles anidados, tenemos que:


el bucle 'd1' consume 7 ciclos por vuelta
el bucle 'd2' consume 5 ciclos por vuelta
el bucle 'd3' consume 5 ciclos por vuelta
la última vuelta de cada bucle, siempre consume 2 ciclos, independientemente de su nivel de anidamiento
De forma matemática, queda así:

<ciclos consumidos por 3 niveles de anidamiento> =
<ciclos consumidos por 2 niveles de anidamiento, en su primero vuelta> +
<ciclos consumidos por cada vuelta en el tercer nivel> * 
(<ciclos consumidos por el decremento en el tercer nivel> +
 <ciclos consumidos por el núcleo duro del segundo nivel>)
+<ciclos de la última vuelta del tercer nivel>

Esta es una fórmula que podemos desplegar, pero es más fácil si la vemos con los valores del ejemplo (3 niveles de anidamiento):

d3 = <nivel 2>
     + (d3-1)(3 + <núcleo duro nivel 2>)+2 =

d3 = <nivel 1>
     + (d2-1)(5+<núcleo duro nivel 1>)+2
     + (d3-1)(3+<núcleo duro nivel 2>)+2 =

d3 = 0+(d1-1)(7+0)+2
     + (d2-1)(5+0+(256-1)(0+7)+2)+2
     + (d3-1)(3+<núcleo duro nivel 1>+(256-1)(<núcleo duro nivel 1>+5)+2)+2 =

d3 = 7(d1-1)+2
   +  (d2-1)(5+ 7(256-1)+2)+2
   +  (d3-1)(3+ 7(256-1)+2 +(256-1)(7(256-1)+2+5)+2)+2 =

d3 = 6 + 7(d1-1)
   +  (d2-1)(5+ 7*256-7+2)
   +  (d3-1)(3+ 7*256-7+2 +(256-1)(7*256-7+2+5)+2) =

d3 = 6 + 7(d1-1)
   +  (d2-1)(7*256)
   +  (d3-1)(3+ 7*256-7+2 +(256-1)(7*256)+2) =

d3 = 6 + 7(d1-1)
   +  7(d2-1)256
   +  (d3-1)(7*256 +7(256-1)256) =

d3 = 6 + 7(d1-1)
   +  7(d2-1)256
   +  (d3-1)(7(256 + (256-1)256)) =

d3 = 6 + 7(d1-1)
   +  7(d2-1)256
   +  (d3-1)(7(256 (1 + (256-1))) =

d3 = 6 + 7(d1-1)
   +  7(d2-1)256
   +  7(d3-1)256*256 =

d3 = 6 + 7((d1-1) + (d2-1)256 + (d3-1)256*256)

Y ya vemos un patrón:

<ciclos consumidos por N niveles de anidamiento> =
2 * N
+ <ciclos consumidos por el nivel más anidado = 2*N+1>
* (
    (<contador nivel más anidado>-1)
 +(<contador nivel siguiente>-1) * 256
 +(<contador nivel siguiente>-1) * 256 * 256
 +( ... )
)

Vemos que obtenemos la simplificación comentada antes: los ciclos que consumen los bucles más externos (5 y 3 ciclos) no aparecen en la fórmula. Sólo son relevantes los del bucle más interno. Esto simplifica el cálculo de los contadores, para obtener un determinado retardo.

Sólo falta añadirle la cantidad de ciclos consumidos por la carga de los contadores y los ciclos extra que necesitamos al final (ver explicación unos párrafos más abajo).

*Contadores*
Ese cálculo se puede realizar de varias maneras. Una de ellas es ir probando partiendo de un sólo bucle anidado. Si con los ciclos consumidos no es suficiente, probamos con un nivel de anidamiento más.

En cada vuelta de este cálculo, hacemos lo siguiente: dividimos de forma sucesiva la cantidad de ciclos del retardo por el número de ciclos del bucle más interno, y luego por una división entera de (256^(nivel de anidamiento-1)). Esto último es para saber la cantidad de vueltas del 'núcleo duro' que necesitamos generar (ver pseudo-código en el mensaje anterior).

Para tres niveles, por ejemplo

```
ciclos := número_de_ciclos_que_deseamos_generar - (2 * 2 * 3)
loop := int(ciclos / (1 + 2 * 3))
d1 := 1 + (int(loop/1)     % 256) = 1 + loop % 256
d2 := 1 + (int(loop/256)   % 256)
d3 := 1 + (int(loop/65536) % 256)
ciclos_restantes_al_final := ciclos - loop * (1 + 2 * 3)
```
Al final, como es normal, el cálculo del retardo no es perfecto. Suelen quedar (<niveles de anidamiento>*2+1)-1 ciclos fuera del cálculo, pero eso se remedia con el añadido, al final de unos cuantos goto +$0 que consumen 2 ciclos cada uno, y si acaso un nop, que consumirá 1 ciclo más.

El cálculo no es sencillo si lo hacemos de forma manual. Pero para eso están los ordenadores 

Adjunto un libro de hojas de cálculo (en formato OpenDocument) donde, cada hoja, contiene los cálculos para diversos niveles de anidamiento: desde 1 a 5 niveles. Con un nivel de anidamiento -vamos, un solo bucle-, se pueden obtener retardos de casi 800 ciclos, mientras que con 5 niveles, se puede obtener retardos de más de 12 billones de ciclos. Para un sistema a 4 Mhz, eso son unos 139 días.

He creado también un programa en Perl v5.14 que produce la misma salida que el Generador de Golovchenko. Estos son los argumentos de entrada:

```
./delay_pic.pl [-s <nombre subrutina>] <frecuencia([hz]|khz|mhz|ghz)> <espera([c]|d|h|m|s|ms|us|ns)>

Subrutina:
    -s: generar código para subrutina

Frecuencia:
    hz : hertzios (por defecto)
    khz: kilohertzios
    mhz: megahertzios
    ghz: gigahertzios

Retardo:
    c:  ciclos de procesador (por defecto)
    d:  días
    h:  horas
    m:  minutos
    s:  segundos
    ms: milisegundos
    us: microsegundos
    ns: nanosegundos

Ejemplos:
    ./delay_pic.pl 4Mhz 300ms
    ./delay_pic.pl 32768Hz 1s
    ./delay_pic.pl 20000000 80000000
```
Al programa hay que darle dos parámetros: la frecuencia de reloj del sistema, y la cantidad de retardo que queremos. Ese retardo se puede expresar en unidades de tiempo o en ciclos de procesador.

De forma opcional, se puede indicar el argumento '-s' junto con un nombre, y genera el código necesario para hacer un retardo en forma de subrutina, con el nombre indicado, y teniendo en cuenta los ciclos consumidos por el call y el return de la misma.

Ejemplo: 

```
$ ./delay_pic.pl -s Delay 4mhz 400ms
; Delay = 400ms
; Clock frequency = 4mhz

; Actual delay = 0.4 seconds = 400000 cycles
; Error = 0.00 %

        cblock 0x70
        d1
        d2
        d3
        endc

Delay
                        ;399992 cycles
        movlw   0x35
        movwf   d1
        movlw   0xE0
        movwf   d2
        movlw   0x01
        movwf   d3
Delay_0
        decfsz  d1, f
        goto    $+2
        decfsz  d2, f
        goto    $+2
        decfsz  d3, f
        goto    Delay_0

                        ;4 cycles
        goto    $+1
        goto    $+1

                        ;4 cycles (including call)
        return

; Mon May 19 03:21:05 2014
; Generated by delay_pic.pl (Joaquin Ferrero. May 19, 2014 version)
```
Bueno, el resultado no es exactamente idéntico, pero son detalles menores (como es el caso de darle un valor inicial al cblock).

Naturalmente, como el código Perl está disponible, se puede modificar como se quiera (por ejemplo, el nombre de los contadores).

Y nada más. Espero que esta explicación, las hojas de cálculo y el programa os sirvan para vuestros proyectos.

Saludos.


----------



## cesar767_7 (May 18, 2014)

Hola Joaquin... que bueno eres si que te tomaste tu tiempo... esto si sirve de mucha ayuda muchas gracias...


----------



## Meta (Ago 3, 2014)

Buenísimo. Has hecho un ods y todo. Deja ver como funciona.


----------



## Electronikz_CL (Oct 11, 2020)

Buen dia a toda la comunidad, vengo en busca de ayuda ya que soy principiante en microcontroladores PIC y más programando en lenguaje ensamblador, aunque debo reconocer que es complicado este lenguaje pero creo que voy agarrándole cariño jeje. 
Bueno el tema que se pide es el siguiente:

Elaborar un programa que genere un corrimiento hacia la derecha de un bit.
Entre cambio de bit y bit debe haber un retardo de tiempo t.
Cada que se presione un push button conectado al microcontrolador, el 
tiempo de retardo se va a incrementar por un tiempo t: se presiona una
vez, el retardo seria 2t, se presiona otra vez, el retardo seria 3t, y
así hasta tener un retardo de 5t. Si se vuelve a presionar el push button
el retardo se va decrementando por un tiempo t: 4t,3t,2t hasta tener solo t.
una vez el tiempo de retardo llega a t, entonces se vuelve nuevamente a 
incrementar con cada presion del push button.

La verdad solo tengo lo del corrimiento, pero tengo duda en como integrar o añadir
los retardos en un solo push button. Si alguien podría orientarme y explicarme por favor
seria de gran ayuda.


----------



## switchxxi (Oct 14, 2020)

Buenas, Electronikz..

Algunas notas:

- El puerto A del 18F14K50 (por cierto lindo micro, lastima que sea medio difícil de conseguir y caro por acá), es de 6 bits nomas, no existe el bit RA7 que usas en tu código. Ademas hay que prestar atención que RA0 y RA1 son de solo 3.3v.

- Te falta la rutina de tratamiento del pulsador con su código anti rebote. Mucha info hay en el foro para buscar sobre el tema.

- Yo usaría un registro que lleve la cuenta de cuantas veces se presiono el pulsador, al llegar a 5 empezaría a bajar. Quizá el bit 7 de ese registro lo pondría como bandera para saber si estoy subiendo o tengo que bajar (cambiaría al llegar a los limites).
Al llamar a la rutina de retardo un simple "movlw 0x07" y un "andwf contador, w" con el registro para eliminar la bandera pero sin cambiar el registro y cargaría W en un segundo registro que haga que los dos loop del retardo se ejecuten X veces. Lo que es lo mismo, se añadiría un tercer loop en la rutina de retardo.

Ojala te sirva.



PD: Por cierto aun espero ver como quedo el robot de jairlobato XD


----------



## uriel04 (Abr 23, 2021)

Muy buenos días a todos. Soy nuevo utilizando el lenguaje de ensamblador y tengo como proyecto realizar un retardo para un pic16f84a el cual al encienda un led y espere 1 minuto con 15 segundos encendido, espero y me puedan apoyar con algunos consejos a cerca de como utilizar los ciclos de retardo y como anidar dichos ciclos. Gracias y que tengan un buen día.


----------



## Dr. Zoidberg (Abr 23, 2021)

Y que llevás hecho???


----------



## DOSMETROS (Abr 23, 2021)

Debes subir todo lo que llevas hecho para poder recibir ayuda.


----------



## uriel04 (Abr 23, 2021)

Hasta el momento llevo esto:

N=33
M=97
P=100

Con estos valores me da un aproximado de 1 segundo.

```
LIST p=16f84a

#include <p16f84a.inc>


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


_config_CP_OPP & _WDT_OFF & _PWRTE_ON & _XT_OSC


;****************************************VARIABLES QUE ASIGNA EL DESTINO DONDE SE GUARDA


w EQU 0x00        ; EL RESULTADO SE GUARDA EN W

f EQU 0x01        ;EL RESULTADO SE GUARDA EN EL REGISTRO


;****************************************DECLARACION DE VARIABLES


PORTA    EQU 0x05    ;DECLARACION DEL PUERTO A

PORTB    EQU 0x06    ;DECLARACION DEL PUERTO B

STATUS    EQU 0x03    ;DECLARACION DE ESTADO

RP0        EQU 0x05    ;BIT RP0 DEL REGISTRO STATUS


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


CONTA1 EQU 0x0C        ;REGISTRO UTILIZADO PARA EL RETARDO

CONTA2 EQU 0x0D        ;REGISTRO UTILIZADO PARA EL RETARDO

CONTA3 EQU 0x0E        ;REGISTRO UTILIZADO PARA EL RETARDO


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


        ORG        0x000       

        goto    main            ;REGRESA AL INICIO DEL PROGRAMA

        ORG        0x05            ;INICIO DEL PROGRAMA


main    bcf        STATUS,RP0        ;SELECCIONA LA PAGINA 1 DE LA MEMORIA PARA TENER ACCESO AL TRIS

        clrf    TRISB            ;SELECCIONA EL PUERTO B COMO SALIDA

        bcf        STATUS,RP0        ;VUELVE A LA PAGINA 0

        clrf    PORTB            ;APAGA LOS LEDS DEL PUERTO

        clrf    CONTA1            ;LIMPIA LOS REGITROS DEL CONTADOR DEL RETARDO

        clrf    CONTA2            ;LIMPIA LOS REGITROS DEL CONTADOR DEL RETARDO

        clrf    CONTA3            ;LIMPIA LOS REGITROS DEL CONTADOR DEL RETARDO


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


RETARDO

        movlw    P            ;1

        movwf    CONAT3        ;1

CICLO3    movlw    M            ;1

        movwf    CONTA2        ;1

CICLO2    movlw    N            ;1

        movwf    CONTA1        ;1

CICLO    decfsz    CONTA1,f    ;1*(N-1)+1

        goto    CICLO        ;2*(N-1)

        decfsz    CONTA2,f    ;1*(M-1)+1

        goto    CICLO        ;2*(M-1)

        decfsz    CONTA2,f    ;1*(P-1)+1

        goto    CICLO        ;2*(P-1)
```


----------



## switchxxi (Abr 23, 2021)

¿ Y el resto del programa ?

¿ Donde asignas el valor a las letras que usas como constantes P, M, N ?

Además si luego de finalizar un nido llamas siempre a "CICLO", la cuenta ahora empezará desde 0xFF y los cálculos ya no serán correctos.


----------



## Dr. Zoidberg (Abr 23, 2021)

Eso ni siquiera ensambla!! También hay etiquetas mal escritas con letras cambiadas:


> movwf    *CONAT3*        ;1


----------

