mikey.antonakakis
Active member
I've got a Teensy 4.0 integrated into my project car, and its main job is sending and receiving CAN messages, reading sensors, actuating things, and sending data to a Nextion display. Overall the system is working great, but lately I've been noticing some suspicious data from one of its smaller jobs, using FreqMeasure to monitor the shaft speed of a turbo.
The turbo has a variable relucatance sensor generating a signal with one pulse per revolution, run through a VR signal conditioner and logic level shifter to generate a 3.3v square wave. Shaft speed with engine running could range from 0rpm to 120,000rpm, or 0Hz to 2kHz (although mostly it shouldn't exceed 90,000rpm with my current engine setup). I know this is higher than the ideal range for FreqMeasure, but when it works I see up to 90,000rpm readings coming out nice and clean.
However, I've noticed at times that it will start reporting smaller values, almost like it's hitting a wall (for example, reading a very flat 30,000rpm when it should be increasing from about 50,000 to 80,000rpm). Conversely, at idle the shaft speed should be something like 3,000rpm, but I see frequent spikes to very high values unless I use a more aggressive moving average filter. I am fairly certain this is an issue with my implementation - I first wrote the turbo speed portion of my code about 2 years ago so I don't clearly remember my reasons for writing it the way I did.
Overall, my code runs on an event-based timing, using receipt of a CAN message to run the bulk of the code, using FlexCAN_T4. Messages from different devices run different sections of code, all contained within a CAN handler function. The loop() function contains just 2 lines of code:
The updateTurboSpeed() function:
All the variables in the function are global uint32_t except for turboRPM (float). Looking at this code now. I'm drawn to this as a start:
Honestly not sure why I added the >3 condition, but think the effect would be that I only read every 3rd measurement (or maybe 4th or more depending on how long the CAN event code blocks?). I'm guessing this will fill the 12-element FreqMeasure buffer pretty quickly, and may be part of my issue. The next if/else statement is just for my moving average of size freqSize. Upon startup it fills the array, after that casts out the oldest reading and updates with newest. Shouldn't be an issue there, I've used versions of it in plenty of projects.
I will make a few changes (e.g. get rid of the ">3" condition or add a for loop to ensure I read every measurement until the buffer is empty). But overall, are there any glaring issues with calling this function from the loop when Can.events() is going to have a highly variable run time? Or, anything I should be doing differently?
The turbo has a variable relucatance sensor generating a signal with one pulse per revolution, run through a VR signal conditioner and logic level shifter to generate a 3.3v square wave. Shaft speed with engine running could range from 0rpm to 120,000rpm, or 0Hz to 2kHz (although mostly it shouldn't exceed 90,000rpm with my current engine setup). I know this is higher than the ideal range for FreqMeasure, but when it works I see up to 90,000rpm readings coming out nice and clean.
However, I've noticed at times that it will start reporting smaller values, almost like it's hitting a wall (for example, reading a very flat 30,000rpm when it should be increasing from about 50,000 to 80,000rpm). Conversely, at idle the shaft speed should be something like 3,000rpm, but I see frequent spikes to very high values unless I use a more aggressive moving average filter. I am fairly certain this is an issue with my implementation - I first wrote the turbo speed portion of my code about 2 years ago so I don't clearly remember my reasons for writing it the way I did.
Overall, my code runs on an event-based timing, using receipt of a CAN message to run the bulk of the code, using FlexCAN_T4. Messages from different devices run different sections of code, all contained within a CAN handler function. The loop() function contains just 2 lines of code:
Code:
void loop(void) {
Can.events();
updateTurboSpeed();
}
The updateTurboSpeed() function:
Code:
void updateTurboSpeed() {
// Update turbo speed only if engine is running:
if (rpm > 0) {
if (FreqMeasure.available() > 3) {
if (n < freqSize) { //first few readings only
freqNew = FreqMeasure.read();
n++; //current number of samples in freq[]
freqAvgNew = freqAvgOld + (freqNew - freqAvgOld) / n; //update the average
turboRPM = FreqMeasure.countToFrequency(freqAvgNew) * 60;
freq[k] = freqNew;
freqAvgOld = freqAvgNew;
k = (k + 1) % freqSize;
}
else { //once we have at least freqSize readings
freqNew = FreqMeasure.read();
freqAvgNew = freqAvgOld + (freqNew - freq[k]) / n; //update the average
turboRPM = FreqMeasure.countToFrequency(freqAvgNew) * 60;
freq[k] = freqNew;
freqAvgOld = freqAvgNew;
k = (k + 1) % freqSize;
}
}
}
turboSpeed16 = (uint16_t)(turboRPM / 10); //for MSCAN message, 10-bit(?) data
}
All the variables in the function are global uint32_t except for turboRPM (float). Looking at this code now. I'm drawn to this as a start:
Code:
if (FreqMeasure.available() > 3)
I will make a few changes (e.g. get rid of the ">3" condition or add a for loop to ensure I read every measurement until the buffer is empty). But overall, are there any glaring issues with calling this function from the loop when Can.events() is going to have a highly variable run time? Or, anything I should be doing differently?