I dont want to reinvent the wheel but have not found any code for the stm32duino using a hall effect sensor for measuring RPM, just code for the Arduinos.
Has anyone got a working code to calculate RPM from a hall effect sensor? typically, I want to sample for 3-5 seconds and calculate RPM from the number of pulses from the sensor.
Cheers, Steve.
At least one other person is doing something similar
I am not planning to share any actual implementation code here because your arrangements can be so different compared to my solution that more effort is required for refactoring than for creating a solution from scratch.
Here is a list of solutions that you can use for the speed (and position measurement)
- An interrupt that is triggered by the hall sensor. Increment a 32 or 64 bit counter in every interrupt.
When calculating the RPM calculate the delta time and delta count.
In the case of 32 bit counter, ignore the wraparound. The delta count will handle that inherently. - A timer is connected to the hall sensor. The timer counter is incremented by the sensor.
- If a position is required in addition of the speed (RPM), then you have to use two hall sensors for quadrature encoding
> Interrupts can be used to calculate the speed and positions
> the timers have a special mode for quadrature encoding, but speed cannot be measured at the same time
I have developed DC motor controllers (30V, 15A) using the hall sensor quadrature encoders connected to interrupts in the following configurations
– 2 motors by Teensy 3.2
– 4 motors by Teensy 3.5
– 6 motors by STM32F407VE (blue pad)
With the inside knowledge of the magnets driving the hall sensors, I can have an accurate speed reading at the millisecond level, This requires a lot of special implementations that are not relevant for RPM readings at the 3-5 second intervals.
Basically, I want to take a vibration reading from a variable speed machine. The fMax (maximum frequency in the FFT spectra) will depend on the machine’s speed. I only need to read the speed once every five minutes or so when a vibration reading is required. The way I thought about doing it is as follows
start looking (polling) at my input pin
wait until it goes high and set the start time
apply a small delay for debouncing
wait until it goes high again and set end time
calculate time taken for one revolution and therefore speed
do this several times and average
I have made a unit to display flow rate and cumulative flow for a peristaltic effluent pump at a manufacturers site and that has been installed about a year now – on that I used an arduino UNO and set up an interrupt which incrimented a counted each revolution. The screen updated avery 5 seconds and reset the counter to zero. I suppose I could do this in the same way, resetting the counter when I want to calculate speed and finding out how many pulses have been in a given time – as the speed is going to be about 20-25 revs/second, If I read for say 3 seconds, it should be reasonably accurate. However, until now, I have not used interrupts on the stm32’s
I’d appreciate your thoughts.
Steve
1. Busy reading of an input is not a good idea. At that time, your application cannot react to anything else – unless you are using a genuine RTOS
2. Resetting counters/timers can have unexpected side effects. It is better to allow the counters to wrap around and just take the reading whenever required. By storing the old value is logically same as resetting the counter.
3. If you are doing any FFT type of analysis, then the average values are impacting the frequency information. For display purposes, a simple exponential filter can be used instead of the boxcar averages.
4. You should learn the interrupt handling in the 3 different Arduino platforms
– Atmel AVR, quite easy
– Teensy 2x/3x, similar with AVR
– STM32, the architecture “limitations” are visible to the application developer.
> some users do prefer to do it close to the iron
> some users like the additional library layers which are simplifying some things but adding some complexity
If you will do several apps on STM32, then the knowledge of interrupt handling is very useful.
With 25rev/sec you get ~40000micros difference each time, that might be precise enough..
For example (simplified):
volatile uint32_t current_rev_period, tstamp, old;
..
attachInterrupt(Hall_PIN_INPUT, Hall_interrupt, RISING);
..
void Hall_interrupt() {
tstamp = micros();
current_rev_period = tstamp - old;
old = tstamp;
}
the two should be fairly easy to port to / from.
basically, set up an isr that’s called when the tachometer output changes state (or rising / falling). and calculate timing differentials and add oversampling if you wish.
that’s pretty much all you need.
something like this would work:
void tacho_isr(void) {
static uint8_t count=0;
static uint32_t time_elapsed=0; //time elapsed
uint32_t tmp;
count+=1; //increment counter
if (count == RPM_COUNT) {
tmp = time_now(); //save current time
time_elapsed = tmp - time_elapsed; //calculate time elapsed
//calculate rpm from time_elapsed, RPM_COUNT, and other parameters here
time_elapsed = tmp; // update time_elapsed for the next round of counting
count = 0;
}
}
static inline uint32 micros(void) {
uint32 ms;
uint32 cycle_cnt;
do {
ms = millis();
cycle_cnt = systick_get_count();
asm volatile("nop"); //allow interrupt to fire
asm volatile("nop");
} while (ms != millis());
#define US_PER_MS 1000
/* SYSTICK_RELOAD_VAL is 1 less than the number of cycles it
* actually takes to complete a SysTick reload */
return ((ms * US_PER_MS) +
(SYSTICK_RELOAD_VAL + 1 - cycle_cnt) / CYCLES_PER_MICROSECOND);
#undef US_PER_MS
}
http://www.stm32duino.com/viewtopic.php … nos#p26692
volatile uint32_t current_rev_period, tstamp, old;
..
attachInterrupt(Hall_PIN_INPUT, Hall_interrupt, RISING);
DWTEn();
CpuTicksEn();
..
double my_revs = 1.0 / (current_rev_period * 13.888889e-9); //Revs per second
// ISR
void Hall_interrupt() {
tstamp = CpuGetTicks();
current_rev_period = tstamp - old;
old = tstamp;
}
Thanks
I don’t remember reading that thread
I see you defined
#define DWTEn() (*((uint32_t*)0xE000EDFC)) |= (1<<24)
#define CpuTicksEn() (*((uint32_t*)0xE0001000)) = 0x40000001
#define CpuTicksDis() (*((uint32_t*)0xE0001000)) = 0x40000000
#define CpuGetTicks() (*((uint32_t*)0xE0001004))
I have also bought a STM board and learned interrupts, now trying to build the same RPM meter using hall sensor.
I have measured the RPM of simple DC motor with Arduino using below code. Now trying to use the same concept in STM. You can also look at the code:
void loop()
{
/*To drop to zero if vehicle stopped*/
if(millis()-dtime>1500) //no magnet found for 1500ms
{
rpm= v = 0; // make rpm and velocity as zero
Cycle_BT.write(v);
dtime=millis();
}
v = radius_of_wheel * rpm * 0.37699; //0.33 is the radius of the wheel in meter
}
void magnet_detect() //Called whenever a magnet is detected
{
rotation++;
dtime=millis();
if(rotation>=2)
{
timetaken = millis()-pevtime; //time in millisec for two rotations
rpm=(1000/timetaken)*60; //formulae to calculate rpm
pevtime = millis();
rotation=0;
Cycle_BT.write(v);
//Cycle_BT.println("Magnet detected...."); //enable while testing the hardware
}
}
i'm halfway wondering if a simple magnet on the wheel & a coil


