Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
9 years, 4 months ago.
Wait function shows incorrect timing on Nucleo F103 board
Hello Community,
I might have found a bug in the wait function when used with Nucleo F103BR boards. I need some simple port toggling after 150us, 1ms and 100ms.
Upon measuring the timings via oscilloscope I see myself confronted with wrong timings at the I/O pins 150µs are 34ms 1ms 65ms and so on
I also verified the problem with 2 other F103 boards to rule out a hardware bug The same code on a F411 Nucleo shows correct timing behavior.
The code of the application can be found in this discussion: https://forums.mbed.com/t/wait-function-shows-incorrect-timing-on-nucleo-board/1673/2
your faithfully, Chris
Question relating to:
3 Answers
9 years ago.
Thank you Zoltan for your answer. It helped me to understand. I solved this problem. so: Between the lines
mbed-dev/targets/hal/TARGET_STM/TARGET_STM32F1/us_ticker.c
counter = (uint32_t)(SlaveCounter << 16);
counter += cnt_val;
some times was interrupt TIM4_IRQn and update the variable cnt_val. So I was banned it.
mbed-dev/targets/hal/TARGET_STM/TARGET_STM32F1/us_ticker.c
........
// Timer selection
#define TIM_MST TIM4
........
uint32_t us_ticker_read()
{
uint32_t counter;
if (!us_ticker_inited) us_ticker_init();
//Current value of TIM_MST->CNT is stored in cnt_val and is
//updated in interrupt context
NVIC_DisableIRQ(TIM4_IRQn);
counter = (uint32_t)(SlaveCounter << 16);
counter += cnt_val;
NVIC_EnableIRQ(TIM4_IRQn);
return counter;
}
seems that is there an official fix for this problem https://github.com/ARMmbed/mbed-os/pull/3076/files
posted by 20 Oct 2016Hello Slava,
Thank you for your suggestion. It sounds logical and elegant, but when I tried to test it on a NUCLEO-F103RB with the following code:
main.cpp
#include "mbed.h"
int main()
{
Serial pc(USBTX, USBRX);
DigitalOut myled(LED1);
Timer timer;
int begin;
int end;
timer.start();
while(1) {
myled = !myled;
begin = timer.read_us();
wait_us(1);
end = timer.read_us();
pc.printf("Delay = %d\r\n",(end - begin));
}
}
I have got the following results:
Delay = 1001
Delay = 1001
Delay = 1001
Delay = 1001
Delay = 1001
Delay = 1001
Delay = 642
Delay = 1001
Delay = 1001
...
With the fix proposed officially, which is the code already used in mbed-dev revision #135, the results were as follows:
Delay = 8
Delay = 8
Delay = 8
Delay = 8
Delay = 7
Delay = 7
Delay = 8
Delay = 7
...
I am currently using the following implementation:
us_ticker.c
uint32_t us_ticker_read()
{
uint32_t counter, counter2;
if (!us_ticker_inited) us_ticker_init();
// A situation might appear when Master overflows right after Slave is read and before the
// new (overflowed) value of Master is read. Which would make the code below consider the
// previous (incorrect) value of Slave and the new value of Master, which would return a
// value in the past. Avoid this by computing consecutive values of the timer until they
// are properly ordered.
counter = (uint32_t)(SlaveCounter << 16);
counter += TIM_MST->CNT;
do {
counter2 = counter;
counter = (uint32_t)(SlaveCounter << 16);
counter += TIM_MST->CNT;
} while(counter < counter2);
return counter;
}
With the results as follows:
Delay = 6
Delay = 6
Delay = 6
Delay = 7
Delay = 6
Delay = 6
Delay = 7
Delay = 6
Delay = 6
...
Update #1:
Hello Slava,
After a minor change it seems that your elegant proposal works correctly:
us_ticker.c
uint32_t us_ticker_read()
{
uint32_t counter;
if (!us_ticker_inited) us_ticker_init();
NVIC_DisableIRQ(TIM4_IRQn);
counter = (uint32_t)(SlaveCounter << 16);
counter += TIM_MST->CNT;
NVIC_EnableIRQ(TIM4_IRQn);
return counter;
}
The results were as follows:
Delay = 6
Delay = 6
Delay = 6
Delay = 7
Delay = 6
Delay = 6
Delay = 7
Delay = 6
Delay = 6
...
Update #2:
Hello Slava,
Finally I have tested the following simple solution:
us_ticker.c
uint32_t us_ticker_read()
{
uint32_t counter;
if (!us_ticker_inited) us_ticker_init();
counter = TIM_MST->CNT;
counter += (uint32_t)(SlaveCounter << 16);
return counter;
}
And guess what, it worked like a charm :)
Delay = 5
Delay = 6
Delay = 7
Delay = 5
Delay = 6
Delay = 7
...
Update #3:
Hello Slava,
After about an hour also the code above returned an inconsistent time stamp. So my proposal is as follows:
us_ticker.c
volatile uint32_t SlaveCounter = 0;
...
volatile uint8_t tim_it_update; // TIM_IT_UPDATE event flag
volatile uint32_t tim_it_counter = 0; // time stamp to be updated by timer_irq_handler()
uint32_t us_ticker_read()
{
uint32_t counter;
if (!us_ticker_inited) us_ticker_init();
tim_it_update = 0; // clear TIM_IT_UPDATE event flag
counter = TIM_MST->CNT + (uint32_t)(SlaveCounter << 16); // calculate new time stamp
if (tim_it_update == 1)
return tim_it_counter; // in case of TIM_IT_UPDATE return the time stamp that was calculated in timer_irq_handler()
else
return counter; // otherwise return the time stamp calculated here
}
hal_tick.c
extern volatile uint32_t SlaveCounter;
...
extern volatile uint8_t tim_it_update;
extern volatile uint32_t tim_it_counter;
void timer_irq_handler(void) {
cnt_val = TIM_MST->CNT;
TimMasterHandle.Instance = TIM_MST;
// Clear Update interrupt flag
if (__HAL_TIM_GET_FLAG(&TimMasterHandle, TIM_FLAG_UPDATE) == SET) {
if (__HAL_TIM_GET_IT_SOURCE(&TimMasterHandle, TIM_IT_UPDATE) == SET) {
__HAL_TIM_CLEAR_IT(&TimMasterHandle, TIM_IT_UPDATE);
SlaveCounter++;
tim_it_counter = cnt_val + (uint32_t)(SlaveCounter << 16);
tim_it_update = 1;
}
}
...
}
The results are promising :)
Thank you Zoltan! Your solution very good! My old solution really does not work well with low wait. This is due to the fact that variables (cnt_val, SlaveCounter) are updated every 1 ms and when the timer overflow So my solution is the same as yours: To check for updates to the SlaveCounter. during the reading of the data variables. The difference: If SlaveCounter updated, I repeat the operation.
mbed-dev/targets/hal/TARGET_STM/TARGET_STM32F1/us_ticker.c
uint32_t us_ticker_read()
{
uint32_t counter,counter2;
if (!us_ticker_inited) us_ticker_init();
counter = (uint32_t)(SlaveCounter << 16);
uint16_t val = TIM_MST->CNT;
counter2 = (uint32_t)(SlaveCounter << 16);
if (counter!=counter2) //To check for updates to the SlaveCounter
counter= counter2+TIM_MST->CNT; //repeat the operation
else
counter+=val;
return counter;
}
9 years, 1 month ago.
Hello Chris,
Looks like the bug you reported also on the Forum is still there. I think it's caused by late update of the SlaveCounter variable in interrupt context while used in the us_ticker_read() function. One solution could be to wait until the SlaveCounter gets updated by the timer_irq_handler() as follows:
mbed-dev/targets/hal/TARGET_STM/TARGET_STM32F1/us_ticker.c
// ...
const int MAX_LOOPS = 5
// ...
volatile uint32_t previousCounter = 0;
// ...
uint32_t us_ticker_read()
{
uint32_t counter;
int i = 0;
if (!us_ticker_inited) us_ticker_init();
//Current value of TIM_MST->CNT is stored in cnt_val and is
//updated in interrupt context
do {
counter = (uint32_t)(SlaveCounter << 16);
counter += cnt_val;
// Repeat until SlaveCounter gets updated in interrupt context
// Also take into account roll over and prevent endless looping
} while((counter < previousCounter) && (SlaveCounter != 0) && (i++ < MAX_LOOPS));
previousCounter = counter;
return counter;
}
Although that isn't a perfect solution it would considerably improve the performance of wait() function for NUCLEO-F103RB boards.
9 years, 1 month ago.
but is there an official issue opened on GIthub ?
Opened an official issue, confirmed the bug https://github.com/ARMmbed/mbed-os/issues/2910
posted by 07 Oct 2016