6 votos

Uso de las interrupciones del ADC y del TIMER al mismo tiempo

Soy programador y no tengo experiencia con arduino ni con ningún microcontrolador. Especialmente la parte técnica.

He soldado una matriz de leds RGB de 6x8 y uso modulación codificada binaria http://www.batsocks.co.uk/readme/art_bcm_3.htm para mezclar los colores.

Los Leds son controlados por 4 74hc595 shiftregisters. Básicamente cambio los bits para controlar lo realmente rápido durante una interrupción del temporizador.

Ahora quiero influir en el color de los leds midiendo las frecuencias de sonido con un Placa de conexión para micrófono electret . Para lograr una detección rápida de la frecuencia, utilizo una técnica descrita aquí http://www.instructables.com/id/Arduino-Frequency-Detection/#step1 .

Se basa en las interrupciones del CAD.

Ambas implementaciones 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

Así que mi pregunta es: ¿es posible utilizar esas interrupciones juntas? Y cómo tengo que configurarlos para que funcione. ¿O es un callejón sin salida?

EDITAR 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 bucle for para las filas y los otros colores. Lo dejé fuera porque parecía confuso. El registro de comparación se desplaza a la izquierda durante la interrupción y el temporizador se retrasa para generar un retardo "creciente". Hay una interrupción por ciclo. Se ejecuta en un reloj/32 tickrate

http://www.batsocks.co.uk/img/art_bcm/bcm_1.png

La interrupción del ADC tiene este aspecto:

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 del ADC se activa cada vez que un valor de A0 está listo. En el bucle principal intento poner algunos leds pero no funciona.

1 votos

Por supuesto, debería ser posible. ¿Podría explicar un poco más? ¿Qué es exactamente lo que no funciona como se espera? También, podrías publicar el código completo (o al menos los manejadores de interrupción y tu bucle principal). También: ¿Con qué frecuencia esperas una interrupción del ADC y con qué frecuencia esperas que se produzca la interrupción del temporizador?

0 votos

He editado la pregunta espero que sea más clara ahora

1 votos

Consulta la hoja de datos de tu microcontrolador exacto (ATmega[algo]). Cada interrupción tiene su propio vector y viene con su propia prioridad, esto define qué interrupción será atendida cuando dos llegan al mismo momento. Mientras una interrupción es atendida, la otra interrupción será puesta en espera hasta que la primera ISR termine.

3voto

user38728 Puntos 11

Una cosa que noto en tu código es que estás adquiriendo las muestras en modo de funcionamiento libre con el reloj del ADC a F_CPU/2. Según la hoja de datos del Atmega 328P

En el modo de ejecución libre, se iniciará una nueva conversión inmediatamente después de que la conversión com- de la conversión, mientras el ADSC se mantiene alto.

y el tiempo de conversión para el modo de funcionamiento libre (en la tabla 23-1, página 255) se especifica como 13 ciclos de reloj del ADC. Esto significa que el ISR del ADC se dispara cada 26 ciclos de reloj con la configuración actual del preescalador. Además, tu TIMER1_COMPA El ISR también se dispara con tanta frecuencia (cada 64 ciclos) que su ISR podría tardar más en ejecutarse que el tiempo deseado. Básicamente usas todo tu tiempo adquiriendo datos y/o en tu ISR del temporizador. Una solución fácil es aumentar la cuenta en OCR1A a, por ejemplo, 5 y aumentar el preescalador del ADC

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

Si quieres exprimir hasta la última gota de rendimiento del viejo 328 deberías inspeccionar el ensamblaje generado para tus rutinas de interrupción, y averiguar si puedes hacer algo para optimizarlas. Los problemas de rendimiento más comunes son, por ejemplo, la obtención frecuente de datos de la SRAM, pero si usas gcc puedes encontrar todo tipo de cosas interesantes en la entrada de la ISR.

Hay que tener en cuenta que el ojo humano no puede detectar un parpadeo increíblemente rápido (de ahí que los leds accionados por pwm funcionen como lo hacen, sin parpadeo visible). Si la velocidad de funcionamiento más lenta parece visualmente desagradable, por supuesto, puede tratar de reducir el preescalador y OCR1A Pero también deberías considerar la posibilidad de prescindir de los ISR's y simplemente ejecutar tu código de parpadeo de leds en el bucle principal y hacer que sólo el ISR del ADC adquiera los datos para ser procesados. Esto podría funcionar mejor ya que cada llamada a la función toma al menos 8 ciclos (podría ser 6, la memoria es un poco confusa en este detalle en particular) MÁS toda la pila PUSH 'n an' POP 'n, que depende de su uso variable. Así que usted podría ir para algo como:

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

y que ambas funciones sean inline.

0voto

Chirry Puntos 104

Sí se puede, sólo tres cosas:

  • Intenta utilizar el bucle principal para hacer todo lo que necesitas, intentando programar un ejecutivo cíclico y utilizar las interrupciones sólo para activar banderas o leer valores del ADC. Esto lo puedes lograr estableciendo tiempos para cada tarea, por ejemplo, leer el ADC cada 5ms, poner los LEDs cada 10ms, etc.

  • En algunos dispositivos Atmel, como el Atmega16, es necesario resetear (configurar de nuevo) el ADC cada vez que éste termina una medición.

  • Desactivar la bandera de interrupción general al entrar a una función de interrupción y activarla de nuevo al salir. cli() y sei().

Cualquier pregunta, hágamelo saber, no usé el comentario porque no tengo reputación para hacerlo, pero tengo mucha exeperiencia trabajando en microcontroladores Atmel y sistemas digitales.

0 votos

1) El ADC se ha configurado correctamente para el autodisparo, y al salir de la ISR se volverá a disparar. 2) Las interrupciones están deshabilitadas cuando se ejecuta una ISR; es necesario habilitarlas explícitamente si se van a utilizar interrupciones anidadas.

0 votos

Esa sería una razón por la que no está funcionando con ambos vectores de interrupción, sei() tiene que ser llamado al terminar cualquier función de interrupción oyente.

0 votos

La otra cosa que veo, es que la interrupción del temporizador está siendo llamada cada 64 ciclos de reloj ( CS20 and CS21 = 64 prescaling and OCR1A = 1 ), y tal vez se está disparando cuando el micro está "ocupado" haciendo la función de interrupción del ADC. Al menos, la función del vector del temporizador tarda más de 64 ciclos de reloj en sí misma.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X