アナログシンセの 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 ;