Multiplexar dos pantallas de 7 segmentos (problemas de fantasmas)

5

Actualmente estoy trabajando en un marcador simple con 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 utilizando 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, sin embargo, estoy experimentando problemas con el efecto 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 allí sería apreciada también. 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 visualización activa
  2. Desplazar bits al registro de desplazamiento para otra pantalla
  3. Enciende otra pantalla

Desafortunadamente, parece que no puedo reducir el efecto de imagen fantasma en la pantalla roja (la imagen 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!

EDIT 1

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

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

Comoseindicóanteriormente,misresistoreslimitadoresactualessonde220ohmios.¿PodríanlosdiferentesvoltajesdeavanceentrelapantallaBLUE7-segylapantallaRED7-segestarcausandoesteproblemadefantasma?Nuevamente,elúnicodígitoqueexperimentaesteproblemaeslapantallaROJA,lapantallaconelvoltajedeavancemásbajo(2V[rojo]frentea3V[azul]).Siestaeslacausa,¿elusode8resistenciasadicionalesparalasegundapantallasolucionaríaesteproblema?Esperabapodersalirconmenosresistenciasparaahorrarmealgodesoldaduraenelfuturo,perosisolucionaesteproblemadevisualización,valdríalapena.

¿Másideaschicos?Gracias!

EDIT2

EstoypublicandomifuncióndisplayDigit:

voiddisplayDigit(bytescreen){//ShiftdataintotheshiftregisterdigitalWrite(latchPin,0);//LATCHLOWTOSHIFTshiftOut(dataPin,clockPin,MSBFIRST,screen);digitalWrite(latchPin,1);//ENABLESHIFTEDBITS}

EDIT3

¿PodríanculparmemistransistoresPNP2N3906?Meparecequemientraslostransistorespermitenquelapantallaseenciendamuyrápidamente,apagarlosseproducemuchomáslento.Permitirqueeldígitorojo"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 de "apagado"? "Off" aquí es técnicamente Vcc (5V) debido al diseño del ánodo común. La pantalla funciona cuando el ánodo se tira a tierra.

EDICIÓN FINAL

Doy las gracias a Myforwik por proporcionar la respuesta más breve y también la 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 momento dado, plagándome con este problema de fantasmas que no era en absoluto un problema de fantasmas.

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 en la resolución de problemas de mi circuito. ¡No puedo expresarles lo mucho que 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 de utilidad para alguien, ¡en algún lugar en el futuro!

Gracias de nuevo!

    
pregunta Tim Bueno

5 respuestas

8

El problema es que se ha activado y desactivado.

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

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

    
respondido por el Myforwik
3

Para complementar la respuesta de Oli, seguiré adelante 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 de Google reveló que es el valor predeterminado) y el chip es un ATMEGA328P .

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

Como dice el comentario, esta parte deshabilita 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:

Se puede ver que los valores iniciales de estos registros al reiniciarse 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);

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

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

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

Ladescripcióndescribeloqueseestádeshabilitando.¿PorquémemolestóelcódigoparadesactivarsololacomparaciónAynolacomparaciónB(queestándesactivadaspordefectoenprimerlugar)?Siguiente!

/*NowconfiguretheprescalertoCPUclockdividedby128*/TCCR2B|=(1<<CS22)|(1<<CS20);//SetbitsTCCR2B&=~(1<<CS21);//Clearbit

¡Horadeajustarlavelocidaddelrelojdeltemporizador!Estopreescala(dividirá)elrelojIOqueseleccionamosanteriormenteparaalimentareltemporizador.Estopermitiráunavelocidadderelojmásbajaqueesmásprácticaparanuestraaplicaciónparaeltemporizador.

La tabla anterior muestra que se borran los bits CS20 y CS22 y se configura el CS21, pero se preescala el reloj IO en 128. Esto significará que el reloj que alimenta el temporizador2 será:

 16MHz/128 = 125 kHz

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 hasta 1024 (el máximo):

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

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

 16MHz / 1024 = 15.625 kHz 

dando un impulso 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 darle una visualización de aspecto razonable.

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

  1. Incrementa una variable cada interrupción. Una vez que esta variable alcance un cierto valor, realice las funciones de interrupción deseadas (actualizar la pantalla) y vuelva a poner 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 disminuir del valor preescalado precargando 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 mediante un múltiplo entero del reloj de E / S preescalado que se alimenta al temporizador. (este método no le permitirá aumentar el retraso)

respondido por el justing
2

Parece que su rutina de interrupción se está activando demasiado rápido, y más rápido no queda más tiempo para ejecutar el código principal, por lo que la unidad de control de la unidad aparecerá sin responder.

Para una pantalla típica, una frecuencia de actualización de alrededor de 50Hz es adecuada para que el ojo crea que es continuo.
Ajuste su temporizador por un período de alrededor de 10 ms (100Hz dividido por dos pantallas equivale a una actualización de 50Hz para cada pantalla) entre interrupciones (si es solo un temporizador de 8 bits, use el prescaler para dividir el reloj un poco más, o si el divisor no lo hace) t ir 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 fijó el tiempo de interrupción a un valor más razonable pero el parpadeo persiste. De la forma en que tiene el código en el ISR, parece que el período entre el apagado de una pantalla y el de la otra está fijado en el retraso de 1000us de todos modos, siempre que no esté llamando al ISR tan rápido como para que el uC no responde, entonces no debería importar demasiado (aunque obviamente limita demasiado rápido el tiempo que tiene en su bucle principal para realizar otras cosas, así que solo vaya tan rápido como sea necesario para detener el parpadeo, digamos que < 400Hz aproximadamente)

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

  • Intente cambiar las resistencias de base 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 rastro / cableado largo?)
  • Es posible que el PNP en la pantalla roja esté "con fugas". Compruebe si la pantalla roja alguna vez se apaga completamente con todos los dígitos en la pantalla azul. Cambiar el transistor no haría daño de todos modos para asegurarse de esto.

Tendría algún sentido que la imagen fantasma no ocurra en la pantalla azul, ya que tendrá un mayor voltaje hacia adelante que la pantalla roja, por lo que se apagará más rápidamente (y más lentamente)

Si tiene una sonda de alcance, la base de PNP y las líneas del colector mientras cambia para ver cuánto tiempo lleva la transición.

    
respondido por el Oli Glaser
1

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 usas, son básicamente amplificadores actuales: toman su corriente base, la multiplican por su \ $ h_ {FE} \ $ y suben a tanta corriente desde su colector .

Por lo tanto, su transistor debe ser capaz de generar la corriente máxima (todos los segmentos) desde 5 V fuera del colector. Teniendo en cuenta sus resistencias de segmento de 220 \ $ \ Omega \ $ y los voltajes directos de 2 o 3 V, obtenemos:

\ begin {equation} I = \ frac {V} {R} = \ frac {V_ {SUPPLY} - V_ {F} - V_ {CESAT}} {R} \ end {equation}

\ begin {equation} I_ {RED} = \ frac {5 \ mathrm {V} - 2 \ mathrm {V} - 0.4 \ mathrm {V}} {220 \ Omega} = 12 \ mathrm {mA} \ fin {ecuación}

\ begin {equation} I_ {GREEN} = \ frac {5 \ mathrm {V} - 3 \ mathrm {V} - 0.4 \ mathrm {V}} {220 \ Omega} = 7 \ mathrm {mA} \ fin {ecuación}

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

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 del voltaje de saturación del colector-emisor frente a la corriente del colector muestra \ $ V_ {CESAT} \ $ con una pendiente de 50 a 100 mA. Refiriéndose a las ecuaciones actuales anteriores, puede ver que las corrientes de segmento cambiarán mucho si \ $ V_ {CESAT} \ $ cambia incluso 0.1 V.
  • El gráfico de ganancia de corriente pulsada típica frente a colector actual muestra \ $ h_ {FE} \ $ descendiendo bastante rápido en el rango de 20-100 mA.

Entonces, averigüemos cuál debe ser su actual base de caso más desfavorable. Supongamos un \ $ h_ {FE} \ $ mínimo de 30 (de la tabla "En Características" en la página 2, usando \ $ I_C \ $ = -100 mA). Eso significa que necesita 2.8 mA de unidad en la base. Usando un \ $ V_ {BESAT} \ $ de -1.0 V de la misma tabla:

\ begin {equation} R = \ frac {V} {I} = \ frac {5 - 1} {2.8} = 1.43 \ mathrm {k} \ Omega \ end {equation}

Por lo tanto, las resistencias de 2,2 k \ $ \ Omega \ $ no son suficientes (pero son suficientes para típico en lugar de mínimo \ $ h_ {FE} \ $; típico es al menos 75 de la gráfica en la página 3) y la resistencia 470 \ $ \ Omega \ $ debe ser bastante fuerte. También verifique que su pin de E / S pueda acumular tanta corriente.

En mi humilde opinión, o estás poniendo demasiada corriente en cada segmento o simplemente estás pidiendo demasiado al 2N3906. Realmente quieres que tu transistor tenga un \ $ V_ {CESAT} \ $ razonablemente plano en tu rango actual.

Además, al conducir corrientes altas en pantallas de 7 segmentos, he visto efectos de "efecto fantasma" en los que la luz de un segmento simplemente se derrama en segmentos adyacentes físicamente. Asegúrese de que esto no le esté sucediendo a usted: conduzca todos los segmentos en una pantalla y el otro en la otra, y vea si los 6 segmentos "apagados" muestran diferentes brillos de fantasma según la proximidad al segmento encendido.

Personalmente, uso MOSFET para estas aplicaciones. El \ $ R_ {DSON} \ $ de 1 \ $ \ Omega \ $ típico produce una caída mucho más baja de la tensión y un cambio de corriente frente a los segmentos iluminados (siempre que \ $ R_ {DSON} < < R_ { SEGMENTO} \ $), la corriente de la compuerta es tan baja que cualquier cosa puede conducirla (es impulsada por voltaje, no por corriente), y no hay voltaje de saturación con el que lidiar.     

respondido por el Mike DeSimone
0

A menudo, para cosas como esta, la multiplexación de LED no requiere una escritura completa en el LED con cada interruptor. Personalmente, encerrarí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á conduciendo, y simplemente dejar que la interrupción se ejecute libremente. Cargue nuevos números en los pestillos SOLAMENTE cuando necesite cambiar lo que hay en los 7 segmentos.

El hecho de tener que lidiar con el segmento completo de 7 segmentos a la derecha con cada conmutador múltiplex parece que está incrustando esta tarea en el nivel equivocado, lo que genera grandes ineficiencias.

Alternativamente, hay IC que lo hacen específicamente para 7 segmentos, como el SAA1064.

    
respondido por el Scott Seidman

Lea otras preguntas en las etiquetas

Comentarios Recientes

DOTN FIXES ". En su limitada vida de publicación, SOE no ha publicado ninguna nota de parche para SWTOR December Patch 1.8 debido al amplio alcance de red de los problemas de rendimiento y estabilidad que causó ISD Engine. Sin embargo, el 2 de diciembre, cuando se alcanzaron los tiempos de juego normales, ISD Engine se apagó y SOE apagó SWTOR tan pronto como dejó de funcionar. El juego parece haberse bloqueado después de encenderlo, pero no se capturó ningún flash. Algunas imágenes fueron inicialmente enviado... Lees verder