Jump to content
gonya707

FM synthesizer

Recommended Posts

This is a FM synthesizer made for the Stellarpad using the AD9850 function generator.

 

YDiYoFh.jpg

The FM synthesis is teorethicaly the same as the all-known frequency modoulation used for radio communications, but in this case the frequencies are suited to stay at the audio range, i.e. from 20Hz to 20kHz. Basically The AD9850 is used as a sine wave generator, and the microcontroller create each cycle a sample of ANOTHER sine wave, this one goes from 5Hz to 100Hz more or less. We can modulate this two sinewaves, the AD signal being the carrier and the stellaris signal the baseband. The outcoming spectrum can be represented by the Bessel functions for each harmonic, making some interesting sounds for electronic music. Attaching two potentiometers we can control the baseband sine frequency and its amplitude, thus controlling the modulation index.

This is a recording I made with this project (is there any way to display souncloud embed on the forum?):

https://soundcloud.com/gonya707/fm-synthesizer-grsynth-com

The first seconds were non-modulated waves and then I pressed a note (C3 if I recall correctly) and played with the potentiometers..

 

This is a simplfied schematic of the project.

 

DrEDT8v.png

 

The complete schematic of each module can be found respectively here:

 

AD9850 module: http://www.analog.com/static/imported-files/data_sheets/AD9850.pdf

MIDI boosterpack by RobG: http://forum.43oh.com/topic/1773-midi-booster-pack/page-3#entry23763

 

 

And finally, this is the main code for this project:





//
// FM Synthesizer for Stellaris Launchpad and AD9850
// Coded by Gonzalo Recio
//

#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "inc/hw_gpio.h"
#include "inc/hw_sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/uart.h"
#include "inc/hw_ints.h"
#include "driverlib/interrupt.h"
#include "driverlib/adc.h"

#include "math.h"
#include "midi.h"
#include "STELLARIS_AD9850.h"

#define PI 3.141592
#define GPIO_PB0_U1RX 0x00010001
#define GPIO_PB1_U1TX 0x00010401

// Global variables
unsigned long message;
unsigned long note, velocity;
int on=0, off=0;
int flagON = 0, flagOFF = 0;
unsigned short i = 0, z,y;
float t = 0;
float freq = 0;
unsigned long ulADC0_Value[4];

int main(void){

	//50MHz clock
	SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

	//enable UART for MIDI T/R
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
	GPIOPinConfigure(GPIO_PB0_U1RX); //B0 receptor
	GPIOPinConfigure(GPIO_PB1_U1TX); //B1 transmitter
	GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 31250, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE) );

	//enable AD9850
	SysCtlPeripheralEnable(PORT_ENABLE);
	GPIOPinTypeGPIOOutput(PORT, W_CLK|FQ_UD|DATA|RESET);
	AD9850_Init();
	AD9850_Reset();

    //ADC0 config
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    SysCtlADCSpeedSet(SYSCTL_ADCSPEED_125KSPS);
    ADCHardwareOversampleConfigure(ADC0_BASE, 4);
    ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_4|GPIO_PIN_5);
    ADCSequenceStepConfigure(ADC0_BASE,  1 , 0 , ADC_CTL_CH8 );
    ADCSequenceStepConfigure(ADC0_BASE,  1 , 1 , ADC_CTL_CH9 | ADC_CTL_IE | ADC_CTL_END);
    ADCSequenceEnable(ADC0_BASE, 1);
    ADCIntClear(ADC0_BASE, 1);

	//Begin the interrupt detection
	IntMasterEnable();
	IntEnable(INT_UART1);
	UARTIntEnable(UART1_BASE, UART_INT_RX | UART_INT_RT);

	while(1){

		if (on == off){
			AD9850_PowerDown();
		}
		else{
	        ADCProcessorTrigger(ADC0_BASE, 1 );
	        while(!ADCIntStatus(ADC0_BASE, 1 , false)){  }
	        ADCIntClear(ADC0_BASE, 1 );
	        ADCSequenceDataGet(ADC0_BASE, 1 , ulADC0_Value);

			AD9850_Osc(freq + y*sin(t), 0);
		}

		z=(ulADC0_Value[0] / 4 ) + 1;
		y=(ulADC0_Value[1]);
		i=(i + 1) % z;
		//timescale between 0 and 2*pi
		t=((float)i / (float)z ) * 2 * PI;

	}
}

//uart1handler
void UARTIntHandler(void){
	IntDisable(INT_UART1);									   

	unsigned long ulStatus = UARTIntStatus(UART1_BASE, true);  //get interrupt status. RX or RT?
	UARTIntClear(UART1_BASE, ulStatus); 					   //clear the asserted interrupts

	while(UARTCharsAvail(UART1_BASE)){ 						   //loop while there are chars
		message = UARTCharGetNonBlocking(UART1_BASE);

		if(flagON){							// if the last message was a note ON, then this message is
			on++;							// the note code.
			if (note != message){
				note = message;
				freq = code2Freq(note); 
			}
		}
		else if(flagOFF){
			off++;
		}

		flagON  = isNoteOn(message);
		flagOFF = isNoteOff(message);
	}

	IntEnable(INT_UART1);
}

The codes for the AD9850 library were also made by me, you can find them in this other thread.
The MIDI library used in this project is here midi.c, midi.h. I was also the author for this one.

If you don't have a MIDI boosterpack I've made also a no-MIDI version, the on-board switches trigger two different notes. You still need to attach a potentiometer to E4 and E5 though. You can find this version HERE.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×