I've built a quick synthesiser, using an 8 bit R-2R DAC across port D, and am using a look up table to read waveforms which play at a pitch controlled by a potentiometer.
When the same pitch is selected for more than a few seconds, the pitch moves upward to a new pitch, then after a while moves back to the original pitch. I've narrowed the problem down to software, its not the pot being incorrectly attached, or any problems with the DAC hardware.
It kind of feels like a problem with something overflowing and landing on the correct position afterwards.
void interrupt(){
// create an 8 bit output for the DAC from
// the current waveTable values
byte output = waveTable[(byte) index0];
// map the byte on to PORTD
PORTD=output;
index0+=increment0;
}
This interrupt is triggered at 40 kHz, and reads from an 8 bit LUT with 256 entries. Frequency is calculated using the following:
void freqReader()
{
int freqRead = analogRead(5);
// map the frequency to wavetable values
int freqMap = map(freqRead,0,1023,30,0);
// stops its from constantly reading the LUT. index=0; was put in to reset the pitch accurately when transitioning between notes
if (!(freqMap==prevFreqMap))
{
// read a frequency in Hz from the freqTable
int frequency = freqTable[freqMap];
// calculate the frequency from i=n*f/Fs
// where n=number of values in the wavetable
increment0 = 0.0064*frequency;
index0 = 0;
}
prevFreqMap = freqMap;
}
Index=0; manages to make the pitch accurate, but is only triggered when new notes are played as there is a pop when triggered. It sounds acceptable when a new note is played, kind of like a plucking, but to use it during the same note would ruin the sound from the synthesiser.
I can't trace the problem any further, or come up with any fixes, has anyone had this problem before?
I should explain some things that I didn't in the first post:
Its an audio synthesiser that I have attached a headphone amp and jack to, so I just listen to the changes. I could measure with an oscilloscope and see if the difference is anything meaningful eg x% of frequency.
The frequency is read from another array, and is at fixed points of a scale. By shrinking the scale from 1024 of the 10 bit ADC to the 31 entry array, the knob actually has some distance to travel between changes in frequency.
The declaring of index0 isn't shown, but it is a float. The increment calculations require a lot of decimals, which need to be added to the index, which shouldn't be truncated or it won't be accurate. The reason it is then cast as a byte is to fit it to the 256 entry LUT. I might be wrong in presuming that by just casting it in the interrupt, I do not change the value of index0, just use the cast version for the array. Please correct me if this is not the case.
I'm beginning to think this solution wasn't as elegant as I originally thought...
For anyone interested, I'm attaching the full code, I hope the commenting is sufficient.
I think the problem is that you are using a float as an index. As the length of the note increases, the value of the float gets larger, and its least significant bits get less accurate.
Preferably, use integer arithmetic in place of floating point arithmetic. However, one simple change that might help is this. Whenever you increment 'index' such that it becomes greater than or equal to 256.0, subtract 256.0 from it.