アナログシンセの VCO ブロック (49) -- ATtiny13 版プログラム (2)

ATtiny13 版のプログラム・リストを下に示します。
ATtiny13 ではコンパレータ入力として内部バンドギャップ・リファレンス (約 1.1 V) を選択できるので、そのサポートも付け加えてありますが、動作確認はしていません。
外部クロックへの切り替えは、プログラム自体には関与せず、フラッシュ・ライタでフューズビットを書き換えます。
下のリストでは、(外部) 12 MHz クロックを使った場合にリセット/リワインド・パルス幅が約 5 μs、および SYNC パルス出力モードの設定になっており、その他の設定ではテストしていません。

;
; scr_tn13.asm : software SCR (VCO building block)
;                for Atmel AVR ATtiny13
;
; 2013/08/03 modified for ATtiny13
; 2011/01/26 'scr_tn10.asm' created by pcm1723
; 

        .nolist
        .include "tn13def.inc"
        .list

;
; invert comparator output
;
        .set    COMP_INV = 0 ; no invert
;        .set    COMP_INV = 1 ; invert
;
; use internal bandgap reference (1.1 V)
; as comparator IN+ 
;
        .set    BANDGAP_SEL = 0  ; bandgap ref not used
;        .set    BANDGAP_SEL = 1  ; bandgap ref used
;
; set external sync IN / OUT mode
;
        .set    SYNC_MODE = 1 ; sync OUT mode
;        .set    SYNC_MODE = 2 ; sync IN mode
;
; set sync polarity
;
        .set    SYNC_POL = 0 ; negative going pulse
;        .set    SYNC_POL = 1 ; positive going pulse
;
; set discharge polarity
;
        .set    DISCHG_POL = 1  ; discharge to 'VCC' rail
;        .set    DISCHG_POL = 0  ; discharge to 'GND' rail
;
; set discharge period in terms of CPU clocks
;
        .set    DISCHG_CLKS = 60 ; discharge period = 60 clocks
                                 ; 60 / 12 [MHz] = 5 [us]
;
; sync OUT / IN port definition
;
        .set    SYNC_BIT = PORTB4  ; PB4 (pin 3)
;
;
; discharge port definition
;
         .set    DISCHG_BIT = PORTB2 ; PB2 (pin 7)
;
; register assignment (refer to AVR GCC convention)
;  
        .def    _temp_ = r16  ; temporary register
        .def    _zero_ = r17  ; zero register
;
; macro for sbi @0,@1 / cbi @0,@1 selection by @2
;
        .macro  csbi
         .if     (1 == @2)
          sbi     @0,@1
         .else
          cbi     @0,@1
         .endif
        .endm
;
; reset / interrupt vector table
;
        .cseg
        rjmp    reset_entry
        rjmp    int0_entry
        rjmp    pcint0_entry
        rjmp    tim0_ovf_entry
        rjmp    ee_rdy_entry
        rjmp    ana_comp_entry
        rjmp    tim0_compa_entry
        rjmp    tim0_compb_entry
        rjmp    wdt_entry
        rjmp    adc_entry
;
reset_entry:
        ldi     _temp_,low(RAMEND)
        out     SPL,_temp_
        clr     _zero_         ; ZERO register
;
; setup clock prescaler to 1/1 (8 MHz)
;
        ldi     _temp_,0x80    ; CLocK Prescacle Change Enable
        out     CLKPR,_temp_   ; CLKPR <-- 0x80
        ldi     _temp_,0x00    ; 0x00 = 1/1 
        out     CLKPR,_temp_   ; set CLocK PreScaleR
;
; setup port bits
;
        ldi     _temp_,0x3f      ; for all port bits,
        out     DIDR0,_temp_     ; disable digital input
        out     DDRB,_zero_      ; set all port bit to input
        out     PORTB,_zero_     ; disable pull up
; sync IN/OUT
        .if     (1 == SYNC_MODE) ; sync OUT mode?
          csbi    PORTB,SYNC_BIT,(1-SYNC_POL) ; set normally level
          sbi     DDRB,SYNC_BIT  ; set to output direction
        .elif   (2 == SYNC_MODE) ; sync IN mode?
          cbi     DIDR0,SYNC_BIT ; enable digital input
          sbi     PORTB,SYNC_BIT  ; enable pull up
        .endif
; discharge port
        csbi     PORTB,DISCHG_BIT,DISCHG_POL  ; discharge to 'VCC'/'GND' rail
;
; setup analog comparator
;
        .if    (BANDGAP_SEL)
          sbi    ACSR,ACBG        ; bandgap ref select
        .endif
        sbi    ACSR,ACIS1
        .if    (0 == COMP_INV)
          sbi    ACSR,ACIS0       ; positive edge sync
        .endif
        sbi    ACSR,ACIE        ; interrupt enable
;
; setup Pin Change INTerrupt if external sync IN mode
;
        .if    (2 == SYNC_MODE)
        sbi    PCMSK,SYNC_BIT   ; set Pin Change bit MaSK
        ldi    _temp_,(1 << PCIE)
        out    GIMSK,_temp_      ; Pin Change Interrupt Enable
        .endif
;
        rcall  discharge_25ms   ; initial discharge
;
; enable watchdog timer in interrupt mode
; timeout = 4 sec
;
        ldi    _temp_,(1<<WDTIE)|(1<<WDCE)|(1<<WDP3) ; WatchDog Interrupt Enable
        out    WDTCR,_temp_    ; WDP[3:0] = 1000 for 4 sec timeout
;
        sei                     ; enable interrupt globally
;
; setup for sleep
;
sleep_loop:
        ldi    _temp_,(1<<SE)   ; idle mode, Sleep Enable
        out    MCUCR,_temp_
        sleep                   ; sleep to wait comarator interrupt
        out    MCUCR,_zero_      ; clear Sleep Enable bit
        rjmp   sleep_loop       ; sleep again
;
; discharge for approx. 25 ms @ 8 MHz clock
;
discharge_25ms:
        sbi     DDRB,DISCHG_BIT ; start discharge
        .if     (1 == SYNC_MODE) ; sync OUT mode?
          csbi    PORTB,SYNC_BIT,SYNC_POL ; set active level
        .endif
        clr     _temp_          ; loop_count = 256 * 256
d25ms_L0:
        dec     _temp_          ; 
        brne    d25ms_L0        ; inner loop
        dec     _zero_          ; 
        brne    d25ms_L0        ; outer loop
        cbi     DDRB,DISCHG_BIT ; end discharge, go to Hi-Z
        .if     (1 == SYNC_MODE) ; sync OUT mode?
          csbi    PORTB,SYNC_BIT,(1-SYNC_POL) ; set normally level
        .endif
        ret
;
; Pin Change INTerrupt service routine
; for external sync input
;
pcint0_entry:  
        .if     (2 == SYNC_MODE)  ; external sync IN mode
          .if   (1 == SYNC_POL)   ; sync at 0 --> 1 edge
            sbis    PINB,SYNC_BIT ; (1 == SYNC_BIT) ?
            reti                  ; no, immediate return
          .else                   ; sync at 1 --> 0 edge
            sbic    PINB,SYNC_BIT ; (0 == SYNC_BIT) ?
            reti                  ; no, immediate return
          .endif 
        .else
          reti                    ; immediate return 
        .endif           
;
; sync edge condition met,
; fall into 'ana_comp_entry'
;
; ANAlog COMParator interrupt service routine
; for discharge pulse generation
;
ana_comp_entry:
;                              [#Clocks] 
        push    _temp_          ; [2]
        in      _temp_,SREG     ; [1]
        push    _temp_          ; [2] prologue
        .if     (1 == DISCHG_POL)
          sbi     PORTB,DISCHG_BIT ; [1] set discharge polarity to 'H'
                                   ;     and pullup enabled by side effect
        .endif
        sbi     DDRB,DISCHG_BIT ; [1] switch to Lo-Z to start discharge
        .if     (1 == SYNC_MODE) ; sync OUT mode
          csbi    PORTB,SYNC_BIT,SYNC_POL ; [1] set active level
        .endif
;
        ldi     _temp_,(2*DISCHG_CLKS-2*4+1)/(2*3) ; [1] init loop count
ana_comp_L1:
        dec     _temp_          ; [1]
        brne    ana_comp_L1     ; [1/2]
;
        cbi     DDRB,DISCHG_BIT ; [1] end discharge, go to Hi-Z
        .if     (1 == DISCHG_POL)
          cbi     PORTB,DISCHG_BIT ; [1] disable pullup
        .endif
        .if     (1 == SYNC_MODE) ; sync OUT mode?
          csbi    PORTB,SYNC_BIT,(1-SYNC_POL) ; set normally level
        .endif
        wdr                     ; watchdog timer reset
        .if     (2 == SYNC_MODE) ; external sync IN mode
          out     GIFR,_zero_    ; clear Pin Change Interrupt Flag
        .endif
        sbi     ACSR,ACI        ; clear spurious comparator interrupt
        pop     _temp_          ; [2] epilogue
        out     SREG,_temp_     ; [1]
        pop     _temp_          ; [2]
        reti
;
; WatchDog Timer interrupt service routine
; to recover stucked output
;
wdt_entry:
        push    _temp_          ; 
        in      _temp_,SREG     ; 
        push    _temp_          ; prologue
;
        rcall   discharge_25ms  ; discharge for 25 ms
;
        pop     _temp_          ; epilogue
        out     SREG,_temp_     ; 
        pop     _temp_          ; 
        reti
;
; invalid interrupts
;
int0_entry:
tim0_capt_entry:
tim0_ovf_entry:
tim0_compa_entry:
tim0_compb_entry:
ee_rdy_entry:
adc_entry:
        reti
;