ATtiny10 用プログラム (15)
VCO 回路の「コンパレータ + ワンショット」部分として機能する ATtiny10 のプログラム「scr_tn10.asm」のソースを下に示します。
AVR Studio 4.19 で「Atmel AVR Assembler」プロジェクトとして「scr_tn10」を新規作成し、デフォルトで自動作成される空の「scr_tn10.asm」に下の内容をコピー・アンド・ペーストします。
「scr_tn10.asm」
; ; scr_tn10.asm : software SCR (VCO building block) ; for Atmel AVR ATtiny10 ; ; 2011/01/26 created by pcm1723 ; ; .include "tn10def.inc" .nolist .include "tn10def.inc" .list ; ; set external sync IN / OUT mode .set SYNC_MODE = 0 ; no sync OUT / IN ; .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 = 41 ; discharge period = 41 clocks ; ; 41 / 8 [MHz] = 5.125 [us] ; ; sync OUT / IN port definition ; .set SYNC_BIT = PORTB2 ; PB2 (pin 4) ; ; discharge port definition ; .if (0 == SYNC_MODE) ; no sync IN/OUT .set DISCHG_BIT = PORTB2 ; PB2 (pin 4) .else ; sync IN/OUT mode .set DISCHG_BIT = PORTB1 ; PB1 (pin 3) .endif ; .set DISCHG_BIT = PORTB3 ; PB3 (pin 6) ; ; 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_capt_entry rjmp tim0_ovf_entry rjmp tim0_compa_entry rjmp tim0_compb_entry rjmp ana_comp_entry rjmp wdt_entry rjmp vlm_entry rjmp adc_entry ; reset_entry: ldi _temp_,low(RAMEND) out SPL,_temp_ ldi _temp_,high(RAMEND) out SPH,_temp_ ; setup SP to point RAMEND clr _zero_ ; ZERO register ; ; setup clock prescaler to 1/1 (8 MHz) ; ldi _temp_,0xD8 ; protect signature out CCP,_temp_ ; CCP <-- 0xD8 ldi _temp_,0x00 ; 0x00 = 1/1 out CLKPSR,_temp_ ; set CLocK PreScaleR ; ; setup port bits ; ldi _temp_,0x0f ; for all port bits, out DIDR0,_temp_ ; disable digital input out DDRB,_zero_ ; set all port bit to input out PUEB,_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 PUEB,SYNC_BIT ; enable pull up .endif ; discharge port csbi PORTB,DISCHG_BIT,DISCHG_POL ; discharge to 'VCC'/'GND' rail ; ; setup analog comparator ; sbi ACSR,ACIS1 sbi ACSR,ACIS0 ; positive edge sync 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 sbi PCICR,PCIE0 ; Pin Change Interrupt Enable .endif ; rcall discharge_25ms ; initial discharge ; ; enable watchdog timer in interrupt mode ; timeout = 4 sec ; ldi _temp_,(1<<WDIE)|(1<<WDP3) ; WatchDog Interrupt Enable out WDTCSR,_temp_ ; WDP[3:0] = 1000 for 4 sec timeout ; sei ; enable interrupt globally ; ; setup for sleep ; sleep_loop: ldi _temp_,0x01 ; idle mode, Sleep Enable out SMCR,_temp_ sleep ; sleep to wait comarator interrupt out SMCR,_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 .endif ; ; sync edge condition met, ; fall into 'ana_comp_entry' ; ; ANAlog COMParator interrupt service routine ; for discharge pulse generation ; ana_comp_entry: ; [#Clocks] sbi DDRB,DISCHG_BIT ; [1] start discharge .if (1 == SYNC_MODE) ; sync OUT mode csbi PORTB,SYNC_BIT,SYNC_POL ; [1] set active level .endif push _temp_ ; [2] in _temp_,SREG ; [1] push _temp_ ; [2] prologue ; ldi _temp_,(DISCHG_CLKS-11)/3 ; [1] init loop count ana_comp_L1: dec _temp_ ; [1] brne ana_comp_L1 ; [1/2] ; pop _temp_ ; [2] epilogue out SREG,_temp_ ; [1] pop _temp_ ; [2] cbi DDRB,DISCHG_BIT ; [1] end discharge, go to Hi-Z .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 sbi PCIFR,PCIF0 ; clear Pin Change Interrupt Flag .endif 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: vlm_entry: adc_entry: reti ;
これをそのままアセンブルすると、 1 月 26 日付けの記事の HEX ファイルができあがります。
AVR ネイティブのアセンブラなので、不正な 2 ワード lds/sts 命令が生成される可能性はありませんから、HEX ファイルの変換は不要です。
さらには、もともと、このプログラム自体はデータを保持する「変数」としては SRAM を使っていないので、lds/sts 命令そのものを全く使用していません。
プログラム中のシンボル「SYNC_MODE」の設定値、0、1、2 に対して、それぞれ
- 外部 SYNC 入出力なし (放電用端子は独立、Franco の補償が可能)
- 外部 SYNC 出力
- 外部 SYNC 入力
のモードになります。
PB3 (6 番ピン) のリセット機能を活かしたままだと、自由に使える入出力ピンは 3 本だけとなり、外部 SYNC 入出力端子を設けるためには、放電用の端子を電圧センス用の端子と同じピンに割り付ける必要があります。
プログラム中で、「SYNC_MODE」の値が 0 でない場合には、放電用の端子は PB1 (3 番ピン) に割り付け、SYNC 入出力は PB2 (4 番ピン) に割り付けるようにしています。
この場合、Franco の補償のための抵抗を挿入することはできず、SCR を使った回路に、より近づくことになります。
PB3 (6 番ピン) のリセット機能を禁止し、入出力ポートとして使える設定にすれば、放電用端子を独立させられますが、その場合には、チップに HEX ファイルを書き込む際に 12 V を 6 番ピンに加える必要があります。
ソース・プログラム自体は、そのような構成にも対応していますが、「SYNC_MODE」の値で自動的に切り換わるようにはなっていません。 ソース・プログラムに必要な設定を「手動」で行う必要があります。
ソース上の設定を、
.set SYNC_MODE = 1 ; sync OUT mode .set SYNC_POL = 0 ; negative going pulse .set DISCHG_POL = 1 ; discharge to 'VCC' rail .set DISCHG_CLKS = 41 ; discharge period = 41 clocks
とした場合のアセンブル結果の HEX ファイルを下に示します。
「scr_osyn.hex」
:100000000AC046C02EC044C043C042C041C029C03F :1000100037C03EC03DC00FE50DBF00E00EBF112749 :1000200008ED0CBF00E006BF0FE007BB11B913B924 :10003000129A0A9A119AF99AF89AFB9A08D000E64D :1000400001BF789401E00ABF88951ABFFBCF099AD7 :10005000129800270A95F1F71A95E1F70998129A74 :100060000895099A12980F930FB70F930AE00A9513 :10007000F1F70F910FBF0F910998129AA895189553 :100080000F930FB70F93E3DF0F910FBF0F911895E9 :020090001895C1 :00000001FF
SYNC_MODE = 1 の場合の回路は左のようになります。
「SYNC_POL」の設定で、SYNC パルスが正極性か、負極性かを切り替えられるようになっており、上のソース・ファイルでは「SYNC_POL = 0 」なので負極性の設定です。
出力波形自体は 1 月 26 日付けの記事に掲載してあります。
ソース上の設定を、
.set SYNC_MODE = 2 ; sync IN mode .set SYNC_POL = 0 ; negative going pulse .set DISCHG_POL = 1 ; discharge to 'VCC' rail .set DISCHG_CLKS = 41 ; discharge period = 41 clocks
として SYNC 入力モードにしてアセンブルした結果の HEX ファイルを下に示します。
「scr_isyn.hex」
:100000000AC047C02EC045C044C043C042C02BC038 :1000100038C03FC03EC00FE50DBF00E00EBF112746 :1000200008ED0CBF00E006BF0FE007BB11B913B924 :10003000BA981A9A119AF99AF89AFB9A829A909A0F :1000400008D000E601BF789401E00ABF88951ABF86 :10005000FBCF099A00270A95F1F71A95E1F709985D :10006000089502991895099A0F930FB70F930AE014 :100070000A95F1F70F910FBF0F910998A895889AEB :1000800018950F930FB70F93E4DF0F910FBF0F91E8 :040090001895189512 :00000001FF
回路を左に示します。
SYNC 出力モードでの出力ピンが入力ピンになっただけです。
「SYNC_POL」で外部 SYNC 入力の、どちら側のエッジで同期が掛かるかを設定します。
SYNC_POL = 0 でネガティブ・エッジ (1 → 0 のエッジ)、SYNC_POL = 1 でポジティブ・エッジ (0 → 1 のエッジ) が有効になります。
「レベル」ではなく、「エッジ」だけを見ているので、SYNC パルス幅には依存しません。
SYNC 入力ピンはプルアップされるので、オープン状態では「H」レベルを保ちます。
SYNC の機能としては、いわゆる「ハード・シンク」で、出力波形の位相にかかわらず、SYNC 入力のアクティブ・エッジで必ずリセットされます。