cancel
Showing results for 
Search instead for 
Did you mean: 

Exclusively Read/Write a register & Problem of 4 CC interrupts connected to one Interrupt Vector

helixwmonkey
Associate II
Posted on April 11, 2014 at 12:38

Special Thanks to Mr. Clive. Your replies are of great help to me.

Now the problem has been almost almost solved. There used to be near 900ms of vacant of pulses, and Now the pulses are almost continuous. The stepper motor runs well with this waveform.

Following Clive's advices I adapted my code, then it WORKED!

but the waveform is near ideality

. There are glitches in the waveform and Here is my Explanation:

For normal cases, the CC1IF bit is set and then the TIM1_CC_Interrupt Pending bit(NVIC_SETPEND0.27) is also set by hardware, then

 the hardware could toggle the pin of TIM1_CH1 ,generating the desired waveform.

BUT, there is another situation:

suppose the ISR entered and it was triggered by CC2IF. in the ISR, first I would read TIM1_SR to memory: TIM1_SR_mask = TIM1->SR;

But JUST after I read TIM1_SR, the CC1IF occurs. Because CC2IF bit is high now, the Interrupt Pending bit(NVIC_SETPEND0.27)

will not be set

, so the hardware won't toggle TIM1_CH1 output pin. So we observed a missing of pulse, i.e. two successive low/high voltage levels.

Are my guesses right?

 

By the way, I think this is not a good design of the chip that if the CC2IF is 1 and configured as a TIM1_CC_IRQ source, then a new arrival of CC1IF(also enabled as a interrupt source) would not pend the Interrupt Pending bit again.

This compels me to add a judgement whether I missed a CC1IF or CC2IF and then manually pend the Interrupt Pending bit.

And this is why I use ''if'' statements and 

NVIC_SetPendingIRQ(TIM1_CC_IRQn) before I quit the ISR. This is also a cost of the valuable time of the ISR.

Below is the waveform quite near to ideality. Glitches could still be observed.

0690X00000602rhQAA.jpg

Below is my adapted codes:

void TIM1_CC_IRQHandler(void)

{

u16 TIM1_SR_mask;

TIM1_SR_mask = TIM1->SR;

if (TIM1_SR_mask & TIM_SR_CC1IF)

//CC1IF

{

TIM1->SR = ~TIM_SR_CC1IF;

TIM1->CCR1 = TIM1->CCR1 + CCR_Acc_x;

}

if (TIM1_SR_mask & TIM_SR_CC2IF)

//CC2IF

{

TIM1->SR = ~TIM_SR_CC2IF;

}

TIM1_SR_mask = TIM1->SR;

if (TIM1_SR_mask & TIM_SR_CC1IF)

//CC1IF

{

NVIC_SetPendingIRQ(TIM1_CC_IRQn);

}

if (TIM1_SR_mask & TIM_SR_CC2IF)

//CC2IF

{

NVIC_SetPendingIRQ(TIM1_CC_IRQn);

}

}

Thanks again to Clive.

---------------Split Line. Below is my question. Above is my progress solving the problem--------------

I have corrected/adjusted the content to make this question more clear.

My occasion is: using CCR1 and CCR2 in output compare mode(toggle). Every time in their ISR I add a Delte_CCR1(562) and Delta_CCR2(18000) to generate two different frequencies. I configure BOTH CC1IF and CC2IF as the interrupt source. To make the problem clear, I clear CC2IF as soon as TIM1_CC_ISR enters. Then I Only check CC1IF, if it is 1,I add Delte_CCR1(562) to CCR1. Even if while I am reading CC1IF, the hardware set it to 1, that does not matter because the Interrupt Pending bit(NVIC_SETPEND.27) will be set and next time the TIM1_CC_ISR would handle it.

BUT, the PROBLEM came HERE:

In simulation, there is nothing wrong, the running is exactly as what I expected and then a perfect wave is generated.

But, when I connect to the hardware(with JLink), the wave is corrupted and there would be a 900ms vacant of pulses (which is pure high voltage level) every several seconds...Arrrrgggghhhh....

I add several lines and a breakpoint to test this situation would be happening: 

if there are 4 contiuous TIM1_CC_ISR with the CCR1 value unchange, it shows that the problem has happened.

Below is my Test project code.

The startup code is in startup_stm32f10x_hd.s so I did not copy here.(I am using Keil MDK 4.73, JLink 4.76d)

The Variable defination, Registers configuration, and TIM1_CC_IRQHandler is copied here:

&sharpinclude <stm32f10x.h>

&sharpdefine DBGMCU_TIM1_STOP  ((u32)0x00000400)

&sharpdefine BIT_BAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr & 0xFFFFF)<<5)+(bitnum<<2)) 

&sharpdefine MEM_ADDR(addr) *((volatile unsigned long *) (addr)) 

&sharpdefine BIT_ADDR(addr, bitnum) MEM_ADDR(BIT_BAND(addr, bitnum))

&sharpdefine TIMx_SR 0x10

&sharpdefine TIM1_UIF BIT_ADDR(TIM1_BASE+TIMx_SR, 0)

&sharpdefine TIM1_CC1IF BIT_ADDR(TIM1_BASE+TIMx_SR, 1)

&sharpdefine TIM1_CC2IF BIT_ADDR(TIM1_BASE+TIMx_SR, 2)

&sharpdefine TIM1_CC3IF BIT_ADDR(TIM1_BASE+TIMx_SR, 3)

&sharpdefine TIM1_CC4IF BIT_ADDR(TIM1_BASE+TIMx_SR, 4)

u16 CCR_Acc_x;

u16 CCR_Acc_y;

u16 CCR_Mask_x;

u16 CCR_Mask_x_last;

u16 CCR_Mask_x_last2;

u16 CCR_Mask_x_last3;

u16 CCR_Mask_x_last4;

int main(void)

{

RCC->APB2ENR = 0x0804; //Enable TIM1 and GPIOA clock

GPIOA->CRH = 0x0d; //PA8,9 push-pull output,50MHz

TIM1->ARR = 0xffff; //ARR maximum, I use CCR1 and CCR2 compare match interrupt

TIM1->DIER = 0x06; //Enable CC1IE  CC2IE interrupt

TIM1->CCMR1 = 0x3030; //CH1 and CH2 are both compare output(toggle)

TIM1->BDTR = 0x8000; //Enable MOE

TIM1->CR1 |= TIM_CR1_CEN; //Enable Counter

CCR_Acc_x = 562;

//Attention: IF CCR_Acc_x is 4000 or above, The problem would never occur.

CCR_Acc_y = 18000;

TIM1->CCR1 = CCR_Acc_x;

TIM1->CCR2 = CCR_Acc_y;

TIM1->CCR3 = 0xffff;

TIM1->CCR4 = 0xffff;

NVIC_SetPriorityGrouping(4); //3 bits preemption

NVIC_SetPriority(TIM1_CC_IRQn, 0); //highest

NVIC_EnableIRQ(TIM1_CC_IRQn);

DBGMCU->CR |= DBGMCU_TIM1_STOP;

while(1)

{

}

}

void TIM1_CC_IRQHandler(void)

{

TIM1_CC2IF = 0;

TIM1_CC3IF = 0;

TIM1_CC4IF = 0;

TIM1_UIF = 0;

if (TIM1_CC1IF)

{

TIM1_CC1IF = 0;

TIM1->CCR1 = TIM1->CCR1 + CCR_Acc_x;

}

CCR_Mask_x = TIM1->CCR1;

if (CCR_Mask_x_last3==CCR_Mask_x_last2 && CCR_Mask_x_last2==CCR_Mask_x_last && CCR_Mask_x_last==CCR_Mask_x)

{

__nop();

//Set a breakpoint here. Its arrival proves the problem has happened.

}

CCR_Mask_x_last4 = CCR_Mask_x_last3;

CCR_Mask_x_last3 = CCR_Mask_x_last2;

CCR_Mask_x_last2 = CCR_Mask_x_last;

CCR_Mask_x_last = CCR_Mask_x;

}

YES, In simulation ,the __nop() breakpoint would never arrive.

But in real-running with hardware, the __nop() breakpoint would arrive every several seconds.

Previously I thought it was a Exclusively Read/Write problem and I asked the following question, but it turns out the real problem finally turns out as above.

---------------Split Line. Below is my original question--------------

How to Exclusively Read/Write a register?

After I read a register, TIM1->SR for example, Hardware may change it before I write it. So how to get a Exclusive access to it?

I have the following code, but it turns out that LDREX/STREX pair did not work:

unsigned long TIM1_SR_mask;

__TRY_:

TIM1_SR_mask = __LDREXH(&(TIM1->SR));

if (__STREXH(0,&(TIM1->SR)))

{

goto __TRY_;

}

and even if I add a ''TIM1->SR = 1;'' between them, the __STREXH would be always successful...

Or, did I mis-use the LDREX/STREX pair?

Someone please help..

#exclusive-mutex-atom
12 REPLIES 12
helixwmonkey
Associate II
Posted on April 12, 2014 at 15:11

Thanks.

With your kindness explanation and suggestions I found the real problem of my project and I updated this topic. It's not about Exclusively read/write a register, it's about something else.

Please check my corrections & supplement of the topic above.

with my Sincere thanks.

Posted on April 12, 2014 at 15:35

Don't use bit-banding on TIMx->SR, and be very careful using it on any peripheral register. They do not operate like memory cells, so writes are generally dangerous, have side-effects, or simply aren't particularly efficient.

Specific things to watch are registers which point internally to different structures (USART->DR), registers which when read/written impact status stored in other registers (SPI->DR - TXE, RXNE), registers designed to provide atomic clearing of bits which can be modified by the CPU/Peripheral independently (TIM->SR).

When programming peripheral registers it is a lot more efficient to read the whole register, mask the bits being used with an AND, and then OR back all the settings, an then writing it back. Modifying one bit at a time is about the most inefficient thing you can do on a LOAD/STORE architecture.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
helixwmonkey
Associate II
Posted on April 13, 2014 at 12:27

Thanks.

With your help the problem is almost solved. I updated this topic again. I would be very glad if you could offer more advices :D