cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F0Discovery timing issue

chrispadbury
Associate II
Posted on August 29, 2012 at 17:11

I am trying to have a simple millisecond delay function and experiencing some very strange results. I am using Keil uVision V4.00.0. Here is my code:

#include ''stm32f0_discovery.h'' 
//prototypes 
void Delay_ms(long ms); 
//configure clocks 
void RCC_Configuration(void) 
{ 
/* --------------------------- System Clocks Configuration -----------------*/ 
/* GPIOC clock enable */ 
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); 
} 
//configure GPIO 
void GPIO_Configuration(void) 
{ 
GPIO_InitTypeDef GPIO_InitStructure; 
/*-------------------------- GPIO Configuration ----------------------------*/ 
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
GPIO_Init(GPIOC, &GPIO_InitStructure); 
} 
int main(void) { 
RCC_Configuration(); 
GPIO_Configuration(); 
while(1) { 
GPIO_ResetBits(GPIOC, GPIO_Pin_9); 
GPIO_SetBits(GPIOC, GPIO_Pin_8); 
Delay_ms(5); 
GPIO_ResetBits(GPIOC, GPIO_Pin_8); 
GPIO_SetBits(GPIOC, GPIO_Pin_9); 
Delay_ms(5); 
} 
} 
void Delay_ms(long ms) { 
//ms = ms * 48000 / 4; 
ms = ms * 38400 / 4; 
//ms = 48000; 
while (ms) { 
ms--; 
} 
} 

This creates an 33Hz squarewave on pin PC8 (measured using scope). I had expected to use a value of 48000 rather than 38400 but that gave an 80Hz squarewave. Iam trying to create 100Hz, which is why I changed it to 38400 expecting to increase the frequency by 25%. I have tried some different lines and get very stange behaviour:

| Multiplier |

Frequency(Hz) |

debug starting Hex |

debug starting Dec|

Clk per loop|

48000

00

5

24000

00

5

12000

00

5

20000

00

5

38400

33

BB80

48000

6

38402

00

BB82

48002

5

38401

00

BB81

48001

5

38399

00

BB7E

47998

5

What's so special about ''ms = ms * 38400 / 4''? I had expected the loop to take 4 clock cycles (hence starting with ''ms = ms * 48000 / 4''). Next I tried hardwiring the starting values, like using ''ms = 48000''. 48,000 created 125Hz and 60,000 created 100Hz. So that's 4 clocks per cycle. looking at the debug the starting Hex in the first case is BB80! I'm using Level 3 (O3) optimization under Keil. Any ideas why I am seeing such strange behaviour? #loop-timing #keil-uvision #stm32f051
15 REPLIES 15
Posted on August 30, 2012 at 19:18

For a purely software loop, I'd attack it like this.

ALIGN
Spin_Delay PROC
EXPORT Spin_Delay
SUBS R0,R0,#1
BNE Spin_Delay
BX LR
ENDP
extern void Spin_Delay(long);
#define DELAY_MS(x) Spin_Delay(((x) * (48000 / 4)) - 3)
...
while(1)
{
GPIOC->BSRR = (1 << 
8
) | ((1 << 9) << 16);
DELAY_MS(5);
GPIOC->BSRR = (1 << 9) | ((1 << 8) << 16);
DELAY_MS(5);
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
chrispadbury
Associate II
Posted on August 31, 2012 at 11:57

Thanks for this. It does assume that the correct delay figure is in R0, is that a fair assumption?

I have been trying to acheive the desired result in the C file without much success. I tried adding the line

__asm(''ALIGN'');

but Keil complains that: ''main.c(54): error: #1113: Inline assembler not permitted when generating Thumb code''. I also tried:

#pragma arm 
__asm(''ALIGN''); 
#pragma thumb

But Keil complains ''main.c(53): error: #1114-D: this feature not supported on target architecture/processor''. The Cortex M0 is thumb only I think, which means no ALIGN:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CIHJJEIH.html

I suspect the key for consistent operation is to avoid using division in the sub-routine as that is of indetermate length, whereas MULS is of fixed length. I was using the division to make it clear how it used the clock frequency but think it's better to use defines:

#define CLOCK_FREQUENCY 48000000 
#define DELAY_MS_MULTIPLIER (CLOCK_FREQUENCY / 5000)

That way the sub-routine becomes:

void Delay_ms(long ms) { 
ms = ms * DELAY_MS_MULTIPLIER; 
__nop(); 
while (ms) { 
ms--; 
} 
}

notice the __nop(); line is still required. I think this approach should be pretty safe as the function call should be aligned shouldn't it?? If wanting to be extra safe, I could have two loops, one of which will always be aligned and one of which will always be unaligned. This seems excessive though:

#define CLOCK_FREQUENCY 48000000 
#define DELAY_MS_MULTIPLIER (CLOCK_FREQUENCY / 11000)
 
void Delay_ms(long ms) { 
long milliseconds; 
milliseconds = ms * DELAY_MS_MULTIPLIER;//48000 / 11; 
//__nop(); 
while (milliseconds) { 
milliseconds--; 
} 
milliseconds = ms * DELAY_MS_MULTIPLIER;//48000 / 11; 
__nop(); 
while (milliseconds) { 
milliseconds--; 
} 
}

chrispadbury
Associate II
Posted on August 31, 2012 at 12:22

Perhaps a bit obsessive, but I have changed the 11 to 12 so that the division of 48000 is exact. With the ms function it may only result in a 0.014% error, but it's considerable when using the us sub-routine I've just added:

#define CLOCK_FREQUENCY 48000000 
#define DELAY_MS_MULTIPLIER (CLOCK_FREQUENCY / 12000) 
#define DELAY_US_MULTIPLIER (CLOCK_FREQUENCY / 12000000) //adapt sub-routine if clock frequency is not divisible by 12
void Delay_ms(long ms) { 
long milliseconds; 
milliseconds = ms * DELAY_MS_MULTIPLIER;//48000 / 12; 
while (milliseconds) { 
milliseconds--; 
__nop(); 
} 
milliseconds = ms * DELAY_MS_MULTIPLIER;//48000 / 12; 
__nop(); 
while (milliseconds) { 
milliseconds--; 
} 
} 
void Delay_us(long us) { //adapt sub-routine if clock frequency is not divisible by 12
//Only acceptable accuracy (<1%) for durations of 50us or more due to sub-routine call time!!
 

long microseconds; 
microseconds = us * DELAY_US_MULTIPLIER;//48 / 12; 
while (microseconds) { 
microseconds--; 
__nop(); 
} 
microseconds = us * DELAY_US_MULTIPLIER;//48 / 12; 
__nop(); 
while (microseconds) { 
microseconds--; 
} 
}

Of course calling the sub-routine will result in an error for very low numbers of us, something that could be corrected in time if needed.
chrispadbury
Associate II
Posted on August 31, 2012 at 13:36

Here is a more accurate sub-routine but you must never call it with an argument of 0 or it will make very long delays!

void Risky_Delay_us(long us) { //adapt sub-routine if clock frequency is not divisible by 12 
//Only acceptable accuracy (<1%) for durations of 5us or more due to sub-routine call time!! 
//********************* CAUTION NEVER CALL WITH us = 0 !!!!!!! ************************************* 
long microseconds; 
microseconds = us * DELAY_US_MULTIPLIER;//48 / 12; 
while (microseconds) { 
microseconds--; 
__nop(); 
} 
us--; //attempt to compensate for sub-routine call time (~0.5us @48MHZ clock) 
microseconds = us * DELAY_US_MULTIPLIER;//48 / 12; 
__nop(); 
while (microseconds) { 
microseconds--; 
} 
}

Heed the warning! AnIF to check slows the sub-routine down more but is safer so:

void Delay_us(long us) { //adapt sub-routine if clock frequency is not divisible by 12 
//Only acceptable accuracy (<
1
%) for durations of 10us or more due to sub-routine call time!! 
long microseconds; 
if(us >= 1) { 
microseconds = us * DELAY_US_MULTIPLIER;//48 / 12; 
while (microseconds) { 
microseconds--; 
__nop(); 
} 
us--; //attempt to compensate for sub-routine call time (~0.5us @48MHZ clock) 
microseconds = us * DELAY_US_MULTIPLIER;//48 / 12; 
__nop(); 
while (microseconds) { 
microseconds--; 
} 
} 
}

This sort of compensation is really easy when working in assembler of course.
Posted on August 31, 2012 at 14:28

I put my assembler in startup.s

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
chrispadbury
Associate II
Posted on August 31, 2012 at 15:31

That's great thanks Clive1, works a treat. I have made one simple change which is to change the BNE to a BGT for the sake of safety (in case of accidentally sending a negative number to Spin_Delay).

Sits nicely at the end of the startup_stm32f0xx.s file above the ;*******; User Stack... where there was already an ALIGN so I didn't duplicate that.

At 48MHz it should work for delays of up to 357 seconds (2^32 /12e6) which gives good flexibility.