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
stm322399
Senior
Posted on April 11, 2014 at 13:58

It is unlikely that what you try d do will succeed.

Synchronization primitives (LDREX/STREX) of the ARM processor only prevent the processor itself to corrupt a Read-Modify-Write sequence.

Normally peripheral are designed in a way that does not require Exclusive access to their registers.

Follow this advice: when you program timers, forget about LDREX/STREX and explain us what you are trying to do and what is the issue you face.

Posted on April 11, 2014 at 14:01

There's no need for that. Peripheral registers, which are to be changed by both hardware and software, have provisions to avoid the need of read-modify-write operations.

For example, in TIMx_SR, hardware only sets individual flags, and software only needs to clear them. As all bits in TIMx_SR are of rc_w0 type (look at the beginning of any of the Reference Manual for what that means), they are cleared from software by writing 0 to them, i.e. you don't do

TIMx_SR &= ~TIM_SR_xxx;

rather you do

TIMx_SR = ~TIM_SR_xxx;

JW

mckenney
Senior
Posted on April 11, 2014 at 15:58

I've seen this advice a few times on the Forum. While I defer to others' expertise, I also see that the unassigned SR bits are ''Reserved'', which means ''Keep at reset value''. This seems to imply a RMW sequence, not a simple write.

Not to doubt anyone, but is there ''chapter and verse'' about this somewhere?

Posted on April 11, 2014 at 17:11

I'd assume it's always safe to do with the ''reserved'' bits the same as with other bits in the same register. The documentation says this nowhere, but then it lacks more important details than this.

Particularly, in the TIMx_SR register, I believe it is safe to write 1 to the reserved bits.

But if you are not comfortable with this, note, that the reset value is always stated even for the reserved bits in the registers, usually zero, so it should be always safe to write zero to them.

And, of course, you ought to demand improvement in the documentation from the ST staff through https://my.st.com/onlinesupport/app?page=onlineSupport  ... 😉

JW

Posted on April 11, 2014 at 17:21

It should be plainly evident that using RMW on TIMx->SR introduces race conditions because the processor can't access it in an atomic fashion.

Both RMW and bit-band access can be shown to induce failure.

The use of a singular write edge to mask current content resolves this, remember the peripheral register are not and do not behave like regular memory cells, and often have combination logic tied around them.
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 11, 2014 at 17:39

Sorry but what does ''singular write edge to mask current content'' mean? Can you offer more details, thanks a lot.

Posted on April 11, 2014 at 18:08

You understand how synchronous logic works, right?

D := Q & M At the rising edge of the clock the value of Q AND'd with M is latched into D If the SR has 16 bits, Q[0..15] is the current content, D[0..15] will be the new/next value, and M[0..15] is a mask being applied, where the mask bit is ONE the old data passes, where it is ZERO the data is cleared Thus TIMx->SR = M looks like : D[0..15] := Q[0..15] & M[0..15] With the new interupts coming in via I[0..15], the logic is more like : D[0..15] := (Q[0..15] & M[0..15]) | I[0..15] With TIMx->SR &= ~CC1IF; In the most simplistic review of reading and writing (ignoring pipelining, write buffers, and other latency) it will take at least 1 APB cycle between the operations, the TIMCLK is nominally TWICE that.

CC1IF asserts
Cycle +0.0 You READ TIMx->SR with CC1IF asserted (X = CC1IF)
Cycle +0.5 CC2IF asserts
Cycle +1.0 You WRITE TIMx->SR with (X & ~CC1IF) ie ZERO, this clears CC1IF and CC2IF

You have now totally missed the CC2IF event.
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 11, 2014 at 19:16

Thanks, I got it 🙂

Is there any way to Atomically Read & Write this TIM1_SR Register?

I have used a chip before. For that chip if I read a Interrupt Flags Register, the hardware would automatically clear it in an atomic operation. Something like this feature is exactly what I need now.

I notice that some STM32F registers have the attribute ''rc_r'' which means ''clear by read''. And I think TIM1_SR should be this attribute (but actually is ''rc_w0'') because we have to read CCxIF to judge which CCx causes this interrupt with the fact that CC1~4 are sharing the same interrupt route.

Posted on April 11, 2014 at 21:25

Is there any way to Atomically Read & Write this TIM1_SR Register?

No, not in the way you're trying to doing it.

You should read the register, service and clear the SPECIFIC source you are servicing. You can then read the register again, and observe if anything else has asserted, or remains asserted. Bits asserted will remain so until you clear them. The act of clearing them is Atomic, in the sense that at the instant you clear it, another pending interrupt will latch in it's place. The only occasion it's going to miss interrupts is when you do the RMW (&=) stuff, or the equivalent via bit-banding.

wr0 means write-as-zero, writing one bits as no effect.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..