Problemas con Cronometro 7 Segmentos Multiplexado

Hola, soy nueva en el foro y también en Arduino. He estado escribiendo un código para hacer un cronometro MM:SS.d usando la función millis(), con la ayuda de otro post mejoré mucho mi código sin embargo aún no logro una buena visualización.

Empleo un Arduino UNO y 5 displays 7 segmentos de cátodo común por medio de un multiplexado simple, sin integrados adicionales. Probé las décimas de segundo y fue perfecto, las unidades de segundo y bien, las decenas de segundo y ya empezó a verse distorisionado y al finalizar con los minutos ya la calidad es insuficiente y éstos no se ven, se ilumina tenuemente todo el display, no sé que estaré haciendo mal, el código que uso es el siguiente:

He variado los retardos desde 0 a 25 ms y no mejora, les agradecería mucho su ayuda.

Adjunto código, por su cantidad de caracteres no pude pegarlo como mensaje.

Codigo.txt (17.7 KB)

Creo que ya es hora de cambiar ese montón de digitalWrite por la manipulación directa de puertos. Por ejemplo, en vez de esto:

digitalWrite(26, HIGH);
digitalWrite(25, LOW);
digitalWrite(24, LOW);
digitalWrite(23, LOW);
digitalWrite(22, LOW);

Usa esto:

PORTA = B00010000;
// PORTA 76543210
// 1 = encendido

Supongo que usas un Arduino Mega, verdad?

De ser así, puedes tomar de referencia el primer post de este hilo: PortManipulation page lacks information about Arduino Mega - Website and Forum - Arduino Forum

¿Para qué? Déjame y te explico:

"Manipulación de puertos" es una técnica de programación para microcontroladores Atmel, la cual modifica directamente un registro de un grupo de 8 pines. Este registro indica cual pin debe estar encendido y cual debe estar apagado. Al ser una instrucción de más bajo nivel, se ejecuta 10 veces más rápido que digitalWrite; ¡y además se puede manipular más de un pin al mismo tiempo!

Muy bien, ¿pero cómo se hace esto?

Si observas con atención al segundo pedazo de código que he posteado, notarás que el valor de la variable PORTA empieza con la letra 'B', seguido de una secuencia de 0's y 1's; esto es dar un valor en código binario.
PORTA y cualquier otro registro, son variables de tipo byte; y como un byte tiene 8 bits (bit: cada 1 o 0 de la secuencia), por lo tanto, la secuencia siempre debe ser de ocho cifras (en este caso, ocho "bits"). Un bit en '1', es un pin encendido (en HIGH).
Ahora observa el comentario de abajo. Después de "PORTA", verás que hay números debajo de los ya mencionados "bits"; estos números indican la posición de cada cifra. Esto será útil cuando vayas a consultar en la referencia que te acabo de dar en un enlace (link).

Ejemplo de cómo hacerlo

Digamos que quieres encender el pin 26; y apagar el 22, 23, 24 y 25. En vez de hacerlo con digitalWrite uno por uno (y además de que es más lento); lo mejor sería hacer todo al mismo tiempo, ¿no crees?.
Bien, ahora si vamos el hilo; veremos lo siguiente:

22 PORTA 0
23 PORTA 1
24 PORTA 2
25 PORTA 3
26 PORTA 4
27 PORTA 5
28 PORTA 6
29 PORTA 7

Podremos notar que el registro "PORTA" maneja todos los pines que necesitamos controlar, por consiguiente, podremos controlarlos todos en una sola línea de código (y muy pocos ciclos de la CPU).
Como sólo queremos encender el pin 26, el resto estarán apagados (en cero). Ahora notaremos que en el pin 26 dice "PORTA 4"; este ''4' significa la posición del bit que controla este pin. Ese bit es el que hay que poner en '1'. Por esta razón, es que la línea de código debe quedar así:

PORTA = B00010000;

Ahora explicado:

// Encender solamente el pin 26, o sea, el bit 4 del registro "PORTA"
PORTA = B00010000;
// PORTA 76543210 no es por nada que coloqué estos números

Si crees que la explicación ha sido un poco larga, es para poder ayudarte con el tema. En el sitio web de Arduino también tienen documentación sobre esto, pero está en inglés y sin información para el Arduino Mega.

Espero haber hecho un buen trabajo; sino, postea tus dudas... ¡sin pena! :smiley: :wink:

Lucario, muchas gracias por la explicación, ha estado excelente. Ya mismo la pondré en práctica y les compartiré el resultado o las dudas, lo que surja primero.

Bueno mas alla de lo que lucario aporta y mejorará notablemente tu perfomance de multiplexado imagina este problema.
5 digitos. Dime a que frecuencia crees que debes referezcar cada digito para no ver parpadeo?
El ojo humano necesita unas 15 fps osea 15Hz, Debes poder actualizar cada digito a ese ritmo o verás parpadeo.
No se si tu trabajo no permite usar librerías o es un ejercicio personal, pero existe una librería que resuelve tu problema de un plumazo.
Esta libería usa un timer, el timer se encarga del refrezco de los digitos de manera eficiente y quitando trabajo a la CPU como ahroa haces.
Si quieres usar la librería SevenSegments ve al link que te sugiero.
de lo contrario continúa por el camino sugerido por Lucario.

Hola, he aplicado la enseñanza de Lucario y logré mejorar muchísimo el código. Quedando como en el archivo adjunto.

Surbyte, el proyecto es personal y por desconocimiento no usé librerías, haré una nueva versión con lo que me indicas y ya compartiré mi resultado. Muchas gracias por enseñarme otra posibilidad!!! :wink:

via Imgflip GIF Maker

Codigo Mejorado.txt (14.1 KB)

Ese dígito es el que necesitabas refrescar más rápidamente, cierto? De ser así, se nota que ahora el multiplexado se ve mejor.

El parpadeo supongo que ha de ser porque la tasa de refresco y los cuadros por segundo de la videocámara están cerca a empatar o a ser múltiplo uno del otro.

Aún se nota un poco el parpadeo pero ya es completamente legible, muchas gracias!!! Sin embargo ando leyendo la librería del 7 segs a ver si logro dejarlo sin parpadeo aparente.

EliArt:
Aún se nota un poco el parpadeo

Creo que el delay es demasiado largo para el multiplexado.

Primero prueba con delay(1). Si aún así notas parpadeo, ya lo tendrás que hacer a escala de microsegundos (1 milisegundo = 1000 microsegundos). Así:

delayMicroseconds(500);

Gracias Lucario, efectivamente con 1 mSegs mejora bastante.

La curiosidad por SevSegs me tiene aún frente al pc sin embargo no sé si mis displays de Catodo Común conectados como muestro en la imagen me sirvan con la librería, pues veo que cambia si tiene transistores, resistencias, ánodo o catodo común.

He utilizado la opción NP_COMMON_CATHODE pero aún no termino el código para probar y pegarlo.

Por favor, Eli, pon las imágenes como se recomienda en las normas del foro.

Gracias!

Gracias Hector por el recordatorio, espero haya quedado bien la modificación ya.

Como no? SevenSegs es completmente configurable. Catodo o Anodo común. Y los pines que decidas usar.
Mira los ejemplos y ve avanznado paso a paso.
Busca en Search y verás otros ejemplos hechos con todas las configuraciónes posibles.
Aca un ejemplo con dos digitos

Aprende a buscar en Google: arduino sevseg y obtendrás resultados de todo tipo.

Aca te dejo un código con 5 displays

#include "SevSeg.h"

SevSeg sevseg; //Initiate a seven segment controller object

void setup() {
    byte numDigits     = 5;
    byte digitPins[]   = {2, 3, 4, 5, 6};
    byte segmentPins[] = {7, 8, 9, 10, 11, 12, 13, A0};
    sevseg.begin(COMMON_CATHODE, numDigits, digitPins, segmentPins);
    sevseg.setBrightness(1000);
}

void loop() {
    sevseg.setNumber(31451, 3);
    sevseg.refreshDisplay(); // Must run repeatedly
}

Gracias Surbyte, aún sigo probando el uso de esa librería y aunque ya incluí el código que me muestras no logro que funcione debido a que la selección del display es por nivel alto a la base de un transistor y al configurarlo como COMMON_CATHODE me da salidas GND.

Estoy probando con la configuración NP_COMMON_CATHODE pero aún nada, seguiré leyendo los ejemplos que me compartiste y ya les cuento como me va.

porque no muestras tu esquema de conexionado?

Este es mi esquema, no he conectado los 5 displays aún sino que turno la salida del transistor dependiendo de qué dígito quiero ver.

Sin embargo me he dado cuenta que si cambio el tiempo de delay en la visualización altero por mucho el conteo, tanto así que ya no tengo tiempo real, está muy aleatorio.

Logré eliminar el parpadeo!!!!

Pero pasó algo peor, el tiempo se volvió totalmente inestable.

EliArt:
Pero pasó algo peor, el tiempo se volvió totalmente inestable.

Más lento, más rápido o con velocidad variable?
Si el cronómetro está basado en millis, pareciera que algo está alterando el "Timer" con el que esta función se basa.