TODOPIC

Microcontroladores PIC => Todo en microcontroladores PIC => Mensaje iniciado por: arcachofo en 05 de Febrero de 2009, 01:43:49

Título: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 01:43:49
Hola a todos, estoy haciendo algunas cosas con multitarea básica pero sin usar ningún RTOS, usando timer0+interrupciones para crear una base de tiempo, y en la rutina de interrupción una serie de contadores que activan cada tarea cuando ha pasado el tiempo establecido.

Trato de hacer algo como un "delay" pero que no mantenga el uso del procesador, sino con otro contador en interrupciones y cuando pase el tiempo del delay que vuelva al punto donde fué llamado... espero haberme explicado.

Este delay sería una función que activaría un contador y se quedaría en espera de que el contador levante la bandera de "fin de cuenta", entonces volvería al punto desde donde fué llamado el delay.

he hecho alguno específico para una tarea, pero me gustaría hacer uno que pudiera ser usado por cualquier otra tarea, lo que no tengo muy claro es como volver al punto desde donde fué llamado el delay, se puede hacer usando pcl y pclatch?
Si es así: cómo se haría esto?
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: pibe en 05 de Febrero de 2009, 04:42:07
No entiendo muy bien lo que quieres hacer , pero a ver si acierto:

Hacer un delay en una salida cuando ocurre algo en una entrada sin que el micro se quede esperando sin hacer nada, no?

Pues si es así , cuando se origina el pulso en la entrada pones un flag a 1.
Si es un delay de retardo de 2 segundos antes de activar la salida ,por ejemplo :
Al entrar en las interrupciones (que se producen cada 10ms por ejemplo) miras si esa bandera está activada, si lo está sumas 1 a un contador nº1 , miras que no haya llegado a 100 (10msx100=1s) , si no ha llegado sales. Si ha llegado limpias el contador nº1 y le sumas 1 al contador nº2 (2x1s) y miras que no haya llegado a 2, si no ha llegado sales. Si ha llegado limpias el contador 2 y activas la salida y limpias el flag para que lo haga otra vez solo cuando vuelva a tener un pulso en la entrada.
Esto es algo somero, tampoco dices que lenguaje usas.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 06:28:16
Hola pibe... gracias por la respuesta.

Si.. esa es la idea, el tema de los contadores y eso lo tengo hecho y funciona bién, pero solo me sirve para esa tarea en concreto, entonces el tema sería de alguna manera guardar el punto del programa donde se llama al delay y que cuando el delay termine, este direccione la ejecución otra vez a ese punto para que se siga ejecutando lo que viene después del delay.

El tema es que no sé como direccionar la ejecución al sitio que yo quiera, mirando por ahí creo entender que se puede hacer con pcl y pclath, pero nunca he hecho esto, lo más que he hecho es usar pcl para seleccionar opciones de una tabla.

Entonces este delay podría ser una subrutina, tendría que guardar la dirección desde donde se la llamó, y habilitar un contador con el valor que se le haya pasado y salir al bucle principal, donde se seguirían ejecutando otras tareas hasta que el contador del delay terminara, entonces se tendría que volver a la ejecución en el punto que se había guardado previamente. Esto es lo que no tengo nada claro.

El caso es hacer un delay de este tipo que pueda ser llamado desde cualquier sitio.. y el problema es como volver al sitio desde donde fué llamado...

Citar
Si ha llegado limpias el contador 2 y activas la salida y ......
En este caso "activar la salida" sería volver al punto donde se llamó el delay... ¿como hacer eso?

Estoy haciendo pruebas en Basic, pero lo del lenguaje a utilizar en principio es lo de menos.. con una idea general de la manera de proceder creo que me valdría para probar cosas...

Saludos
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 06:58:04
Mira... lo que tengo es algo así (muy resumido):

Código: [Seleccionar]

bucle_principal:

    if flag_tarea1=1 then
        flag_tarea1=0
        ...........
        ...........
        habilita_demora=1
        tiempo_demora=100
        goto salir_tarea1
    volver_aqui:
        ...........
        ...........
    salir_tarea1   
    end if

goto bucle_principal


sub interrupt
   
    if habilita_demora=1 then
        tiempo_demora -= 1
        if tiempo_demora=0 then
            habilita_demora=0
            goto volver_aqui
        end if
    end if

End sub


Pero esa demora solo me sirve para la tarea1, lo que busco es hacer una demora que sea utilizable desde otras tareas (si está libre claro..)


Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: pibe en 05 de Febrero de 2009, 07:43:56
UY pues yo de basic me olvidé todo. Programo en asm.
Vamos a ver

Lo mejor que puedes hacer cuando comienzas con un programa, sea el lenguaje que sea es hacer un diagrama de flujo, sino te pierdes.
Un diagrama de flujo es un borrador donde planeas las decisiones del micro en modo gráfico. O sea con triangulos para decisiones si-no y rectángulos para ejecuciones, etc etc.
Yo SIEMPRE lo hago. Una vez tengo terminado el diagrama de flujo es cuestión de cocer y cantar, porque vas codificando lo que tienes en el diagrama de flujo y te puedo asegurar que tendrás poquísimos errores y además que si tienes que modificar algo lo haces en un periquete porque sabes que hace cada rutina.

tienes varias entradas y varias salidas retardadas?
Un ejemplo con 3 entradas + 3 relays

En el programa principal testeas las entradas 1 , luego la 2 y luego la 3 y pones el flag que le corresponda a 1 cuando se active. Sigues con mas instrucciones , lo que quieras hacer o vuelves al inicio.
En las interrupciones   testeas  flag 1, y si está activo entonces activas contador 1. Testeas  flag 2, y si está activo entonces activas contador 2. Testeas  flag 3, y si está activo entonces activas contador 3. AL finalizar cualquiera de los contadores activas la salida que le corresponda y limpias el flag que le corresponda.

No entiendo que quieres hacer con el pobre pcl /pclath :P
Si lo que quieres es que vuelva desde donde se le llamó te estás equivocando. Yo estaba con un proyecto donde "pensaba" que lo mejor era modificar el pcl para cuando volviese de las interrupciones fuese donde yo quisiera, pero inmediatamente lo descarté ya que se armaría un cacao que no veas.
Cuando las cosas parecen oscuras un simple "flag" me ha despejado todas las dudas.

Si pudieses ejemplificarme un poco más lo que quieres hacer, quisas pueda ayudarte mejor.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 08:18:38
Citar
Lo mejor que puedes hacer cuando comienzas con un programa, sea el lenguaje que sea es hacer un diagrama de flujo, sino te pierdes.
Un diagrama de flujo es un borrador donde planeas las decisiones del micro en modo gráfico. O sea con triangulos para decisiones si-no y rectángulos para ejecuciones, etc etc.
Yo SIEMPRE lo hago. Una vez tengo terminado el diagrama de flujo es cuestión de cocer y cantar, porque vas codificando lo que tienes en el diagrama de flujo y te puedo asegurar que tendrás poquísimos errores y además que si tienes que modificar algo lo haces en un periquete porque sabes que hace cada rutina.
Si... es una buena idea, pero no estoy haciendo una aplicación concreta, sino buscar la manera de hacer una rutina que pueda aplicar en distintas ocasiones,

Si programas en asm estupendo... he encontrado algo que se parece a lo que yo quiero,... lo siento pero usa pcl y pclath... no se me ocurre otra manera.
El tema sería una macro que guarde el punto donde tiene que retornar cuando termine el contador, algo así:

Código: [Seleccionar]

demora macro tiempo

MOVF tiempo,W
MOVWF tiempo_demora ;valor del contador de demora
BSF habilita_demora ;flag que activa el contador de demora (definido anteriormente)
MOVLW HIGH($+.5)
MOVWF vuelve_aqui_H
MOVLW LOW($+.3)
MOVWF vuelve_aqui
goto bucle_principal

endm


Cuando termina el contador vuelve al punto donde guardado en las variables "vuelve_aqui_H" y "vuelve_aqui":

Código: [Seleccionar]
sub interrupt
   
    if habilita_demora = 1 then
        tiempo_demora -= 1
        if tiempo_demora = 0 then
            habilita_demora = 0
            PCLATCH = vuelve_aqui_H
            PCL = vuelve_aqui
        end if
    end if

End sub

Podría funcionar esto?
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: pibe en 05 de Febrero de 2009, 08:27:25
Pero es que lo estás haciendo al revés. A ver

Los contadores y activaciones ponlos en las interrupciones y en el principal pon los test de entradas. Es así de fácil. A menos que no entienda claramente lo que tienes en mente.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 09:13:30
Citar
Pero es que lo estás haciendo al revés. A ver

Los contadores y activaciones ponlos en las interrupciones y en el principal pon los test de entradas. Es así de fácil. A menos que no entienda claramente lo que tienes en mente.

Si... tienes razón, no puedo poner el salto dentro de las interrupciones, entonces sería algo así:

Código: [Seleccionar]

bucle_principal:

    if flag_regreso_demora = 1
        flag_regreso_demora = 0
        PCLATCH = vuelve_aqui_H
        PCL = vuelve_aqui           'Aquí debería saltar a: "RETORNO".. no?
    end if

    if flag_tarea1 = 1 then
        flag_tarea1 = 0
        ...........
        ...........
        demora 100       'llama a la macro "demora" con el parámetro 100
        ...........         'RETORNO
        ...........   
    end if

goto bucle_principal


sub interrupt
   
    if habilita_demora = 1 then
        tiempo_demora -= 1
        if tiempo_demora = 0 then
            habilita_demora = 0
            flag_regreso_demora = 1
        end if
    end if

End sub


demora macro tiempo

MOVF tiempo,W
MOVWF tiempo_demora ;valor del contador de demora
BSF habilita_demora ;flag que activa el contador de demora (definido anteriormente)
MOVLW HIGH($+.5)
MOVWF vuelve_aqui_H
MOVLW LOW($+.3)
MOVWF vuelve_aqui
goto bucle_principal

endm


El compilador GCBasic acepta asm directamente, no he probado con macros, pero eso ya es otra cuestión; lo que me interesa es si esa manera de usar pcl y pclatch podría funcionar...

Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: pibe en 05 de Febrero de 2009, 09:20:38
lo que me interesa es si esa manera de usar pcl y pclatch podría funcionar...

No entiendo por que te empecinas en abusar sexualmente de los pobres registros  :mrgreen:

No puedes hacerlo como yo te he dicho? No te sirve o tienes otra cosa en mente?
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 10:31:22
Citar
No entiendo por que te empecinas en abusar sexualmente de los pobres registros
 

...Es que nunca me he hecho un trío con pcl y pclath.... y me han dicho que son la caña!!! así que lo quiero probar como sea... :-)


Citar
No puedes hacerlo como yo te he dicho? No te sirve o tienes otra cosa en mente?

Si.. algo así es lo que tengo, osea crear un retardo para cada tarea que lo necesite, pero soy tan vago que prefiero aprender a manejar pcl y pclatch con tal de no estar escribiendo lo mismo varias veces...  :lol:

No... hablando en serio, la idea es primero aprender a manejar estos registros, ahora que sé que se pueden usar para esto me ha picado la curiosidad; y luego hacer algún macro o función o algo que pueda servir para llamarlo desde cualquier punto de cualquier tarea; quizás hacer 3 retardos de estos de uso general que puedan ser utilizados en cualquier momento por cualquier tarea, con la limitación de que solo pueden haber 3 retardos funcionando a la vez, pero podrían haber 10 o 15 posibles llamadas a retardos desde distintos sitios y cada uno debería continuar desde donde fué llamado, si tengo que crear 15 retardos con sus flags y sus salidas correspondientes puedo acabar como el muñeco animado que tienes en tus mensajes.. :D

El primer problema que me encuentro es el del direccionamiento al punto desde donde se llamó el retardo, luego tendré que resolver el tema de buscar un temporizador libre y luego... pués lo que venga, al fin y al cabo se trata de aprender...

Saludos y gracias por el interés.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: reiniertl en 05 de Febrero de 2009, 11:06:53
Yo mejor me iría directo a utilizar un RTOS, porque ya pasé por eso y la solución evidente fue utilizar RTOS, así que aprendí y me ha gustado tanto que no me hayo sin RTOS.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 11:39:01
Hola reiniertl...

Si... llega un punto en que se hace necesario, pero me gusta la artesanía y comprender como funcionan las cosas, con un RTOS es todo más facil, pero no sabes lo que está pasando, sobre todo si no es de código abierto.

Lo que estoy haciendo es "multitarea" o algo parecido muy básico, pero me permite hacer cosas que son imposibles en la programación puramente secuencial. Nosé si para esto vale la pena el gasto de recursos que supone un RTOS; aunque mientras haya memoria seguro que vale la pena en el sentido de facilitar la tarea de programar y de las posiblidades que ofrece.

Lo que si estoy viendo es que las posiblidades que abre esto son muy grandes, simplemente con timer0+interrupciones, unos pocos contadores y dividiendo las tareas en partes sencillas se pueden hacer cosas interesantes; por ejemplo, antes tenía un programa que leía unos canales ADC, hacía unos cálculos, movía unos motores según los cálculos y enviaba una cadena de datos por uart a 9600 b,... total.. el ciclo se repetía muy pocas veces por segundo.
Ahora puedo elegir cada cuanto tiempo se hace cada cosa, y aunque enviar los datos por uart tarda casi 20 ms, puedo leer adc cada 1 ms si quiero, hacer los cálculos con la media de 10 lecturas y actualizar los motores cada 10 ms, todo esto con un oscilador de 4 MHz.

Por cierto... he estado viendo tus explicaciones sobre RTOS tanto por aquí como en ucontrol,.. y me están siendo muy útiles, aunque yo no uso CCS pero los conceptos me van quedando algo más claros... gracias.


Saludos.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: dogflu66 en 05 de Febrero de 2009, 12:32:56
Mira esto a ver si te vale.

http://www.todopic.com.ar/foros/index.php?topic=14917.msg112159#msg112159
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: RICHI777 en 05 de Febrero de 2009, 12:48:03
Hola, lo queres hacer en lenguaje se hace utilizando las funciones setjmp y longjmp ( que serian algo asi como un no local goto ) .

Saludos !
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 13:46:23
dogflu66: pues ese estilo de programación con base de tiempo es lo que estoy probando, aunque respecto a los delays, los que he visto en tus ejemplos están basdos en timers, pero creo que siguen siendo "bloqueantes"... no?...osea el programa se para ahí hasta que termina el delay.

Lo que trato es de evitar todos esos tiempos de espera no-útiles; por ejemplo las funciones de enviar una cadena de texto por uart se pasan el 99,999999% del tiempo solo esperando, quitando la parte de gestionar el dato que se vá a enviar, todo que hay que hacer es esperar a que TXIF esté alto y poner el dato en TXREG, y no es necesario quedarse esperando por TXIF, que además vá a tardar varios cientos o miles de ciclos de instrucción en quedar libre... con comprobarla de vez en cuando ya nos vale...

Para que sirve que el pic tenga uart por hardware si nos vamos a quedar esperando el mismo tiempo que si lo hiciéramos por software... habrá que aprovechar las capacidades del pic... no?

RICHI777: gracias por el dato, esas funciones a que compilador o lenguaje pertenecen?

Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: dogflu66 en 05 de Febrero de 2009, 14:22:31
Negativo, mis delays no afectan al flujo del programa. Y lo de enviar datos por el puerto serie creas un búfer donde vas colocando los datos a enviar y una tarea de fondo que los va enviando byte por byte, es decir le aplicas a la rutina de descarga del búfer una base de tiempos y solo le permites enviar 1 Byte hasta que la base de tiempos le vuelva a dar el micro. En realidad se pueden hacer verdaderas maravillas con las bases de tiempos y 4Mhz. Con un buen manejo de las bases de tiempos se puede conseguir una sensación de multitarea muy buena, que nos da un mejor aprovechamiento del tiempo de cpu. Pero siempre será la mal llamada multitarea del pobre.

Por ejemplo hace poco tuve que realizar una pequeña plaquita con display, indicador del voltaje de alimentación, recepción de datos por el puerto serie y envío de datos por el mismo puerto. Utilizando las bases de tiempos da la sensación que todas las funciones se realizan al mismo tiempo, incluso punteando el TX con el Rx se recibe en el display lo mismo que envía la plaquita hacia afuera.

Utilizo el entorno de trabajo del PIC Simulator Ide de oshonsoft.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 05 de Febrero de 2009, 16:29:31
Oye pues a ver si me miro bién lo de los delays... bueno todas tus rutinas...

Yo acabo de empezar a hacer cosas con  bases de tiempos y ahora casi que no entiendo como esta no es la manera normal de programar; para cosas muy sencillas quizás no vale la pena, pero el hecho de poder ir añadiendo tareas sin afectar a las que ya están en funcionamiento, poder cambiar la frecuencia de ejecución de cada tarea, incluso desde el propio programa y ese tipo de cosas, dá una libertad y unas posiblidades que no tiene la programación puramente secuencial.

Por algo el timer0 está siempre en funcionamiento... será que está pensado para ser usado siempre?...  :)

Lo que pasa es que es algo más pesado andar con flags, contadores y demás, estaría bién automatizar un poco esas cosas, crear algunas funciones que se encarguen del trabajo pesado y hacer un "RTOS del pobre"... nada de niveles de prioridad, estados de tarea y demás, solo una base de tiempo y no quedarse esperando.

Volviendo a los delays...
Tienes muchos ejemplos y me pierdo un poco, además creo que han cambiado algo y ahora la ventana que muestra el código es muy pequeñita y es dificil seguir el programa.

Podrías indicarme un ejemplo de delay no bloqueante?

Saludos y gracias por la información.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: dogflu66 en 05 de Febrero de 2009, 21:12:33
Bueno aclaremos, "mal llamadas tareas", son rutinas, subprogramas o funciones que para ser ejecutadas dependen de una condición, en este caso del contador asociado a una base de tiempos.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: dogflu66 en 05 de Febrero de 2009, 21:21:39
Aqui dejo el enlace a un ejemplo:

http://www.todopic.com.ar/foros/index.php?topic=14917.msg162863#msg162863

Y el video de lo que hace el programa:

http://www.todopic.com.ar/foros/index.php?topic=14917.msg162878#msg162878

Si no  recuerdo mal, en este ejemplo uso el modulo CCP1 para generar las bases de tiempos y dejar los timer libres.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: RICHI777 en 05 de Febrero de 2009, 22:17:03
Hola,
Citar
Lo que trato es de evitar todos esos tiempos de espera no-útiles; por ejemplo las funciones de enviar una cadena de texto por uart se pasan el 99,999999% del tiempo solo esperando, quitando la parte de gestionar el dato que se vá a enviar, todo que hay que hacer es esperar a que TXIF esté alto y poner el dato en TXREG, y no es necesario quedarse esperando por TXIF, que además vá a tardar varios cientos o miles de ciclos de instrucción en quedar libre... con comprobarla de vez en cuando ya nos vale...

Para que sirve que el pic tenga uart por hardware si nos vamos a quedar esperando el mismo tiempo que si lo hiciéramos por software... habrá que aprovechar las capacidades del pic... no?

Creo que tenes un problema de conceptos, aunque esto no tiene nada que ver con un esquema multitarea o RTOS, normalmente para aprovechar al máximo los recursos de hardware con respecto a las UARTS, se deben implementar atravez de colas circulares o tambien llamados bufferes en anillo. La idea es tener un buffer en memoria de tamaño X y dos indices, cuando vas a enviar una cadena o un buffer, actualizas uno de los indices y activas la interrupcion por transmicion, en el handler del mismo vas actualizando el otro indice hasta que no los indices son iguales condicion de que no hay nada para transmitir, ahi apagas la interrupcion por transmicion. En definitiva con este metodo no esperas nada, el micro interrumpe solo cuando el buffer de transmicion esta vacio, la unica cosa a tener en cuenta es implementar una funcion que te devuelva cuando el buffer de transmicion esta vacio que te indicaria que el buffer se transmitio. En terminos generales ese es el concepto, en la web vas a encontrar doquier de implementaciones y mejor explicacion.

Citar
RICHI777: gracias por el dato, esas funciones a que compilador o lenguaje pertenecen?

Estas son funciones ANSI C standart, pero solo los compiladores decentes la implementan.

Saludos !
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: Gonzalo_BlackHawk en 06 de Febrero de 2009, 00:23:07
Estas son funciones ANSI C standart, pero solo los compiladores decentes la implementan.

CCS las tiene, aunque no se a que te refieres con compilador decente, pues para mi CCS cae mas en la clasificación de sencillo que de decente :mrgreen:.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: RICHI777 en 06 de Febrero de 2009, 00:27:57
Hola pequeño capo, decente me refiero cuando las extensiones a la arquitectura son hechas como manda el estandart por medio de pragmas, ademas que soporte la implementacion de toda o casi toda la RTL, y que no soporte la terminolgia 0bxxxxx, cuando veo esas cosas digo eso es decente...Lo del CCS no lo conozco asi que mucho no puedo opinar....

Saludos !
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 06 de Febrero de 2009, 03:03:41
Citar
Creo que tenes un problema de conceptos, aunque esto no tiene nada que ver con un esquema multitarea o RTOS, normalmente para aprovechar al máximo los recursos de hardware con respecto a las UARTS, se deben implementar atravez de colas circulares o tambien llamados bufferes en anillo. La idea es tener un buffer en memoria de tamaño X y dos indices, cuando vas a enviar una cadena o un buffer, actualizas uno de los indices y activas la interrupcion por transmicion, en el handler del mismo vas actualizando el otro indice hasta que no los indices son iguales condicion de que no hay nada para transmitir, ahi apagas la interrupcion por transmicion. En definitiva con este metodo no esperas nada, el micro interrumpe solo cuando el buffer de transmicion esta vacio, la unica cosa a tener en cuenta es implementar una funcion que te devuelva cuando el buffer de transmicion esta vacio que te indicaria que el buffer se transmitio. En terminos generales ese es el concepto, en la web vas a encontrar doquier de implementaciones y mejor explicacion.

Gracias por la explicación.
Si... mis conceptos sobre esto están todavía formándose.

Pero si la forma que has explicado permite que se ejecuten otras "tareas" (o como quieran llamarlas)  en esos tiempos de espera... como lo llamarías?....
Una cosa son conceptos y otra cosa es si lo llamamos blanco oscuro o negro claro...

El caso es que normalmente las funciones de los compiladores, incluso las funciones escritas por usuarios para uart(por ejemplo) son de este tipo


 while ( TXIF == 0 );
 TXREG=dato;

Eso es una manera de programar tipo: "hasta que no termine esto no sigo con otra cosa".
La manera que tu explicaste permite que se ejecuten otras tareas sin haber terminado totalmente esta. Eso no es un RTOS, no es un sistema operativo, pero es una manera de programar orientada a que parezca que se ejecutan varias cosas a la vez.... de hecho se ejecutan varias tareas a la vez... no varias instrucciones a la vez, quizás multitarea no sea la terminología correcta, pero el concepto está muy claro.

Usar bases de tiempo también permite este tipo de cosas, el ejemplo básico son dos led parpadeantes a distintas velocidades.

Combinando bases de tiempo con programacion al estilo de lo que explicaste con uart, se consigue realizar muchas tareas a la vez.

Citar
Bueno aclaremos, "mal llamadas tareas", son rutinas, subprogramas o funciones que para ser ejecutadas dependen de una condición, en este caso del contador asociado a una base de tiempos.
Bueno... yo no sé cual es la terminología correcta, pero para mi está claro lo que es una tarea, por ejemplo hacer un arroz es una tarea, que se divide en varios pasos básicos , yo voy realizando cada paso y en medio de paso y paso puedo empezar otra tarea, por ejemplo freir papas; acabar otra que había empezado hace un rato, por ejemplo freir un huevo; incluso empezar y acabar otra pequeña tarea como mirar la hora, todas esas "tareas" las hago a la vez, aunque solo  doy un paso de una tarea cada vez...

Sigo pensando que la idea está clara, las terminologías serán las que sean.

Citar
Hola, lo queres hacer en lenguaje se hace utilizando las funciones setjmp y longjmp ( que serian algo asi como un no local goto )
Citar
Estas son funciones ANSI C standart, pero solo los compiladores decentes la implementan.

Ok... a ver si les echo una mirada... Lo bueno sería compilar una función de esas y ver el asm que sacan, tanto "setjmp" como "longjmp"



Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 06 de Febrero de 2009, 03:33:33
dogflu66: me refería a funciones "delay" que se puedan utilizar  desde cualquier parte del programa, las que he visto en tus programas son este tipo, que según creo se quedan esperando... no?

Código: [Seleccionar]
pause_ms:  'rutina de espera, rutina tipo Waitms, pause establese el tiempo total en mSeg
aux_pause = 0  'variable auxiliar lleva la cuenta de los ms
timer_1ms = 0  'al borrar el contador se activa y comienza la cuenta de 1mSeg
While aux_pause <= pause  'tiempo en mSeg maximos a contar
aux_pause = aux_pause + 1  'variable auxiliar lleva la cuenta de los ms
If timer_1ms > 0 Then timer_1ms = 0  'al borrar el contador se activa y cuenta 1mSeg
While timer_1ms < 1  'espera un miliesegundo
Wend
Wend


EDITO:

Para que te hagas una idea de como estoy haciendolo yo (casi igual que tú), ahí va un ejemplo, un led parpadeando con un periodo de 3 ms, otro led a 400 ms y una frase enviada por uart cada 20 ms, con una base de tiempo de 1ms y un oscilador de 4 MHz:
Código: [Seleccionar]
'_______________________________________________________________________
'
'
'       Multitarea: Dos leds parpadeando a distinta velocidad
'                y una frase enviada por UART
'
'_______________________________________________________________________


'------------------------- CONFIGURAR MICRO -----------------------------
'------------------------------------------------------------------------

#chip 16F876, 4       'modelo de pic y velocidad de reloj

#config HS_OSC, WDT_OFF, LVP_OFF, BODEN_OFF, PWRTE_ON

'----------------------- CONFIGURAR VARIABLES ---------------------------

'------------------------------------------------------------------------
flags = 0

dim tiem_led1 as word          'tiempo de parpadeo led1
dim cont_led1 as word          'contador led1
#define flag_led1 flagas.0     'flag led1

dim tiem_led2 as word          'tiempo de parpadeo led2
dim cont_led2 as word          'contador led2
#define flag_led2 flags.1      'flag led2

dim tiem_frase as word         'tiempo de enviar frase
dim cont_frase as word         'contador enviar frase
#define flag_frase flags.2     'flag enviar frase
#define flag_dato  flags.3
#define flag_uart_busy flags.4

dim  mensaje() as string

'----------------------------- INICIALIZAR ------------------------------
'------------------------------------------------------------------------


TRISA = b'000001'

TRISB = b'00000000'

TRISC = b'10000000'


#define LED1 PORTB.0
#define LED2 PORTB.2
#define baudrate 9600

mensaje = "esto es una prueba de multitarea"

InitUSART
InitTimer0 Osc, PS0_1/4         'Timer0 configurado para 1 ms con Fosc=4MHz     
set INTCON.T0IE on              'Interrupciones Timer0 habilitadas

tiem_led1 = 3                   'Tiempo de tarea led1
tiem_led2 = 400                 'Tiempo de tarea led2
tiem_frase = 20                 'Tiempo de tarea mandar frase

cont_frase = 0                  'se pueden desfasar tareas


'--------------------------- BUCLE PRINCIPAL ----------------------------

'------------------------------------------------------------------------

do  'se comprueban los flags

    if flag_led1 on then        'parpadeo led1
        set flag_led1 off
        if LED1 on then
            set LED1 off
            exit Sub
        End if
        set LED1 on
    End if

    if flag_led2 on then        'parpadeando led2
        set flag_led2 off
        if LED2 on then
            set LED2 off
            exit Sub
        End if
        set LED2 on
    End if

    if flag_frase on then                               'Uart
        if flag_uart_busy on then goto salir_frase
        set flag_uart_busy on
        call manda_frase mensaje
        salir_frase:
    End if

    if flag_dato  on then call manda_dato

loop


'--------------------------- INTERRUPCIONES -----------------------------
'------------------------------------------------------------------------

Sub interrupt

    if INTCON.T0IF on then         'si la interrupcion es de timer0

        TMR0 = 10                  'iniciar cuenta timer0

        cont_led1 += 1                 'incrementar contador de tarea
        if cont_led1 > tiem_led1 then  'si ha pasado el tiempo establecido
            cont_led1 = 0              'borrar contador
            flag_led1 = 1              'subir flag de tarea
        End if

        cont_led2 += 1
        if cont_led2 > tiem_led2 then
            cont_led2 = 0
            flag_led2 = 1
        End if

        cont_frase += 1
        if cont_frase > tiem_frase then
            cont_frase = 0
            flag_frase = 1
        End if

    INTCON.T0IF = 0                 'borrar flag Timer0
    End if
   
    if PIR1.TXIF on then            'si la interrupción es por bufer tx libre
        TXREG = dato
        set PIE1.TXIE off           'deshabilitar interrucion bufer tx libre
    End if       

End Sub

'-------------------------- SUBRUTINAS UART -----------------------------
'------------------------------------------------------------------------

'-----------Mandar datos por uart---------------------

Sub manda_frase (In frase$)
    largo = frase(0)
    if largo = 0 Then exit Sub
    caracter = 0
    set flag_dato on                   'Sube flag de tarea manda_dato
End Sub


Sub manda_dato
    if PIE1.TXIE on then exit Sub       'si no ha mandado la letra anterior
    caracter += 1
    if caracter > largo then            'si yá mandó todas las letras
        dato = 0x0A                     'Retorno de carro
        set PIE1.TXIE on
        set flag_dato off               'Borra flag de tarea manda_dato
        set flag_uart_busy off          'Borra flag uart ocupado
        exit Sub
    End if
    dato = frase(caracter)
    set PIE1.TXIE on
End Sub

Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: dogflu66 en 06 de Febrero de 2009, 15:24:27
dogflu66: me refería a funciones "delay" que se puedan utilizar  desde cualquier parte del programa, las que he visto en tus programas son este tipo, que según creo se quedan esperando... no?

Código: [Seleccionar]
pause_ms:  'rutina de espera, rutina tipo Waitms, pause establese el tiempo total en mSeg
aux_pause = 0  'variable auxiliar lleva la cuenta de los ms
timer_1ms = 0  'al borrar el contador se activa y comienza la cuenta de 1mSeg
While aux_pause <= pause  'tiempo en mSeg maximos a contar
aux_pause = aux_pause + 1  'variable auxiliar lleva la cuenta de los ms
If timer_1ms > 0 Then timer_1ms = 0  'al borrar el contador se activa y cuenta 1mSeg
While timer_1ms < 1  'espera un miliesegundo
Wend
Wend

Esta rutina es un ejemplo de pausa absoluta que no le afectan o se ve afectada muy poco por las interrupciones que estén activas en el programa. Como has podido observar se hace un uso abusivo en general de las paradas absolutas en los programas y sobre todo cuando se es principiante. En la realidad prácticamente no se usan y cuando se usan estas funciones especificas (delays) del lenguaje junto a las interrupciones, los delays se alargan enormemente, por lo que la rutina anteriormente citada para hacer una pausa no tiene estos problemas. Pero en el caso que nos ocupa esta fuera de lugar porque como se deduce de este hilo no tiene sentido hacer ir un micro a 20Mhz y luego incrustarle paradas absolutas por doquier.

Pause, wait, delay o cualquier otra variante de parada absoluta = KK

Y como bien indicas las bases de tiempos son muy similares, en mis programas en general intento no sobre cargar mucho la rutina main y solo utilizo los gotos en depuración del programa pero nunca como línea activa, y en general desde que al lenguaje que utilizo le incluyeron las funciones y procedimientos junto con el uso de punteros estoy abandonado el uso del gosub. Y para hacer un delay en una rutina determina sin que afecte a otras pues lo hago añadiendo más bases de tiempos de control en la rutina afectada hasta que haga exactamente lo que quiero.
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: arcachofo en 06 de Febrero de 2009, 16:12:18
Citar
Esta rutina es un ejemplo de pausa absoluta que no le afectan o se ve afectada muy poco por las interrupciones que estén activas en el programa.

Ok... creo que ya entiendo el porqué de esta rutina,... no se vé afectada por las interrupciones ya que usa la "cuenta" de la base de tiempo, es una parada pero al menos una parada exacta...

Citar
en mis programas en general intento no sobre cargar mucho la rutina main y solo utilizo los gotos en depuración del programa pero nunca como línea activa, y en general desde que al lenguaje que utilizo le incluyeron las funciones y procedimientos junto con el uso de punteros estoy abandonado el uso del gosub

Porqué lo de los gotos y gosub?... te refieres a que usas flags o algo así?

Citar
Y para hacer un delay en una rutina determina sin que afecte a otras pues lo hago añadiendo más bases de tiempos de control en la rutina afectada hasta que haga exactamente lo que quiero.

Ya veo... nada de almacenar direcciones y luego usar pclatch y pcl... osea nada de delays reutilizables... quizás sea lo mejor.

A ver si voy aprendiendo más cosas de esta manera de programar...
Título: Re: como hacer un delay "no bloqueante" ??
Publicado por: dogflu66 en 06 de Febrero de 2009, 21:21:35
Los saltos con goto junto con el crecimiento del programa generan un codigo muy difícil de seguir, modificar, corregir, mantener, etc., ya que no mantienen ninguna regla.

El Gosub es un paso mas avanzado que el goto ya que todo salto con Gosub tiene que terminar con un return, esto quiere decir que puedes llamar a una rutina con un Gosub y todo el que vea el código sabe que tarde o temprano obligatoriamente la ejecución del programa se reanudara en la próxima instrucción al Gosub que produjo el salto.

De todas formas ambos generan un código poco transportable entre programas, por eso se utiliza llamadas a funciones, las funciones son pequeñas islas en un código que tienen sus propias variables locales, es decir las variables son invisibles al resto del programa, por lo que al transportarlas de uno a otro no crean conflicto con las demás variables existentes. Pero conlleva generar una estructura de transferencia de datos interna por el compilador muy rígida entre el programa y funciones.. y etc.