Estoy buscando desarrollar software alimentado por batería usando los controladores EFM Gekko (http://energymicro.com/) y me gustaría que el controlador esté dormido cada vez que no haya nada útil para hacerlo. La instrucción WFI (Wait For Interrupt) se utiliza para este propósito; pondrá el procesador en suspensión hasta que ocurra una interrupción.
Si el sueño se activara almacenando algo en algún lugar, uno podría usar operaciones exclusivas de carga / exclusivas de tienda para hacer algo como:
// dont_sleep se carga con 2 cada vez que sucede algo que // debería forzar al ciclo principal a circular al menos una vez. Si una interrupción // ocurre que hace que se restablezca a 2 durante la siguiente declaración, // el comportamiento será como si la interrupción ocurriera después. store_exclusive (load_exclusive (dont_sleep) >> 1); mientras que (! dont_sleep) { // Si se produce una interrupción entre la siguiente instrucción y store_exclusive, no duermas load_exclusive (SLEEP_TRIGGER); si (! dont_sleep) store_exclusive (SLEEP_TRIGGER); }
Si ocurriera una interrupción entre las operaciones load_exclusive y store_exclusive, el efecto sería omitir store_exclusive, haciendo que el sistema se ejecute a través del ciclo una vez más (para ver si la interrupción había establecido dont_sleep). Desafortunadamente, Gekko utiliza una instrucción WFI en lugar de una dirección de escritura para activar el modo de suspensión; escribir código como
si (! dont_sleep) WFI ();
correría el riesgo de que se produzca una interrupción entre el 'if' y el 'wfi' y establezca dont_sleep, pero el wfi continuará y se ejecutará de todos modos. ¿Cuál es el mejor patrón para evitar eso? Establezca PRIMASK en 1 para evitar que las interrupciones interrumpan el procesador justo antes de ejecutar el WFI, y bórrelo inmediatamente después. ¿O hay algún truco mejor?
EDITAR
Me pregunto sobre el bit del evento. Según la descripción general, desearía que fuera compatible con varios procesadores, pero se preguntaba si algo como lo siguiente podría funcionar:
if (no dormir) SEV (); / * Hará que la siguiente marca de evento WFE sea clara pero no duerma * / WFE ();
Cada interrupción que establezca don't_sleep también debe ejecutar una instrucción SEV, por lo que si la interrupción ocurre después de la prueba "if", el WFE borrará el indicador de evento pero no se irá a dormir. ¿Suena eso como un buen paradigma?
Respuestas:
No entendí completamente el
dont_sleep
asunto, pero una cosa que podría intentar es hacer el "trabajo principal" en el controlador PendSV, configurado con la prioridad más baja. Luego, solo programe un PendSV de otros controladores cada vez que necesite hacer algo. Vea aquí cómo hacerlo (es para M1 pero M3 no es muy diferente).Otra cosa que podría usar (tal vez junto con el enfoque anterior) es la función Dormir al salir. Si lo habilita, el procesador se suspenderá después de salir del último controlador ISR, sin que tenga que llamar a WFI. Vea algunos ejemplos aquí .
fuente
Ponlo dentro de una sección crítica. Los ISR no se ejecutarán, por lo que no corre el riesgo de que Dont_sleep cambie antes de WFI, pero aún así despertará al procesador y los ISR se ejecutarán tan pronto como finalice la sección crítica.
Su entorno de desarrollo probablemente tenga funciones de sección críticas, pero es más o menos así:
EnterCriticalSection es:
ExitCriticalSection es:
fuente
Su idea está bien, esto es exactamente lo que implementa Linux. Ver aquí .
Cita útil del hilo de discusión mencionado anteriormente para aclarar por qué WFI funciona incluso con interrupciones deshabilitadas:
fuente
Asumiendo que:
Entonces, la solución es usar PRIMASK para bloquear las interrupciones entre la validación del indicador y WFI:
fuente
¿Qué pasa con el modo Sleep on Exit? Esto se desactiva automáticamente cada vez que sale un controlador IRQ, por lo que no hay realmente ningún "modo normal" ejecutándose después de que esté configurado. Ocurre una IRQ, se despierta, ejecuta el controlador y vuelve a dormir. No se necesita WFI.
fuente