calib_freq = 4096000 ; Hz
calib_vdd = 3000 ; mV
+; Clock Parameters:
+; during playback: IHRC/4, WDT off, keep ILRC on
+active_clock = (( 0<<5 | 1<<4 | 0<<3 | 1<<2 | 0<<1 | 0<<0 ))
+; during deep-sleep: ILRC/1, WDT off
+sleep_clock = (( 7<<5 | 1<<4 | 0<<3 | 1<<2 | 0<<1 | 0<<0 ))
+; for extra power saving, consider: 6<<5|0<<3 for ilrc/6, 2<<5|1<<3 for ilrc/16
+
; cycle count (worst-case)
; mod3: 28
; g: 81
RET
init:
- ; clock setup (no calibration):
+ ; clock setup:
SET1 clkmd, #4 ; enable IHRC
- MOV a, #(( 0<<5 | 1<<4 | 0<<3 | 1<<2 | 0<<1 | 0<<0 ))
- MOV clkmd, a ; switch to IHRC/4, WDT off; keep ILRC on
+ MOV a, #active_clock
+ MOV clkmd, a ; switch to IHRC
;; .org 0x46 ; comment out on 2nd iteration
; calibration placeholder:
MOV sp, a
; portA setup:
- MOV a, #0xff
- MOV pac, a ; data direction: all output
- MOV a, #0
- MOV padier, a ; disable pin wakeup
+ MOV a, #0x50 ; data direction: PWM & debug output, rest input
+ MOV pac, a ; (conserves power, apparently)
+ MOV a, #(( 1<<4 ))
+ MOV padier, a ; disable pin wakeup, except on audio pin
MOV pa, a ; PortA data = 0
MOV paph, a ; disable all pull-ups
; highest "carrier frequency" for the PCM samples we can generate is by
; setting Timer2 to 6 bit, (IHRC/1)/1 mode, giving a frequency of
; (4*4.096MHz)/2^6 = 256kHz.
- MOV a, #0x7f
- MOV pwm, a
- MOV tm2b, a ; pwm duty cycle 50%
+ MOV pwm, a ; clear
+ MOV tm2b, a ; clear
MOV a, #(( 2<<4 | 3<<2 | 1<<1 | 0<<0 ))
MOV tm2c, a ; timer2: IHRC, PA4, PWM, not inverted
MOV a, #(( 0<<7 | 1<<5 | 0<<0 ))
ENGINT
loop:
- ;TODO: test i2==0x78 to enter halt()
+ MOV a, i2
+ CEQSN a, #0x78 ; compare, skip next if equal
; Note: usually, this is the place where the MCU is put into some
; sort of low power/sleep mode. But the Padauk's stopexe instruction
; causes the ISR to a) run at greatly reduced frequency (100hz vs
; anyways).
GOTO loop
+ ; at this point, i2==0x78, i.e. the music is finished.
+ ; => goto halt (fallthrough)
+halt:
+ DISGINT
+ CLEAR i2 ; clear halting signal
+
+ ; Note: disabling the timers isn't strictly necessary (as stopsys halts
+ ; all timers anyways), but I'm hoping it may reduce power consumption.
+ ; We're lucky that we only need to toggle a single bit to switch
+ ; between the required clock source and 'off' (0010xxxx->0000xxxx for
+ ; timer2, 100xxxxx->000xxxxx for timer16), so we can hack our way out
+ ; of loading an immediate each time.
+ SET0 tm2c, #5
+ SET0 t16m, #7
+
+ SET1 pa, #4 ; assert a high level on the audio pin for good measure
+ SET0 pac, #4 ; ... before setting it to input mode (optional)
+
+ ;switch to ilrc clock
+ MOV a, #sleep_clock
+ MOV clkmd, a
+ SET0 clkmd, #4 ; disable ihrc
+
+ STOPSYS
+ ; (at this point, we wait for an i/o-toggle wake up event to resume execution)
+
+ MOV a, #active_clock
+ MOV clkmd, a ; switch to IHRC again
+
+ SET1 pac, #4 ; restore output mode for audio pin
+
+ ;reenable timer16, timer2
+ SET1 tm2c, #5
+ SET1 t16m, #7
+
+ ENGINT
+ GOTO loop
+
interrupt:
PUSH af
- SET1 pa, #3 ;debug2
T1SN intrq, #2 ; if intrq.t16 is triggered, skip next
GOTO ivr_end
SET1 pa, #6 ; debug
- ;TODO: send pwm data to timer2
+ ; send pwm data to timer2:
MOV a, pwm
ADD a, #4
MOV tm2b, a