Multiplexación de dos pantallas de 7 segmentos (problemas de imágenes fantasma)

Actualmente estoy trabajando en un marcador simple usando dos pantallas de 7 segmentos, un registro de desplazamiento (74HC595N), dos transistores PNP (2N3906) y un arduino uno.

Cada pantalla de 7 segmentos es un ánodo común, sin embargo, una pantalla es azul con un voltaje directo de ~3.3V, la otra pantalla es roja y tiene un voltaje directo de ~2V.

Estoy usando resistencias limitadoras de corriente de 220 ohmios en serie con el registro de desplazamiento y los cátodos de los LED. (Sospecho que esto podría ser parte de mi problema, ya que cada pantalla tiene una caída de voltaje diferente en los LED).

Estoy intentando multiplexar las pantallas, pero tengo problemas con las imágenes fantasma. Estoy usando timer1 en el arduino para facilitar este comportamiento.

He configurado el temporizador con el siguiente código:

// Setup TIMER2
/* First disable the timer overflow interrupt while we're configuring */
TIMSK2 &= ~(1<<TOIE2);

/* Configure timer2 in normal mode (pure counting, no PWM etc.) */
TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
TCCR2B &= ~(1<<WGM22);

/* Select clock source: internal I/O clock */
ASSR &= ~(1<<AS2);

/* Disable Compare Match A interrupt enable (only want overflow) */
TIMSK2 &= ~(1<<OCIE2A);

/* Now configure the prescaler to CPU clock divided by 128 */
TCCR2B |= (1<<CS22)  | (1<<CS20); // Set bits
TCCR2B &= ~(1<<CS21);             // Clear bit

/* We need to calculate a proper value to load the timer counter.
 * The following loads the value 131 into the Timer 2 counter register
 * The math behind this is:
 * (CPU frequency) / (prescaler value) = 125000 Hz = 8us.
 * (desired period) / 8us = 125.
 * MAX(uint8) + 1 - 125 = 131;
 */
/* Save value globally for later reload in ISR */
tcnt2 = 5; 

/* Finally load end enable the timer */
TCNT2 = tcnt2;
TIMSK2 |= (1<<TOIE2);

En el desbordamiento del temporizador, se ejecuta la siguiente función:

ISR(TIMER2_OVF_vect){

 // Turn off the active display
  if(active_display == 0){
    digitalWrite(BLUETRANS, LOW);
  }
  else{
    digitalWrite(REDTRANS, LOW);
  }

  delayMicroseconds(1000);

  // Toggle Display
  active_display ^= 1;

  // Shift out screen bits
  if(active_display == 0){
    displayDigit(blueByte);
  }
  else{
    displayDigit(redByte);
  }

  // Turn display back on
  if(active_display == 0){
    digitalWrite(BLUETRANS, HIGH);
  }
  else{
    digitalWrite(REDTRANS, HIGH);
  }
  // Reload the timer
  TCNT2 = tcnt2;
}

No entiendo completamente el uso de Timer2, por lo que cualquier ayuda también sería apreciada. Entiendo que el temporizador cuenta hasta el valor de desbordamiento especificado y luego ejecuta el ISR. Sin embargo, mis intentos de reducir la frecuencia de actualización de las dos pantallas parecen hacer que mi programa no responda.

Creo que estoy multiplexando correctamente la pantalla:

  1. Desactivar pantalla activa
  2. Desplazar bits al registro de desplazamiento para otra visualización
  3. Encender otra pantalla

Desafortunadamente, parece que no puedo reducir el efecto fantasma en la pantalla roja (el efecto fantasma no parece ocurrir en la pantalla azul).

¡Cualquier ayuda sería muy apreciada!

Entiendo que es posible que no haya incluido toda la información relevante, ¡así que pregunte y recibirá!

¡Gracias!

EDITAR 1

Gracias a Justing, ahora entiendo mucho mejor cómo funciona Timer2 en Arduino. Gracias por eso.

Desafortunadamente, incluso a 60 Hz, puedo ver un efecto fantasma significativo, junto con un parpadeo desagradable y perceptible a medida que alterna entre las pantallas. Con mi nuevo conocimiento de Timer1, pude aumentar con éxito la frecuencia de actualización hasta 244 Hz. Mi circuito actual sigue este diseño básico:

Mi diseño de circuito básico.

Como se indicó anteriormente, mis resistencias limitadoras de corriente son de 220 ohmios. ¿Podrían los diferentes voltajes directos entre la pantalla AZUL de 7 segundos y la pantalla ROJA de 7 segundos estar causando este problema de efecto fantasma? Una vez más, el único dígito que experimenta este problema es la pantalla ROJA, la pantalla con el voltaje directo más bajo (2 V [rojo] frente a 3 V [azul]). Si esta es realmente la causa, ¿el uso de 8 resistencias adicionales para la segunda pantalla solucionaría este problema? Esperaba poder salirme con la mía con menos resistencias para ahorrarme algo de soldadura en el futuro, pero si soluciona este problema de visualización, valdría la pena.

¿Alguna idea más chicos? ¡Gracias!

EDITAR 2

Estoy publicando mi función displayDigit:

void displayDigit(byte screen){
    // Shift data into the shift register
    digitalWrite(latchPin, 0); // LATCH LOW TO SHIFT
    shiftOut(dataPin, clockPin, MSBFIRST, screen);  
    digitalWrite(latchPin, 1); // ENABLE SHIFTED BITS
}

EDITAR 3

¿Podrían mis transistores PNP 2N3906 ser los culpables? Me parece que si bien los transistores permiten que la pantalla se encienda muy rápidamente, apagarlos ocurre mucho más lento. Permitiendo que el dígito rojo "vea" el valor de los dígitos anteriores. Sin embargo, esto no explica por qué no hay imágenes fantasma en la pantalla azul. ¿Puedo "levantar" fácilmente el ánodo de la pantalla con una resistencia para ayudar al transistor en su estado "apagado"? "Apagado" aquí es técnicamente Vcc (5V) debido al diseño de ánodo común. La pantalla funciona cuando el ánodo se pone a tierra.

EDICIÓN FINAL

Agradezco a Myforwik por proporcionar la respuesta más breve y también más útil a mi pregunta. Mi ISR ​​ahora se ve así:

ISR(TIMER2_OVF_vect){

 // Turn off the active display
  if(active_display == 0){
    digitalWrite(BLUETRANS, HIGH);
  }
  else{
    digitalWrite(REDTRANS, HIGH);
  }

  delayMicroseconds(1000);

  // Toggle Display
  active_display ^= 1;

  // Shift out screen bits
  if(active_display == 0){
    displayDigit(blueByte);
  }
  else{
    displayDigit(redByte);
  }

  // Turn display back on
  if(active_display == 0){
    digitalWrite(BLUETRANS, LOW);
  }
  else{
    digitalWrite(REDTRANS, LOW);
  }
  // Reload the timer
  TCNT2 = tcnt2;
}

Resulta que estaba pasando por alto algunas de las partes más fundamentales de mi código. Ambas pantallas estaban encendidas en un instante dado, atormentándome con este problema de imágenes fantasma que en realidad no era un problema de imágenes fantasma en absoluto.

Muchas gracias a Justing, quien proporcionó posiblemente la mejor explicación que he visto sobre cómo configurar el temporizador en el Arduino. Mike DeSimone y Oli Glaser también ayudaron mucho cuando se trataba de solucionar problemas en mi circuito. ¡No puedo expresarles cuánto aprecio su ayuda!

Si bien mi problema real fue extremadamente básico, ¡espero que todas las respuestas que se encuentran en esta página puedan ser útiles para alguien, en algún lugar en el futuro!

¡Gracias de nuevo!

No creo que las diferentes resistencias cambien mucho (tal vez lo hagan más brillante o más tenue, sin "efecto fantasma"). Intente ajustar la frecuencia de actualización total a aproximadamente 100-150 Hz (ISR sucede con tanta frecuencia).
deshacerse de delayMicroseconds(1000); en el ISR .... No importa, me di cuenta de que no tiene registros de desplazamiento de bloqueo
Eliminé 'delayMicroseconds (1000) de mi ISR ​​y el efecto fantasma se redujo en brillo pero no a una cantidad aceptable. También ajusté la frecuencia de actualización a 150 Hz y ahora las pantallas se muestran normalmente. 120 Hz era un poco lento para mis ojos. Ahora, si tan solo pudiéramos resolver este persistente problema de imágenes fantasma....
¿Puedes explicar a qué te refieres con ghosting? ¿Qué tal si simplemente hacemos un bucle sin interrupciones en su función principal que alterna con un retraso en el medio y no hay otras interrupciones ni ningún otro código en ejecución?
retraso dentro de un ISR? eso está mal, tal vez hacer una con estados oscuros y estados claros.
Tenía el mismo problema, sincronización incorrecta. Primero borre la pantalla antes de cualquier nuevo ciclo de actualización y espere 200 ms. Después de esto, muestre el nuevo ciclo y espere otros 200 ms.

Respuestas (5)

El problema es que tienes encendido y apagado mezclado.

El transistor pnp estará en corte cuando la salida sea alta, no baja.

Por lo tanto, está encendiendo ambas pantallas durante la espera de 1000us.

Hmmm, ese es un buen punto :-) No puedo creer que me lo haya perdido... +1
¡Asombroso! No puedo creer que yo mismo haya pasado por alto esto. Desearía poder darles a todos aquí el crédito por la respuesta a mi problema de imágenes fantasma. Aprendí mucho sobre transistores, temporizadores e interrupciones durante el último día. ¡GRACIAS A TODOS!
Siempre que haga circuitos como este, es mejor eliminar todo el código y comenzar con pruebas simples: encienda una salida y no haga nada más. Luego simule la interrupción con retardo de encendido retardo de apagado en un ciclo while(1). Elimine los problemas de hardware y luego pase al software.

Para complementar la respuesta de Oli, continuaré y revisaré el código de configuración de Timer2. Asumiré que el Arduino está funcionando a un reloj de 16 MHz (la búsqueda en Google reveló que esto es probablemente el valor predeterminado) y el chip es un ATMEGA328P .

TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
TCCR2B &= ~(1<<WGM22);

Como dice el comentario, esta parte desactiva las características especiales de este contador (PWM, PWM rápido, etc.). Se puede ver que los bits WGM2 se dividen entre los registros de control A y B:

tccr2a tccr2b

Se puede ver que los valores iniciales de estos registros al reiniciar son todos ceros de todos modos, por lo que el código anterior realmente ni siquiera es necesario.

A continuación configuramos la fuente del reloj:

ASSR &= ~(1<<AS2);

registro assr como 2 bits

El registro y la descripción de bit anterior nuevamente nos muestran borrando un bit, esta vez el bit AS2. Esto indica que el temporizador contará desde el reloj de E/S del reloj principal del sistema. La alternativa se describe como un cristal externo que podría usarse. (puede ser más lento, menos potencia, mejor precisión)

La siguiente línea inhabilita una interrupción de comparación de salida:

 /* Disable Compare Match A interrupt enable (only want overflow) */
 TIMSK2 &= ~(1<<OCIE2A);

ocie2a

La descripción describe lo que se está deshabilitando. ¡Por qué se molestó en deshabilitar solo la comparación A y no la comparación B (que de todos modos están desactivadas de forma predeterminada en primer lugar) me supera! ¡Próximo!

 /* Now configure the prescaler to CPU clock divided by 128 */
 TCCR2B |= (1<<CS22)  | (1<<CS20); // Set bits
 TCCR2B &= ~(1<<CS21);             // Clear bit

¡Es hora de configurar la velocidad del reloj del temporizador! Esto preescalará (dividirá) el reloj IO que seleccionamos anteriormente para alimentar el temporizador. Esto permitirá una velocidad de reloj más baja que es más práctica para nuestra aplicación para el temporizador.

tccr2b preescalador tccr2b

La tabla anterior muestra que borrar los bits CS20 y CS22 y configurar CS21 pero preescalará el reloj IO en 128. Esto significará que el reloj que alimenta el temporizador 2 será:

 16MHz/128 = 125 kHz

lo que significa un pulso de reloj cada:

 1/125kHz = 8us

que desbordaría un temporizador de 8 bits cada:

 8us*2^8 = 2ms -> 488 Hz

¡que es mucho más rápido que los 50 Hz que sugiere Oli!

Lo primero que podríamos intentar sería aumentar el prescaler desde arriba a 1024 (el máximo):

 TCCR2B |= (1<<CS22) | (1<<CS21) | (1<<CS20); // Set bits            

El uso de este preescalador daría un intervalo de interrupción de:

 16MHz / 1024 = 15.625 kHz 

dando un pulso de reloj de:

 1/15.625kHz = 64us

Desbordando el temporizador cada:

 64us * 2^8 = 16.384 ms --> 61 Hz

Esto da un valor muy cercano a la recomendación de Oli de 50 Hz y debería brindarle una pantalla de aspecto razonable.

Si aún necesita ajustar la pantalla, tiene dos opciones:

  1. Incrementa una variable cada interrupción. Una vez que esta variable alcance un valor determinado, realice las funciones de interrupción deseadas (actualice la pantalla) y vuelva a establecer la variable en cero. Esto le dará la posibilidad de realizar la interrupción de cualquier múltiplo entero del intervalo de desbordamiento.

  2. El intervalo de interrupción se puede reducir desde el valor preescalado cargando previamente el registro del contador del temporizador (tcnt2). Esto significa que el registro del temporizador comenzará a contar en ese valor especificado en lugar de cero. Este valor deberá recargarse en cada ISR. Este método le permite disminuir el intervalo de interrupción en un múltiplo entero del reloj de E/S preescalado que se alimenta al temporizador. (este método no le permitirá aumentar el retraso)

Gracias. Creo que tengo una mejor comprensión del trabajo de estos temporizadores en el Arduino. Desafortunadamente, a 60 Hz, las pantallas parpadean bastante. Cambié el prescaler a 256 y (con una frecuencia de actualización de 244 Hz) las cosas se ven bien. Curiosamente, incluso con la frecuencia de actualización relativamente baja de 60 Hz, todavía noté un efecto fantasma muy prominente en la pantalla roja de 7 segmentos. Ahora me inclino a que mi circuito sea el problema. ¿Hay alguna manera de hacer que "levante" el ánodo común muy rápidamente?
Tiene sentido que 60Hz no sea suficiente. Creo que la declaración de Oli se refiere a una frecuencia de actualización de pantallas individuales. El tuyo está dividido por 2 ya que te estás moviendo entre 2 pantallas. Tal vez intente obtener el tiempo de ISR a aproximadamente 100-150 Hz (usando el registro tcnt2 como lo describí).
intente usar el prescaler 1024 y cargue tcnt2 = 100 cada ISR. debe dar alrededor de 100Hz.
"Actualización de 50 Hz" significa que vuelves a dibujar todo cada 20 ms. Entonces, si le toma 4 pasos completar un ciclo de redibujado, necesitaría tener un paso cada 5 ms. Prefiero frecuencias de actualización de alrededor de 100-200 Hz; reduce los efectos de ilusión extraños cuando la pantalla o mi cabeza se mueven, y no tiene que preocuparse por molestar a otra persona cuyos ojos pueden ver un parpadeo rápido. (Ya sabes, el tipo de persona que ve luces fluorescentes estroboscópicas).
@MikeDeSimone O mientras usas un cepillo de dientes eléctrico :P
@justing: sí, me refería a las pantallas individuales y olvidé duplicar la frecuencia ISR, por lo que 100 Hz (o un poco más, como sugiere Mike) es lo que debería haber dicho (editando ahora)
También +1 para un buen recorrido por la configuración del temporizador.

Parece que su rutina de interrupción se activa demasiado rápido, y más rápido no deja tiempo para ejecutar el código principal, por lo que el uC parecerá no responder.

Para una pantalla típica, una frecuencia de actualización de alrededor de 50 Hz es adecuada para engañar al ojo haciéndole creer que es continuo.
Ajuste su temporizador por un período de alrededor de 10 ms (100 Hz dividido por dos pantallas equivale a una actualización de 50 Hz para cada pantalla) entre interrupciones (si es solo un temporizador de 8 bits, use el preescalador para dividir el reloj un poco más, o si el divisor no lo hace). t vaya lo suficientemente alto, entonces, como dice Mike en los comentarios, use una variable de conteo en el ISR y ejecute el evento cada n interrupciones. O use el temporizador de 16 bits)

EDITAR: está bien, entonces corrigió el tiempo de interrupción a un valor más razonable pero el parpadeo persiste. Por la forma en que tiene el código en el ISR, parece que el período entre el apagado de una pantalla y el encendido de la otra está fijo en el retraso de 1000us de todos modos, siempre que no llame al ISR tan rápido como para hacer el uC no responde, entonces no debería importar demasiado (aunque obviamente demasiado rápido limita el tiempo que tiene en su ciclo principal para realizar otras cosas, así que solo vaya tan rápido como necesite para detener el parpadeo, digamos <400Hz o menos)

De todos modos, habría pensado que el retraso de 1 ms debería ser suficiente para volver a apagar el PNP. Supongo aquí que el pin es una salida push-pull con un impulso razonable para alto y bajo (lo comprobaré en breve)
Así que un par de cosas para probar:

  • Intente cambiar las resistencias base de 2k2 para decir, 470Ω Suponiendo que el pin sea capaz de hacerlo, esto proporcionará un impulso más fuerte para cargar/descargar la capacitancia (¿tiene un cableado/traza largo?)
  • Es posible que el PNP en la pantalla roja tenga "fugas". Compruebe si la pantalla roja alguna vez se apaga por completo con todos los dígitos encendidos en la pantalla azul. Cambiar el transistor no estaría de más de todos modos para asegurarse de esto.

Tendría sentido que las imágenes fantasma no ocurran en la pantalla azul, ya que tendrá un voltaje directo más alto que la pantalla roja, por lo que se apagará más rápido (y se encenderá más lentamente)

Si tiene un osciloscopio, pruebe la base del PNP y las líneas del colector mientras cambia para ver cuánto tarda la transición.

Supuse que este era el caso. Sin embargo, tengo problemas para entender cómo reducir el período a 20 ms. Por lo que entiendo el prescaler funciona como tal: 16MHz/128 = 125000 Hz Que es una vez cada 8uS. Ahora, si quiero que se ejecute cada 20 ms: 20 ms/8us = 2500. Sin embargo, no puedo usar (256 - 2500) para el intervalo de interrupción. ¿Debería considerar usar un temporizador de 16 bits?
Utilice un temporizador de 16 bits o configure el temporizador de 0 a 1 ms (divisor = 125) y haga que actualice la pantalla cada 20 interrupciones. Por cierto, el uso típico es hacer que el temporizador 0 sea una interrupción de tic del sistema a 1000 Hz como esta, y hacer que inicie varios eventos que deben ocurrir cada N tics o lo que sea.

Me gustaría ampliar la respuesta de Oli con respecto a los transistores.

Los BJT, en una configuración de emisor común como la que está usando, son básicamente amplificadores de corriente: toman su corriente base, la multiplican por su h F mi , y sacar esa cantidad de corriente de su colector.

Por lo tanto, su transistor debe poder generar la corriente máxima (todos los segmentos) de 5 V fuera del colector. Dado tu 220 Ω resistencias de segmento y los voltajes directos de 2 o 3 V, obtenemos:

I = V R = V S tu PAG PAG L Y V F V C mi S A T R

I R mi D = 5 V 2 V 0.4 V 220 Ω = 12 metro A

I GRAMO R mi mi norte = 5 V 3 V 0.4 V 220 Ω = 7 metro A

(En mi humilde opinión, estos parecen bastante altos. Por lo general, solo necesito un promedio de 2 mA, que sería 4 mA aquí debido a su ciclo de trabajo del 50%, para iluminar los LED más antiguos, y mucho menos para los LED más nuevos de "alta eficiencia").

Entonces, las corrientes totales son 84 mA para el rojo y 49 mA para el azul. Mirando la hoja de datos 2N3906 , veo un par de problemas potenciales, mirando los gráficos en la página 3:

  • El gráfico de voltaje de saturación del colector-emisor frente a la corriente del colector muestra V C mi S A T inclinando mucho a 50 a 100 mA. Con referencia a las ecuaciones de corriente anteriores, puede ver que las corrientes del segmento cambiarán mucho si el V C mi S A T cambia incluso 0.1 V.
  • El gráfico típico de ganancia de corriente pulsada frente a corriente de colector muestra h F mi descendiendo bastante rápido en el rango de 20-100 mA.

Entonces, averigüemos cuál debe ser su corriente base en el peor de los casos. Supongamos un mínimo h F mi de 30 (de la tabla "Sobre las características" en la página 2, usando I C = -100mA). Eso significa que necesita 2,8 mA de impulso en la base. Usando un V B mi S A T de -1,0 V de la misma tabla:

R = V I = 5 1 2.8 = 1.43 k Ω

Entonces los 2.2 k Ω resistencias no son suficientes (pero son suficientes para típico en lugar de mínimo h F mi ; típico es al menos 75 del gráfico en la página 3) y el 470 Ω La resistencia debe ser bastante fuerte. También verifique que su pin de E / S pueda absorber tanta corriente.

En mi humilde opinión, está poniendo demasiada corriente a través de cada segmento o simplemente está pidiendo demasiado al 2N3906. Realmente desea que su transistor tenga un nivel razonablemente plano. V C mi S A T a lo largo de su rango actual.

Además, al conducir altas corrientes en pantallas de 7 segmentos, he visto efectos de "fantasma" en los que la luz de un segmento simplemente se derrama hacia los segmentos físicamente vecinos. Asegúrese de que esto no le suceda a usted: controle todos los segmentos en una pantalla y uno en la otra, y vea si los 6 segmentos "apagados" muestran diferentes brillos fantasma dependiendo de la proximidad al segmento iluminado.

Personalmente, uso MOSFET para estas aplicaciones. Típico R D S O norte de 1 Ω da una caída de voltaje mucho menor y cambio de corriente frente a segmentos iluminados (siempre y cuando R D S O norte << R S mi GRAMO METRO mi norte T ), la corriente de la compuerta es tan baja que cualquier cosa puede impulsarla (está impulsada por voltaje, no por corriente), y no hay que lidiar con el voltaje de saturación.

además, los 84 mA parecen ser más altos que la corriente máxima para el 74HC595N
Reduje la resistencia a 470 ohmios y aún se produce el efecto fantasma. También he descartado sangrado leve de un segmento al siguiente. Me inclino por la idea de que se le pide demasiado al 2N3906. ¿Alguna recomendación sobre el transistor PNP correcto para usar? Estaría bien usando MOSFETS también. ¡Gracias por toda tu ayuda!

A menudo, para cosas como esta, la multiplexación de LED no requiere una escritura completa en el LED con cada interruptor. Personalmente, bloquearía lo que debe estar en AMBOS 7 segmentos, y luego solo usaría la interrupción del temporizador SOLAMENTE para cambiar el LED que está manejando, y simplemente dejaría que la interrupción se ejecutara libremente. Cargue números nuevos en los pestillos SOLO cuando necesite cambiar lo que hay en los 7 segmentos.

Tener que lidiar con los 7 segmentos completos correctamente con cada conmutador múltiplex parece que está incrustando esta tarea en el nivel incorrecto, lo que genera grandes ineficiencias.

Alternativamente, hay circuitos integrados que hacen esto específicamente para 7 segmentos, como el SAA1064.