サイドトーン発生器
波形テーブルルックアップのDDS方式で正弦波を出力する,単なる発振器です.
エレキーなどと組み合わせてモールスのサイドトーンにするような想定で600~900Hzで発振周波数が可変できます.
ON/OFFの機能は今のところありません.
AVRマイコン,ATtiny85を使っています.このシリーズは内部RC発振器に加えてPLLが載っています.RCを源発振にPLLを動かし64MHzを生成,2回叩いた16MHzでCPUを動かします.64MHzはそのままタイマに供給できます.これで8bitタイマを駆動してPWMすると64MHz/8bit = 250kHz周期のPWMとなり,後ろに繋ぐLPFが簡単になりますので,8ピンマイコンにRCフィルタを付けるだけという簡便さで,波形生成を行うことができます.
今回は正弦波テーブルをルックアップする形でのDDS方式です.
Fractional accumulator のフォーマットは F12.20 として整数部12bit,小数部20bitを確保します.ルックアップテーブルは 4096ステップ(=12bit),語調は8bitです.ATtiny85のEEPROMサイズが4KBであるというところから決めました.正弦波テーブルは4096ステップで1周期分です.正弦波の周期性からうまく折り返せば1周期の1/4のテーブル格納サイズですみますが,今回は何も考えずにそのまま持っています.小数点に20bitを確保しているものの,出力周波数を,12bitのADC値からポイントされる600~900Hzで振る分にはほとんど使われることはないでしょう.
/* * * Title: CW Sidetone * Software: * Target: ATtiny85 * */ /* * CKSEL[3:0] : PLL clock 16MHz */ #include <inttypes.h> #include <avr/io.h> #include <avr/eeprom.h> #include <avr/pgmspace.h> #include <avr/interrupt.h> #include <avr/sleep.h> // 4Kbyte 8bit sine table ... period of one cycle #include "table.h" #define F_CPU 16000000 #define FS (F_CPU/256.) // Phase Accumulator notation // 12.20 : [b11 b10 b9 ... b1 b0 (.) f0 f1 ... f18 f19] // phase acc fractional format : (select one) // 12.20 -> NTABLE 4096, 20bit shift // FORMAT12_20 // NTABLE for Number of sinusoidal TABLE for one cycle // kstep for frequency value calculation // FORMAT12_20 #define NTABLE 4096 #define _KSTEP 65536 //const double kstep = 1. * 1024. * 1024. * (double)NTABLE / FS; /* How to calculate acc update step value(N) for f[Hz] N = FS / f ex) 105 -> 595.24Hz 83 -> 753.01Hz 69 -> 905.80Hz */ /* How to tie ADC value to osc frequency(update step) (105-69)/128 = 0.28125 128: 7bit ADC <update step> = 105 - 0.28125 * <ADC value> */ #define LFREQ 600L #define HFREQ 900L #define ADCRANGE 256 // Port definition #define PWM_A OCR1A #define PWM_A_BIT PB1 #define PWM_A_PORT PORTB #define PWM_A_DDR DDRB // Phase accumulator structure definition struct _Phase { uint32_t step; uint32_t acc; }; // Phase acc instantiation volatile static struct _Phase Osc = { 0, 0 }; volatile static uint8_t gflg_ADC = 0; /* Timer0 sampling output every 256-cycle */ // FCPU = 20MHz -> // FCPU = 16.7MHz -> ISR(TIM0_OVF_vect) { static uint16_t ptr; static uint16_t tmp; static uint8_t cnt; PWM_A = pgm_read_byte(&tblSine[ptr]); // FORMAT12_20 // 20-bit shift because fractional part in the phase accumulator is 20-bit. // ptr = Osc.acc>>20; tmp = Osc.acc / 65536; ptr = tmp>>4; Osc.acc += Osc.step; // phase accumulator update of Modulator // ADC execution semaphore if (!cnt++) gflg_ADC = 1; } void init(void) { // ports PWM_A_DDR |= (1<<PWM_A_BIT); // PWM_A port as output // PLL enable for PWM 64MHz clock PLLCSR = (1<<PLLE)|(1<<PCKE); // timer1 for PWM output TCCR1 = (1<<PWM1A)|(1<<COM1A1)|(1<<CS10); GTCCR = 0; // timer0 for periodical phase update TIMSK |= (1<<TOIE0); // start timer0 TCCR0A = 0; TCCR0B = (1<<CS00); // adcs // ADC1 Frequency // b001000xx : Vref=Vcc, shifted left, ADC1 ADMUX = (0<<REFS1)|(0<<REFS0)|(0<<ADLAR)|(0x01<<MUX0); ACSR = (1<<ACD); DIDR0 = (1<<ADC1D); ADCSRA = (1<<ADEN)|(0x07<<ADPS0); ADCSRB = 0x00; } int main(void) { volatile static uint16_t adc_data; volatile static uint32_t N, tmp32; init(); sei(); while (1) { sleep_mode(); // A/D Conv. if (gflg_ADC) { ADCSRA |= (1<<ADSC); while (ADCSRA & (1<<ADSC)) ; adc_data = ADC >> 2; N = (uint32_t)(LFREQ*256 + (HFREQ-LFREQ) * adc_data); // =<update step> *16 // calculated <update step> puts HERE // <b7 ... b1 b0 (.) f0 f1 ... f7> // 12.20 : [b11 b10 b9 b8 b7 ... b1 b0 (.) f0 f1 ... f7 f8 ... f18 f19] // tmp32 <<= 12; tmp32 = N << 8; cli(); Osc.step = tmp32; sei(); gflg_ADC = 0; } } return 0; }