サイドトーン発生器

波形テーブルルックアップの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;
}