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.
8 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
8 years, 1 month 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; }
8 years, 2 months 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.
8 years, 2 months 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