Created
February 21, 2019 23:03
-
-
Save jrepp/916864c42675a36d6f487631d9036e87 to your computer and use it in GitHub Desktop.
FM synth program
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // fm_mod.pde | |
| // minimalisitic fm synth program | |
| // guest - openmusiclabs.com - 12.12.16 | |
| // takes in audio data from the ADC and plays it out on | |
| // Timer1 PWM. 16b, Phase Correct, 31.25kHz. | |
| #include "MiniArDSP.h" | |
| #define PWM_FREQ 0x00FF // pwm frequency - see table | |
| #define PWM_MODE 0 // Fast (1) or Phase Correct (0) | |
| #define PWM_QTY 2 // number of pwms, either 1 or 2 | |
| // create sinewave lookup table | |
| // PROGMEM stores the values in the program memory | |
| // it is automatically included with MiniArDSP.h | |
| PROGMEM prog_int16_t sinewave[] = { | |
| // this file is stored in MiniArDSP and is a 1024 value | |
| // sinewave lookup table of signed 16bit integers | |
| // you can replace it with your own waveform if you like | |
| #include <sinetable.inc> | |
| }; | |
| PROGMEM prog_uint16_t logtable[] = { | |
| #include <logtable2.inc> | |
| }; | |
| volatile unsigned int freq1; // osc1 frequency | |
| volatile unsigned int freq2; // osc2 frequency | |
| volatile unsigned int amp1; // osc1 amplitude | |
| volatile unsigned int amp2; // osc2 amplitude | |
| volatile unsigned long phase1; // osc1 phase accumulator | |
| volatile unsigned long phase2; // osc2 phase accumulator | |
| volatile int output; // output storage register | |
| void setup() { | |
| // setup ADC | |
| ADMUX = 0x40; // right adjust, adc0, internal vcc | |
| ADCSRA = 0xe5; // turn on adc, ck/32, auto trigger | |
| ADCSRB =0x07; // t1 capture for trigger | |
| DIDR0 = 0x0f; // turn off digital inputs for adc0:3 | |
| // setup PWM | |
| TCCR1A = (((PWM_QTY - 1) << 5) | 0x80 | (PWM_MODE << 1)); // | |
| TCCR1B = ((PWM_MODE << 3) | 0x11); // ck/1 | |
| TIMSK1 = 0x20; // interrupt on capture interrupt | |
| ICR1H = (PWM_FREQ >> 8); | |
| ICR1L = (PWM_FREQ & 0xff); | |
| DDRB |= ((PWM_QTY << 1) | 0x02); // turn on outputs | |
| OCR1AH = 0; | |
| OCR1BH = 0; | |
| TIMSK0 = 0; // turn of t0 - no delay() or millis() | |
| sei(); // turn on interrupts - not really necessary with arduino | |
| } | |
| void loop() { | |
| while(1); // gets rid of jitter | |
| // nothing happens up here. if you want to put code up here | |
| // get rid of the ISR_NAKED and the reti(); below | |
| } | |
| ISR(TIMER1_CAPT_vect, ISR_NAKED) { // ISR_NAKED is used to save | |
| // clock cycles, but prohibits code in the loop() section. | |
| // output data | |
| OCR1AL = (output + 0x8000) >> 8; // convert to unsigned | |
| // and output the top byte | |
| OCR1BL = output; // output the bottom byte | |
| // get ADC data | |
| byte temp1 = ADCL; // you need to fetch the low byte first | |
| byte temp2 = ADCH; // yes it needs to be done this way | |
| unsigned int input = ((temp2 << 8) | temp1); // make a unsigned 16b value | |
| input = pgm_read_word_near(logtable + input); // get 2^x | |
| byte temp3 = ADMUX; | |
| if (temp3 == 0x40) { | |
| freq1 = input; | |
| ADMUX = 0x41; | |
| } | |
| if (temp3 == 0x41) { | |
| freq2 = input; | |
| ADMUX = 0x42; | |
| } | |
| if (temp3 == 0x42) { | |
| amp1 = input - 64; | |
| ADMUX = 0x43; | |
| } | |
| if (temp3 == 0x43) { | |
| amp2 = input - 64; | |
| ADMUX = 0x40; | |
| } | |
| phase1 += freq1; // increment to next sample | |
| int temp = pgm_read_word_near(sinewave + ((phase1 >> 8) & 0x03ff)); // get sinewave | |
| MultiSU16X16toH16(output, temp, amp1); // set amplitude output | |
| phase2 += freq2; //+ output; // increment to next sample | |
| phase2 += output; | |
| phase2 += output; | |
| phase2 += output; | |
| phase2 += output; | |
| temp = pgm_read_word_near(sinewave + ((phase2 >> 8) & 0x03ff)); // get sinewave | |
| MultiSU16X16toH16(output, temp, amp2); // set amplitude output | |
| reti(); // return from interrupt - required because of ISR_NAKED | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment