This post is concerned with the techniques used to harvest and regulate energy for the Energy Harvesting Remote Control.
The main source of energy harvested in the remote control is solar power. The idea here was that you could leave the remote control to charge up during the day and then when you came to use it at night it would have a sufficient amount of charge for a night of usage. The remote control also features a coil and magnet. This was included to harvest some extra energy, from movement, during use. Both of these sources of energy are used to charge up a 5 Volt, 3 Farad Super Capacitor. The Super Capacitor has a Zener Diode protection circuit to prevent overcharging.
The super capacitor is used to power a 2.5 volt voltage reference with a quiescent current of 50uA. It is capable of supplying up to 25 mA. This reference can thus power the remote control most of the time. The exception is if an IR code is being sent, or the e-ink screen needs updating.
When IR codes are being sent, or the e-ink screen needs updating, a buck-boost converter is used to buck or boost the voltage accordingly. A 22uH inductor was used to achieve 3 volts from a 2.7-5 volt range.
The buck boost power is enabled using an output pin on the MCU and the following circuit:
A switching speed of about 2MHz was used for the buck-boost PWM pins. The output voltage is controlled by the duty cycle of the switch frequency. The 16-bit counters on the MkLl25Z PWM module provide a lot of control over the output voltage. The setup code for the power management is shown below:
/*------------------------------------------------------------------------------ Set up and configure the buck boost regulator to start. ADC set up to interupt if the output voltage is not within set limits *------------------------------------------------------------------------------*/ char start_buckboost(void) { unsigned short adc_read; short PWM0,PWM1; SIM->SCGC5 |= (1UL << 13) | (1UL << 9); //BB PWM0 PORTE->PCR[22] = (0x3 << 8); FPTE->PDDR |= (1UL << 22); //BB PWM1 PORTE->PCR[23] = (0x3 << 8); FPTE->PDDR |= (1UL << 23); //BB Enable PORTA->PCR[12] = (0x1 << 8); FPTA->PDDR |= (1UL << 12); /*------------------INSERTED TO FIX VREF PROBLEM----------------------*/ ADC_configure(); ADC0->CFG2 |= (1UL << 4); // READ ADC ADC_SExb adc_read = ADC_readVal(6); TPM_enable(2); TPM2->MOD = 0xFF;
The ADC is used to set the PWM for the buck-boost converter. The reference voltage generator provides a reference voltage for the ADC to compare against. The input to the ADC is made using a potential divider to halve the voltage. This ensures that the voltage going into the ADC is always less than the reference voltage. The code below shows some of the duty cycles being set to buck the buck boost converter.
//4.8-5 V Buck if((adc_read <= 0xAD00) && (adc_read > 0xA500)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x090); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //4.5-4.8 V Buck else if((adc_read <= 0xA500) && (adc_read > 0xA0E5)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0A5); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //4.1-4.5 V Buck else if((adc_read <= 0xA0E5) && (adc_read > 0x9400)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0B9); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //3.8-4 V Buck else if((adc_read <= 0x9400) && (adc_read > 0x88E4)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0CA); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //3.6-3.7 V Buck else if((adc_read <= 0x88E4) && (adc_read > 0x7F00)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0D9); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //3.5 V Buck else if((adc_read <= 0x7F00) && (adc_read > 0x7E00)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0DB); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //3.4 V Buck else if((adc_read <= 0x7E00) && (adc_read > 0x78D6)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0E0); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //3.3V Buck else if((adc_read <= 0x78D6) && (adc_read > 0x7650)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0F0); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //3.2V Buck else if((adc_read <= 0x7650) && (adc_read > 0x7200)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0F9); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //3.1V Buck else if((adc_read <= 0x7200) && (adc_read > 0x6F00)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x0FF); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x100); } //3V Stay the same else if((adc_read <= 0x6F00) && (adc_read > 0x68F3)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x100); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x0F0); } //2.9 Volt boost else if((adc_read <= 0x68F3) && (adc_read > 0x66DF)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x100); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x0D5); } //2.8 Volt boost else if((adc_read <= 0x66DF) && (adc_read > 0x6473)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x100); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x0A5); } //2.7 Volt Boost else if((adc_read <= 0x6473) && (adc_read > 0x5FE0)) { TPM_configure(2,0,((0x1<<2)|(0x1<<5)), 0x100); TPM_configure(2,1,((0x1<<2)|(0x1<<5)), 0x0A0); } else { return 0; } TPM_start(2); FPTA->PDOR |= (1UL << 12); return 1;
This solution for power management is elegant and allows the MCU to do most of the power management. As a result, we were able to save power here when compared with other power management solutions.
The next development step was Utilising The LLS Mode And The LLWU For The Kl25z Series