Autor Tema: como hacer un delay "no bloqueante" ??  (Leído 9879 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado dogflu66

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3510
Re: como hacer un delay "no bloqueante" ??
« Respuesta #15 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.
Saludos desde Granada, España.

Desconectado arcachofo

  • PIC16
  • ***
  • Mensajes: 126
    • Foro para usuarios Linux.
Re: como hacer un delay "no bloqueante" ??
« Respuesta #16 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.

Desconectado dogflu66

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3510
Re: como hacer un delay "no bloqueante" ??
« Respuesta #17 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.
Saludos desde Granada, España.

Desconectado dogflu66

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3510
Re: como hacer un delay "no bloqueante" ??
« Respuesta #18 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.
Saludos desde Granada, España.

Desconectado RICHI777

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1498
Re: como hacer un delay "no bloqueante" ??
« Respuesta #19 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 !

Desconectado Gonzalo_BlackHawk

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 519
Re: como hacer un delay "no bloqueante" ??
« Respuesta #20 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:.
"Siempre piensa si el jugo vale la exprimida..."

"La muerte esta tan segura de vencer que nos da toda una vida de ventaja."

Desconectado RICHI777

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1498
Re: como hacer un delay "no bloqueante" ??
« Respuesta #21 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 !

Desconectado arcachofo

  • PIC16
  • ***
  • Mensajes: 126
    • Foro para usuarios Linux.
Re: como hacer un delay "no bloqueante" ??
« Respuesta #22 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"




Desconectado arcachofo

  • PIC16
  • ***
  • Mensajes: 126
    • Foro para usuarios Linux.
Re: como hacer un delay "no bloqueante" ??
« Respuesta #23 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

« Última modificación: 06 de Febrero de 2009, 04:33:45 por arcachofo »

Desconectado dogflu66

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3510
Re: como hacer un delay "no bloqueante" ??
« Respuesta #24 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.
« Última modificación: 06 de Febrero de 2009, 15:26:36 por dogflu66 »
Saludos desde Granada, España.

Desconectado arcachofo

  • PIC16
  • ***
  • Mensajes: 126
    • Foro para usuarios Linux.
Re: como hacer un delay "no bloqueante" ??
« Respuesta #25 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...

Desconectado dogflu66

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3510
Re: como hacer un delay "no bloqueante" ??
« Respuesta #26 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.
Saludos desde Granada, España.