2016-01-18 11:10 AM
Hello,
I'd like to create a sequencer on my STM32F100RB (@8MHz) to control 8 independants PWM channels. I configured a timer to generate a 10us interrupt wich call the update function.//timer 10µs
void Init_TIMER(void)
{
TIM2->PSC = 7; // Set prescaler to 24 000 (PSC + 1)
TIM2->ARR = 10; // Auto reload value 1000
TIM2->DIER = TIM_DIER_UIE; // Enable update interrupt (timer level)
TIM2->CR1 = TIM_CR1_CEN; // Enable timer
NVIC_EnableIRQ(TIM2_IRQn); // Enable interrupt from TIM2 (NVIC level)
}
Here my interrupt function :
void TIM2_IRQHandler(void)
{
if(TIM2->SR & TIM_SR_UIF) // if UIF flag is set
{
if (currentPeriod[0] > 0){
currentTick[0]++;
if (currentTick[0] >= currentPeriod[0]){
move(0,STEP_0,DIR_0);
currentTick[0]=0;
}
}
if (currentPeriod[1] > 0){
currentTick[1]++;
if (currentTick[1] >= currentPeriod[1]){
move(1,STEP_1,DIR_1);
currentTick[1]=0;
}
}
[6 more blocks]
TIM2->SR &= ~TIM_SR_UIF; // clear UIF flag
}
}
and my move function :
void move (int mot_id, uint16_t pin, uint16_t direction_pin) {
//Switch directions if end has been reached
if (currentPosition[pin] >= MAX_POSITION) {
currentState[direction_pin] = 1;
GPIO_SetBits(GPIOC,direction_pin);
}
else if (currentPosition[pin] <= 0) {
currentState[direction_pin] = 0;
GPIO_ResetBits(GPIOC,direction_pin);
}
//Update currentPosition
if (currentState[direction_pin] == 1){
currentPosition[pin]--;
}
else {
currentPosition[pin]++;
}
//Pulse the control pin
if (currentState[pin]== 0){
GPIO_ResetBits(GPIOC,pin);
}
else{
GPIO_SetBits(GPIOC,pin);
}
currentState[pin] = ~currentState[pin];
}
When I try this code with one single output I generate a signal with the right frequency but if i had more output(blocks) the frequency decrease.
Have you ever experienced such a problem ? #sequencer #pwm #timer2016-01-18 11:31 AM
Expecting an 8 MHz CPU to interrupt at 100 KHz is probably a bit optimistic. You have 80 machine cycles to play with. C lines != machine cycles
ARR = 10 - 1; // DIV10, remember it is N-1TIM2->SR &= ~TIM_SR_UIF; // this RMW form is wrongTIM2->SR = ~TIM_SR_UIF; // Correct form2016-01-18 12:07 PM
Clive, I have also one question.
Why this is wrong: TIM2->SR &= ~TIM_SR_UIF;It should works too, doesn't it?Is this (TIM2->SR = ~TIM_SR_UIF;) correct because flags could not be set by software anyway?2016-01-18 01:18 PM
It has the potential to clear interrupts other than the one you want, it is not atomic.
Review the design, and the manual, the single write clears the interrupt(s) in an atomic fashion.This hazard has been covered here numerous times before, don't use bit-banding on TIM->SR either.2016-01-18 02:12 PM
It is a self replicating trap for the unwary, ready to be cut-n-pasted throughout your own code, and over the internets
Using RMW, it is not atomic on a load-store RISC processorTIMx_SR = 0x0000 No Interrupts PendingTIMx_SR = 0x0001 Update Interrupt Occurstemp = TIMx_SR Read Register.. a few bus cycles passTIMx_SR = 0x0003 Now a CC1 Interrupt Occurstemp &= ~0x0001So temp = 0x0001 & 0xFFFEtemp = 0x0000 (remember 1 & 0 = 0, and 0 & 1 = 0).. a few more bus cycles passWrite Register TIMx_SR with temp (equivalent to TIMx_SR &= 0 at the register)TIMx_SR = 0x0000 Look both Update and CC1 cleared, and no one noticesCompared toTIMx_SR = 0x0000 No Interrupts PendingTIMx_SR = 0x0001 Update Interrupt OccursTIMx_SR = 0x0003 Now a CC1 Interrupt OccursWrite Register TIMx_SR with 0xFFFE (equivalent to TIMx_SR &= 0xFFFE at the register)TIMx_SR = 0x0002 Just Update clearedWrite Register TIMx_SR with 0xFFFD (equivalent to TIMx_SR &= 0xFFFD at the register)TIMx_SR = 0x0000 Just CCR1 cleared2016-01-19 11:50 AM
Thank you for your answers, it was very interresting. I can change my 8MHz quartz to a 24MHz one It would give my more machine cycle, but i don't know if it could solve my problem.
If I lower the sequencer frequency to 40KHz for example I loose also frequency accuraty.