Usando interrupciones ADC y TIMER al mismo tiempo

6

Soy un programador y no tengo experiencia con arduino ni con ningún microcontrolador. Especialmente el aspecto técnico.

Soldé una Matriz Led RGB 6x8 y uso modulación codificada en binario enlace Mezclar los colores.

Los Leds están controlados por 4 74hc595 shiftregisters. Básicamente, muevo los bits para controlar el realmente rápido durante una interrupción del temporizador.

Ahora quiero influir en el color de los leds mediante la medición de las frecuencias de sonido con un Electret micrófono breakout board . Para lograr una detección de frecuencia rápida, utilizo una técnica que se describe aquí enlace .

Se basa en interrupciones ADC.

Ambas mejoras funcionan solas, pero cuando intento unir el código, se rompe.

El temporizador y el ADC se inicializan así:

cli(); //stop all interrupts

/////////////////////
//initialize TIMER//
//////////////////// 
TCCR1A = 0;
TCCR1B = 0;
TCNT1  = 0;

OCR1A = 1;            // compare match register
TCCR1B |= (1 << WGM12);   // CTC mode
TCCR1B |= ((1<<CS21)|(1<<CS20)) ;
TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt

//////////////////
//initialize ADC//
//////////////////
ADCSRA = 0;
ADCSRB = 0;

ADMUX |= (1 << REFS1); //set reference voltage
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from          ADCH register only

//ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
ADCSRA |= (1 << ADATE); //enabble auto trigger
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADSC); //start ADC measurements

sei(); //start all interrupts

Entonces, mi pregunta es: ¿es posible usar esas interrupciones juntas? ¿Y cómo tengo que configurarlos para que funcione? ¿O es un callejón sin salida?

EDIT La interrupción del temporizador:

ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
zaehlbit <<=1; 
//reset timercompare and zaehlbit
if( zaehlbit == 0 ) {
  OCR1A= 1;
  zaehlbit  = 1;
}
//latch low
bitClear(PORTB, latchPinPORTB);

//red
for (int i=0; i<8; i++){
  // clock low
  bitClear(PORTB, clockPinPORTB);
  //check led position and brightness
  //and set Bit on Datapin if zaehlbit in Brightness
  if (ledCounter&led && zaehlbit&red){
    bitClear(PORTB, dataPinPORTB);
  }
  else {
  bitSet(PORTB, dataPinPORTB);
  }
  // clock low (register bit)
  bitSet(PORTB, clockPinPORTB);
  ledCounter >>= 1;
  if( ledCounter == 0 ) {
  ledCounter  = 128;
 }  
}

//shift timer compare and set timer back to generate delay (Bit Angle Modulation)
OCR1A <<= 1;
TCNT1 = 0;
}

Hago el mismo for-loop para las filas y los otros colores. Simplemente lo dejé afuera porque parecía confuso. El registro de comparación de coincidencias se desplaza hacia la izquierda durante la interrupción y el temporizador se retrasa para generar un retraso "creciente". Hay una interrupción por ciclo. Se ejecuta en un reloj / 32 tickrate

La interrupción de ADC se ve así:

ISR(ADC_vect) {//when new ADC value ready

  prevData = newData;//store previous value
  newData = ADCH;//get value from A0
  if (newData > prevData){//if positive slope
    bitSet(PORTB, ledPORTB);//set pin 12 high
  }
  else if (newData < prevData){
    bitClear(PORTB, ledPORTB); //set pin 12 low
  }

}

La interrupción de ADC se activa cada vez que un valor de A0 está listo. En el mainloop solo trato de configurar algunos leds pero no funciona.

    
pregunta user1210456

2 respuestas

3

Una cosa que observo en su código es que está adquiriendo las muestras en modo de ejecución libre con el reloj ADC en F_CPU / 2. Según la hoja de datos de Atmega 328P

  

En el modo Free Running, se iniciará una nueva conversión inmediatamente después de la conversión.   Pletes, mientras que ADSC sigue siendo alta.

y el tiempo de conversión para el modo de ejecución libre (en la tabla 23-1, página 255) se especifica como 13 ciclos de reloj ADC. Esto significa que su ISR de ADC está disparando cada 26 ciclos de reloj con sus ajustes actuales de prescaler. Además de eso, su TIMER1_COMPA ISR también se dispara con tanta frecuencia (cada 64 ciclos) que su ISR puede tardar más en ejecutarse que el tiempo deseado. Básicamente, utiliza todo el tiempo adquiriendo datos y / o en su temporizador ISR. Una solución fácil es aumentar el recuento en OCR1A a, digamos, 5 y aumentar el prescaler del ADC

ADCSRA |= (1 << ADPS1); // ADC prescaler 8, conversion time 4*26 MCU cycles
OCR1A = 5; // 5*64;

Si desea exprimir hasta la última gota de rendimiento del antiguo 328, debe inspeccionar el ensamblaje generado para sus rutinas de interrupción, y averiguar si puede hacer algo para optimizarlas. Los cerdos de rendimiento común son, por ejemplo, las recuperaciones de datos frecuentes de la SRAM, pero si estás usando gcc, es posible que encuentres todo tipo de cosas geniales en la entrada de ISR.

Tenga en cuenta que el ojo humano no puede detectar parpadeo increíblemente rápido (por lo tanto, los leds impulsados por pwm funcionan como lo hacen, sin parpadeo visible). Por supuesto, si la velocidad de funcionamiento más lenta es visualmente desagradable, puede intentar reducir el preescalador y el OCR1A , pero también debe considerar la posibilidad de dispensar el ISR en su totalidad y simplemente ejecutar su código de parpadeo del led en el bucle principal y hacer que el ISR ADC adquiera datos para ser procesado Esto podría funcionar mejor ya que cada llamada a una función toma un mínimo de 8 ciclos (podría haber sido 6, la memoria está un poco borrosa en este detalle en particular) MÁS toda la pila PUSH 'n y' POP 'n, que depende de su uso variable Así que podrías ir por algo como:

while(1) {
   checkData();
   handleLeds();
}

y tener ambas funciones en línea.

    
respondido por el hlindblo
0

Sí, puedes solo tres cosas:

  • Intente usar el bucle principal para hacer todo lo que necesita, intente programar un ejecutivo cíclico y use las interrupciones solo para activar marcas o leer valores de ADC. Puede lograr esto estableciendo tiempos para cada tarea, por ejemplo, lea el ADC cada 5 ms, configure los LED cada 10 ms, etc.

  • En algunos dispositivos Atmel como Atmega16, debe restablecer (configurándolo nuevamente) el ADC cada vez que el ADC finalice una medición.

  • Desactive el indicador de interrupción general cuando ingrese a una función de interrupción y actívelo nuevamente cuando salga. cli () y sei ().

Cualquier pregunta, hágamelo saber, no usé el comentario porque no tengo reputación de hacerlo, pero tengo mucha experiencia trabajando con los microcontroladores de Atmel y los sistemas digitales.

    
respondido por el Chirry

Lea otras preguntas en las etiquetas