Skip to content

Instantly share code, notes, and snippets.

@jrepp
Created February 21, 2019 23:03
Show Gist options
  • Select an option

  • Save jrepp/916864c42675a36d6f487631d9036e87 to your computer and use it in GitHub Desktop.

Select an option

Save jrepp/916864c42675a36d6f487631d9036e87 to your computer and use it in GitHub Desktop.
FM synth program
// 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