Hello,
I’m having a problem reading tachometers and maybe someone in this forums know how to approach this.
I suspect that interrupts are being lost when reading LM393 tachometers for a 4-wheeled rover with software based counters using four instances of the lm393_tachometer class listed below, specially when the system is being overloaded by other interrupts, such as UARTs. I understand that counters could be implemented by hardware, avoiding the need to use interrupts, but is there a way to implement this in MbedOs? Board is Nucleo-144 STM32F767ZI.
Any help will be appreciated.
Kind regards,
Nicolás
Code:
#ifndef LM393_H
#define LM393_H
#include "DigitalOut.h"
#include <mbed.h>
#define DEFAULT_TACHO1_PIN PE_7
#define DEFAULT_TACHO2_PIN PE_8
#define DEFAULT_TACHO3_PIN PG_9
#define DEFAULT_TACHO4_PIN PG_14
class lm393_tachometer {
public:
static constexpr uint32_t max_interval = 1000000;
lm393_tachometer(PinName pin,int n_ticks_per_rev);
~lm393_tachometer() = default;
float get_rpm();
inline uint32_t get_total_tick_count() const { return this->counter; }
private:
Timer timer;
InterruptIn irq;
volatile uint32_t counter;
volatile float dt;
volatile float rpm;
const float encoder_k;
void tick();
DigitalOut debug_led;
};
#endif
#include "lm393_tachometer.h"
lm393_tachometer::lm393_tachometer(PinName pin,int n_ticks_per_rev):
irq(pin) //PullUp, PullDown, PullNone, PullDefault
, counter(0)
, dt(0)
, rpm(0)
, encoder_k(1.0f/n_ticks_per_rev)
, debug_mode(false)
, debug_led(LED2)
{
}
void lm393_tachometer::setup()
{
this->irq.fall(callback(this, &lm393_tachometer::tick));
this->irq.enable_irq();
this->timer.start();
}
void lm393_tachometer::tick()
{
this->dt = timer.elapsed_time().count();
this->timer.reset();
/* Avoid out of range readings */
this->rpm = (this->dt <= lm393_tachometer::max_interval) ?
( this->encoder_k / (this->dt/float(1000000.0f))) *60.0f : 0.0f;
this->counter++;
this->debug_led.write(!this->debug_led.read());
}
float lm393_tachometer::get_rpm()
{
/* Avoid out of range readings */
if(this->timer.elapsed_time().count()>= lm393_tachometer::max_interval)
{
this->rpm = 0.0f;
}
/* A frecuencias muy bajas se producen errores ocasionales. */
if( this->rpm > 1000 )
{
this->rpm = 0.0f;
}
return this->rpm;
}
hudakz
(Zoltan Hudak)
September 13, 2021, 8:04pm
2
Hello Nicolás,
You can try the code below. A Timeout
is used rather than a Timer
to set up a counting period with us precision. The example program is counting pulses for 500ms and assumes 32 pulses per revolution. Adapt to your needs:
#include "mbed.h"
#define DEFAULT_TACHO1_PIN PE_7
#define DEFAULT_TACHO2_PIN PE_8
#define DEFAULT_TACHO3_PIN PG_9
#define DEFAULT_TACHO4_PIN PG_14
#define TO_COUNT_PER_MIN (60 * 1E6)
class Tachometer
{
public:
Tachometer(PinName inputPinName, PinMode inputPinMode, uint16_t pulsesPerRev);
~Tachometer() { }
void begin();
uint32_t totalCount();
void rpmCountFor(uint32_t timeFrame_us);
bool isRpmCounting();
bool isRpmAvailable();
float rpmRead();
private:
InterruptIn _input;
uint16_t _pulsesPerRev;
uint32_t _totalCount;
uint32_t _timeout_us;
bool _rpmCounting;
uint32_t _rpmCount;
Timeout _timer;
bool _rpmAvailable;
void _onPulse();
void _rpmCountStop();
};
//=====================================
Tachometer::Tachometer(PinName inputPinName, PinMode inputPinMode, uint16_t pulsesPerRev) :
_input(inputPinName, inputPinMode),
_pulsesPerRev(pulsesPerRev),
_totalCount(0),
_rpmCounting(false),
_rpmCount(0),
_rpmAvailable(false)
{ }
void Tachometer::begin()
{
_rpmCounting = false;
_rpmAvailable = false;
_totalCount = 0;
_input.fall(callback(this, &Tachometer::_onPulse));
}
void Tachometer::rpmCountFor(uint32_t timeFrame_us)
{
_timeout_us = timeFrame_us;
_rpmAvailable = false;
_rpmCount = 0;
_rpmCounting = true;
_timer.attach_us(callback(this, &Tachometer::_rpmCountStop), _timeout_us);
}
bool Tachometer::isRpmCounting()
{
return _rpmCounting;
}
void Tachometer::_onPulse()
{
_totalCount++;
if (_rpmCounting) {
_rpmCount++;
}
}
void Tachometer::_rpmCountStop()
{
_rpmCounting = false;
_rpmAvailable = true;
}
bool Tachometer::isRpmAvailable()
{
return _rpmAvailable;
}
float Tachometer::rpmRead()
{
return (_rpmCount * TO_COUNT_PER_MIN) / _pulsesPerRev / _timeout_us;
}
//=====================================
// Let's assume 32 pulses per revolution
Tachometer* tacho[] =
{
new Tachometer(DEFAULT_TACHO1_PIN, PullUp, 32),
new Tachometer(DEFAULT_TACHO2_PIN, PullUp, 32),
new Tachometer(DEFAULT_TACHO3_PIN, PullUp, 32),
new Tachometer(DEFAULT_TACHO4_PIN, PullUp, 32)
};
int main()
{
for (uint8_t i = 0; i < 4; i++) {
tacho[i]->begin();
}
while (true) {
for (uint8_t i = 0; i < 4; i++) {
if (!tacho[i]->isRpmCounting()) {
// let's count pulses for 500000 us (= 500 ms);
tacho[i]->rpmCountFor(500000);
}
ThisThread::sleep_for(1000);
if (tacho[i]->isRpmAvailable()) {
printf("Tacho[%i] = %7.2f RPM\r\n", i, tacho[i]->rpmRead());
}
}
printf("------------------------\r\n");
}
}
Thank you very much for the advise @hudakz , I will try your suggestion : )
hudakz
(Zoltan Hudak)
September 13, 2021, 8:51pm
4
Please notice that I have modified the Tachometer::begin()
function in the code above (added _input.fall(...)
).
1 Like