Detección de entrada de interruptor en ARM 7 y multiplexación de dos pantallas de siete segmentos

Estoy tratando de multiplexar dos pantallas de siete segmentos, una vez que se detecta una entrada de interruptor.

  1. Si se presiona un interruptor, las pantallas de siete segmentos deben mostrar1
  2. Si se pulsa de nuevo, 2, luego 3y así sucesivamente.

Mi problema surge cuando llego al número, 9ya que se mostrará la siguiente entrada del interruptor 10. 1en el primer SSD y 0en el segundo SSD. A continuación se muestra un fragmento de mi código con respecto a cómo detecto las entradas del 1 al 9.

   int k = 0;
   unsigned int SW1;

   PINSEL0 = 0;                             //Set P0.0 - P0.15 as GPIOS
   PINSEL1 = 0;                             //Set P0.16 - P0.31 as GPIOS
   IO0DIR  = 0xFFFFF9FF;                //Setting  P0.9 as input for SW1     

    while (1)
   {

    SW1 = IO0PIN & 0x00000200; //switch connected on P0.9
    if ( SW1 != 0x00000200 )  //when pressed        
      {
        k++;                
        IO0SET = T1;
        IO0SET = T2;

        if (k == 1){
        IO0SET = T1;               //switching on left seven segment display
        IO0CLR = T2;               //clearing the right seven segment display 
        IO0CLR = a;
        IO0SET = b;
        IO0SET = c;
        IO0CLR = d;             
        IO0CLR = g;                //displaying number 1
        IO0CLR = e;
        IO0CLR = f;
        small_delay();
        }       
       }

Estoy siguiendo la misma estructura para la décima pulsación del interruptor:

        else if (k == 10){ 
        IO0CLR = 0x000000FF;        //turn off all the segments as well as both common anodes. Then your outputs will be ready for the new segment patterns to be set up.
        IO0SET = T2;                //switching on first display                            
        IO0SET = b;                     
        IO0SET = c;                 //displaying number 1         
        small_delay();              //calling short delay           
        IO0CLR = 0x000001FF;        

        IO0SET = a;
        IO0SET = b;
        IO0SET = c;             
        IO0SET = d;                 //displaying number 0
        IO0SET = e;
        IO0SET = f;
        IO0SET = T1;    
        small_delay();
        IO0CLR = 0x000000FF;}

El problema con el segundo código que proporciono es que la pantalla de siete segmentos solo se muestra 10una vez. Es decir, primero 1se muestra en la pantalla izquierda y luego 0en la segunda pantalla según sea necesario; sin embargo, esto se realiza solo una vez y no continúa hasta que se detecta la siguiente entrada del interruptor.

Una solución a este problema que se me ocurrió es que incluí una declaración while después de if elsetal que:

    else if (k == 10){
        while (k == 10) {   //rest of code

Esto realmente hace lo que quiero que haga, y sigue multiplexando ambos siete segmentos, mostrando el número 10, sin embargo, el problema en este caso es que no sale del ciclo while para adaptarse a la siguiente entrada del interruptor.

¿Cómo puedo multiplexar ambas pantallas hasta que se detecte la siguiente entrada del interruptor? Cualquier idea/sugerencia sería muy apreciada.

¿Cómo estás llegando a la primera línea? ¿Cuál es el contexto más completo aquí?
Edité mi pregunta, mostrando dónde está conectado el interruptor 1. Espero que esto ayude.
@Andrew brindó una excelente solución y una mucho mejor que la que estoy tratando de implementar aquí. Buscaré probar algo por el estilo. Sin embargo, estaba buscando una solución dentro de mis declaraciones if else, ya que pensaría que mi problema se resolvería fácilmente sin el uso de funciones.
¿Quieres contar hasta 99 y luego volver a 0? Y no estoy seguro de por qué no te gusta el enfoque de Andrew lo suficiente como para implementarlo. ¿Estás atascado en algún aspecto?
@jonk, lamentablemente, todavía no soy tan competente en lenguaje C y aún no he cubierto las funciones por mi cuenta del libro del que estoy aprendiendo. Por lo tanto, deseaba implementar mi solución utilizando métodos básicos si no. Tampoco necesariamente hasta 99, pero eso sería suficiente. Quiero poder multiplexar 2 SSD en una entrada detectada desde un interruptor. Hasta qué número quiero contar no es realmente relevante. Acabo de ver tu respuesta, gracias por tu sugerencia.

Respuestas (3)

Dos opciones:

La forma correcta de hacer esto sería que su código de conteo solo cuente, no configura la pantalla, establece una variable. Luego tiene código ejecutándose en una interrupción de temporizador que lee la variable de conteo y muestra el número. Si el número es superior a 9, este temporizador puede cambiar entre dígitos según sea necesario.

La segunda forma es establecer solo un dígito cada vez a través de su ciclo

int digitToDisplay = 0; // track which digit we need to display next
while (true) {          // loop forever 
  if (buttonPress())      // count button presses
    count++;

  // no need to do this every time, could be only when count changes but I'm being lazy
  int topDigit = count / 10;       // split value into separate digits.
  int bottomDigit = count % 10;      

  setDisplayOff();                // avoid glitches by turning all LED pins off

  displaySelect(digitToDisplay);  // select the display digit to drive
  if (digitToDisplay == 0)         
    setDisplay(bottomDigit);      // set the pins to display a number
  else
    setDisplay(topDigit);

  if (++digitToDisplay == 2)      // next time we display the other digit
    digitToDisplay = 0;
}

He asumido algunas funciones, bool buttonPress(void)que devuelve tru si se ha presionado el botón, void setDisplay(int value)que activa los pines IO correctos para mostrar el valor dado de 0 a 9 y void setDisplayOff(void)que apaga todos los LED.
Al establecer las líneas de E/S en una función separada, mantiene el comando de cambio grande y desordenado (o si... más si... más si...) lejos de su lógica central, esto hace que la lógica sea mucho más fácil de seguir ya que ahora todo cabe en la pantalla al mismo tiempo.

Actualización:
en caso de que no le gusten las funciones y los comandos de cambio se deba a la falta de familiaridad con su uso, aquí están las definiciones de funciones. Estos deben ir antes de main() o debe declararlos por adelantado y luego colocar el código después de main(). También moví la selección de pantalla a una función para que todo el IO esté fuera del código principal.
Deberá completar los valores restantes del interruptor, pero debería ser bastante obvio cómo.

void setDisplay(int value) {
  IO0CLR = 0x000000FF; // all pins low (probably redundant but best to be safe.)
  switch (value) {
    default:
        // outside the allowed range. Ignore it. (or display an E I suppose)
      break;
    case 0:
      IO0SET = a;
      IO0SET = b;
      IO0SET = c;             
      IO0SET = d; 
      IO0SET = e;
      IO0SET = f;
      break;
    case 1:
      IO0SET = b;                     
      IO0SET = c;
      break;
    case 2:
      IO0SET = a;
      IO0SET = b;
      IO0SET = d; 
      IO0SET = e;
      IO0SET = g;
      break;
    }
}

void displaySelect(int digitToDisplay) {
  if (digitToDisplay == 0) { // right hand digit
    IO0CLR = T1;  // always clear first.
    IO0SET = T2;           
  } else {
    IO0CLR = T2;  // always clear first.
    IO0SET = T1;           
  }
}


void setDisplayOff(void) {
  IO0CLR = 0x000000FF;
}


// detect button press with basic de-bouncing
// button must have been down exactly 5 calls to this function to return true.

int buttonPress(void) {
  static int downCount= 0; // static = value isn't lost between calls.
  //switch connected on P0.9
  int currentButton = ((IO0PIN & 0x00000200) == 0x00000200); 

  if (currentButton) { // button is down
    if (downCount == 5) { // button has been down for 5 counts
        downCount++;      // increase the count so we don't trigger next time
        return 1;
    } else {              // some count other than 5
      if (downCount < 5)  // increase if it's less than 5
         downCount++;
    }
  } else  // button is up
    downCount=0; 

  return 0;
}
Gracias por los buenos consejos y sugerencias. Este método es mucho mejor que el que estoy usando y buscaré implementar algo por el estilo. Sin embargo, ¿hay algún otro método que pueda implementarse en esta if elseestructura desordenada para evitar que el SSD se apague una vez, después de haber mostrado los dos dígitos una vez? (sin el uso de funciones en este caso)
Siempre puede poner la instrucción if para configurar los LED en lugar de la llamada de función. Si tuviera que hacer eso, agregaría int valueToDisplay; if (digitToDisplay == 0) valueToDisplay = bottomDigit; else valueToDisplay = topDigit;y luego usaría if (valueToDisplay == 0) <set IO for 0> else ...para configurar el IO, eso evita la necesidad de tener el mismo código allí dos veces. Y realmente debería usar un interruptor en lugar de una pila de if, es mucho más limpio para este tipo de cosas.
Estoy tratando de implementar esto, sin embargo, sigo atascado en el error: called object type int is not a function or function pointeren la tercera línea:if (buttonPress())
¿Definiste esa función? Debe devolver verdadero cuando se presiona el botón. Esto podría ser tan simple como int buttonPress(void) { return (IO0PIN & 0x00000200); }si tuviera un botón de hardware antirrebote. Agregué una versión más larga con software que elimina el rebote del código anterior.
Hola, Andrew, perdón por molestarte con esto. Tengo algunos pequeños problemas con el software antirrebote. No logro implementar este código para incrementar un contador. Por ejemplo: if (buttonPress()) count++;un código tan simple no funciona. Debo estar pasando por alto algo por mi parte, si el código que proporcionó funcionó para usted. EDITAR: el interruptor y las pantallas de 7 segmentos funcionan correctamente (hardware)
¿Intentó reemplazar la función de presionar el botón con la del comentario anterior sin eliminar rebotes para verificar que la eliminación de rebotes es la causa del problema? Existe una gran posibilidad de que haya un error en el código, no lo he ejecutado, ni siquiera lo he ingresado en un compilador para verificar la sintaxis, pero no puedo ver nada obviamente incorrecto en él. ¿Tu botón está activo alto o bajo? El código asume alto, si es bajo, entonces debe cambiar == 0x00000200a == 0pero aún debería haberse activado cuando soltó el botón.
También recuerde que si mantiene presionado el botón, solo recibirá una sola llamada a buttonPress() que devuelve verdadero, no seguirá siendo verdadero todo el tiempo que el botón esté presionado.
He realizado pruebas simples usando un LED. Si lo uso int buttonPress(void) { return (IO0PIN & 0x00000200); }, el LED se enciende durante todo el tiempo que se presiona el interruptor. Bien. Si utilizo el código provisto para el software que elimina el rebote, el LED solo se enciende durante una fracción de segundo (lo suficiente para que yo lo vea), lo que creo que también funciona como debería. Probaré más usandocount++;
Parece que todo funciona como se esperaba entonces. Solo quería que el conteo aumentara una vez por botón presionado, no contar rápidamente todo el tiempo que el botón está presionado, ¿no es así?
Tienes razón, está funcionando correctamente. Es posible que haya redactado mis inquietudes incorrectamente, pero según mi pregunta original, cuando presiono el interruptor, el SSD muestra 01 solo durante un período de tiempo extremadamente corto (cuando se presiona el interruptor) y luego regresa a 00. Al principio pensé que se trataba de un problema de eliminación de rebotes, pero resultó que no lo es. El problema rodea mi otra parte de mi codificación donde necesito mostrar 01 incluso después de que se haya soltado el interruptor, y luego mostrar 02 si se presiona nuevamente. Gracias por su ayuda en este problema de eliminación de rebotes.

Para las pantallas LED, prefiero asegurarme de que a cada LED se le dé tiempo al menos 100 veces por segundo. Entonces, en este caso, me gustaría que su rutina de "pequeño retraso" se tratara de 5 EM .

También uso una máquina de estado, documentada en EE.SE aquí , para eliminar los rebotes de los interruptores.

Con eso en la mano:

int main() {
    unsigned int previous, temp;
    unsigned int state= 0;
    unsigned int current= 0;
    unsigned int debounced= 0;
    unsigned int prior_debounced= 0;
    unsigned int ones_digit= 0;
    unsigned int tens_digit= 0;
    unsigned int multiplex= 0;
    IO0CLR= T1;
    IO0CLR= T2;
    for ( ; ; small_delay() ) {

        /* debounce the switch */
        previous= current;
        current= (IO0PIN & 0x00000200);
        temp= (previous ^ current) | state;
        prior_debounced= debounced;
        debounced= (debounced & temp) | (current & ~temp);
        state= previous ^ current;

        /* increment the display value each time a button is pressed */
        if ( debounced != prior_debounced && debounced != 0 ) {
            if ( ++ones_digit > 9 ) {
                if ( ++tens_digit > 9 ) tens_digit= 0;
                ones_digit= 0;
            }
        }

        /* multiplex */
        IO0CLR= T1;
        IO0CLR= T2;
        if ( multiplex == 0 ) {
            set_segments( ones_digit );
            IO0SET= T2;
        } else {
            set_segments( tens_digit );
            IO0SET= T1;
        }
        multiplex= 1 - multiplex;
    }
    return 0; /* never gets here */
}

Por supuesto, no tengo forma de verificar el código anterior. Y es muy posible que me perdí un detalle. No sé cómo configura sus opciones de segmento A a F a partir de un valor de dígito binario, así que simplemente usé una función "set_segments ()" que supuestamente logra esto. Deberá reemplazarlo con el código apropiado. (Sin embargo, su código especifica cómo habilitar cada dígito para que se muestre. Así que lo copié, tal como lo entendí).

Hola Jonk, ¿serías tan amable de explicarme if ( multiplex == 0 ) ? ¿ Dónde multpilexse está configurando antes en el programa? No estoy muy seguro de cómo cambia de 0a ``1`
@Rizzo buena captura!!! Se agrega ahora.

Es posible que desee buscar una rutina rápida que publiqué sobre la conducción de LED de 14 segmentos. El proceso básico es el mismo.

  1. Apague la pantalla actual.
  2. Envía datos de segmento al led.
  3. Encienda el siguiente dígito.

Puede escribirlo de forma genérica para que el código sea útil para todos los mcus. También desea llamarlo desde un temporizador Isr para que la pantalla se actualice a intervalos efervescentes.

editar: aquí está el enfoque anterior implementado en un lpc2106:

//update the display
void led7_display(void) {
    static uint8_t dig=0;         //current digit to be displayed

//blanking
DIG_OFF(DIG_0 | DIG_1 | DIG_2 | DIG_3 | DIG_4 | DIG_5 | DIG_6 | DIG_7);     //turn off all digits
SEG_OFF(SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G | SEG_DP);    //turn off all segments

//update segment data
SEG_ON(led7_font[lRAM[dig]]); //update the segment data

//turn on the current digit, and advance to the next
//expand to support more digits
//set dig to 0 to force an earlier return - to support fewer digits
switch (dig) {
    case 0: DIG_ON(DIG_0); dig = 1; break;
    case 1: DIG_ON(DIG_1); dig = 0; break;      //only need to support 2 digits
    case 2: DIG_ON(DIG_2); dig = 3; break;
    case 3: DIG_ON(DIG_3); dig = 4; break;
    case 4: DIG_ON(DIG_4); dig = 5; break;
    case 5: DIG_ON(DIG_5); dig = 6; break;
    case 6: DIG_ON(DIG_6); dig = 7; break;
    case 7: DIG_ON(DIG_7); dig = 0; break;      //round off to digit 0 on the next round
    default: DIG_ON(DIG_0); dig = 1; break;     //default to digit 0
}

}

Notarás que sigue exactamente la misma lógica. tal como está, el código admite 8 dígitos, pero se puede expandir en este chip en particular para admitir hasta 32 dígitos. el código aquí solo admite dos dígitos.

así es como funciona en la simulación: muestra un número fijo de 12 -> solo se muestra '2' debido a la multiplexación.

ingrese la descripción de la imagen aquí

No ejecuté led_display() en un temporizador isr por simplicidad aquí, pero en un proyecto real, debería hacerlo.

como puede ver, el código es inmensamente portátil: no hay forma de saber si el código se ejecuta en una mcu en particular. de hecho, el código se ejecutó originalmente en un PIC de 8 bits y lo cambié ligeramente para que se ejecutara en un ARM7 de 32 bits.

en un PIC de 8 bits, el código se compila en unos 100 bytes con XC8 en modo libre.

edición 2: aquí está el mismo código ejecutándose en un attiny2313 bajo que muestra un número 2313.

ingrese la descripción de la imagen aquí

el código permite la conexión arbitraria de los pines mcu a los pines led, lo que es genial para los chicos de diseño.

y en la medida en que haya pines sin usar en un puerto, sus operaciones no se verán obstaculizadas por el código, es decir, puede usarlos para otra cosa.

todo lo que hace el usuario es incluir los archivos .h/.c en el proyecto, especificar la conexión y llenar el búfer de visualización lRAM[] con los números que se mostrarán y el módulo hace sus propias cosas, disparar y olvidar.

Oh, el código es compatible con los LED de ánodo común y de cátodo común: los LED del lpc2106 son de tipo de cátodo común y los de la simulación attiny son de tipo de ánodo común.

editar 3:

el controlador led anterior permite la asignación arbitraria de pines led a pines mcu, siempre que estén en el mismo puerto.

aquí hay una versión revisada donde los pines LED se pueden conectar a pines totalmente diferentes en diferentes puertos, completamente arbitrario.

sigue el mismo proceso que el controlador anterior. aquí está la simulación donde se ejecuta en un pic16f1936 que conduce una pantalla LED de ánodo común.

ingrese la descripción de la imagen aquí

dannyf, el problema con mi código es que no puedo hacer que la multiplexación continúe hasta que el interruptor haya detectado una entrada. Logré multiplexar dos SSD sin cambiar la entrada con éxito.