I don't know when it started, but now, any electronic product, at least battery-powered, requires low power consumption. Fortunately, almost any chip on the market dares to prominently display "low power consumption" on the first page of its datasheet. But what exactly constitutes low power consumption? Less than 5mA? Less than 1ms? Less than 100uA? Outside of the application context, these numbers seem to lose their simple meaning; basically, the lower the better. But intuitively, an application that can power a piece of fruit should qualify as low power consumption, right?
Honestly, I kind of miss the days when a random application would draw 500mA, the chip would get slightly warm, and as long as it was still manageable to hold your hand, you could confidently say "no problem." Lately, I've been dealing with µA all the time. If it exceeds 100µA, everyone around me looks grim. And if I manage to get it below the legendary 20µA, they're all smug about it. Ugh… µA… it's unbearable! It's too much to handle…
As the saying goes, "practice makes perfect," and gradually I've gained some insights. It seems that the low-power development process can be done methodically and systematically, and it's no longer just a series of scattered "depend-on" or "intuitive" things. So I couldn't help but record these seemingly ambiguous steps for the benefit of beginners.
Whether you achieve twice the result with half the effort or half the result with twice the effort—your mindset determines success or failure.
"When I'm hungry, I fall asleep and then I don't feel hungry anymore... So, on my precious weekends, I stay home to catch up on sleep, and I often only eat one meal a day" - Tech Geek_That's pretty much it.
Nobody can work while sleepwalking, right? So, during normal working hours, we still need to eat. Sleeping and working are inherently contradictory. Therefore, those "surprisingly small" sleep power consumption figures in chip datasheets seem to be mostly just for show; while operating power consumption is the real issue. Sometimes, to demonstrate so-called low power consumption, a so-called low-power mode needs to be designed into the application—when the system determines there's nothing to do for a period of time, it simply goes to sleep—this is roughly the current state of low-power applications on the market. Therefore, reducing the operating frequency—the "the horse runs, but the horse doesn't eat" logic—becomes the conventional choice to reduce system power consumption in normal operating mode. It's tough…so many people struggle between operating frequency and power consumption…and how many functions themselves have minimum frequency requirements…it's tough—I'm talking about programmers who write code.
In fact, reducing power consumption seems to be a problem that can only be solved by the collaboration of software and hardware. For example, the voltage divider resistor in AD sampling will continuously consume current if it is directly connected to ground. If the grounding method is controlled through an I/O port, and it is only grounded when sampling is needed, and then floated or pulled high after sampling is completed, this overhead can be minimized.
Clearly, it is inappropriate to make low power consumption entirely a matter of hardware or software design.
From a hardware perspective, identifying all possible current-consuming loops and determining which ones can be optimized for power consumption through software control and which are unavoidable, while providing programmers with a table showing the impact of all I/O port states on power consumption (a simple table illustrating the effects of high, low, and floating states is usually sufficient, without needing precise numerical values), essentially completes the hardware work. The remaining work is left to the software developers. Software-based power reduction strategies are the focus of this article.
When it comes to software power optimization, it's both simple and complex. In short, it boils down to: application modularization, functional task-based management, task periodization, power consumption self-management, and hibernation as a veto power factor. Still not simple enough? To condense it further: hibernate whenever possible, and decide how to hibernate through voting. Heh… that's probably too simplistic, losing some information. Let's elaborate on each aspect below:
1. Application modularization, functional task-based approach, and task periodicization.
A specific application typically consists of many sub-functions and sub-tasks. Those familiar with embedded system software architecture will understand this better: an application is implemented by calling several services. These services can be hardware services, such as AD sampling, serial communication, external interrupt triggering, and timer services; or software services, such as various communication protocol stacks, FAT file systems, queues, software filtering, etc. A service usually implements one or more functions (good task partitioning avoids having a service containing more than two unrelated functions). Simple functions, such as CRC checks where the function inputs and outputs results almost immediately, are not discussed here; complex functions are best implemented using tasks. Speaking of tasks involves operating systems, schedulers, or even simple state machines. In short, the implementation of a task can be understood as a process. Since it's a process, the work a task needs to do is periodic. For example, an AD sampling task consists of at least three steps: channel selection and initiating sampling, sampling and waiting for sampling to complete, and data processing. These three steps together form a task cycle, and once all three steps are completed, we can consider a cycle finished. To give another example, in I2C communication, the transmission of a complete data packet usually includes several states. This series of states constitutes a task. When the last state (or some abnormal exit state) ends, a task cycle ends.
In summary, the ultimate goal of modularization, task-based functionality, and task periodization is task periodization. Only by achieving periodization can a task have a beginning and an end. With a clear beginning and an end, "salaries" can be distributed as needed, avoiding waste. The most common way to achieve task periodization is to isolate functions through modular services, making it easier to manage and identify the start and end points of a task.
Finding the soldiers that don't pull the strings is the starting point for power management. Achieving this requires a holistic understanding of embedded systems and experience in modular, service-oriented, and interface-based development. The accumulation of experience and the holistic understanding are the most complex parts.
2. Power consumption self-management and sleep mode veto function
Once task periodization is achieved, the entire system is essentially divided into many smaller, periodically operating tasks. These tasks may appear interleaved, parallel, or unrelated, but fundamentally, each smaller task only needs to focus on its own start and end. The system's power management ultimately simplifies to the power management of each task—if each task minimizes its power consumption, the entire system, under effective coordination, can achieve the same level of power minimization.
Based on the above description, task-based power management is actually artificially divided into two parts: power management of the task itself from a micro perspective and coordination of multi-task sleep from a macro perspective.
Let's start from the micro level. A task must first be able to independently complete its own function. This may seem insignificant, but it's crucial. It ensures that all steps within the task are deterministic, "self-determined," and "black boxes" to the outside world—in short, "autonomous." Based on this, if a task is required to meet low power consumption requirements, it falls into one of the following categories:
1) Hibernation is not allowed during task execution. Therefore, a sign must be set at the beginning and end of the task to inform the coordination system that "hibernation is not allowed until I say OK" and "the task continues as long as the person is there."
2) During task execution, some stages allow hibernation, while others do not. If we consider "no hibernation" as the lowest level of hibernation, then hibernation can be divided into several levels from low to high based on power consumption. In this case, we can modify the above definition to: different hibernation levels are allowed in different stages during task execution.
3) During task execution, it does not matter whether there is a sleep mode.
Clearly, if all three types of tasks exist simultaneously in the system, the third type is essentially "air" and can be ignored, while the first type is quite domineering; as long as it's executing, it absolutely forbids hibernation. The second type of task, which completes its task while also allowing hibernation, is a commendable "good comrade." When designing system tasks, we should try to write the latter two types of tasks as much as possible, while avoiding or attempting to split the first type of task.
From a macro perspective, at any given time, multiple tasks may be executing simultaneously, so each task has a different need for hibernation. How would we establish a coordination mechanism? Would this mechanism need to know the details of each task and then "intelligently" find the appropriate time and level for hibernation? That's too cumbersome! Actually, it's quite simple: each task can elect a representative to a meeting. Whenever this coordinating body wants to hibernate, it convenes all representatives to vote. Each person provides their highest tolerable hibernation level, and the meeting's arbitrator finds the lowest hibernation level from these votes—the shortest link in the bucket—as the "meeting consensus," and then proceeds to that hibernation level. Obviously, if someone votes "no hibernation," the arbitrator has no choice but to abandon hibernation. Therefore, each "task" should be a responsible one, and it shouldn't hastily choose to "disallow hibernation" during its execution period simply to ensure its own task's execution. The correct approach is for each task to update its tolerance for hibernation in a timely manner based on its different steps, thereby ensuring meaningful results are achieved during meetings.
In summary, if everyone involved is responsible and tasks are divided reasonably, then through this negotiation mechanism, the system will naturally eliminate "soldierless soldiers" and ensure that tasks "hibernate when possible." In this case, the power consumption figures for hibernation in the chip datasheet become truly meaningful. So, if you've grasped the concept, consider yourself lucky. If you don't yet know how to implement these steps, don't worry, we'll explain them one by one in the following chapters.
The first step in low-power design: bottom-up approach, following the clues.
Imagine you have a new project in your hands. The specific requirements include multi-channel AD data sampling, post-sampled data processing (which is quite complex), and support for I2C communication, which also has a complex protocol. Such a device must have minimal power consumption. To meet this requirement, if you already have a suggested chip, such as AVRMEGA or Tiny, how should you proceed?
"First, figure out how we sleep."
The first step is to consult the datasheet to find the maximum sleep mode that can be woken up. Then, write a test project—following the requirements in the datasheet, implement a completely idle sleep mode, while simultaneously disabling all power-consuming functions that can be turned off—such as cutting off the clocks of certain peripherals or correctly configuring the I/O port states. This gives us a maximum power consumption, which is a power consumption that you can approach infinitely close under normal system design.
At this point, you need to configure the I/O states according to the I/O port settings recommendations provided by the hardware design engineer to obtain a minimum power consumption that you can manually determine. Download the code to the target circuit and measure the power consumption. If, after eliminating inherent power consumption on the circuit board that cannot be optimized—such as the power consumption of certain resistors that consume fixed current—and still the maximum value for the corresponding sleep mode stated in the datasheet is not reached, you'll need to have a chat with your hardware design colleague, and the two of you should work together to find the cause from both hardware and software perspectives. Continue until you obtain a satisfactory "pure sleep power consumption" that allows for automatic wake-up.
This process is crucial, directly determining the best results you can achieve. Investing a little more time is worthwhile because it allows you to gain a very subtle, preliminary understanding of which peripherals and configurations affect power consumption, how they affect it, and the extent of that impact. A journey of a thousand miles begins with a single step; spending a little more time is worthwhile!
Importantly, software developers shouldn't immediately assume the problem lies with the hardware design. For example, at this step, I consistently failed to reach the maximum allowable power consumption in sleep mode as stated in the datasheet, even though I believed I had disabled all peripherals that could be turned off. So I contacted the IC design department to complain. The final result was that one peripheral used an independent clock—an asynchronous clock. In this case, the CPU needs a certain number of clock cycles to synchronize its register settings. However, after simply setting the register to "disable" it, I immediately went into sleep mode. As you can imagine, because I didn't wait for the asynchronous clock to synchronize—meaning the operation hadn't taken effect—I went into sleep mode, leaving the peripheral still active, resulting in high power consumption. Ugh… embarrassing…
"Let's figure out how we woke up."
The second step is to identify the system's pulse. Identifying the system's pulse involves a comprehensive review of the entire application's operation, identifying the maximum clock cycle of the system clock, and determining the wake-up source used by the chip based on this requirement. Many people may be accustomed to using timer overflow interrupts or compare-match interrupts to generate a millisecond-level system clock. However, except for RTCs using external watch crystal oscillators or timers with asynchronous clock sources, ordinary timers require the system master clock to provide a clock source for normal operation, which is not allowed in the low-power mode we are pursuing. Sometimes, upon closer examination, is a millisecond-level system clock truly necessary?
In AVR systems, the watchdog timer is typically the source of the system clock in low-power mode. Through configuration, the watchdog timer can wake the system from its maximum sleep state at fixed time intervals (16ms/32ms/64ms/128ms). Therefore, in applications where system ticks are crucial, if 16ms is an approximation of your required system tick period, you can consider using a watchdog timer to provide a stable clock source, even if it's not perfectly accurate. Otherwise, you face the following choices:
a. My system's operating mode dictates that I must have a system clock of less than 16ms; therefore, does the overall design allow the use of an external clock source to provide a clock source for an asynchronous timer (Timer2's asynchronous mode) or some dedicated RTC—these peripherals typically support waking the system from its maximum sleep mode, much like a watchdog timer does?
b. If my system does not allow the addition of an external clock source, is it allowed to work in an externally triggered mode—that is, to wake up the system via an external interrupt or a pin level change interrupt, start a workflow, and then put the system back into permanent sleep after completion?
c. If none of the above works, you can consider changing the chip or modifying the system requirements—at least the system requirements will need to make some compromises in terms of power consumption.
The above steps for "confirming the system's pulse" are actually an "example"—an example of system design, or more specifically, an example of system operating mode design. This is something a system architect, or rather, a "conscious" system architect like myself, should seriously learn and frequently practice. A general and complete description is as follows:
1) Investigate and confirm a target chip: Confirm all its sleep modes and the clock sources that are turned off in the corresponding sleep modes. These clock sources involve peripherals. You can't cook without rice. This step is to first figure out what materials you have on hand when designing the system.
2) Study the specific application requirements and clarify the system's operating mode (for sampling-based systems, this means sampling, resting, re-sampling, and resting again; the entire system is a state machine driven by sampling events; sampling not only provides information but also provides the system's pulse. Even if such systems involve LCD refresh or I2C/serial communication, since the source of information is sampling, the sampling period itself determines the validity of the information. Therefore, there is no reason why the LCD refresh period or the communication buffer update period must be greater than the sampling update period). Under this premise, clarify the system's requirements for wake-up sources and wake-up modes, thereby determining the system's basic sleep mode. Furthermore, comparing the power consumption of this basic sleep mode with the power consumption required by the application can provide a preliminary evaluation result of the system design. Sometimes, it can even provide some direct expected data on system power consumption.
For example:
If, through evaluation, we find that the peripherals involved in the application can wake the system from a maximum permissible SLEEP mode (e.g., Power-SaveMode), then this SLEEP mode is the basic Sleep mode. Our method for evaluating system power consumption is to estimate what percentage of time is spent sleeping and what percentage of time is spent working (active time) when completing a task (task cycle). Then, we can use the following formula to estimate the normal power consumption of the system:
Equation A: normal power consumption = sleep consumption * sleep time (%) + active consumption * active time (%) 3) Based on the research report, discuss the feasibility of the system. If it is not feasible, then according to the already defined system operating mode, the corresponding wake-up source requires a new chip selection, and return to step 1). If it is feasible, then the subsequent design can proceed.
Figure 1.1 A system state diagram illustrating low-power clock speed design.
This system design pattern may seem like putting the cart before the horse—deciding on the chip before even understanding the requirements—but in reality, it aligns very well with our typical development model: First, we have a preliminary concept or alternative chip solutions. These solutions might come from an existing one, a compatible solution, or a solution proposed by the boss or a senior employee/experienced developer—in short, we have a foundation or prototype. We then begin investigating and researching the feasibility of specific low-power solutions and produce a research report. This report directly guides the next steps: because the requirements are very clear, we can decide whether to change the chip or proceed directly to the next stage of development. The three steps above actually form a loop, which originates from the "rapid prototyping" method, an old "agile" development model that is natural, standardized, and efficient.
Having completed the above two steps, this stage can be considered complete. At this point, we have a firm grasp of the system's key performance data: the minimum achievable power consumption; how certain sensitive peripheral parameter settings will affect power consumption—this information will be invaluable when we need to make compromises and trade-offs between peripheral performance and power consumption; we even have a general understanding of how the system works externally, or rather, the system's most fundamental question: "the time factor, where and what pushes the first domino in this complex domino effect?" We even know what power consumption the system will achieve if all goes well.
In short, we now know the system is feasible, and we have a general idea of the power consumption range. Everything is ready except for the final step. To be continued...
An appendix to this summary: System configuration methods for exploring power consumption limits—sharing some practical experience.
A. What should be done with unused pins in an AVR?
>>If this pin belongs to the ADC sampling pin
i) Disable the digital input of the corresponding pin by using the DIDRn register. In this case, the corresponding bit of PINX will always read 0.
ii) Set the corresponding pins as inputs using the DDRx and PORTx registers to "disable" the pull-up resistors.
>>If this pin is a regular GPIO
The officially recommended approach is to assign a specific voltage level to this pin, for example:
i) Set the corresponding pins to input state via the DDRx and PORTx registers, and "enable" the pull-up resistors.
ii) Set the corresponding pin to the output state through the DDRx and PORTx registers and output a low level. In the PCB design, ground the pin.
If this pin is a RESET pin, simplify the circuit as much as possible while ensuring stability. Also, if the VCC voltage does not rise slowly at power-on, connect an external pull-up resistor to VCC. Although this has little impact on power consumption, it can improve the anti-interference ability to a certain extent.
>>If this pin is an extended ADC pin, it can basically be ignored, or grounded.
B. For AVR, what about the pins that need to be used?
>>If this pin is an open-drain output/line-and-in pin, such as a TWI pin, and it needs to be connected to an external device, and this connection allows for plugging and unplugging.
i) If logically permissible, connect a pull-down resistor.
ii) For external interrupt pins or interrupt pins affected by pin level changes, avoid floating; choose pull-down pins over pull-up pins whenever possible. This is to prevent unpredictable voltage levels from frequently waking the system. The reason for choosing a pull-down pin is to give the power to select the high voltage level to an external device, and also to avoid the risk of overcharging (^_^).
>>For ADC pins
i) If it will never be used for digital signal input, please refer to the handling of unused pins.
ii) If it is to be used for digital signal input, before reading the level through PINx, enable the digital input of the corresponding pin through the DIDRn register, insert two NOPs and then read the level. After reading, immediately disable the digital input function.
>>For control signal pins
i) Directly driving LEDs is always the beginning of a tragedy. Don't forget to add a current-limiting resistor and use high-brightness LEDs whenever possible.
ii) If you absolutely must output a level control signal, please carefully consider the possibility of current leakage. If so, please process the I/O port to a state with no current leakage (or a state with minimal current leakage; you understand the difference between output and input terms) whenever possible, without needing to output control signals. Specific state diagrams should be suggested by the hardware designer. The general principle is to allocate resources as needed.
C. Talk about the PRR register
>>If the power supply to a peripheral device is cut off via the setting of the PRR register.
i) Unless otherwise specified, all registers of this peripheral are unreadable and unwriteable.
ii) If the peripheral's interrupt flag is not cleared before the peripheral is powered off; or if the peripheral is powered off while the interrupt handler is executing, the interrupt flag will not be cleared. The symptom is that the interrupt is continuously triggered. It's actually quite simple; refer to i).
iii) In many cases, disabling peripherals such as timers does not reduce power consumption significantly, typically less than 5uA. However, experience shows that disabling modules that are highly dependent on analog characteristics or have their own independent clock sources usually reduces power consumption considerably.
iv) Use this feature with caution unless you have carefully read the datasheet. This is also one of the main reasons why many people say they can't wake up from hibernation or that their system is malfunctioning.
D. Talk about work frequency
>>In normal operating mode, the higher the frequency, the higher the power consumption.
i) Corollary 1: For the same task, the higher the frequency, the shorter the time to complete the task.
ii) Corollary 2: According to Equation A, for the same task, the shorter the Active time, the longer the Sleep time.
>>Assuming the frequency doubles and power consumption increases fourfold, then:
i) Corollary 3: Sleep power consumption is typically at least one-quarter or even more of the power consumption at a certain frequency. When the frequency doubles, the operating time is halved. Assuming that power consumption quadruples when power doubles, the actual power consumption of the active component doubles (50% * 4). Simultaneously, the saved time becomes the sleep power consumption. At this point, the system power consumption changes as follows:
Assuming the power consumption of the Active component before doubling the frequency is C, and the operating time is T, then: before doubling, the power consumption of the Active component is C*T. After doubling the frequency, the power consumption of the Active component is 4C, and the operating time is T/2. Simultaneously, assuming the power consumption of the Sleep component is C/4, then the actual power consumption after doubling is 4C*(T/2) + (C/4)*(T/2) = 4.25C*T/2 = 2.125(C*T). It is clear that the actual power consumption after doubling the frequency is 2.125 times that before. Obviously, from a power consumption perspective, this is not cost-effective.
However, please note that the power consumption in Sleep mode is typically much less than 1/4 of this value. For example, with the ATmega88, the maximum power consumption is 550uA at 1MHz VCC=2V, while the maximum power consumption in Sleep mode (Power-down VCC=3V) is only 15uA, which is approximately 36 times less. In this case, it is clear that doubling the frequency results in nearly doubling the power consumption.
Interestingly, if we iterate over the above formula, we'll find that the more the frequency doubles, the greater the difference between the Sleep power consumption and the Active power consumption before doubling. In other words, power consumption essentially doubles with each frequency doubling. Meanwhile, the benefit of increasing frequency is that the processing speed doubles with each frequency increase. From this perspective, is a higher frequency always better? (Higher speed, smaller increase in power consumption). Considering the power consumption directly related to external pin operations, a higher frequency means a smaller controllable power consumption portion in external pin operations (please refer to the previous AD example). Therefore, from this perspective, it seems that power consumption might even decrease somewhat.
To reiterate, the above conclusions are based on the assumption that the device will immediately go to sleep after completing its task, and that the power consumption of the sleep mode must be much less than that of the active mode (typically 8 to 10 times less). The additional power consumption required by the external clock source and crystal oscillator, as well as the power consumption required during the clock stabilization period when waking up from sleep, have not been taken into account.
In reality, the power consumption of an MCU consists of two parts: one related to frequency and the other related to operating voltage.
E = E(V) + E(f)
When the frequency doubles, only E(f) is affected, and this power consumption does not ultimately lead to a 4-fold increase in power consumption; its value is often 2 times or even lower. In this case, the Sleep+Active working mode will result in lower power consumption. Proof omitted.
Typically, we measure power consumption by measuring current.
i) Corollary 4: In non-integral power consumption measurement methods, the higher the operating frequency, the longer the sleep time. During current measurement, the active current is closer to a "glitch," leading to the illusion that higher frequency means lower power consumption. This is because most current sampling points fall during the sleep phase.
ii) Corollary 5: Only by using an integral power consumption measurement method can the actual power consumption be accurately measured. Of course, if you want to fool ordinary customers, the current result (even the average current) of a high-precision ammeter will give you a "very ideal result".
Low-power design step two: Let's play chess, a very, very big game of chess.
Based on the discussion in the previous section, assuming that the corresponding steps in your design have been completed, we have obtained at least the following information and results:
A limit power consumption
A feasible system operating mode (meeting application requirements while maintaining low power consumption and system cycle time)
When using the system operating mode mentioned in section 2, the theoretical system power consumption (power consumption under normal system operation) is calculated using the formula.
Clearly, everything is ready except for the final push—the actual implementation. But hold on, let's clarify a few definitions first.
a. Normal Working Mode (NormalMode/ActiveMode)
The normal operating mode here refers to the mode that enables normal system functions. In this mode, the MCU is not always active, but rather combines the Active and Sleep MCU states according to the power consumption formula mentioned in the previous chapter. In short, it's an operating mode that ensures basic application functionality while allowing the MCU to sleep whenever possible.
b. Low-power mode (IdleMode/SleepMode)
The low-power mode mentioned here refers to a mode that, based on user application needs and under certain specific conditions specified by the user or automatically detected by the system, disables unnecessary functions as much as possible, retaining only essential application tasks to reduce power consumption. (For example, turning off the LCD and LED backlight after a period of inactivity.)
In summary, the normal operating mode and low-power mode here do not directly correspond to the Active and Sleep modes provided by the MCU. Instead, they are two power consumption modes with different functional configurations defined according to user functional requirements. They may both involve the MCU's Active and Sleep states. Clarifying these two definitions will prevent confusion in many subsequent discussions.
2.1 To do a good job, one must first have the right tools.
Before implementing low-power system design, we must have an effective means to detect or observe the system's current operating mode. In simpler terms, we need to know at any time when the system is working and when it is in sleep mode, and ideally, we should be able to accurately determine the ratio of working to sleep time. This observation method is even more important in situations where high-precision ammeters are unavailable.
In short, we need a debugging method to trace the system's power consumption patterns in real time. Since it's real-time tracing, common methods like breakpoints and single-stepping are insufficient; the system must be in a normal, uninterrupted operating state. Simultaneously, as a debugger, it must accurately display the sleep and operating states and save this information. Imagine a device similar to a seismic recorder continuously recording power consumption changes on paper tape. While it sounds complex, it's actually quite simple to implement. During the debug phase, we're concerned with how the system sleeps. The actual power consumption may not be accurate due to the debugging methods, but this information is sufficient. In subsequent testing, we can generate a release version (removing debug-related code) and directly verify the system power consumption using an ammeter. Code 2.1 is a good example. The code outputs a high level on a signal pin before sleep mode and a low level after exiting sleep mode. At this point, with the help of an oscilloscope or logic analyzer, we can record and detect the power consumption mode of the system, and even calculate a theoretical power consumption that is very close to the actual result based on the active current and sleep current provided in the datasheet.
Code 2.1: Example of code for entering Sleep mode under Tiny
staticvoidenter_sleep_mode(uint8_tchLevel){/*!\briefsleepmodeselectbits*!MODESM2SM1SM0*!idle000*!ADCNoiseReduction001*!Power-save011*!Power-off100*/staticFLASHuint8_tc_chSleepLevel[]={((0x00<
Figure 2.1 An example of a possible system power consumption mode monitoring
Disclaimer: This article is a reprint. If there are any copyright issues, please contact us promptly for deletion (QQ: 2737591964). We apologize for any inconvenience.