El módulo ADC del STM32 se usa en mi aplicación, que es muy sensible al consumo de energía.
En esta aplicación, se requiere que el ADC funcione solo a 20 muestras por segundo. Usar el DMA usa más energía de lo que esperaba. Decidí hacer que funcionara en modo de muestra única haciendo que una tarea (FreeRTOS) desencadenara una conversión y esperara a que se realizara cada 50 ms.
Aquí está mi código:
u16 i;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
for(i=0;i<sizeof(ADC_Channel_Table)/sizeof(u8);i++)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel_Table[i], 1, ADC_SampleTime_71Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
{
}
ADCConvertedValue[i] = ADC_GetConversionValue(ADC1);
}
ADC_Cmd(ADC1, DISABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, DISABLE);
Donde "sizeof(ADC_Channel_Table)" es 5 porque se muestrean 5 canales.
El tiempo de muestra es ADC_SampleTime_239Cycles5 (en realidad, 256 ciclos donde se incluye el tiempo de conversión). 5 canales por lo tanto requiere alrededor de 1500 ciclos. El reloj ADC es de 12 MHz y 1500 ciclos son aproximadamente 120 µs.
Y mira el código:
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
{
}
Esto significa que la CPU se mantiene ocupada esperando a que se realice la conversión, y el tiempo de espera es de 120 µs en total.
120 µs es grande ya que la CPU debe esperar tanto tiempo y esto desperdicia energía, pero este nivel de tiempo es demasiado pequeño para el RTOS. El RTOS no puede utilizar un tiempo tan pequeño.
Así que quiero insertar algunas instrucciones de "ahorro de energía" en el ciclo de espera.
Por ejemplo:
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
{
__ASM("NOP");__ASM("NOP");__ASM("NOP");__ASM("NOP");
}
Pero NOP consume la misma cantidad de energía que cualquier otra instrucción que haya probado.
¿Qué instrucción puedo insertar en mi ciclo while que consuma la menor cantidad de energía?
Si desea ahorrar energía, ponga la MCU en reposo. Las instrucciones relevantes son WFI y WFE: espera por interrupción y espera por evento, respectivamente.
WFI se explica por sí mismo: se activa cuando recibe una interrupción. (¡Sin embargo, la interrupción debe estar habilitada!)
WFE podría merecer un poco más de explicación. Para usarlo, probablemente sea suficiente saber que si configura SEVONPEND, una interrupción que no está habilitada en el NVIC califica como un evento. Entonces, si habilita la interrupción de ADC en el periférico pero no en el NVIC, puede esperar a que se complete con WFE. Todavía use el bucle con la verificación de bandera, ya que puede haber otros eventos que despierten la MCU. (Simplemente reemplace los NOP con un WFE) Como de costumbre, para obtener información más detallada, consulte el manual de referencia.
Probablemente también desee seleccionar qué tan profundo desea ir al sueño, pero la forma en que se hace puede depender de la MCU STM32 específica que esté utilizando. Al menos en un modelo, las banderas relevantes eran PDDS, LPDS y SLEEPDEEP. Sin duda querrá leer la sección correspondiente del manual de referencia en cualquier caso.
Intentemos responder la pregunta opuesta para explicar por qué NOP
no ahorra energía. El Cortex-M3 es un procesador diseñado para ser pequeño y relativamente simple: no tiene muchos de los circuitos diseñados para realizar tareas dedicadas (cachés, coma flotante, predicción de bifurcaciones), o donde los tiene, son implementaciones limitadas a obtenga el mayor éxito al menor costo.
La mayoría de las instrucciones avanzan a través de la tubería de 3 etapas y realizan cantidades similares de trabajo. Obtención de instrucciones, decodificación y operación de ALU. Para aumentar la potencia, tal vez podríamos agregar una transferencia de datos (que enciende más lógica fuera del núcleo, pero lo más probable es que detenga toda la tubería ya que el núcleo está en orden). Tal vez podríamos usar la división de un solo ciclo justo después de hacer una transferencia de datos; allí posiblemente podría obtener dos partes adicionales de actividad al mismo tiempo. Sin embargo, tal vez hacer esto reducirá la actividad del archivo de registro, por lo que no todo ayuda a alcanzar la potencia máxima.
Aunque hay alguna variación de instrucción a instrucción (y una buena cantidad de esfuerzo de diseño para optimizar esto), la mayor parte de la potencia activa no depende demasiado de las instrucciones. Claro, llenar la tubería con varios NOP
detendrá cualquier actividad de alternar, pero la tubería aún avanza. Detener la tubería (para una transferencia de datos lenta) hará mucho más, pero el único estado bien definido en el que puede confiar para ser óptimo será WFI
y los diversos estados de suspensión.
Los núcleos más grandes tendrán mucho más pico-a-media porque una mayor proporción de su silicio estará inactiva en un bucle inactivo previsto y precargado (y probablemente más otra lógica activa en el chip, que a menudo también puede ser controlada por reloj/energía). )
Si está satisfecho con las muestras cada 62,5 ms o 31,25 ms (en lugar de 50 ms), puede usar la función de alarma RTC para activar el STM32 y ponerlo en modo de espera/detenerse entre conversiones. Esto ahorrará energía de forma masiva, especialmente si configura el ADC para pasar al modo de bajo consumo después de la conversión (esto puede depender de la serie STM32). Si inicia una conversión, luego entra en modo de espera/parada, una vez que se despierte, la conversión se completará de todos modos. Esto agrega una latencia de una muestra y requiere que procese la muestra antes de iniciar una nueva muestra, pero dependiendo de su aplicación, esto puede ser factible.
Fluir:
Inicie la conversión ADC y habilite la alarma RTC
Entrar en modo de parada/modo de espera
Una vez que RTC haya despertado el microcontrolador, tome el valor de conversión de ADC
Procesar muestra (almacenar/filtrar, etc.)
¡Iniciar proceso de nuevo!
Los modos de parada y espera utilizan muy poca energía, por lo que dependiendo de su aplicación, este puede ser un método adecuado.
Buscaría usar un temporizador para iniciar la conversión y una interrupción para recopilar el resultado.
El núcleo de la CPU puede dormir hasta que los datos estén disponibles.
Como alternativa a dormir, es posible que pueda configurar el generador de reloj para que solo proporcione al núcleo uno de cada 2, 4, 8, etc. relojes que ingresan al chip. Si la CPU necesita realizar muchas operaciones que requieren seis ciclos de código, pero solo se puede activar una vez cada 64 ciclos debido a limitaciones de hardware externo, reducir la velocidad de la CPU a 1/8 puede ser justo lo que necesita. Si bien el uso de un modo inactivo con activación de interrupción puede ser mejor que dejar la CPU a toda velocidad, es posible que la CPU deba pasar muchos ciclos para cada evento configurando las transiciones inactiva/activada. Algunos controladores tienen opciones para establecer velocidades por separado para la línea principal versus los controladores de interrupción, de modo que incluso si la línea principal reduce la CPU a 1/8 de velocidad, los controladores de interrupción funcionarán a velocidad normal. Sin embargo, no creo que sea una característica muy común. De lo contrario, tú
O si un ligero jitter del reloj no es problema.
De lo contrario, para poner la MCU en suspensión, simplemente coloque asm("WFI") en el bucle inactivo de FreeRTOS.
Tengo una situación similar, pero acabo de alinear todo con el temporizador de tic de 1 ms de FreeRTOS.
Ignacio Vázquez-Abrams
efox29
efox29
Ale..chenski