mostrar datos múltiples

0

Quiero mostrar 4 datos diferentes en una pantalla multiplexada de siete segmentos. Puedo mostrar 2 datos durante un cierto intervalo, ahora quiero expandir esto a 4. Cómo hacerlo.

main(void) { 

    InitIO();
    InitTimer2();
    int i;

    while(1) {
        switch(i) {

            case 1:
                input();
                break;

            case 2:
                output();
                break;
        }

        if (0==my_timer) {
            i=(1==i)?2:1;
            my_timer = 30 * 50;  // MUX_FREQUENCY must be < 1092 Hz
        }
    }     
}

// código completo

/* 
 * File:   main.c
 * Author: EmbeddedBuzz
 *
 * Created on December 4, 2014, 7:24 PM
 */
#include <htc.h>
__CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & CP_OFF & BOREN_ON);
#include <stdio.h>
#include <stdlib.h>
#define _XTAL_FREQ 4000000
#define K 0.01f
//Display Configuration
unsigned short shifter, portb_index;
unsigned int digit, number;
 unsigned short portb_array[4];
float i,adc,adc1;
float result,result1;
volatile int my_timer=0;

unsigned short mask(unsigned short num)

{
 switch (num)
 {
  case 0 : return 0x3F;
  case 1 : return 0x06;
  case 2 : return 0x5B;
  case 3 : return 0x4F;
  case 4 : return 0x66;
  case 5 : return 0x6D;
  case 6 : return 0x7D;
  case 7 : return 0x07;
  case 8 : return 0x7F;
  case 9 : return 0x6F;
     case 'I':return 0x06;
    }
}

void interrupt ISR()
{
 PORTC = 0;                           // Turn off all 7seg. displays;
 PORTB =portb_array[portb_index];    // Bring appropriate value to PORTB;
 PORTC = shifter<<4;                     // Turn on appropriate 7seg. display;

 //move shifter to next digit;
 shifter <<= 1;
 if(shifter > 8u)
 shifter = 1;

 //increment portb_index;
 portb_index ++ ;
 if (portb_index > 3u)
 portb_index = 0;                     //turn on 1st, turn off 2nd 7 seg.;
  if (my_timer > 0) my_timer--;
 TMR2 = 0;                            //reset TIMER0 value;
   TMR2IF = 0;
     TMR2IE = 1;       // Turn on TMR2 interrupt                      //clear T0IF, Bit T0IF=0, T0IE=1;
 }
void InitTimer2(void)
{
     T2CON = 0x3C;
/*   Prescale 1:16
 *   Timer off for now
 */
     PR2 = 249;
/*   With prescale 1:16 and at 4MHz clock,
 *   this gives 2ms period.
 */
     TMR2IF = 0;
     TMR2IE = 1;       // Turn on TMR2 interrupt
     GIE = 1;          // Turn on global interrupts
     PEIE = 1;         // Turn on peripheral interrupts
     TMR2ON = 1;       // Start TMR2
}
void InitIO(void)
{

 digit = 0;
 portb_index = 0;
 shifter = 1;
   PORTB = 0;
     TRISB = 0;
     PORTC = 0;
     TRISC = 0;
     ADCON1 =0x60;
     ADCON0 = 0xC1;
     PORTA = 0;
     TRISA = 0x00;          // Inputs for ADC

}
void UpdateDisplay(int display)
{
     number=display;
     digit = number % 10u;            //extract ones digit;
     portb_array[0] = mask(digit);    //and store it to PORTB array;
     digit = (number / 10u) % 10u;    //extract tens digit;
     portb_array[1] = mask(digit);    //and store it to PORTB array;
     digit = (number / 100u) % 10u;   //extract hundreds digit;
     portb_array[2] = mask(digit);    //and store it to PORTB array;
     digit = number / 1000u;          //extract thousands digit;
     portb_array[3] = mask(digit);    //and store it to PORTB array;

}
void DisplayINPUT()
{
     portb_array[0] = mask('I');    //and store it to PORTB array;
     //portb_array[1] = mask(I);    //and store it to PORTB array;
  //   portb_array[2] = mask(I);    //and store it to PORTB array;
    // portb_array[3] = mask('I');    //and store it to PORTB array;

}

float filter( float aData )
{
    static float memory;
    memory = memory*(1-K) + aData*K;
    return memory;
}
unsigned int ADC_Read(unsigned char channel)
{
  if(channel > 5) //If Invalid channel selected
    return 0;     //Return 0

  ADCON0 &= 0xC5; //Clearing the Channel Selection Bits
  ADCON0 |= channel<<3; //Setting the required Bits
  __delay_ms(2); //Acquisition time to charge hold capacitor
  GO_nDONE = 1; //Initializes A/D Conversion
  while(GO_nDONE); //Wait for A/D Conversion to complete
  return (ADRES); //Returns Result
}

void input()
{

         adc=0;
     for(i=0;i<20;i++)    //adc=adc+result*1.96078;// 1s delay;
     {
     adc=adc+ ADC_Read(0);
     }
             result=19.6078431372549*(adc/20);//Endless loop;
              UpdateDisplay( result);
              __delay_ms(1000);
}
void output()
{
         adc1=0;
     for(i=0;i<20;i++)    //adc=adc+result*1.96078;// 1s delay;
     {
     adc1=adc1+ ADC_Read(1);
     }
             result1=19.6078431372549*(adc1/20);//Endless loop;
              UpdateDisplay( result1);
              __delay_ms(1000);
}
main(void)
{
     InitIO();
     InitTimer2();
     int i=1;
     while(1)
     {
     switch(i)
     {
     case 1:
     input();
     break;
     case 2:
         DisplayINPUT();
     case 3:
     output();
    case 4:
  input();
     break;
     }


if (0==my_timer)
{
    i++;
    if (i>3) {
        i = 0;
    }
    my_timer = 30 * 50;  // MUX_FREQUENCY must be < 1092 Hz
}
}
}
    

2 respuestas

2

Hay muchos errores y posibles errores en tu código. Por favor en el futuro:

  1. NUNCA NUNCA utilice variables globales a menos que no exista ninguna otra forma de almacenar y comunicar datos. Es un dolor real en el extremo para depurar las corrupciones de los datos definidos globalmente y de acceso global. Limite el alcance de las variables a los bloques de código donde se utilizan. En su caso portb_array , portb_index y my_timer están bien para ser globales porque se usan en la ejecución fuera de orden (interrupciones), el resto no. Algunas variables son simplemente inútiles.

  2. NUNCA NUNCA uses números mágicos. 20 o 19.78452 son buenos números, pero ¿podrás decir en un par de años cuál fue el significado deseado? ¿Qué pasa si lo cambias en un lugar y te olvidas de cambiar en los otros? Definir macros o enumeraciones son las mejores soluciones.

  3. Por el mismo motivo que en el artículo 2 anterior, a toda costa evite copiar y pegar el código. Crea funciones generalizadas para el código compartido. Hágalos "en línea" o incluso "en línea estática" si desea ahorrar en las llamadas / devoluciones. ¿Cuál es el uso de las funciones separadas de entrada () y salida () si realmente hacen lo mismo, simplemente usando diferentes ADC? Además, dar nombres relevantes a las funciones también ayuda mucho a largo plazo. En este ejemplo en particular, output () es un mal nombre porque en realidad no genera nada. Estas dos funciones realizan una lectura ADC. Si pretendía indicar qué valor (entrada o salida) leen, podría usar mejor un parámetro de entrada y definir macros para él, o mejor aún una enumeración (porque puede ver las enumeraciones en un depurador).

  4. Cuando utilice variables locales, no olvide inicializarlas en la declaración. De lo contrario terminarás con basura en ellos. Se asignan en la pila y pueden contener cualquier valor aleatorio. Bueno, no es tan aleatorio para un sistema integrado, pero aún así ... Su función de filtro () es un buen ejemplo de este tipo de error. Es bueno que esta función esté totalmente sin usar.

  5. Para recorrer un conjunto limitado de números, use el operador de módulo, no if (). Es decir, en lugar de

    i++;
    if(i>3)
        i = 0;
    

    usa esto:

    #define NDIGITS 4
    ...
    i = (i + 1) % NDIGITS;
    

    ya que esto es generalmente más fácil de entender y es mucho más compacto.

  6. Use tipos apropiados para variables. El uso de float para un iterador de bucle generalmente no es la mejor idea, en cuanto a rendimiento.

  7. No olvide terminar correctamente sus bloques case . Un break; olvidado puede darte algunas canas adicionales a veces.

  8. Use matrices para mapear siempre que sea posible. Esto es mucho más rápido y mucho más compacto que la creación de interminables bloques switch() . Las asignaciones basadas en matrices funcionan mejor para rangos de entrada contiguos (por ejemplo, 0..9 o 25..178). En su caso, son ideales si se deshace de un mask('I') absolutamente inútil que puede ser reemplazado por mask(1) , lo que esencialmente hace dentro de mask() .

  9. Por amor de Dios, UTILICE LA INDENTACIÓN ADECUADA. Es un dolor leer y entender un código mal sangrado. Mejor aún, siga el método de "sangrar con pestañas, alinear con espacios".

Además, debo tener en cuenta que no entiendo cómo el código que publicaste logró mostrar dos valores. Eso debe haber sido una coincidencia.

Habiendo dicho todo eso, creo que el siguiente código debería hacer el truco por ti. Simplemente defina todos los canales que tenga en las posiciones apropiadas dentro de la enumeración channel_t. También es posible que desee ajustar la macro DISPLAY_DURATION . Si entendí correctamente su hardware a partir de su código, ese debe ser el retraso entre los valores mostrados:

    /*
     * File:   main.c
     * Author: EmbeddedBuzz, Alexander Amelkin ([email protected])
     *
     * Created on December 4, 2014, 7:24 PM
     * Updated on July 22, 2015
     */
    #include <htc.h>
    __CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & CP_OFF & BOREN_ON);
    #include <stdio.h>
    #include <stdlib.h>
    #define _XTAL_FREQ 4000000
    #define K 0.01f

    #define MAX_DIGITS        4
    #define MS_IN_SEC         1000
    #define DISPLAY_DURATION  (1 * MS_IN_SEC) // Make sure this is longer than at least MAX_DIGITS ISRs
    #define ADC_SCALE         19.6078431372549
    #define ADC_READS         20

    typedef enum {
        CHANNEL_NONE = -1,
        CHANNEL_IN,        // 0
        CHANNEL_OUT,       // 1
        CHANNELS
    } channel_t;

    //Display Configuration
    unsigned short portb_index;
    unsigned short portb_array[MAX_DIGITS];
    volatile int digits_tdigits_to_show;

    unsigned short mask(unsigned short num)
    {
        unsigned short res[10] = {
            0x3F, 0x06, 0x5B, 0x4F, 0x66, // 0..4
            0x6D, 0x7D, 0x07, 0x7F, 0x6F  // 5..9
        }

        return res[num % 10];
    }

    void interrupt ISR()
    {
        PORTC = 0;                          // Turn off all 7seg. displays;
        PORTB = portb_array[portb_index];   // Bring appropriate value to PORTB;
        PORTC = 1 << (portb_index + 4);     // Turn on appropriate 7seg. display;
        // FIXME: What is this 4 above? Define a macro.

        //increment portb_index;
        portb_index = (portb_index + 1) % MAX_DIGITS;

        if (digits_to_show > 0)
            digits_to_show--;

        TMR2 = 0;                           // reset TIMER0 value;
        TMR2IF = 0;
        TMR2IE = 1;                         // Turn on TMR2 interrupt
        //clear T0IF, Bit T0IF=0, T0IE=1;
    }

    void InitTimer2(void)
    {
        T2CON = 0x3C;
        /*   Prescale 1:16
         *   Timer off for now
         */
        PR2 = 249;
        /*   With prescale 1:16 and at 4MHz clock,
         *   this gives 2ms period.
         */
        TMR2IF = 0;
        TMR2IE = 1;       // Turn on TMR2 interrupt
        GIE = 1;          // Turn on global interrupts
        PEIE = 1;         // Turn on peripheral interrupts
        TMR2ON = 1;       // Start TMR2
    }

    void InitIO(void)
    {
        portb_index = 0;
        PORTB = 0;
        TRISB = 0;
        PORTC = 0;
        TRISC = 0;
        ADCON1 =0x60;
        ADCON0 = 0xC1;
        PORTA = 0;
        TRISA = 0x00;          // Inputs for ADC
    }

    void UpdateDisplay(int number)
    {
        int i, divisor, digit;

        for(i = 0, divisor = 10; i < 3; i++, divisor *= 10) {
            digit = number % divisor;
            portb_array[i] = mask(digit);
        }
    }

    unsigned int ADC_Read(channel_t channel)
    {
        if(channel >= CHANNELS) //If Invalid channel selected
            return 0;     //Return 0

        ADCON0 &= 0xC5; //Clearing the Channel Selection Bits
        ADCON0 |= channel<<3; //Setting the required Bits
        __delay_ms(2); //Acquisition time to charge hold capacitor
        GO_nDONE = 1; //Initializes A/D Conversion
        while(GO_nDONE); //Wait for A/D Conversion to complete
        return (ADRES); //Returns Result
    }

    void read_channel(channel_t which)
    {
        int i;
        float adc;
        float result;

        for(adc = 0.0, i = 0; i < ADC_READS; i++)
        {
            adc = adc + ADC_Read(which);
        }
        //adc=adc+result*1.96078;
        result = ADC_SCALE * (adc / ADC_READS);
        UpdateDisplay(result);
    }

    main(void)
    {
        InitIO();
        InitTimer2();
        channel_t channel = CHANNEL_NONE;

        while(1) {
            if (!digits_to_show) {
                channel = (channel + 1) % CHANNELS;
                read_channel(channel);
                digits_to_show = MAX_DIGITS;
                __delay_ms(DISPLAY_DELAY);
            }
        }
    }
    
respondido por el Alexander Amelkin
2

En este momento está cambiando un entero i entre 1 y 2:

i=(1==i)?2:1;

En su lugar, todo lo que necesita hacer es contar de 1 a 4. O, mejor aún, ya que los números comienzan en 0, no 1, cuentan de 0 a 3.

Cuando llegas a 3, comienzas desde 0 otra vez.

En mano larga:

if (0==my_timer) {
    i++;
    if (i>3) {
        i = 0;
    }
    my_timer = 30 * 50;  // MUX_FREQUENCY must be < 1092 Hz
}

O en una mano corta, que parece que prefieres mirar en tu código:

if (0==my_timer) {
    i = (i+1) % 4;
    my_timer = 30 * 50;  // MUX_FREQUENCY must be < 1092 Hz
}

Luego tienes 4 entradas de mayúsculas y minúsculas, para 0, 1, 2 y 3.

Oh, y una cosa más. Si tuviera que activar las advertencias, vería algo similar a:

  

Advertencia: la variable 'i' se puede usar sin inicializar en esta función.

Estás creando la variable i pero no le das un valor inicial. Puede comenzar en 0, o puede comenzar en 4892, o 11944, o quién sabe qué. Las posibilidades son 0, ya que está en main, y nada más debería haber usado la pila en ese punto, pero ¿quién puede decirlo? Siempre debe inicializar una variable con un valor antes de modificar ese valor. En este caso:

int i = 0;

sería una buena idea. Para su código original, inicializarlo en 1 sería mejor, ya que es una entrada válida en su conmutador.

    
respondido por el Majenko

Lea otras preguntas en las etiquetas