Perdón por cualquier noobiness, soy más un ingeniero de software.
¿Cómo uso las interrupciones de botón en general?
Es decir, si pongo mi microcontrolador a dormir, me gustaría un botón para despertarlo.
¿Cómo debo hacer esto?
Agregando esto a main, gracias a @jippie
DDRD &= ~(1<<PIND2)
PORTD |= 1<<PIND2
GICR |= ( 1 << INT0 ); // Enable INT0
MCUCR = (0<<ISC00)|(1<<ISC01);
sei();
Y esto fuera de main
ISR(INT0_vect) {
sleep_disable(); // If ISR got called while it was sleeping, this would work
lcd.string("works");
};
ISR funciona, pero solo si el dispositivo no está durmiendo.
Cambiando de INT0 a INT2 (y pasando de PIND2 a PINB2), funciona...
De acuerdo con la documentación, INT0 e INT1 usan interrupciones de nivel, pero PINB usa edge... así que... ¿quizás por eso?
Mi medidor de voltaje cae a .2 mientras está en modo de suspensión, luego se despierta de ISR.
Un pulsador de a tierra es la forma más fácil, pero olvida el estado actual por completo.
Para que las interrupciones funcionen, necesita un pin que se atribuya como INTn
y para ATmega32 está limitado a PB2
, PD2
o PD3
. No todos los modos de suspensión admiten la activación por interrupción externa y los diferentes modos de suspensión admiten interrupciones activadas por borde o nivel.
Para estos pines tienes que:
GICR
;MCUCR
y MCUCSR
.No necesita agregar una ISR (rutina de servicio de interrupción) completa si todo lo que quiere hacer es despertar y continuar donde lo dejó, pero no debe confiar en el vector predeterminado. Depende del compilador lo que realmente suceda en una mala interrupción. Consulte aquí el nombre del vector de interrupción a utilizar.
ISR() { INT0_vect , EMPTY_INTERRUPT };
Para obtener más información, consulte el capítulo titulado 'Interrupciones externas' en la hoja de datos de ATmega32 .
Para que un botón se conecte a tierra en PD2 ( INT0
), el código podría parecerse un poco a esto (no probado):
DDRD &= ~ ( 1 << PD2 ); // Configure PD2 as input
PORTB |= ( 1 << PD2 ); // Enable pull up resistor
// MCUCR default triggers on LOW level. Changing to edge trigger requires different sleep mode to wake up.
GICR |= ( 1 << INT0 ); // Enable INT0
PD5
como entrada de interrupción. Actualicé mi respuesta. Se agregó un código de muestra (no probado).La respuesta de Jippie tiene los detalles técnicos, pero es posible que no estés comprendiendo los modos de suspensión de los AVR. Los microcontroladores operan en un ámbito diferente al que estamos acostumbrados a que las computadoras de escritorio funcionen y, de hecho, golpean algunas de las mismas palabras para significar cosas que los desarrolladores de software no esperan.
Lo siguiente es lo que he recopilado, como un tipo de software. ¡Agradezco de todo corazón las modificaciones a mi publicación para corregir las cosas en las que me he equivocado!
El silicio se divide en varios bloques de construcción para la funcionalidad. Temporizadores, IO, SRAM, generador de reloj, núcleo de ejecución. El mundo integrado considera estos periféricos . Me equivoqué constantemente, asumiendo que los periféricos eran cosas conectadas al exterior de la MCU, ya que, bueno, eso es lo que son en una computadora de escritorio.
Lo que esto significa, en muchos casos, es que cuando el AVR se va a dormir, apagará estas otras partes del silicio. Los primeros en irse son algunos temporizadores, seguidos quizás por el generador de reloj principal. El rápido apagado y arranque de estos periféricos significa que los microcontroladores entran y salen constantemente de varios estados de suspensión, al contrario de un gran SUEÑO, NO SUEÑO como una computadora de escritorio. Un ejemplo de pseudocódigo que hace una docena de suposiciones incorrectas, de una comunicación en serie ficticia:
// bad, the loop burns CPU time for no benefit
int message = 2830;
int counter = 0;
while (counter++ < 32) {
sendBit(PIN_TX, message & (2 << counter));
_delay_us(2500); // cpu spinwait is here
}
// better, uses sleeps
int message = 2830;
int counter = 0;
function sendData() {
sendBit(PIN_TX, message & (2 << counter++));
if (counter >= 32)
TIMER_A = DISABLED;
}
TIMER_A = TIMER_INCREMENT | TIMER_OVERFLOW_CALLBACK | ONE_PER_MICROSECOND;
TIMER_A_MAX = 2500;
TIMER_A_CALLBACK = &sendData;
DEFAULT_POWERSTATE = SLEEP3;
Nuevamente, tendrá que perdonar el vago código handwavey. La idea aquí es que la comunicación en serie tiene que esperar un número determinado de microsegundos entre bits, y hay dos formas de hacerlo. Uno es por un spinwait, el otro es interrupciones. Una interrupción permitirá que la MCU atienda otras interrupciones mientras espera, o si no sucede nada, se apagará a un modo de suspensión más bajo. El equivalente correspondiente en el desarrollo de software es generar un hilo frente a while (verdadero);
Muy poco de esto responde a su pregunta original, y lo escribo como una respuesta para ayudar a adjuntar información adicional sobre cómo el sueño puede afectar a los microcontroladores de maneras que no son necesariamente intuitivas para los desarrolladores de software.
editar:
Para lograr el ciclo de activación que está preguntando, se vería algo como lo siguiente:
function buttonPress() { _set_power_mode(FULL); }
void main() {
while (true) {
_set_power_mode(SLEEP2);
// button press logic here
}
}
Así es vagamente como recuerdo que funcionaba mi MSP430, y considerando todo, los microcontroladores tienden a seguir aproximadamente las mismas reglas. _set_power_mode de un sueño lo suficientemente profundo fue una llamada de bloqueo. Fiel al espíritu de cómo suelen ser mis proyectos, el AVR probablemente lo haga de manera completamente diferente y mi respuesta es totalmente discutible.
Chris Laplante
usuario11047