2013-02-11 10:01 AM
Hello to everyone.
I have just started to study STM32 microcontrolles (using STM32F4 Discovery kit) and can't understand one thing.Test task is simple �? set up Timer 7 to generate 100000 update events per second, and in the interrupt service routine toggle one pin of the port to check settings of the timer.So, here is my code (IAR Embedded Wokbench for ARM):#include <stdio.h>#include <stdint.h>#include ''stm32f4xx.h''void initiazliation(void);void initiazliation(void){ RCC->AHB1ENR|=0x09; // Enable clock on PORTA and PORTD GPIOA->MODER|=0x00000004; // PA1 is output GPIOA->OTYPER|=0x00000000; // Push pull output GPIOA->OSPEEDR|=0x00000004; // 25 MHz output GPIOA->PUPDR|=0x00000000; // No pull-up, no pull-down GPIOA->IDR|=0x0000; // Input register GPIOA->ODR|=0x0000; // Output register DBGMCU->APB1FZ|=DBGMCU_APB1_FZ_DBG_TIM7_STOP; RCC->APB1ENR|=0x00000020; // Enale clock on Timer/Counter 7 TIM7->ARR=79; // Fsclk=8MHz, Fuev=8000000/(79+1)=100KHz TIM7->PSC=0; // No clock prescaling TIM7->DIER=0x0001; // Enable update interrupt TIM7->CR1=0x0001; // Enable Timer NVIC_EnableIRQ(TIM7_IRQn); // NVIC interrupt enable}void TIM7_IRQHandler(void){// if (TIM7->SR&0x0001)// { GPIOA->ODR^=0x0002; TIM7->SR&=0xFFFE;// }}void main(void){ initiazliation(); __enable_irq(); while(1);}As you can see, I use Timer 7 to generate Update Events and PA1 pin to toggle. MCU uses HSE as a main system clock (8MHz crystal), AHB, APB1 and APB 2 prescalers are set to 1, prefetch for instructions is enabled and flash latency is zero.I thought, that this code will output squarewave pulses at PA1, and its frequency and duty cycle have to be 50 KHz and 0.5 (50 %) correspondingly. But in reality I have squrewave at 100 KHz, and duty cycle 0.41 (41 %). When I uncomment commented lines, everything is OK �? frequency is 50 kHz and duty cycle is 50 %.I can't understand, why it happens? Does such behaviour means, that ISR is entered again, after it was executed once? But why? I clear UIF flag in the Status Register of the Timer... And NVIC registers say's, that there is no pending interrupts from this source. Disassembler doesn't show difference betwee two variants of the ISR �? both of them finish with BX LR instruction and there is no pending requests.Please, help me to understand, what actually happens in this case.P.S.Sorry for my bad English.2013-02-11 10:48 AM
If you program at a register level you need to read the documentation, and understand the part, a lot better than someone using the library.
void TIM7_IRQHandler(void)
{
if (TIM7->SR&0x0001)
{
TIM7->SR = ~0x0001; // Clear the IRQ
GPIOA->ODR^=0x0002;
}
}
Be aware that there is a prefetch/write buffer hazard (race condition) when clearing interrupts and the NVIC causing a tail chain re-entry.
Be aware that the registers in the peripheral are not always like memory, and doing a RMW is not always appropriate or correct.
2013-02-12 12:30 AM
I was looking, by chance, at some timer interrupts today on STM32F4. I was using TIM4.
I'm also a bit of a register basher!Do have a look at TIMx->EGR. I needed to use it, and you may too!2013-02-12 02:34 AM
2013-02-12 02:42 AM
2013-02-12 03:00 AM
>> Be aware that the registers in the peripheral are not always like memory, and doing a RMW is not always appropriate or correct.
> And, I'm sorry, but what does ''RMW'' mean? Read-modify-write. As in TIMx_SR &= mask. Read the manual carefully: all register descriptions have marks below the bits' names; they are explained at the very beginning of the manual. The TIMx_SR register's bits are all marked as ''rc_w0'', which means ''readable, can be cleared by writing 0''. Therefore, you should write the mask directly to the register as Clive did (TIMx_SR = mask); it's the hardware which performs the clear. If you read-modify-write, you can clear bits you did not intend to. Note, that there are SFR bits which don't have an appropriate description, usually for flags which are cleared by a more complex sequence of events (e.g. SPIx_SR.OVR). (ST: it might be a good idea to mark these bits in a different way). JW PS. Please don't write your answers below the original post.2013-02-12 10:03 AM
Thanks everyone for the help.
Clive1, you were right. When I've changed operator's order, code started to work correctly:void TIM7_IRQHandler(void){ TIM7->SR&=0xFFFE; // If flag is cleared here, everything works OK GPIOA->ODR^=0x0002;// TIM7->SR&=0xFFFE; // But if here, then code execution fails}If I place __ISB() instruction, then position of the line, which clears interrupt flag is not significant.Is is so simple, that I feel a bit of shame... I know, that ARM Cortex core does not guarantee the order of instruction execution, but i didn't expected it in this case...So, cause of my situation is, that in spite of I clear interrupt flag in my ISR, actually it is executed by the microcontroller's core after return from the routine, and this causes tail-chaining of the interrupt? Do I correctly understand source of this problem?2013-02-12 10:30 AM
You don't need &=, you should just write this register
The processor completes in order, but is pipelined, and the writes occur through a write buffer arrangement so they may retire somewhat later than you expect. The exact speed will depend on the bus(es) over which the transaction occurs and whatever else is going on, prefetch, contention, etc. The write buffer will be fenced by a read, ie the write will complete before the read starts. A prefetch read on the instruction bus may not count. The prefetch, pipelining, and write buffer create a hazard. Your operation on TIM->SR causes a write across the AHB/APB buses and will take at least 4 cycles, where as your internal register moves (LR, PC) will take 1. By the time the processor decides whether to tail-chain the interrupt is still signalling as the clear request is still propagating, the interrupt will re-enter (this or another), but a read to SR will fence and indicate one is not pending.2013-02-13 02:47 AM
Ok, everything is clear now. Thank You for your answers.