STM32F4 - Using a timer to control non-adjacent timer - timer

My processor is an STM32F437ZGT6 and I wish to count two different pulse trains (RPM). The range is quite wide, I may have an engine that idles at 150 rpm and we get a pulse from the cam, so 0.5 pulses per revolution, or 1.25 pulses per second. At the other extreme I may need to count 460 flywheel teeth at 3000 rpm, 23000 pulses per second. I have a prescaler available so I can divide the external event by up to 8 but even so this become too intense at higher speeds because every event or eight event causes an interrupt.
One alternative I am considering would be to have one timer use the external event as the clock and it would just count events within a time window. My difficulty comes from determining how to use another timer to control the window by setting and clearing CEN or some similar action.
In RM0090, section 18.3.15 Timer synchronization the example shows one timer controlling another, timer 1 controlling timer two. I thought that may be useable but althought I did not read otherwise I don't see that any two timers could be paired. The signal I am interested in actually feeds two timers. TIM1 ch1 and TIM9 ch1.
Any suggestions would be appreciated as I don't want to cobble up some Rube Goldberg scheme where one timer fires off an ISR and then the ISR opens and closes the time window.
I should have noted that a lookup table is provided that provides the expected engine speed and the number of pulses per revolution.
Thanks,
jh

If you want to just count external events, you can select external clock source for timer. (Point "Clock selection" of reference manual). SPL should have an example.
And read count from Tim CNT register every time you need.
The problem here is to read counts often enough.
Usually auto-reload register is 2 bytes so you have up to 2 ^ 16 counts before overflow, and loosing counted value.
Timers 2 and 5 have 4 bytes auto-reload registers so you have up to 2 ^ 32 counts.
If you need more then 2 ^ 32 counts you have at least two ways:
  - Timer cascade, by setting one timer event as a clock for another.
You can find this in the reference manual as "Using one timer as prescaler for another timer".
Cascading offers you up to 2 ^ 64 timer.
There is an example for SPL in "TIM_CascadeSynchro" folder.
  - Less beautiful, but more easy way is to create a counter variable and, increment it in timer irq handler.
Number of counts may be found as cnt_variable * TIMx-> ARR.
Several cascaded variables give the unlimited counter).

Thanks for the post. I will try to add a little detail. RPM 1 is fed into TIM3 ch2 and TIM4 ch1. RPM 2 is fed into TIM1 ch1 and Tim9 ch1. Both have a range of 1.25 pulses per second up to 30000 pulses per second. I am given the number of pulses per revolution which can range from 0.5 to 460 and the expected engine rpm, 150 - 3000 rpm so I can scale things a bit. The reason for feeding two different timers is to be able to use different counting techniques based on speed (pulses per second). For low speed I can capture events (pulses) and grab the timer count using an ISR. But when the pulse count gets high I want to use a different method so as not to incur more than 1000 interrupts per second per channel. So my idea there is to have one timer control another. One timer would simply count events without generating interrupts. The second counter would control the period of time that the first timer would be allowed to collect events.
Thanks,
jh

Seems like you need: timer synchronization with enabling/disabling slave timer according to the trigger output of master timer.
Description can be found in the following sections of RM0090:
18.3.14 Timers and external trigger synchronization in paragraph Slave mode: Gated mode
18.3.15 Timer synchronization in paragraph Using one timer to enable another timer
Also good explanation can be found in TIMx register section for registers TIMx_SMCR: bits TS and SMS; TIMx_CR2: bits MMS.
TIMx internal trigger connection (tables 93, 97 and 100) сontains possible connections of the trigger output of one timer with the input of another. Timers that you can use as master are marked in the picture below:
TIM_ExtTriggerSynchro example from SPL library can be used for code copy-paste.
I think the best way is:
Set RPM pins as external clock source for slave timer.
Set enabling/disabling of slave timer from the output compare of master timer. So changing TIMx_CCRx register value you can change duration of measurement.
Set master timer interrupts on update event (maybe on few events TIMx_RCR register).
Do all the calculations in master timer interrupt handler
Also it seems to me that you can just use 16 bit timer as RPM counter. Even on 30000 pulses you will have overflow every 2^16/30000 = 2,18 seconds which is rarely rare for STM32F4 clock frequencies. And use other timer, with, for example, 2 second period, interrupt for calculations.
Good luck!

Related

STM32: How to configure timer to trigger interrupt in every increments in quadrature encoder mode?

I have a rotary encoder with STM32F4 and configured TIM4 in "Encoder Mode TI1 and TI2". I want to have an interrupt every time the value of timer is incremented or decremented.
The counting works but I only can configure an interrupt on every update event, not every changes in TIM4->cnt. How can I do this?
In other words: My MCU+Encoder in quadrature mode could count from 0 to 99 in one revolution. I want to have 100 interrupts in the revolution but if I set TIM4->PSC=0 and TIM4->ARR=1, results 50 UPDATE_EVENTs, so I should set ARR=0 but it does not work. How can I sole that?
To get 100 interrupts per revolution keep PSC=0, ARR=1, setup the two timer channels in output compare mode with compare values 0 and 1 and interrupts on both channels.
Or even use ARR=3 and setup all four channels, with compare values of 0,1,2 and 3. This will allow to detect the direction.
Normally, the whole point of using the quadrature encoder mode is counting the pulses while avoiding interrupts. You can simply poll the counter register periodically to determine speed and position.
Getting interrupts on every encoder pulse is extremely inefficient, especially with high resolution encoders. Yours seems to be a low resolution one. If you still think you need them for some reason, you can connect A & B into external interrupts and implement the counting logic manually. In this case, you don't need quadrature encoder mode.

Calculating pulse width (duty cycle) using stm32 DMA. Is it possible?

I'm working on a project that a series of duty cycles must be measured. A sample of related waveforms is displayed below:
As you can see from the signal, the frequency is too high, and calculating it using bit functions is impossible. In controller's tech website here, they used the timer's input capture modes and rising-falling edges interrupts for calculating the difference between two captures of the timer.But this method is too slow and cannot fulfill our desires for high-frequency signals.
The other solution is to use DMA for fast transferring the capture data to the memory. But in STM32cubemx it is not possible to assigned two DMAs for two captuers of a timer as you can see below:
Could some one give me a suggestion for this issue?
Using the DMA channel is not likely to provide a good solution for fast signals since the memory buss is shared between the DMA controller and CPU, so predictable timing over the capture event time is not guaranteed. Also, the timing relationship between the DMA transfers and the external signal will be difficult to resolve. So I'd say "no" to your question.
With 16-bit timers that can run up to 120 MHz, the STM32 featured timers is your best choice. An 800kHz signal is not considered too fast for these critters! The trick is how you make use of the timers. You want to use the input capture mode. Capture several samples of logic high signal times, then average these numbers, do the same for logic low signal times, then add for total timer ticks, then multiply this by the timer tick period for the external signal period.

Hertz used in AVR

I'm very new to AVR and got confused about a question from one of our tutorials. It says:
"To toggle an output compare pin 8 times per second (4Hz period), what clock prescale and output output compare values do we need?"
My confusion is:
why it says "4Hz period"? isn't Hertz a measurement for frequency? why is it describing time period?
You are correct Hz is the unit for frequency. Frequency is changes per second so if you toggle a pin 4 times per second its frequency is 4Hz. The author was problably lazy and did not want to calculate the period time to 1/4 s
Hertz represents >> cycles per seconds in Metrical System SI system.
As mentioned above it tells you how many times a process, event anything else takes place. Mostly everything is measured in Hz because its convenient and has direct relation to time. So one can easily jump back and forth in units.
For the AVR has a system clock which is then prescaled to lower rates for use in different system peripherals. The event loop speed is also measured in Hertz. Currently on mine the loop execution time is 2.7 MHz so very fast loop.

Better way to determine seconds since power on in microcontroller?

Short question: How to get seconds since reset in STM32L051T6 microcontroller?
My effort and detailed issue:
I am using an STM32L051T6 series microcontroller. I need to count seconds since power on. I am also using low power mode. So I wrote code to use wakeup timer interrupt functionality of internal RTC of microcontroller. I used 1 second interval wake up timer with external LSE clock of 32768 Hz. I observed the accumulated seconds since power on (SSPO) after 3 days and found that it is falling behind by 115 seconds compared to actual time elapsed. My guess for this drift is interrupt latency in executing wakeup timer interrupt. How can I remove drift of this 115 seconds? Or is there any other better method than using wakeup interrupt to count seconds since power on?
UPDATE:
I tried to use Systick with HAL_GetTick() function as seconds since power on. But even systick is also getting delayed over time.
If you want to measure time with accuracy over a longer period, an RTC is the way to go. As you mentioned that you have an RTC, you can use the method below.
At startup, load the RTC with zero.
Then you can read the seconds elapsed when required without the errors above.
Edit: As per comment, the RTC can be changed by user. In that case,
If you can modify the RTC write function called by the user, then when the user calls the RTC write function, you update a global variable VarA = time set by user. The elapsed time will be Time read by RTC - VarA.
If the RTC is accurate, you should use the RTC by storing its value at boot time and later comparing to that saved value. But you said that the RTC can be reset by user so I can see two ways to cope with it:
if you have enough control on the system, replace the command or IHM that a user can use to reset the clock with a wrapper that inform you module and allows to read the RTC before and after it has been reset
if you have not enough control or cannot wrap the user's reset (because it uses a direct system call, etc.) use a timer to control the RTC value on every second
But you should define a threshold on the delta on RTC clock. If it is small, it is likely to be an adjustment because unless your system uses an atomic clock, even RTC can derive over time. In that case I would not care because you can hardly know whether it derived since last reboot or not. If you want a more clever algorythm, you can make the threshold dependant on the current time since last reboot: the longer the system is up, the higher the probability it has derived since then.
On the opposite, a large delta is likely to be a correction because RTC was blatantly erroneous, the saving battery is out of use, or what else. In that case you should compute the new start RTC time that gives same duration with the new RTC value.
As a rule of thumb, I would use a threshold of about 1 or 2 seconds per uptime day without RTC clock adjustement (ref) - meaning I would also store the time of last RTC adjustement, initially boot time.

Long Delay using Delay Functions from C18 Libraries for PIC18

I'm using a PIC18 with Fosc = 10MHz. So if I use Delay10KTCYx(250), I get 10,000 x 250 x 4 x (1/10e6) = 1 second.
How do I use the delay functions in the C18 for very long delays, say 20 seconds? I was thinking of just using twenty lines of Delay10KTCYx(250). Is there another more efficient and elegant way?
Thanks in advance!
It is strongly recommended that you avoid using the built-in delay functions such as Delay10KTCYx()
Why you might ask?
These delay functions are very inaccurate, and they may cause your code to be compiled in unexpected ways. Here's one such example where using the Delay10KTCYx() function can cause problems.
Let's say that you have a PIC18 microprocessor that has only two hardware timer interrupts. (Usually they have more but let's just say there are only two).
Now let's say you manually set up the first hardware timer interrupt to blink once per second exactly, to drive a heartbeat monitor LED. And let's say you set up the second hardware timer interrupt to interrupt every 50 milliseconds because you want to take some sort of digital or analog reading at exactly 50 milliseconds.
Now, lastly, let's say that in your main program you want to delay 100,000 clock cycles. So you put a call to Delay10KTCYx(10) in your main program. What happenes do you suppose? How does the PIC18 magically count off 100,000 clock cycles?
One of two things will happen. It may "hijack" one of your other hardware timer interrupts to get exactly 100,000 clock cycles. This would either cause your heartbeat sensor to not clock at exactly 1 second, or, cause your digital or analog readings to happen at some time other than every 50 milliseconds.
Or, the delay function will just call a bunch of Nop() and claim that 1 Nop() = 1 clock cycle. What isn't accounted for is "overheads" within the Delay10KTCYx(10) function itself. It has to increment a counter to keep track of things, and surely it takes more than 1 clock cycle to increment the timer. As the Delay10KTCYx(10) loops around and around it is just not capable of giving you exactly 100,000 clock cycles. Depending on a lot of factors you may get way more, or way less, clock cycles than you expected.
The Delay10KTCYx(10) should only be used if you need an "approximate" amount of time. And pre-canned delay functions shouldn't be used if you are already using the hardware timer interrupts for other purposes. The compiler may not even successfully compile when using Delay10KTCYx(10) for very long delays.
I would highly recommend that you set up one of your timer interrupts to interrupt your hardware at a known interval. Say 50,000 clock cycles. Then, each time the hardware interrupts, within your ISR code for that timer interrupt, increment a counter and reset the timer over again to 0 cycles. When enough 50,000 clock cycles have expired to equal 20 seconds (or in other words in your example, 200 timer interrupts at 50,000 cycles per interrupt), reset your counter. Basically my advice is that you should always manually handle time in a PIC and not rely on pre-canned Delay functions - rather build your own delay functions that integrate into the hardware timer of the chip. Yes, it's going to be extra work - "but why can't I just use this easy and nifty built-in delay function, why would they even put it there if it's gonna muck up my program?" - but this should become second nature. Just like you should be manually configuring EVERY SINGLE REGISTER in your PIC18 upon boot-up, whether you are using it or not, to prevent unexpected things from happening.
You'll get way more accurate timing - and way more predictable behavior from your PIC18. Using pre-canned Delay functions is a recipe for disaster... it may work... it may work on several projects... but sooner or later your code will go all buggy on you and you'll be left wondering why and I guarantee the culprit will be the pre-canned delay function.
To create very long time use an internal timer. This can helpful to avoid block in your application and you can check the running time. Please refer to PIC data sheet on how to setup a timer and its interrupt.
If you want a very high precision 1S time I suggest also to consider an external RTC device or an internal RTC if the micro has one.

Resources