Haz una pregunta
  Foros de Electrónica » Diseño digital » Microcontroladores y sistemas embebidos
Foros Registrarse ¿Olvidaste tu contraseña?

Temas similares

04/05/2014 #21

Avatar de papirrin

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
04/05/2014 #22

Avatar de Scooter

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)
04/05/2014 #23

Avatar de papirrin

¡¡Cuatro millones de ciclos!! eso ya...
bueno en realidad fueron mas porque el pic "iba" a 48MHz
04/05/2014 #24

Avatar de Scooter

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.
04/05/2014 #25


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.
04/05/2014 #26

Avatar de Scooter

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í.
04/05/2014 #27


JoaquinFerrero dijo: Ver Mensaje
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.
04/05/2014 #28

Avatar de papirrin

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.
04/05/2014 #29

Avatar de JoaquinFerrero

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!
04/05/2014 #30


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.
04/05/2014 #31

Avatar de papirrin

miren estoy estudiando un codigo para utilizar un monitor VGA y este es un segmento del codigo:
Código:
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:
        nop                             ;5
        nop                             ;6
        movlw   .2                      ;7
        movwf   CurPos                  ;8
        nop                             ;9
        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"
04/05/2014 #32

Avatar de Scooter

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.
04/05/2014 #33

Avatar de JoaquinFerrero

cesar767_7 dijo: Ver Mensaje
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.
04/05/2014 #34

Avatar de papirrin

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
06/05/2014 #35

Avatar de JoaquinFerrero

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í:

Código PHP:
ciclos := número_de_ciclos_que_deseamos_generar 12
loop 
:= int(ciclos 7)
d1 := + (int(loop/1)     % 256) = loop 256
d2 
:= + (int(loop/256)   % 256)
d3 := + (int(loop/65536) % 256)
ciclos_restantes_al_final := ciclos loop 
Esto, en parte, es lo que está en la página, en forma de macro para el ensamblador.
18/05/2014 #36

Avatar de JoaquinFerrero

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:

Código:
; 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 0xDF. 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
Código PHP:
ciclos := número_de_ciclos_que_deseamos_generar - (3)
loop := int(ciclos / (3))
d1 := + (int(loop/1)     % 256) = loop 256
d2 
:= + (int(loop/256)   % 256)
d3 := + (int(loop/65536) % 256)
ciclos_restantes_al_final := ciclos loop * (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:
Código:
./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:
Código:
$ ./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.
Archivos Adjuntos
Tipo de Archivo: zip delays.ods.zip (16,9 KB (Kilobytes), 14 visitas)
18/05/2014 #37


Hola Joaquin... que bueno eres si que te tomaste tu tiempo... esto si sirve de mucha ayuda muchas gracias...
03/08/2014 #38

Avatar de Meta

Buenísimo. Has hecho un ods y todo. Deja ver como funciona.
Respuesta
¿Tienes una mejor respuesta a este tema? ¿Quieres hacerle una pregunta a nuestra comunidad y sus expertos? Registrate

Foros de Electrónica » Diseño digital » Microcontroladores y sistemas embebidos

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO ©2011, Crawlability, Inc.