tone()で利用するタイマのモードは、CTC(Clear Timer on Compare)というモードです。タイマカウンタ(TCNTn)と比較レジスタ(OCRnx)との値を比較し、同じ値になったときにタイマカウンタを0に戻します。この際、割り込みを発生させ、割り込みハンドラの中でピン出力のオン・オフを切り替えます。
volatilelongtimer0_toggle_count;volatileuint8_t*timer0_pin_port;volatileuint8_ttimer0_pin_mask;volatilelongtimer1_toggle_count;volatileuint8_t*timer1_pin_port;volatileuint8_ttimer1_pin_mask;volatilelongtimer2_toggle_count;volatileuint8_t*timer2_pin_port;volatileuint8_ttimer2_pin_mask;voidtone(uint8_t_pin,unsignedintfrequency,unsignedlongduration){uint8_tprescalarbits=0b001;longtoggle_count=0;uint32_tocr=0;int8_t_timer;_timer=toneBegin(_pin);if(_timer>=0){// Set the pinMode as OUTPUT
pinMode(_pin,OUTPUT);// if we are using an 8 bit timer, scan through prescalars to find the best fit
if(_timer==0||_timer==2){ocr=F_CPU/frequency/2-1;prescalarbits=0b001;// ck/1: same for both timers
if(ocr>255){ocr=F_CPU/frequency/2/8-1;prescalarbits=0b010;// ck/8: same for both timers
if(_timer==2&&ocr>255){ocr=F_CPU/frequency/2/32-1;prescalarbits=0b011;}if(ocr>255){ocr=F_CPU/frequency/2/64-1;prescalarbits=_timer==0?0b011:0b100;if(_timer==2&&ocr>255){ocr=F_CPU/frequency/2/128-1;prescalarbits=0b101;}if(ocr>255){ocr=F_CPU/frequency/2/256-1;prescalarbits=_timer==0?0b100:0b110;if(ocr>255){// can't do any better than /1024
ocr=F_CPU/frequency/2/1024-1;prescalarbits=_timer==0?0b101:0b111;}}}}if(_timer==0){TCCR0B=(TCCR0B&0b11111000)|prescalarbits;}else{TCCR2B=(TCCR2B&0b11111000)|prescalarbits;}}else{// two choices for the 16 bit timers: ck/1 or ck/64
ocr=F_CPU/frequency/2-1;prescalarbits=0b001;if(ocr>0xffff){ocr=F_CPU/frequency/2/64-1;prescalarbits=0b011;}if(_timer==1){TCCR1B=(TCCR1B&0b11111000)|prescalarbits;}}// Calculate the toggle count
if(duration>0){toggle_count=2*frequency*duration/1000;}else{toggle_count=-1;}// Set the OCR for the given timer,
// set the toggle count,
// then turn on the interrupts
switch(_timer){case0:OCR0A=ocr;timer0_toggle_count=toggle_count;bitWrite(TIMSK0,OCIE0A,1);break;case1:OCR1A=ocr;timer1_toggle_count=toggle_count;bitWrite(TIMSK1,OCIE1A,1);break;case2:OCR2A=ocr;timer2_toggle_count=toggle_count;bitWrite(TIMSK2,OCIE2A,1);break;}}}
// if we are using an 8 bit timer, scan through prescalars to find the best fit
if(_timer==0||_timer==2){ocr=F_CPU/frequency/2-1;prescalarbits=0b001;// ck/1: same for both timers
if(_timer==2&&ocr>255){ocr=F_CPU/frequency/2/32-1;prescalarbits=0b011;}if(ocr>255){ocr=F_CPU/frequency/2/64-1;prescalarbits=_timer==0?0b011:0b100;if(_timer==2&&ocr>255){ocr=F_CPU/frequency/2/128-1;prescalarbits=0b101;}if(ocr>255){ocr=F_CPU/frequency/2/256-1;prescalarbits=_timer==0?0b100:0b110;if(ocr>255){// can't do any better than /1024
ocr=F_CPU/frequency/2/1024-1;prescalarbits=_timer==0?0b101:0b111;}}}}
else{// two choices for the 16 bit timers: ck/1 or ck/64
ocr=F_CPU/frequency/2-1;prescalarbits=0b001;if(ocr>0xffff){ocr=F_CPU/frequency/2/64-1;prescalarbits=0b011;}if(_timer==1){TCCR1B=(TCCR1B&0b11111000)|prescalarbits;}
// Set the OCR for the given timer,
// set the toggle count,
// then turn on the interrupts
switch(_timer){case0:OCR0A=ocr;timer0_toggle_count=toggle_count;bitWrite(TIMSK0,OCIE0A,1);break;case1:OCR1A=ocr;timer1_toggle_count=toggle_count;bitWrite(TIMSK1,OCIE1A,1);break;case2:OCR2A=ocr;timer2_toggle_count=toggle_count;bitWrite(TIMSK2,OCIE2A,1);break;}