I'm new to ARM MCUs (STM32F411), and I have been trying to find my way around the peripherals using STM's HAL library and STM32Cube.
I've already configured my board in order to use some peripherals:
Timer 2 for running an interrupt with a certain frequency
Timer 3 for running PWMs on 3 channels of it.
ADC with 4 channels, into DMA mode, for reading some analog input.
Let us suppose, now, that the PWM's whole period is 100 ms and its duty cycle is 50% (50 ms PWM on and 50 ms PWM off).
I would like to trigger an interrupt after a certain time of the PWM on level, let us say 50% of it.
Hence, I would like to run an interrupt at 25 ms in order to use the ADC for sampling it's analog inputs.
Do you have any suggestion on how could I implement such a kind of interrupt?
Thank you in advance for your help!
Since the ADC of the STM32F411 is used in Regular mode (not Injected mode) and only three channels out of four are used to generate PWM on Timer 3, the fourth channel can be used to trigger the ADC.
Hence Timer 3 is configured as follows:
CH1 used for Output Compare mode 0 (TIM3->CCMR1.OC1M = 0)
CH2, CH3, CH4 used for PWM outputs
Therefore TIM3->CCR1 is loaded to a value that gives 25% of duty, then it will generate TIM3_CH1 events that can be used to trigger ADC start-of-conversion at 25% of your TIM3 timebase.
Related
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.
MCU: f446rct6
System: freertos
Library: hal
Program logic:
Initialize the timer to output the PWM wave and initialize the DMA and connect to the Capture/Compare register
Start the timer
When data needs to be updated, start a dma transmission
Phenomenon: There is no problem with the frequency and duty cycle of the PWM wave, but a strange triangular waveform often appears in the first or second waveform of DMA transmission.
Ask everyone, do you encounter similar waveforms when using pwm? Please give me a hint to locate this problem, thank you
Potential causes include:
You're switching the pin to DAC mode (if your MCU has a DAC) and driving it with an increasing value.
The pin is disabled, and what you see is the drift of the voltage on the pin's and probe's combined capacitance.
You're inadvertently switching the PWM to a very high frequency and the scope is set to too low sample rate. Make sure you set the scope to highest possible sample rate / sample depth / sampling length, and decrease the horizontal scale (i.e. decrease the time per division by 2x or 5x).
I am trying to control a servo motor (link). It is a brushless DC motor with an interface similar to a stepper motor.
The motor rotates for a defined distance based on the number of pulses it receives from the PWM. The speed is determined by the pulse frequency of the PWM, like a stepper motor.
To control this motor I am using a microcontroller STM32F407ZET6. I can easily change the frequency and Duty Cycle of PWM, but my doubt is the following:
How do I generate a fixed number of pulses in the PWM? For example, I want the PWM to send 1000 pulses at a certain time with a frequency of 20KHz and a Duty Cycle of 50%. 20KHz and 50% Duty Cycle are easy to define, but I can't determine how to generate the 1000 fixed pulses.
One of the solutions I tried was to connect the PWM back to a timer in counter mode and stop the PWM when the required number of pulses has been generated. But the number of pulses is not always fixed, sometimes ranging from 998 to 1005 (for example).
Is it possible to do this without the need for feedback?
Simpest way:
UG interrupt = count cycles. After n cycles disable the timer.
In the memory create a buffer with the timer register values and use timer burst mode.
Configure DMA mem-mem with n cycles same source and destination address. After n cycles end of transaction interrupt will be generated - disable the timer.
Use slave timer counting when the PWM is updating (overflowing). Set the overflow interrupt and disable the PWM timer.
many other methods.
This can be easily achieved by combining the so called "One-pulse mode" (reference manual page 551) with the repetition counter (page 529). All you've got to do is enabling this mode (OPM bit in CR1), set the repetition counter (RCR) and start the timer. IIRC you also have to force an update event in order to get RCR loaded. The reference manual will have more information on that.
I've got an STM32F4, and I want to PWM a GPIO port that's been OR'd with a mask..
So, maybe we want to PWM 0b00100010 for awhile at 200khz, but then, 10khz later, we now want to PWM 0b00010001...then, 10kHz later, we want to PWM some other mask on the same GPIO.
My question is, how do you do this with DMA? I'm trying to trigger a DMA transfer that will set all the bits on a rising edge, and then another DMA transfer that will clear all the bits on a falling edge.
I haven't found a good way to do this, (at least with CubeMX and my limited experience with C & STM32's) as it looks like I only get a chance to do something on a rising edge.
One of my primary concerns is CPU time, because although I mention hundreds of kilohertz in the above example, I'd like to make this framework very robust in-so-far as it isn't going to be wasteful of CPU resources...That's why I like the DMA idea, since it's dedicated hardware doing the mindless lifting of a word here to a word there type of stuff, and the CPU can do other things like crunch numbers for a PID or something.
Edit
For clarity : I have a set of 6 values that I could write to a GPIO. These are stored in an array.
What I'm trying to do is set up a PWM timer to set the GPIO during the positive width of the PWM and then I want the GPIO to be set to 0b00000000 during the low period width if the pwm.
So, I need to see when the rising edge is, quickly write to the gpio, then see when the falling edge is, and write 0 to the gpio.
Limited solution without DMA
STM32F4 controllers have 12 timers with up to 4 PWM channels each, 32 in total. Some of them can be synchronized to start together, e.g. you can have TIM1 starting TIM2, TIM3, TIM4 and TIM8 simultaneously. That's 20 synchronized PWM outputs. If it's not enough, you can form chains where a slave timer is a master to another, but it'd be quite tricky to keep all of them perfectly synchronized. Not so tricky, if an offset of a few clock cycles is acceptable.
There are several examples in the STM32CubeF4 library example projects section, from which you can puzzle together your setup, look in Projects/*_EVAL/Examples/TIM/*Synchro*.
General solution
A general purpose or an advanced timer (that's all of them except TIM6 and TIM7) can trigger a DMA transfer when the counter reaches the reload value (update event) and when the counter equals any of the compare values (capture/compare event).
The idea is to let DMA write the desired bit pattern to the low (set) half of BSRR on a compare event, and the same bits to the high (reset) half of BSRR on an update event.
There is a problem though, that DMA1 cannot access the AHB bus at all (see Fig. 1 or 2 in the Reference Manual), to which the GPIO registers are connected. Therefore we must use DMA2, and that leaves us with the advanced timers TIM1 or TIM8. Things are further complicated because DMA requests caused by update and compare events from these timers end up on different DMA streams (see Table 43 in the RM). To make it somewhat simpler, we can use DMA 2, Stream 6 or Stream 2, Channel 0, which combine events from 3 timer channels. Instead of using the update event, we can set the compare register on one timer channel to 0.
Set up the DMA stream of the selected timer to
channel 0
single transfer (no burst)
memory data size 16 bit
peripheral data size 16 bit
no memory increment
peripheral address increment
circular mode
memory to peripheral
peripheral flow controller: I don't know, experiment
number of data items 2
peripheral address GPIOx->BSRR
memory address points to the output bit pattern
direct mode
at last, enable the channel.
Now, set up the timer
set the prescaler and generate an update event if required
set the auto reload value to achieve the required frequency
set the compare value of Channel 1 to 0
set the compare value of Channel 2 to the required duty cycle
enable DMA request for both channels
enable compare output on both channels
enable the counter
This way you can control 16 pins with each timer, 32 if using both of them in master-slave mode.
To control even more pins (up to 64) at once, configure the additional DMA streams for channel 4 compare and timer update events, set the number of data items to 1, and use ((uint32_t)&GPIOx->BSRR)+2 as the peripheral address for the update stream.
Channels 2 and 4 can be used as regular PWM outputs, giving you 4 more pins. Maybe Channel 3 too.
You can still use TIM2, TIM3, TIM4, and TIM5 (each can be slaved to TIM1 or TIM8) for 16 more PWM outputs as described in the first part of my post. Maybe TIM9 and TIM12 too, for 4 more, if you can find a suitable master-slave setup.
That's 90 pins toggling at once. Watch out for total current limits.
what PWM 0b00100010 means? PWM is a square wave with some duty ratio. it wil be very difficult to archive using DMA but you will need to have table with already calculated values. For example to have 2kHz PWM with 10% ratio you will need to have 10 samples one with bit set, nine with bit zeroed. You configure the timer to 20k / sec trigger mem-to-mem (GPIO has to be done this way) DMA transmission in the circular mode. On the pin you will have 2kHz 10% wave. The PWM resolution will be 10%. If you want to make it 0.5% you will need 200 samples table and DMA triggered 400k times per second.
IMO it is better to use timer and DMA to load new values to it (read about the burst DMA mode in the timer documentation in the Reference Manual)
Im using Ethernut 2.1 B and I need a C program that outputs a clock signal at the timer 1 output B, with other words on output OCIB. The frequency of the clock signal should be at 1.0 kHz.
Anyone know how this could be done?
You need to look in COM bits for your timer. For instance, for Timer0 (8-bit), the COM bits are set in the TCCR0 register. Probably the setting you'd be interested in is
TCCR0 |= (0<<COM1)|1<<COM0); // Toggle OC0 on compare match
This will toggle the OC0 (pin14) line when timer reaches the specified value.
Which timer you use depends on the precision you need: obviosely the 16-bit timers can give you more precise time resolution then the 8-bit timers.
The setting of the registers for your specific frequency (1Khz) depends on the clock speed of your chip, and which timer you are using: the timers use a pre-scaled general clock signal (see table 56 of the datasheet for possible values). This means that the prescaler settings will depend on your clock speed, and how high you want to count. For most precision you will want to count as high as possible, which means the lowest possible prescaler setting compatible with your timer's maximum value.
As far as where to start, generally, reading the datasheet is a good place, but googling "AVR timer" can also be very helpful.
It seems to be based on the Atmel ATmega 128, so read that CPU's data sheet to figure out how to program the timer hardware.
Not sure if this microcontroller supports directly driving an output from a timer, if it doesn't you're going to have to do it in software from the interrupt service routine.