SuperCap II - the low low clock speed
"I'm always chasing rainbows!" - nice lyrics from Thomas Joseph McCarthy and in my case it could be better phrased as "I'm always chasing low clock speeds and energy requirements!"
It's a little fruitless and frustrating at times but I'm a big fan of Thomas Edison who said a couple of things that resonate in these strange pursuits:
1. "Opportunity is missed by most people because it is dressed in overalls and looks like work.", and
2. "I have not failed 10,000 times. I have successfully found 10,000 ways that will not work."
So in this project the plan was to take a successful light design and change out the NiMH battery for a super-capacitor. The reason is that NiMH batteries have a much smaller cycle lifetime compared to super capacitors.
As well I want to wind down the clock speed on the Padauk PFS154 and see how low it can go.
Also I will be using the 5050 Leds that I've used before to minimise the energy requirements.
The super-capacitor used in the last video was a "paltry" 4 Farads. The current version is 2x30 Farad capacitors, and I have some bigger ones coming - er, because see 2nd quote from Edison above!
The code side was where the big gains have been made - I was able to successfully wind down the clock to around 17000Hz (!) and still run the "candle".
/* Candle with Three PWM Pseudo-random flickering to simulate a candle. Output is via 3xPWM channels, variables can be changed to alter the simulation Wed 12 Oct 2022 20:46:06 AEDT */ #include <stdint.h> #include <stdlib.h> #include "../device.h" #include "../easy-pdk/calibrate.h" #include "../auto_sysclock.h" #include "../delay.h" #include <stdbool.h> #define LED4_BIT 4 #define LED0_BIT 0 #define LED3_BIT 3 uint16_t myrand = 2901; // happy birthday // global variables randomised later for flickering, using the // "waveslow" and "wavefast" arrays uint8_t slowcounter = 0; uint8_t medcounter = 0; uint8_t fastcounter = 0; uint8_t slowstart = 0; uint8_t slowend = 0; uint8_t medstart = 0; uint8_t medend = 0; uint8_t faststart = 0; uint8_t fastend = 0; uint8_t faster = 0; uint8_t waveslow[] = {50, 100, 170, 200}; // 1 leds on PA4 uint8_t wavemed[] = {40, 120, 140, 220}; // 1 led on PA3 uint8_t wavefast[] = {40, 80, 150, 240}; // 1 led on PA0 // booleans to keep track of "fading up" or "fading down" // in each of the slow and fast cycles bool fastup = true; bool slowup = true; bool medup = true; void mydelay(uint8_t counter) { for (uint8_t thiscount = 0; thiscount <= counter; thiscount++) { _delay_us(1); } } uint16_t gimmerand(uint16_t small, uint16_t big) { myrand ^= (myrand << 13); myrand ^= (myrand >> 9); myrand ^= (myrand << 7); return abs(myrand) % 23 * (big - small) / 23 + small; } void getnewslow() { slowstart = gimmerand(waveslow[0], waveslow[1]); slowend = gimmerand(waveslow[2], waveslow[3]); } void getnewmed() { medstart = gimmerand(wavemed[0], wavemed[1]); medend = gimmerand(wavemed[2], wavemed[3]); } // initialise a new fast cycle including the new speed of cycle void getnewfast() { faststart = gimmerand(wavefast[0], wavefast[1]); fastend = gimmerand(wavefast[2], wavefast[3]); faster = gimmerand(1, 8); } // Main program void main() { // Initialize hardware // Set LED as output (all pins are input by default) PAC |= (1 << LED4_BIT) | (1 << LED0_BIT) | (1 << LED3_BIT); PWMG1DTL = 0x00; // Clear the LED PWM duty value PWMG1DTH = 0x00; PWMG1CUBL = 0xff; // set PWM counter upper bound to 0xffff PWMG1CUBH = 0xff; PWMG1C = 0x87; PWMG1S = 0b00000000; // prescaler=4, divider=1, no interrupt PWMG0DTL = 0x00; // Clear the LED PWM duty value PWMG0DTH = 0x00; PWMG0CUBL = 0xff; // set PWM counter upper bound to 0xffff PWMG0CUBH = 0xff; // PWMG0C = (uint8_t)(PWMG0C_ENABLE | PWMG0C_INVERT_OUT | PWMG0C_OUT_PA0 | PWMG0C_CLK_IHRC); PWMG0C = 0x87; PWMG0S = 0b00000000; // prescaler=4, divider=1, no interrupt PWMG2DTL = 0x00; // Clear the LED PWM duty value PWMG2DTH = 0x00; PWMG2CUBL = 0xff; // set PWM counter upper bound to 0xffff PWMG2CUBH = 0xff; PWMG2C = 0x87; PWMG2S = 0b00000000; // prescaler=4, divider=1, no interrupt getnewfast(); getnewslow(); getnewmed(); slowcounter = slowstart; fastcounter = faststart; medcounter = medstart; // Main processing loop while (1) { // ramp up slow if (slowup) { slowcounter++; if (slowcounter > slowend) { // ramp finished so switch boolean slowup = !slowup; } } else { // ramp down slow slowcounter--; if (slowcounter < slowstart) { // ramp finished so switch boolean slowup = !slowup; getnewslow(); } } // ramp up med if (medup) { medcounter++; if (medcounter > medend) { // ramp finished so switch boolean medup = !medup; } } else { // ramp down med medcounter--; if (medcounter < medstart) { // ramp finished so switch boolean medup = !medup; getnewmed(); } } // ramp up fast if (fastup) { fastcounter = fastcounter + faster; if (fastcounter > fastend) { // ramp finished so switch boolean fastup = !fastup; } } else { // ramp down fast fastcounter = fastcounter - faster; if (fastcounter < faststart) { // ramp finished so switch boolean fastup = !fastup; getnewfast(); } } // delay + a re-purposed random for ramp speeds mydelay(3+faster); PWMG1DTL = slowcounter & 255; PWMG1DTH = slowcounter; PWMG0DTL = fastcounter & 255; PWMG0DTH = fastcounter; PWMG2DTL = medcounter & 255; PWMG2DTH = medcounter; } } // Startup code - Setup/calibrate system clock unsigned char _sdcc_external_startup(void) { // Initialize the system clock (CLKMD register) with the IHRC, ILRC, or EOSC clock source and correct divider. // The AUTO_INIT_SYSCLOCK() macro uses F_CPU (defined in the Makefile) to choose the IHRC or ILRC clock source and divider. // Alternatively, replace this with the more specific PDK_SET_SYSCLOCK(...) macro from pdk/sysclock.h PDK_SET_SYSCLOCK(SYSCLOCK_ILRC_DIV4); // Insert placeholder code to tell EasyPdkProg to calibrate the IHRC or ILRC internal oscillator. // The AUTO_CALIBRATE_SYSCLOCK(...) macro uses F_CPU (defined in the Makefile) to choose the IHRC or ILRC oscillator. // Alternatively, replace this with the more specific EASY_PDK_CALIBRATE_IHRC(...) or EASY_PDK_CALIBRATE_ILRC(...) macro from easy-pdk/calibrate.h EASY_PDK_CALIBRATE_ILRC(17000,3000); return 0; // Return 0 to inform SDCC to continue with normal initialization. }
Compiling was fine and oh so nice to have 2k available instead of the 1k ATTiny13 (although I am still keen to learn Padauk assembler and make this a bit smaller).
$> make program
sdcc -mpdk14 -c --std-sdcc11 --opt-code-size -DPFS154 -DF_CPU=1000000 -DTARGET_VDD_MV=3000 -I. -I../include -o .build/main.rel main.c sdcc -mpdk14 --out-fmt-ihx -o .output/FadeLED_PFS154.ihx .build/main.rel .build/lib.lib makebin -p .output/FadeLED_PFS154.ihx .output/FadeLED_PFS154.bin ---------- Segments ---------- . .ABS. 00000000 00000000 = 0. bytes (ABS,CON) . .ABS. 00000000 00000000 = 0. bytes (ABS,CON) HEADER1 00000000 00000002 = 2. bytes (ABS,CON) HEADER3 00000000 00000010 = 16. bytes (ABS,CON) PREG2 00000000 00000002 = 2. bytes (ABS,CON) RSEG0 00000000 00000002 = 2. bytes (ABS,CON) DATA 00000002 00000047 = 71. bytes (REL,CON) HOME 00000022 00000002 = 2. bytes (REL,CON) GSINIT 00000024 0000006C = 108. bytes (REL,CON) SSEG 0000004C 00000001 = 1. bytes (REL,CON) GSFINAL 00000090 00000002 = 2. bytes (REL,CON) CODE 00000092 000003E6 = 998. bytes (REL,CON) ------------------------------ Size of FadeLED_PFS154.bin: 1144 bytes easypdkprog -i noblankchk -n PFS154 write .output/FadeLED_PFS154.ihx Erasing IC... done. Writing IC (572 words)... done. Calibrating IC * ILRC SYSCLK=17000Hz @ 3.00V ... calibration result: 16436Hz (0xF0) done.
Left to do? Basically I want to keep on "Edisoning" this thing as follows:
a) Change the code to wind back the power of the ramping (e.g. instead of changing PWM from 40 to 220, try 30 to 160 or similar)
b) Upscale the super-caps. At the moment I'm getting around 4 hours of light - I think 6-8 hours is a reasonable goal.
c) Change some or all of the code from C to Assembler. This could be very scary given that assembler programming (which I love) is a pretty good path to madness.
d) Any suggestions??
Here's the video - enjoy!
No comments:
Post a Comment