TODOPIC
Microcontroladores PIC => * PROYECTOS * => Mensaje iniciado por: japdos en 02 de Diciembre de 2008, 06:30:17
-
Hola,
tengo un marcador electrónico cuyo circuito está diseñado con un pic y varia circuitería TTL por delante.
La entrada del sistema son varios 'botones' que van directos a las entradas de un 'codificador'
La salida de este codificador, va a los puertos de entrada del PIC (que mediante interrupcion de cambio de estado del puerto, realiza una suma de puntos)
Por su parte, el pic, por sus salidas activa varios displays de 7 segmentos. (estando realmente activo uno al tiempo, pero engañando al ojo humano).
Problema: Rebotes al pulsar los botones, se producen sumas repetidas.
La solución software me empeora el problema: Si establecezco un retardo para estabilizar la señal en la rutina ISR del pic, cada vez que pulso un boton se apagan los displays durante el tiempo de estabilización.
La solución hardware, no acabo de hacerla funcionar al 100%:
La entrada de los codificadores es activa en baja, tengo los pulsadores al aire (TTL=1) y cuando pulso un botón, lo llevo a masa, activando el valor correcto en el codificador. con esto asi, se producen decenas de rebotes.
La unica solución que he encontrado es poner un condensador de 10nf en paralelo a los contactos del pulsador. esto me arregla el problema
en un 95% de las ocasiones, pero hay alguna vez, que se produce.
Necesito un sistema que aumente más la eficiencia, porque ahora no es fiable, y no te das cuenta cuando se ha producido una suma incorrecta.
¿Puede ser que no se trate de un rebote eléctrico en la entrada, sino en la salida de los codificadores? (que van a la entrada del pic, que es
quien detecta una interrupción por cambio de estado).
gracias.
-
No sé exactamente a qué te refieres con un 'codificador', así que no estoy seguro si la solución que te propongo te servirá.
1.- Cuando se detecte un pulso de un pulsador, activamos un Timer y no hacemos nada más.
2.- En la interrupción del Timer se comprueba si el pulsador ya ha sido soltado.
3.- Si el pulsador se ha soltado ya puedes acumular el contador.
4.- En caso que el pulsador siga apretado,se vuelve a cargar el timer para que haga otro ciclo.
De esta manera dejas el micro libre para que siga refrescando los displays, y eliminas los rebotes del pulsador. La clave está en configurar el timer en un periodo que sea adecuado.
-
Gracias por tu respueta Nocturno, has arrojado luz sobre la solución software que pensaba que ya estaba descartada.
Lo que yo llamo codificador es un 'encoder', estoy utilizando dos 74LS148 juntos para generar a poder conectar 16 pulsadores y 'codificarlos'
en los 4 bits que me permite el pic aceptar como entrada (RB4-7) (y con interrupción).
Por ejemplo se activa el 5º pulsador: las entradas de los encoders serían 1111111111011111 (el 0 indica el pulsador que ha sido accionado)
la salida del encoder sería '0101' Dicho valor va al puerto B del pic. siendo RB4=0 RB5=1 RB6=1 RB7=0
Tu mecanismo es muy distinto a todo lo que hay ya implementado (ahora en la interrupción ya compruebo el valor y sumo directamente), pero merece la pena probarlo, en el punto 1, comprobaré cual es el valor 'codificado'
para saber qué valor he de acumular, lo almacenaré en una variable, y luego lo sumaré si es caso, en el punto 3.
Aún me quedan dudas de si realmente el rebote lo estoy teniendo en la lectura del pulsador (por parte del encoder), o en la lectura del puerto B (por parte del pic (puesto que quizá el encoder no ponga todas las salidas 0101 a la vez, y el pic lea una vez 0001 y otra vez 0101). Es decir, que la lectura del puerto cuando se produce interrupción por cambio de estado sea más rápida que la estabilización de niveles por parte del encoder.
No tengo un osciloscopio digital (sí analógico) para poder apreciar las señales o rebotes en ambos puntos del circuito.
-
Seguro que esos rebotes son mecánicos; el codificador no pone unas salidas antes que otras. Creo que vas a tener problemas con mi método si se pulsan varias teclas a la vez...
-
No se va a dar el caso, por la naturaleza del juego, solo se va a pulsar un botón a la vez, así que estoy deseando probar tu idea.
gracias.
-
Hola:
Ademas de la soluccion software, ¿no se podria implementar una solución hardware, mediante un bascula, de manera que a la salida solo se tenga un valor por cada actuacio?
Claro que si son 16 pulsadores, seria utilizar ese circuito x16, lo que aumentaria el hardware, considerablemente. ¡En fin no sé tu veras!
F.
-
Hola japdos, te comento que el 74148 esta hecho de un arreglo de compuertas, esto genera un dato de salida casi instantanea ( hay que tener en cuenta la velocidad del chip ), no realiza el conteo de 0 al valor del pulsador.
Creo que con el timer lograras solucionar lo de los rebotes.
Y cuentanos, el barrido de los displays lo haces por interrupcion de timer o haces un barrido continuo, te recomiendo que lo hagas por interrupcion de timer, en el foro hay bastante info acerca de eso, inclusive creo gu1llermo publicó codigo.
saludos.
-
El barrido lo hago continuo, esperando un par de ms antes de activar el transistor del siguiente display.
Voy a intentar localizar ese post que comentas, porque pensaba que era la manera correcta.
-
...
Problema: Rebotes al pulsar los botones, se producen sumas repetidas.
...
eso es debido a tiempo establecido entre rebotes, a mi me ha pasado y la solución puede estar en como manejes la lógica del programa
mira aquí como he realizado un pequeño ejemplo:
http://www.todopic.com.ar/foros/index.php?topic=21747.0
-
Hola:
Para este tipo de programas en el que el pic tiene que hacer varias cosas a la vez (que en realidad no es a la misma vez pero que da la apariencia de que sea asi) se usa otro enfoque.
Se usa el Servicio de Interrupciones (RSI) habilitando la interrupción del Timer0, por ejemplo cada 10mS, entonces en el programa principal puedes hacer el barrido de los displays y en el RSI hacer el test de los pulsadores o puede ser al reves, en el RSI los displays y en el programa principal los pulsadores.
La técnica es la misma: convenientemente se escoge un valor del timer0 para que interrumpa al pic via el RSI, digamos 5mS, entonces, cada 5mS el pic es interrumpido y ejecuta lo que está en el RSI, por ejemplo puede activar el siguiente transistor para activar el siguiente display cada 5mS de esta forma cada 5mS automaticamente se interrumpe el pic y lo que hace en el RSI es simplemente cambiar al siguiente transistor (display) y sale deñ RSI ... todo esto no le tomo al pic mas de 50uS tal vez y regresa al programa principal.
Si te das cuenta el pic se la pasa todo el tiempo en el programa principal (aprox el 99%) solo entra al RSI cada 5mS (habilita el siguiente display y sale del RSI) y ahi se demora tan solo digamos 50uS y retorna al programa principal en donde vuelves a testear los pulsadores y ahi le puedes poner los delays antirebotes que necesites y con una mayor facilidad de tratamiento de los mismos.
Lo mismo lo puedes hacer con los pulsadores en el RSI y el display en el programa principal, pero en este caso no te conviene, mejor coloca en el RSI el display y en el programa principal los pulsadores.
Javicho.
PD: Problema con rebotes SI solucionables por software.
-
Gracias javichu.
Me temo que he de rehacer todo el software con tu idea, pero creo que lo voy a hacer, parece que tiene más sentido.
Ayer estuve intentanto implementar la primera solución que me habían propuesto aquí, y algo estoy haciendo mal:
Se producía la interrupción por un pulsador (por cambio de estado de RB4-7) y entonces cargo el timer y salgo con retfie, pero la interrupción por descbordamiento del timer nunca se llega a producir (al menos bajo el simulador). (el pic programado y puesto en la placa me muestra los displays pero no me responde a ninguna pulsación (señal de que el timer no se está activando)
Tengo activado el TOIE el RBIE y el GIE. cargado el TMR0 y el TOCS a 0 y el preescaler calculado para que se desborde cada 10ms.....
Nunca había usado timers, ¿algo me falta?
-
¿Estás seguro que no desborda?, a ver si va a ser que en el código de la interrupción no hace nada, o no hace lo que esperas.
-
Tengo un breakpoint en la ISR, captura la de la simulacion de un pulso en RB4-7 y entonces recarga TMR0 y se sale. Pero no vuelve a pararse de nuevo en el breakpoint para capturar la ISR de desbordamiento.
Algo estará entrando en conflicto, porque he hecho otro programita que tiene un bucle esperando a que se produzca el desborde, y efectivamente funciona. pero en el código del programa del marcador, junto con la interrupción por cambio de estado no me tira.
Este el el código que no me funciona: (solo entra aquí cuando hay un cambiod e estado en RB) (los puntos indican código del propio software no relevante)
.......
BSF STATUS,RP0 ;AL BANCO 1
........
BSF OPTION_REG,5
BCF STATUS,RP0 ;AL BANCO 0
BSF INTCON,GIE ; HABILITA INTERRUPCIONES GENERALES
BSF INTCON,RBIE ; POR CAMBIO DE ESTADO EN RB4-7
BSF INTCON,T0IE ; Y POR timer
.........
........
.....
ISR
btfsc INTCON,0 ;Si es interrupcion por cambio de estado, activa el timer : AQUI PONGO EL BREAK POINT
goto activa_timer
btfsc INTCON,2 ;Si es interrupcion por timer, entonces haz toda la operativa de sumar
nop
call OPERATIVA
goto salir_int
activa_timer
BCF INTCON,2
MOVLW 0XD8 ; cargo w con 216
MOVWF TMR0 ; lo paso a TMR0
MOVF PORTB,W ; Lee el valor del botón pulsado a W
andlw b'11110000' ; se queda solamente con el nibble alto
MOVWF BTN_PRES ; y además lo almacena en BTN_PRES
salir_int
retfie
-
Si me pones código en
arameo ensamblador yo ni m'entero, seguro que otros compañeros pueden ayudarte.
-
Nocturno, es lo que tengo. :( ¿Qué utilizas tu? imagino que C, pero qué compilador, entorno etc?
-
Yo uso C de CCS, pero no te preocupes, que hay mucho asmadicto por aquí que te echará una mano.
-
Hola:
En el código que pusiste, faltan algunos detalles importantes, por ejemplo cuando ingresas al RSI debes guardar, por lo menos, el registro W y STATUS, y al salir del RSI debes recuperarlos. Recuerda que el RSI apunta a la dirección 04 de la memoria de programa, entonces antes del label ISR debes colocar el ORG H'04'.
Ten en cuenta siempre que el valor que hayas cargado al prescaler se borra cuando escribes un valor al Timer0 por ello primero debes cargar el valor que deseas al Timer0 y a continuación cargas el valor del prescaler. Antes del retfie debes borrar los flags de las interrupciones que se producen.
Javicho.
-
Javicho, 'casi' todo lo que me dices, estaba hecho, solo que al cortar y pega el código, pues no lo he puesto.
La copia y restauracion de los registros W y demás está en la subrutina OPERATIVA.
El poner a cero de nuevo el flag de la interrupcion producida, no lo copié.
y efectivamente en la direccion 0x04 tengo un goto a ISR...
Date cuenta que tenía el programa entero funcionando correctamente, me refiero a la versión que sólo usaba interrupciones por cambio de estado del puerto B (y que es la que tenía el problema de los rebotes).
Ahora al meter la T0I es cuando he empezado a tener ese problema de no producirse la int.
Lo que no sabía era que el preescaler se borra, quizá hayas dado ahí con mi problema, aunque pienso que de borrarse y quedar sin preescalar, se producirían más interrupciones que las calculadas.... y no se me produce ninguna.
Incluso si cargo en el inicio del programa el timer, nunca se llega a producir (ni siquiera una vez)
(es que poner todo el código aquí es un tocho impresionante, siento haber copiado y pegado mal).
-
Los registros w y status se guardan al comienzo del RSI, algo asi:
;---------------
RSI movwf W_TEMP1
swapf STATUS,w
clrf STATUS
movwf STATUS_TEMP
movf PCLATH,w
movwf PCLATH_TEMP
clrf PCLATH
movf FSR,w
movwf FSR_TEMP
;---------------
btfsc INTCON,T0IF
goto Interrupción_TMR0
...
...
...
;---------------
Salir_RSI
movlw .131
movwf TMR0
Banco1
movlw b'00000100' ;4mS = (4/4MHz) * 32 * (256-131=125)
movwf OPTION_REG ;precarga de TMR0: 131 y prescaler de 8
Banco0
movlw b'11111000'
andwf INTCON,f ;LIMPLIA todos los FLAGS Y RESTABLECE INTS.
movf FSR_TEMP,w
movwf FSR
movf PCLATH_TEMP,w
movwf PCLATH
swapf STATUS_TEMP,w
movwf STATUS
swapf W_TEMP1,f
swapf W_TEMP1,w
retfie
;---------------
En este ejemplo cada 4mS se produce el desborde del Timer0
Javicho.
-
Bueno, pues ya está efectuado el cambio en todo el código.
Ahora, según vuestras indicaciones, tengo un bucle principal, donde testeo la pulsación de los botones, y una ISR activada por desbordamiento del timer, encargada de visualizar los displays.
Parece que todo funciona bien, salvo unas excepciones que me están volviendo loco.
Los marcadores se muestran correctamente, y 7 botones funcionan bien, otros 7 no funcionan.
El bucle principal del programa es el siguiente:
polling
btfsc BANDERAS,F_PARTIDA_REINICIAR
goto INICIO_2 ; Si se tiene la orden de reiniciar, salta del bucle principal
MOVF PORTB,W ; Lee el valor del botón pulsado (a través de RB4-7)
andlw b'11110000' ; Del puerto B se queda solamente con el nibble alto (rb4-7)
sublw b'00000000' ; y se comprueba que no haya ningun boton pulsado
btfsc STATUS,2 ; (¿rb4-7=0?)
goto fin_polling ; sí, no hay boton pulsado, sigue testeando
call Retardo_10ms ; no, espera a que es estabilice y...
MOVF PORTB,W ; Vuelve a leer el puerto para leer el valor estabilizado
movwf BTN_PRES ; Lo almacena en una variable
andlw b'11110000' ; Del puerto B se queda solamente con el nibble alto (rb4-7)
sublw b'00000000' ; y se comprueba que no sea un rebote
btfsc STATUS,2 ; si lo es...
goto fin_polling ; sigue testeando
call ejecuta ; si no es rebote efectua los calculos
call convierte_bcd
EsperaDejePulsar
MOVF PORTB,W ; Mientras el boton está pulsado continua en un bucle leyendo el puerto
andlw b'11110000' ; hasta que sea soltado
sublw b'00000000'
btfss STATUS,2
goto EsperaDejePulsar
fin_polling goto polling
La rutina de la ISR, como he dicho antes, visualiza alternativamente cada display (cada 500us). y la subrutina "ejecuta", hace los calculos de suma dependiendo de qué boton de los 14 existentes se haya pulsado.
Cada botón suma un valor determinado, los 7 primeros suman bien, pero los últimos 7 no lo hacen.
Después de devanarme mucho los sesos, pienso que quizá sea un problema 'hardware':
Como no tengo un pin de un puerto para indicar la pulsación de un botón, sino que son 4 pines a la vez, tengo que comprobar con comparacion de palabras enteras y quizá esto sea un problema porque no todos los bits estén a la vez. Esto es así por el encoder que utilizo y que he comentado anteriormente, lo que ocurre, es que el encoder, no es de 16 entradas y 4 salidas (sino que son dos 74ls148 de 8 entradas y 3 salidas, colocados encadenados en cascada), y quizá se deba a un comportamiento relativo a los tiempos en los que tarda el acarreo en llegar al cuarto bit de la salida que conforman los 2 enconder unidos. Pienso esto, porque curiosamente, los botones que no funcionan, son los que tienen ese cuarto bit a 1.
1XXX no suman nada (si los pulso rápidamente repetidas veces, alguna vez sí suman el valor correcto, pero 1 de cada 30...)
0XXX sí suman bien siempre.
(el diseño electrónico funciona porque con la versión anterior del programa (con RBIE) , sumaba los 14 valores correctamente. pero estaba el problema del rebote. Y como he comentado antes, si pulso repetidas veces, tb suma correctamente el valor del botón pulsado.
¿Puede ser? ¿y como puedo solventarlo? sino, ¿a alguien se le ocurre qué otra cosa puede estar pasando?
gracias de nuevo.
-
Lo que pasa es que 10mS de delay antirebote es muy poco dale entre 20 y 50mS y en la rutina "EsperaDejePulsar" ten en cuenta que tambien se produce rebote del pulsador al soltarlo, es decir, el rebote se produce cuando presionas y cuando sueltas el pulsador.
Javicho.