AVR MCU corriendo @ 32.7Khz Interrupción de problema de multiplexación de LED de baja velocidad

0

Funciona bien con 8 mhz interno, pero cuando se cambia a 32,7 khz, la pantalla led de multiplexación no funciona correctamente (parpadea de forma muy aleatoria). ¿Es este el límite de baja velocidad de reloj o hay un problema con mi código?

Timer0 es para multiplexar la pantalla de 7 segmentos; timer2 es para un conteo de reloj externo.

También he jugado con el TCNT0, es decir, para aumentar la velocidad de multiplexación, en la interrupción, pero sin suerte;

///////////////////////////////////////////////////
// B1, B2, D3, D5, D6, B3, D2, D4 (+) active HIGH
// B0, D7, B5, B4 (-) active LOW
///////////////////////////////////////////////////
#include <avr/io.h> 
#include <avr/interrupt.h>

int8_t portA = 0b11011100;
int8_t pinA[8] = {1, 2, 3, 5, 6, 3, 2, 4};

uint8_t counter=0;

int8_t d1 = 0b00000110;
int8_t d2 = 0b01011011;
int8_t d3 = 0b01001111;
int8_t d4 = 0b01100110;

int8_t left=43;
int8_t right=21;

uint16_t one=9995;
uint8_t good;

uint8_t tsec;
uint8_t tmin;
uint8_t thr;

  int8_t dig[] = //digit mapping
    {
    //HGFEDCBA  
    0b00111111, // 0
    0b00000110, // 1
    0b01011011, // 2
    0b01001111, // 3
    0b01100110, // 4
    0b01101101, // 5
    0b01111101, // 6
    0b00000111, // 7
    0b01111111, // 8
    0b01101111, // 9
    0b00000000, // BLANK
    0b10000000  }; //dot

//----------------------------------------------------

int main(void) {

    //timer 0    
    TIMSK |= (1 << TOIE0);
    TCCR0 |= (1<<CS01); // timer0 No prescaler

    //timer 2

   TIMSK |= (1 << TOIE2);
   TCCR2 |= (1 << CS22) ;   // 64 prescaler
   //TCCR2 |= (1 << CS20) ;  // no prescaler   
   //ASSR |= (1<<AS2);   
     sei();  

    //adc
    ADCSRA |= (1<<ADPS2)|(1<<ADEN)|(1<<ADIE); 
    ADMUX |= (1<<REFS0) | (1<<MUX0)| (1<<MUX1);  
    ADCSRA |= 1<<ADSC; 

    // PORTs
    DDRD = 0xff;
    DDRB = 0xff;   

  while (1) {

    if( one>=9999)one=0;

    left=one/100;
    right=one%100;

    d1=dig[left/10];
    d2=dig[left%10];
    d3=dig[right/10];
    d4=dig[right%10];


 if(counter==0){
  PORTB |=0b00110000;
  PORTD |=0b10000000;  
     pinWrite(d1); 
  PORTB &= ~(1<<0);
  }

 if(counter==1){
  PORTB |=0b00110001;
  PORTD |=0b00000000;  
     pinWrite(d2); 
  PORTD &= ~(1<<7);
  }  

 if(counter==2){
  PORTB |=0b00010001;
  PORTD |=0b10000000;  
     pinWrite(d3); 
  PORTB &= ~(1<<5);
  }  

 if(counter==3){
  PORTB |=0b00100001;
  PORTD |=0b10000000;  
     pinWrite(d4); 
  PORTB &= ~(1<<4);
  }      

  if(counter==4) counter=0;        

  }
}

//----------------------------------------------------
ISR (TIMER0_OVF_vect)  // timer0 overflow interrupt
{
   TCNT0=100;
   counter++;

  //19Khz @ 8Mhz/256/2=15Khz
  }
//---------------------------------------------------- 

  //2hz   @ 32Khz/256/64=2hz  
ISR (TIMER2_OVF_vect)
{
  good=!good;
  if(good==1)one++;
}

//----------------------------------------------------
ISR(ADC_vect)    // adc interrupt
{
  //one = ADC;  
  ADCSRA |= 1<<ADSC; 
}

//----------------------------------------------------
// PortWrite

  int pinWrite ( int8_t data){

    for (int x = 0; x <8; x++) {

      if (data & (1 << x)) {
        if (portA & 1 << x) {
          PORTD |= 1 << pinA[x];
        }
        else {
          PORTB |= 1 << pinA[x];
        }
      }

      else {
        if (portA & 1 << x) {
          PORTD &= ~(1 << pinA[x]);
        }
        else {
          PORTB &= ~(1 << pinA[x]);
        }
      }
    }       
}
    
pregunta Atmega 328

1 respuesta

1

Para empezar, todos esos cálculos de /10 y %10 en cada ciclo tomarán muchos ciclos de reloj, probablemente varios cientos, si no mil. Estás haciendo seis de esos bucles, por lo que es probable que haya varios miles de ciclos en cada bucle para cada segmento que se encienda.

Si su reloj es solo de 32 kHz, entonces cada dígito tarda un décimo sexto de segundo en aparecer. Pero debido a que no tiene sincronización entre la pantalla de dígitos y la interrupción que alterna el dígito que debe iluminarse, entonces si esa interrupción se dispara demasiado rápido, el contador de la pantalla aumentará varias veces antes de que se pueda mostrar un solo dígito (por lo tanto, se omiten los dígitos). / p>

En cada bucle, parece que estás calculando el valor para el segmento de visualización de cada , y luego solo usas uno de ellos, lo que es increíblemente ineficiente, básicamente calculas el valor para cada dígito cuatro veces por cada vez que se muestra.

Además, tienes problemas de acceso atómico. Su interrupción está cambiando los valores de 16 bits con los que también intenta hacer cálculos en el bucle, lo que significa que podría producirse una interrupción y cambiar la variable de 16 bits cuando está a mitad de camino para acceder a ella (corromper así los datos).

Realmente no sé por qué está usando interrupciones aquí, especialmente con una frecuencia de reloj tan baja. Si simplemente agrega la línea counter++ al final del bucle y mueve los cálculos de d1 a d4 dentro de sus correspondientes sentencias if, entonces probablemente comenzará a mostrarse correctamente: el tiempo que toma realizar los cálculos. será suficiente para que el dígito se muestre durante un período de tiempo suficientemente largo para que lo veas.

    
respondido por el Tom Carpenter

Lea otras preguntas en las etiquetas