In my case, I set AHB and APB1 frequencies to 80Mhz. So, How am I going to compute, let's say, 300 miliseconds for a led to blink?
This depends what you exactly want since there are many solutions for this.
Lets start with how I look at it, but as said many things are possible.
Look at the reference manual of your uC and esp. on the timer chapter and look at the time base unit and upcounting mode.
Ok, I think I did that here:
timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT,
TIM_CR1_CMS_EDGE,
TIM_CR1_DIR_UP);
TIM_CR1_DIR_UP is for upcounting.
About base unit, the reference manual says:
The time-base unit includes:
• Counter Register (TIMx_CNT)
• Prescaler Register (TIMx_PSC):
• Auto-Reload Register (TIMx_ARR)
I'm not sure how to interpret this and what to retain for what I need.
So to do this you need to have the clock activated to your timer, you need to know the frequency.
If I did things correctly, the Timer frequency is 80Mhz.
I activate the clock here:
rcc_periph_clock_enable(RCC_TIM2);Then you calculate how much to divide the clock signal to get the wanted frequency.
That division you split between the prescaler and the upcounting register ARR.
So for instance you have a 80MHz clock to the timer and you want the ARR to overflow after 300ms (3.3Hz)
Then you need to divide the 80MHz clock by 24242424. The max value of the prescaler is 65535 you are left with 1220 Hz.
To divide the 1220Hz to 3.3Hz again a factor of 360 is needed. So that is the value of the ARR register.
Ok, just to confirm I got it, the procedure is to divide the timer clock frequency by the time we want our led on. As we can't mix up frequency with time, we convert the time to frequency. That's 1/300ms = 3.3Hz.
So, 80Mhz / 3.3Hz is that 24242424 value that would be the one to set in ARR but it can only take up to 65536, so we need to go back and divide again the 80Mhz by 65536 and we get 1220Hz, approximately.
In practice, correct me if I'm wrong, it will take 300ms for the timer to overflow at a frequency of 1220Hz, yes?
And in terms of code, as I was following the other example I found, I probably did this wrong.
The other example used the
rcc_apb1_frequency constant (which is 4Mhz) to calculate the value for the prescaler, and to be honest, I'm a bit lost here.
timer_set_prescaler(TIM2, (rcc_apb1_frequency/80000)/2*1);Instead of that value, I want there the 1220, right?
Now you still have to do some things. First enable the timer (start timer) and enable the interrupts of that timer register and map the overflow interrupt to your interrupt handler.
In that interrupt handler you toggle the led. Measure with an oscilloscope the exact frequency and correct with the ARR value. If you can not get it perfect you need a multi step plan.
I enabled the timer here:
timer_enable_counter(TIM2);but I didn't do the interrupt thing. I thought that wouldn't be needed because, at least I thought, I was using the Output Compare Mode. Quoting the reference manual:
When a match is found between the capture/compare register and the counter, the output
compare function:
• Assigns the corresponding output pin to a programmable value defined by the output
compare mode (OCxM bits in the TIMx_CCMRx register) and the output polarity (CCxP
bit in the TIMx_CCER register). The output pin can keep its level (OCXM=000), be set
active (OCxM=001), be set inactive (OCxM=010) or can toggle (OCxM=011) on match.
• Sets a flag in the interrupt status register (CCxIF bit in the TIMx_SR register).
• Generates an interrupt if the corresponding interrupt mask is set (CCXIE bit in the
TIMx_DIER register).
• Sends a DMA request if the corresponding enable bit is set (CCxDE bit in the
TIMx_DIER register, CCDS bit in the TIMx_CR2 register for the DMA request
selection).
I think I setup the timer accordingly here:
timer_set_oc_mode(TIM2, TIM_OC1, TIM_OCM_PWM1);But I also got a bit confused here when I was reading the example, because the Output Compare Mode was selected but then there is that
TIM_OCM_PWM1 constant/macro that made me think if we are on Output Compare Mode or in some PMW mode.
So personally I find it a waste to set a hardware timer peripheral to those low speeds.
What I usually do is use one generic HW timer to generate for instance a 1ms interrupt, and in that 1ms interrupt handler I update the software timer module (a piece of software with different user configurable timers 8bit, 16 bit etc) that are increased or decreased by the 1ms interrupt handler.
In your code you have a superloop or RTOS and there you check if the software counter has expired (with downcounting software timers) if so you toggle the led.
Well, I don't want and I can't discuss other solutions because I'm just learning. I'm sure that in a production environment, that would probably be faster, more efficient and less time consuming. As of my situation, I started this as a learning process, so I want to get familiar with the most common libopencm3 functions that setup these most common peripherals, clocks, etc and maybe later I can think about alternatives, when I'm more comfortable.
But I don't want to make it more difficult than it is.
If you do not succeed with the above, then you can do some fast checks: in your code readout the ARR register and display or store it and view it, it should increase to 360 then start over at 0.
If you do not see the ARR increasing, check if you have started the timer (enabled it) , still does not work, set the frequency lower. Change the 80MHz to 16MHz or so and try again.
Yes, as of now, I want to make it as simple and straight forward as possible, taking into account that I want to learn more about libopencm3 and the stm32 chips themselves.