/* --------------------------------------------------- Ultra Low Power LED flasher LED is connected to PA4 and is high active. Jan 16th, 2021, CPLDCPU - Initial version Jan 17th, 2021, Adaptive voltage ---------------------------------------------------- */ #include #include #include void PulseLED(uint8_t); uint8_t MeasureSupply(void); unsigned char _sdcc_external_startup(void) { // PDK_SET_FUSE(FUSE_IO_DRV_LOW); // Set output driving strength to low // PDK_SET_FUSE(FUSE_IO_DRV_NORMAL); // Set output driving strength to low CLKMD = CLKMD_ILRC | CLKMD_ENABLE_ILRC | CLKMD_ENABLE_IHRC; CLKMD = CLKMD_ILRC | CLKMD_ENABLE_ILRC ; // Note: it is important to turn off IHRC only after clock settings // have been updated. Otherwise the CPU stalls. return 0; } const uint8_t PWTAB[]={1,2,2,2,3,3,4,6,9,9,9,9,9,9,9,9}; uint8_t LEDcycles=0; uint8_t updatecounter=10; void main(void) { // Stopsys with 5V: 0.5 µA // The watchdog cannot wake up from stopexe!! PADIER = 0; // disable pins as wakeup source PBDIER = 0; // Also port B needs to be disabled even if it is not connected // to the outside of the package. touching the package can introduce // glitches and wake up the device INTEN = 0; // Make sure all interrupts are disabled INTRQ = 0; MISC = MISC_FAST_WAKEUP_ENABLE | MISC_LVR_DISABLE; // Enable faster wakeup (45 clocks instead of 3000) // This is important to not waste energy, as 40µA bias is already added during wakeup time // Disable LVR (optional) // Configure timer two for output on PA3 // --> Works, timer2 and 2 can be used for PWM while CPU is in STOPEXE mode // It appears that also timer 2 can wake up the CPU. This is not maskable? TM2C = TM2C_CLK_ILRC | TM2C_MODE_PWM; // Oscilator source for timer 2 is LRC (53 kHZ) TM2CT = 0; TM2S = TM2S_PRESCALE_DIV16 | TM2S_SCALE_DIV8; // Divide clock by 16*8=128 -> 53 kHz / 128 = 414 Hz TM2B = 1; // PWM threshold set to 1. The PWM event will trigger the wakeup. Wakeup occurs with 414 Hz / 256 = 1.66 Hz PA = 1<<4; // LED is on PA4, set all other output to zero. PAPH = 0; // Disable all pull up resistors PAC = 0; // Disable all outputs // Note: There is no series resistor for the LED // The LED current is limited to 2-4 mA by LOW IO driving setting // See Section 4.14 (p24) in PFS154 manual // The output is disabled when the LED is off to avoid leakage for (;;) { PulseLED(LEDcycles); if (!--updatecounter) { uint8_t adc=MeasureSupply(); LEDcycles=PWTAB[adc]; updatecounter=10; } __stopexe(); // Put MCU in power down mode until wakeup by timer } } void PulseLED(uint8_t cycles) { PA |=1<<4; switch(cycles) { case 1: PAC |=1<<4;PAC &=~(1<<4);break; case 2: PAC |=1<<4;__nop();PAC &=~(1<<4);break; case 3: PAC |=1<<4;__nop();__nop();PAC &=~(1<<4);break; case 4: PAC |=1<<4;__nop();__nop();__nop();PAC &=~(1<<4);break; case 5: PAC |=1<<4;__nop();__nop();__nop();__nop();PAC &=~(1<<4);break; case 6: PAC |=1<<4;__nop();__nop();__nop();__nop();__nop();PAC &=~(1<<4);break; case 7: PAC |=1<<4;__nop();__nop();__nop();__nop();__nop();__nop();PAC &=~(1<<4);break; case 8: PAC |=1<<4;__nop();__nop();__nop();__nop();__nop();__nop();__nop();PAC &=~(1<<4);break; default: case 9: PAC |=1<<4;__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();PAC &=~(1<<4);break; } } /* Uses the internal comparator to measure the supply voltage */ uint8_t MeasureSupply(void) { GPCC = GPCC_COMP_ENABLE | GPCC_COMP_MINUS_BANDGAP_1V2; // Activate comparator. Negative input is Bandgap, Positive input is resistor ladder // We use case 3 withg GPCS.4 =0 and .5 = 1, So Vinternal=0.25*Vdd+(n+1)/32 // gpcc.6 is 1 when Vresistor > VBG // SAR ADC using the internal resistor ladder __asm mov a, #0x20 ; GPCS.4 = 0 GPCS.5 = 1 or a, #0x08 ; Bit 3 mov __gpcs,a t0sn __gpcc, #6 xor a, #0x08 or a, #0x04 ; Bit 2 mov __gpcs,a t0sn __gpcc, #6 xor a, #0x04 or a, #0x02 ; Bit 1 mov __gpcs,a t0sn __gpcc, #6 xor a, #0x02 or a, #0x01 ; Bit 0 mov __gpcs,a t0sn __gpcc, #6 xor a, #0x01 and a,#15 // Result is now in A register. set0 __gpcc,#7 ; turn comparator off __endasm; // debug output // outputs a long pulse for 1, a short pulse for zero #if 0 __asm mov p, a set1 __pac,#3 sl a set1 __pa,#3 swapc __pa,#3 set0 __pa,#3 sl a set1 __pa,#3 swapc __pa,#3 set0 __pa,#3 sl a set1 __pa,#3 swapc __pa,#3 set0 __pa,#3 sl a set1 __pa,#3 swapc __pa,#3 set0 __pa,#3 sl a set1 __pa,#3 swapc __pa,#3 set0 __pa,#3 sl a set1 __pa,#3 swapc __pa,#3 set0 __pa,#3 sl a set1 __pa,#3 swapc __pa,#3 set0 __pa,#3 sl a set1 __pa,#3 swapc __pa,#3 set0 __pa,#3 mov a, p __endasm; #endif __asm ret __endasm; return 0; // to get rid of warning. This line is never executed }