AnsweredAssumed Answered

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

Question asked by li.guocheng on Apr 11, 2014
Latest reply on Apr 13, 2014 by li.guocheng

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.

WaveFormSTM32.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:

#include <stm32f10x.h>

     #define DBGMCU_TIM1_STOP  ((u32)0x00000400)
     #define BIT_BAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr & 0xFFFFF)<<5)+(bitnum<<2)) 
     #define MEM_ADDR(addr) *((volatile unsigned long *) (addr)) 
     #define BIT_ADDR(addr, bitnum) MEM_ADDR(BIT_BAND(addr, bitnum))
     #define TIMx_SR 0x10
     #define TIM1_UIF BIT_ADDR(TIM1_BASE+TIMx_SR, 0)
     #define TIM1_CC1IF BIT_ADDR(TIM1_BASE+TIMx_SR, 1)
     #define TIM1_CC2IF BIT_ADDR(TIM1_BASE+TIMx_SR, 2)
     #define TIM1_CC3IF BIT_ADDR(TIM1_BASE+TIMx_SR, 3)
     #define 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..

Outcomes