Welcome, Guest
Username: Password: Remember me

TOPIC: Signal Generator

Signal Generator 5 years 11 months ago #10

  • JR
  • JR's Avatar
  • OFFLINE
  • Senior Member
  • Posts: 77
  • Thank you received: 31
  • Karma: 6
Arduino Due can be used as a signal generator, this sinewave.ino code generates a sinusoidal waveform which can be adjusted in frequency and amplitude. This example although simple it is important because other effects like vibrato are based on modulation using this same code.

Direct Digital Synthesis (DDS) in used to generate the signal. The output is generated at 44.1Khz sample rate with 12 bit resolution using both built in Digital To Analog Converter (DAC0 and DAC1)
There is a great tutorial about DDS and signal generation in the RCArduino site.

sinewave.ino:
This program creates a sinusoidal waveform adjusteble in amplitude and frequency.
  • Potentiometer 0: controls the frequency.
  • Potentiometer 2: controls the amplitude.
// Licensed under a Creative Commons Attribution 3.0 Unported License.
// Based on rcarduino.blogspot.com previous work.
// www.electrosmash.com/pedalshield
 
// sinewave.ino program creates a sinusoidal waveform adjustable in amplitude and frequency.
// potentiometer 0 controls the frequency.
// potentiometer 2 controls the amplitude.
 
int in_ADC0, in_ADC1;  //variables for 2 ADCs values (ADC0, ADC1)
int POT0, POT1, POT2, out_DAC0, out_DAC1; //variables for 3 pots (ADC8, ADC9, ADC10)
const int LED = 3;
const int FOOTSWITCH = 7; 
const int TOGGLE = 2; 
int accumulator, sample;
 
// Create a table to hold pre computed sinewave, the table has a resolution of 600 samples
#define no_samples 44100         // 44100 samples at 44.1KHz takes 1 second to be read. 
uint16_t nSineTable[no_samples]; // storing 12 bit samples in 16 bit variable.
 
void createSineTable()
{
  for(uint32_t nIndex=0; nIndex<no_samples; nIndex++)
  {
    // normalised to 12 bit range 0-4095
    nSineTable[nIndex] = (uint16_t)  (((1+sin(((2.0*PI)/no_samples)*nIndex))*4095.0)/2);
  }
}
 
void setup()
{
  createSineTable();
 
  //turn on the timer clock in the power management controller
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(ID_TC4);
 
  //we want wavesel 01 with RC 
  TC_Configure(TC1,1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);
  TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate
  TC_Start(TC1, 1);
 
  //enable timer interrupts on the timer
  TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;
  TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;
 
  //Enable the interrupt in the nested vector interrupt controller 
  //TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number 
  //(=(1*3)+1) for timer1 channel1 
  NVIC_EnableIRQ(TC4_IRQn);
 
  //ADC Configuration
  ADC->ADC_MR |= 0x80;   // DAC in free running mode.
  ADC->ADC_CR=2;         // Starts ADC conversion.
  ADC->ADC_CHER=0x1CC0;  // Enable ADC channels 0,1,8,9 and 10  
 
  //DAC Configuration
  analogWrite(DAC0,0);  // Enables DAC0
  analogWrite(DAC1,0);  // Enables DAC1
}
 
void loop()
{
  //Read the ADCs.
  while((ADC->ADC_ISR & 0x1CC0)!=0x1CC0); // wait for ADC 0, 1, 8, 9, 10 conversion complete.
  in_ADC0=ADC->ADC_CDR[7];            // read data from ADC0
  in_ADC1=ADC->ADC_CDR[6];            // read data from ADC1  
  POT0=ADC->ADC_CDR[10];                  // read data from ADC8        
  POT1=ADC->ADC_CDR[11];                  // read data from ADC9   
  POT2=ADC->ADC_CDR[12];                  // read data from ADC10  
}
 
//Interrupt at 44.1KHz rate (every 22.6us)
void TC4_Handler()
{
  //Clear status allowing the interrupt to be fired again.
  TC_GetStatus(TC1, 1);
 
  //update the accumulator, from 0 to 511
  accumulator=POT0>>3;
 
  //calculate the sample
  if(sample>=no_samples) sample=0;
  sample=sample+accumulator;
 
  //Generate the DAC output
  out_DAC0 = nSineTable[sample];
  out_DAC1 = 4095 - nSineTable[sample];
 
  //Add volume feature
  out_DAC0=map(out_DAC0,0,4095,0,POT2);
  out_DAC1=map(out_DAC1,0,4095,0,POT2);
 
  //Write the DACs.
  dacc_set_channel_selection(DACC_INTERFACE, 0);       //select DAC channel 0
  dacc_write_conversion_data(DACC_INTERFACE, out_DAC0);//write on DAC
  dacc_set_channel_selection(DACC_INTERFACE, 1);       //select DAC channel 1
  dacc_write_conversion_data(DACC_INTERFACE, out_DAC1);//write on DAC 
}
You can listen how the signal generator sounds in SoundCloud.

File Attachment:

File Name: sinewave.rar
File Size: 2 KB
keep it simple
Last Edit: 5 years 10 months ago by JR.
The administrator has disabled public write access.

Signal Generator 5 years 10 months ago #59

  • JR
  • JR's Avatar
  • OFFLINE
  • Senior Member
  • Posts: 77
  • Thank you received: 31
  • Karma: 6
J0uni found a bug in the sinewave.ino code that produces a clicking noise. It is a buffer overflow, easy to fix by changing the lines:
  //calculate the sample
  if(sample>=no_samples) sample=0;
  sample=sample+accumulator;
for
  //calculate the sample
  if(sample>=(no_samples-accumulator)) sample=0;
  sample=sample+accumulator;
you can see the after/before oscilloscope graphs below:
After:
bad.jpg
Before/Updated:
good.jpg
The new code is in the Github repository

Furthermore, J0uni suggested a modified code that could be more intuitive for some users, find below his code which is now also included in the Github folder.
// Licensed under a Creative Commons Attribution 3.0 Unported License.
// Based on rcarduino.blogspot.com previous work.
// www.electrosmash.com/pedalshield
 
// sinewave.ino program creates a sinusoidal waveform adjustable in amplitude and frequency.
// potentiometer 0 controls the frequency.
// potentiometer 2 controls the amplitude.
 
// VERSION HISTORY:
// 5.1.2014 - j0uni : This version uses bitwise AND to get the "rolling" pointer to sinewave table.
 
int in_ADC0, in_ADC1;  //variables for 2 ADCs values (ADC0, ADC1)
int POT0, POT1, POT2, out_DAC0, out_DAC1; //variables for 3 pots (ADC8, ADC9, ADC10)
const int LED = 3;
const int FOOTSWITCH = 7; 
const int TOGGLE = 2; 
int accumulator, sample;
 
// Create a table to hold pre computed sinewave, the table has a resolution of 600 samples
#define no_samples 4096 // must be two's complement!     
uint16_t nSineTable[no_samples]; // storing 12 bit samples in 16 bit variable.
 
void createSineTable()
{
  for(uint32_t nIndex=0; nIndex<no_samples; nIndex++)
  {
    // normalised to 12 bit range 0-4095
    nSineTable[nIndex] = (uint16_t)  (((1+sin(((2.0*PI)/no_samples)*nIndex))*4095.0)/2);
  }
}
 
void setup()
{
  createSineTable();
 
  //turn on the timer clock in the power management controller
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(ID_TC4);
 
  //we want wavesel 01 with RC 
  TC_Configure(TC1,1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);
  TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate
  TC_Start(TC1, 1);
 
  //enable timer interrupts on the timer
  TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;
  TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;
 
  //Enable the interrupt in the nested vector interrupt controller 
  //TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number 
  //(=(1*3)+1) for timer1 channel1 
  NVIC_EnableIRQ(TC4_IRQn);
 
  //ADC Configuration
  ADC->ADC_MR |= 0x80;   // DAC in free running mode.
  ADC->ADC_CR=2;         // Starts ADC conversion.
  ADC->ADC_CHER=0x1CC0;  // Enable ADC channels 0,1,8,9 and 10  
 
  //DAC Configuration
  analogWrite(DAC0,0);  // Enables DAC0
  analogWrite(DAC1,0);  // Enables DAC1
}
 
float pot = 0;
 
void loop()
{
  //Read the ADCs.
  while((ADC->ADC_ISR & 0x1CC0)!=0x1CC0); // wait for ADC 0, 1, 8, 9, 10 conversion complete.
  in_ADC0=ADC->ADC_CDR[7];            // read data from ADC0
  in_ADC1=ADC->ADC_CDR[6];            // read data from ADC1  
 
  POT0=ADC->ADC_CDR[10];                  // read data from ADC8          
  POT1=ADC->ADC_CDR[11];                  // read data from ADC9   
  POT2=ADC->ADC_CDR[12];                  // read data from ADC10  
}
 
//Interrupt at 44.1KHz rate (every 22.6us)
void TC4_Handler()
{
  //Clear status allowing the interrupt to be fired again.
  TC_GetStatus(TC1, 1);
 
  //update the accumulator, from 0 to 511
  accumulator=POT0>>3;
  sample=sample+accumulator;
 
  // Generate the DAC output
  // AND with TWO'S COMPLEMENT value to get the bits we really want to use
  out_DAC0 = nSineTable[sample & (no_samples-1)];
  out_DAC1 = 4095 - nSineTable[sample & (no_samples-1)];
 
  //Add volume feature
  out_DAC0=map(out_DAC0,0,4095,0,POT2);
  out_DAC1=map(out_DAC1,0,4095,0,POT2);
 
  //Write the DACs.
  dacc_set_channel_selection(DACC_INTERFACE, 0);       //select DAC channel 0
  dacc_write_conversion_data(DACC_INTERFACE, out_DAC0);//write on DAC
  dacc_set_channel_selection(DACC_INTERFACE, 1);       //select DAC channel 1
  dacc_write_conversion_data(DACC_INTERFACE, out_DAC1);//write on DAC 
}
keep it simple
Last Edit: 5 years 10 months ago by JR.
The administrator has disabled public write access.
The following user(s) said Thank You: estim8d

Signal Generator 5 years 8 months ago #81

  • estim8d
  • estim8d's Avatar
  • OFFLINE
  • New Member
  • Posts: 2
  • Thank you received: 1
  • Karma: 0
Awesome - thanks!

This and the metronome work great - led blinks, sound works on my newly assembled pedal shield.

However, when I try any of the effects that take the guitar input, I get nothing. How can I test to confirm I have it built correctly? Also, when I hit the foot switch, the guitar works through the pass-through to the amp.

Thanks for your help and what a great project you have here!
The administrator has disabled public write access.

Signal Generator 5 years 8 months ago #82

  • JR
  • JR's Avatar
  • OFFLINE
  • Senior Member
  • Posts: 77
  • Thank you received: 31
  • Karma: 6
If you can load the metronome and the led blinks and you can adjust the speed and tone means that the output stage is working and you can read the ADCs properly.
This are some ideas to troubleshoot the problem:
  • Double check that your top/bottom board looks the same as the one in the picture: www.flickr.com/photos/electrosmash/11155820805/sizes/h/
  • Look for shortcuts of unsoldered joints.
  • Check that the ICs are correctly placed.
  • If you have a multimeter you can verify the voltage at the pins of U1, just pedalSHIELD connected to the computer, with no guitar or amp plugged should be:
    pin1: 1.6V
    pin2: 1.6V
    pin3: 0.1V
    pin4: -5V (-4.5V is also fine)
    pin5: 1.5V
    pin6: 1.6V
    pin7: 1.6V
    pin8: +5V (+4.5V is also fine)
  • Try adjusting the RV1 potentiometer (I normally place it in a mid position).
  • With any code loaded (it does not matter), if you activate/put down the mix switch (left one), you should listen the clean guitar (pat of the guitar signal bypass Arduino and goes to the output mixer directly).

Let me know about your progress!!
keep it simple
Last Edit: 5 years 8 months ago by JR.
The administrator has disabled public write access.
The following user(s) said Thank You: estim8d

Signal Generator 5 years 8 months ago #84

  • estim8d
  • estim8d's Avatar
  • OFFLINE
  • New Member
  • Posts: 2
  • Thank you received: 1
  • Karma: 0
Success! :pedalmxr:

Thanks for your help again. I tracked it down to I had flipped two of the capacitors around the 1uf and 4.7uf. They looked identical and were loose so i never even looked while following the .pdf instructions. I saw the difference when looking at the schematics and your labeled photos.

The two on the left are the ones I swapped.






JR - you are the MAN! Thanks!
Last Edit: 5 years 8 months ago by estim8d. Reason: grammar
The administrator has disabled public write access.
The following user(s) said Thank You: JR

Signal Generator 5 years 8 months ago #90

  • profshadoko
  • profshadoko's Avatar
  • OFFLINE
  • New Member
  • Posts: 1
  • Thank you received: 1
  • Karma: 0
Here is another (corrected) version of sinewave. This one does not require that the wave table size be a power of two.
// Licensed under a Creative Commons Attribution 3.0 Unported License.
// Based on rcarduino.blogspot.com previous work.
// www.electrosmash.com/pedalshield
 
// sinewave.ino program creates a sinusoidal waveform adjustable in amplitude and frequency.
// potentiometer 0 controls the frequency.
// potentiometer 2 controls the amplitude.
 
int in_ADC0, in_ADC1;  //variables for 2 ADCs values (ADC0, ADC1)
int POT0, POT1, POT2, out_DAC0, out_DAC1; //variables for 3 pots (ADC8, ADC9, ADC10)
const int LED = 3;
const int FOOTSWITCH = 7; 
const int TOGGLE = 2; 
int accumulator, sample;
 
// Create a table to hold pre computed sinewave, the table has a resolution of 600 samples
#define no_samples 44100         // 44100 samples at 44.1KHz takes 1 second to be read. 
uint16_t nSineTable[no_samples]; // storing 12 bit samples in 16 bit variable.
 
void createSineTable()
{
  for(uint32_t nIndex=0; nIndex<no_samples; nIndex++)
  {
    // normalised to 12 bit range 0-4095
    nSineTable[nIndex] = (uint16_t)  (((1+sin(((2.0*PI)/no_samples)*nIndex))*4095.0)/2);
  }
}
 
void setup()
{
  createSineTable();
 
  //turn on the timer clock in the power management controller
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(ID_TC4);
 
  //we want wavesel 01 with RC 
  TC_Configure(TC1,1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);
  TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate
  TC_Start(TC1, 1);
 
  //enable timer interrupts on the timer
  TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;
  TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;
 
  //Enable the interrupt in the nested vector interrupt controller 
  //TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number 
  //(=(1*3)+1) for timer1 channel1 
  NVIC_EnableIRQ(TC4_IRQn);
 
  //ADC Configuration
  ADC->ADC_MR |= 0x80;   // DAC in free running mode.
  ADC->ADC_CR=2;         // Starts ADC conversion.
  ADC->ADC_CHER=0x1CC0;  // Enable ADC channels 0,1,8,9 and 10  
 
  //DAC Configuration
  analogWrite(DAC0,0);  // Enables DAC0
  analogWrite(DAC1,0);  // Enables DAC1
}
 
void loop()
{
  //Read the ADCs.
  while((ADC->ADC_ISR & 0x1CC0)!=0x1CC0); // wait for ADC 0, 1, 8, 9, 10 conversion complete.
  in_ADC0=ADC->ADC_CDR[7];            // read data from ADC0
  in_ADC1=ADC->ADC_CDR[6];            // read data from ADC1  
  POT0=ADC->ADC_CDR[10];                  // read data from ADC8        
  POT1=ADC->ADC_CDR[11];                  // read data from ADC9   
  POT2=ADC->ADC_CDR[12];                  // read data from ADC10  
}
 
//Interrupt at 44.1KHz rate (every 22.6us)
void TC4_Handler()
{
  //Clear status allowing the interrupt to be fired again.
  TC_GetStatus(TC1, 1);
 
  //update the accumulator, from 0 to 511
  accumulator=POT0>>3;
 
  //calculate the sample
  sample=sample+accumulator;
  if(sample>=no_samples) sample=sample-no_samples;
 
 
  //Generate the DAC output
  out_DAC0 = nSineTable[sample];
  out_DAC1 = 4095 - nSineTable[sample];
 
  //Add volume feature
  out_DAC0=map(out_DAC0,0,4095,0,POT2);
  out_DAC1=map(out_DAC1,0,4095,0,POT2);
 
  //Write the DACs.
  dacc_set_channel_selection(DACC_INTERFACE, 0);       //select DAC channel 0
  dacc_write_conversion_data(DACC_INTERFACE, out_DAC0);//write on DAC
  dacc_set_channel_selection(DACC_INTERFACE, 1);       //select DAC channel 1
  dacc_write_conversion_data(DACC_INTERFACE, out_DAC1);//write on DAC 
}
The administrator has disabled public write access.
The following user(s) said Thank You: JR

Signal Generator 2 years 6 months ago #851

  • Ray
  • Ray's Avatar
  • OFFLINE
  • Moderator
  • Posts: 692
  • Thank you received: 146
  • Karma: 41
The user PACALO uploaded a song using this sine generator, it is really cool:
This track with my band and arduino sinewave effect! Thank's!!

www.dropbox.com/s/8mwm8lps9soogfw/demokratos%20MBE.mov?dl=0
The administrator has disabled public write access.
Time to create page: 0.439 seconds
Powered by Kunena Forum
Joomla SEF URLs by Artio