this code works on an Arduino uno, a guitar string frequency detector,
but the output values are approx halved when I use stm32f411,
anyone know why, could it be the sample frequency
#define LENGTH 512
byte rawData[LENGTH];
int count;
// Sample Frequency in kHz
const float sample_freq = 8919;
int len = sizeof(rawData);
int i,k;
long sum, sum_old;
int thresh = 0;
float freq_per = 0;
byte pd_state = 0;
void setup(){
//analogReference(EXTERNAL); // Connect to 3.3V
analogRead(A0);
Serial.begin(115200);
count = 0;
}
void loop(){
if (count < LENGTH) {
count++;
rawData[count] = analogRead(A0)>>2;
}
else {
sum = 0;
pd_state = 0;
int period = 0;
for(i=0; i < len; i++)
{
// Autocorrelation
sum_old = sum;
sum = 0;
for(k=0; k < len-i; k++) sum += (rawData[k]-128)*(rawData[k+i]-128)/256;
// Serial.println(sum);
// Peak Detect State Machine
if (pd_state == 2 && (sum-sum_old) <=0)
{
period = i;
pd_state = 3;
}
if (pd_state == 1 && (sum > thresh) && (sum-sum_old) > 0) pd_state = 2;
if (!i) {
thresh = sum * 0.5;
pd_state = 1;
}
}
// for(i=0; i < len; i++) Serial.println(rawData[i]);
// Frequency identified in Hz
if (thresh >100) {
freq_per = sample_freq/period;
Serial.println(freq_per);
}
count = 0;
}
}
this code works on an Arduino uno, a guitar string frequency detector,
but the output values are approx halved when I use stm32f411,
anyone know why, could it be the sample frequency
what determines the sampling rate in the following code? it looks like it's collecting samples as fast as it can and other interrupt processing (e.g. millis()) will affect it
if (count < LENGTH) {
count++;
rawData[count] = analogRead(A0)>>2;
}
Using "analogRead()" on an Arduino Uno and sampling as fast as possible, one gets a sampling rate of about 8919 samples per second.
On the stm32f411 the sampling rate is going to be something else. One would either have to figure out what that sampling rate is, for instance, by calibrating your program to a known pitch source, or by explicitly controlling the sampling rate. One way to do the latter would be to use "micros()" to get the current time and doing "analogRead()" when the time advances some amount of time from the previous sample. E.g. analogRead() each time micros() advances 100 microseconds would give 10000 samples per second.
gcjr:
what determines the sampling rate in the following code? it looks like it's collecting samples as fast as it can and other interrupt processing (e.g. millis()) will affect it
if (count < LENGTH) {
count++;
rawData[count] = analogRead(A0)>>2;
}
Yes, it has a hard coded constant that can be changed for different processors. It just needs to be changed for the STM32F4.
// Sample Frequency in kHz
const float sample_freq = 8919;
changed sample freq to 18460
this correctly gives me 82.41 Hz for a low E string
but incorrectly gives me
109.88 Hz for A string (should be 110.00 Hz)
146.51 Hz for D string (should be 146.83 Hz)
194.32 Hz for G string (should be 196.00 Hz)
246.13 Hz for B string (should be 246.94 Hz)
323.86 Hz for high E string (should be 329.63 Hz)
Is the tutorial there helpful? Also how did you connect AREF? I assume you are using the LM358 circuit? Perhaps you should show us some images of your hardware connections. These may need some changes for the STM32F4.
Have you tried measuring/using different input levels on the UNO and the STM32?
Though I would prefer to set a fixed sample frequency using timers you can probably get away with using millis() to time the collection loop and then, rather than use a const for sample_freq, calculate the value of sample_freq . Simple calculation since you know the number of samples being taken.
Also don’t understand what a period count is from the blog:
I measured the sample frequency to be 8919Hz. So for a low E (E2) you’d get a period count of 108 or 109 which gives a frequency difference of 0.7Hz but at high E (E4) the period would be 27 or 28 which is a frequency difference of 12Hz, so it won’t be as easy to get fine tunings on higher strings.
There are secondary effect but the effective frequency resolution one can obtain from an observation length of T is 1/T Hz. In the original code run on the Uno the observation period is 512 / 8919 seconds which means the frequency resolution is 8919 / 512 which equals 17.4 Hz . Your timeing measurement of the STM32 code indicates that it takes the 512 samples in roughly half the time of that of the Uno so the resoltion is roughly doubled. i.e.about 34 Hz. The resoution is substantially independent of the frequency one is trying to measure.
The only way you can improve the resolution is to increase the observation period. To my mind you have three ways you can implement this. There are probably more then 3 but ...
Setup a timer to interrupt at your desired sample points. In the interrupt handler you take a sample and put it into the data array. When you have read all the samples you need you signal to the loop() that the data needs to be processed.
You can increase the number of samples you are taking from 512 to a value that will give you your desired resolution.
You can put a small delay in your collection loop so that you get back to a sample rate of 8191.
The only method I would consider is 1) but then I am biased since it is the way I have been doing it for nearly 50 years.
The STM32F1 has a plethora of ADC operation modes. From the sheet:
The events generated by the general-purpose timers (TIMx) and the advanced-control timer (TIM1) can be internally connected to the ADC start trigger, injection trigger, and DMA trigger respectively, to allow the application to synchronize A/D conversion and timers.
The STM32F4 will have that and more. I'm just short of the data sheet right now.