2014-04-11 03:38 AM
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 andNVIC_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.
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-atom2014-04-12 06:11 AM
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.2014-04-12 06:35 AM
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.2014-04-13 03:27 AM
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