The PIC12LF1571 is programmed in assembly. The code wakes up every 16ms using the watchdog timer - this is a frequency of 62.5 Hz, above the flicker-fusion rate, so you don't notice the blinking unless the light is moving. Each time the code wakes, it pulses the LED 6 times. The PIC oscillator is set to 500 kHz to save power. The desired 8 us current pulse to the inductor is one instruction cycle long at this frequency.
A few techniques have been used in the code to enhance reliability; this code is supposed to wake up about 20 billion times over the life of the battery. First, all unused locations in the instruction memory are filled with the "reset" instruction. If the program counter gets screwed up and points to a random place, it will just reset the code and everything will start clean again. The code itself actually issues a reset instruction every 256 times it wakes - this periodically re-initializes any register settings that may have become corrupted. It might be safer to reset every time, but this adds a lot of overhead, resulting in excessive current drain and reduced efficiency.
The OSCTUNE register is used to adjust the current drain of the assembled PCB. Since the inductor has a 30% tolerance, the software may require tweaking to obtain the desired battery life. Even though the OSCTUNE register only allows +/-12% adjustment, I have so far always been able to tune the current to the desired value (40 uA). If an inductor was discovered that didn't allow this, the number of LED pulses could be changed from 6 to either 5 or 7 to roughly tune the power, then the OSCTUNE adjustment could be again used for fine tuning.
Here is the code listing. Of course, if you want the code, you should get it from the GitHub repo.
;;;
;;; ten_year_lamp.asm :
;;; PIC12LF1571 code for (2x) LiFeS2 AA-powered LED glow marker
;;;
;;; 20161106 TCY
LIST P=12LF1571
#include <p12lf1571.inc>
ERRORLEVEL -302
ERRORLEVEL -305
ERRORLEVEL -207
;;;
;;; OSCTUNE_VAL: set to fine-tune current draw for compensating for
;;; component tolerances
; OSCTUNE_VAL equ 0
OSCTUNE_VAL equ b'00100000'
; OSCTUNE_VAL equ b'00011111'
;;;
;;; number of LED pulses per WDT timeout loop
;;;
N_PULSES equ 6
LED_PULSE macro
variable i
i = 0
while i < N_PULSES - 1
movwf LATA ;start inductor ramp-up
clrf LATA ;end inductor ramp-up
nop ; 2 nops here - tuned for minimum current
nop
i += 1
endw
movwf LATA ;start inductor ramp-up
clrf LATA ;end inductor ramp-up
endm
;;;
;;; I/O pin configuration
;;;
GATE_DRIVE_A equ 4
GATE_DRIVE_B equ 5
__CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOREN_OFF & _CLKOUTEN_OFF
__CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_HI & _LPBOREN_OFF & _LVP_ON
;;;
;;; variables in Common RAM (accessable from all banks)
;;;
CBLOCK 0x70
reset_counter
ENDC
ORG 0
RESET_VEC:
nop
nop
nop
nop
INTERRUPT_VEC:
BANKSEL OSCCON
movlw b'00111011' ; 500 kHz MF osc
movwf OSCCON
BANKSEL OSCTUNE
movlw OSCTUNE_VAL
movwf OSCTUNE
movlw .255
movwf reset_counter
BANKSEL ANSELA
movlw b'00000000' ; all digital I/O
movwf ANSELA
BANKSEL LATA
clrf LATA
BANKSEL TRISA
clrf TRISA ; set all lines as outputs
BANKSEL WDTCON
movlw b'00001001' ; WDT 16ms timeout
movwf WDTCON
BANKSEL LATA
movlw (1 << GATE_DRIVE_A) | (1 << GATE_DRIVE_B)
MAIN_LOOP:
LED_PULSE
sleep
decfsz reset_counter
goto MAIN_LOOP
reset
;; fill remainder of program memory with reset instructions
fill (reset), 0x0400-$
END
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.