Timer 如何在STM32L Discovery ARM板上制作微秒级精密计时器?

Timer 如何在STM32L Discovery ARM板上制作微秒级精密计时器?,timer,stm32,1wire,Timer,Stm32,1wire,我正在尝试实现Dallas OneWire协议,但在STM32l发现上生成微秒延迟时遇到问题 如何实现一个足够精确的计时器,使程序延迟x微秒?对于start,我必须告诉您,使用软件无法实现精确的usec延迟。即使使用基于中断的系统,也会有延迟。当然,您可以通过更大的CPU频率获得更好的精度 要连接单线设备,您可以使用: 像这样的外部接口 使用引脚轮询的软件单线实现 对于第二种解决方案,您必须调用基于软件的延迟。您可以设置标志轮询延迟或基于中断的标志轮询延迟。在这两种情况下,您都将确定经过了一

我正在尝试实现Dallas OneWire协议,但在STM32l发现上生成微秒延迟时遇到问题


如何实现一个足够精确的计时器,使程序延迟x微秒?

对于start,我必须告诉您,使用软件无法实现精确的usec延迟。即使使用基于中断的系统,也会有延迟。当然,您可以通过更大的CPU频率获得更好的精度

要连接单线设备,您可以使用:

  • 像这样的外部接口
  • 使用引脚轮询的软件单线实现
对于第二种解决方案,您必须调用基于软件的延迟。您可以设置标志轮询延迟或基于中断的标志轮询延迟。在这两种情况下,您都将确定经过了一定的时间,但您无法确定是否经过了更多的时间。这是因为CPU延迟、CPU时钟等

例如,考虑下面的实现。我们编写了一个硬件计时器,以连续计数,并检查计时器的值。我们将“jiffy”命名为每个计时器的滴答声和jiffies之间的时间计时器的最大值:

低电平驱动器部分(例如:driver.h)

低电平驱动器部分(例如:driver.c)

jiffy.c:

#include "string.h"

typedef int32_t   jiffy_t;    // Jiffy type 4 byte integer
typedef int (*jf_setfreq_pt) (uint32_t, uint32_t);   //Pointer to setfreq function

typedef volatile struct {
   jf_setfreq_pt  setfreq;       // Pointer to driver's timer set freq function
   jiffy_t        *value;        // Pointer to timers current value
   uint32_t       freq;          // timer's  frequency
   uint32_t       jiffies;       // jiffies max value (timer's max value)
   jiffy_t        jpus;          // Variable for the delay function
}jf_t;


/*
 *  ============= PUBLIC jiffy API =============
 */

/*
 * Link functions
 */
void jf_link_setfreq (jf_setfreq_pt pfun);
void jf_link_value (jiffy_t* v);

/*
 * User Functions
 */
void jf_deinit (void);
int jf_init (uint32_t jf_freq, uint32_t jiffies);

jiffy_t jf_per_usec (void);
void jf_delay_us (int32_t usec);
int jf_check_usec (int32_t usec);
#include "jiffy.h"

static jf_t _jf;
#define JF_MAX_TIM_VALUE      (0xFFFF)    // 16bit counters

//Connect the Driver's Set frequency function 
void jf_link_setfreq (jf_setfreq_pt pfun) {
   _jf.setfreq = pfun;
}

// Connect the timer's value to jiffy struct
void jf_link_value (jiffy_t* v) {
   _jf.value = v;
}

// De-Initialize the jf data and un-connect the functions
// from the driver
void jf_deinit (void) {
   memset ((void*)&_jf, 0, sizeof (jf_t));
}

// Initialise the jf to a desired jiffy frequency f
int jf_init (uint32_t jf_freq, uint32_t jiffies) {
   if (_jf.setfreq) {
      if ( _jf.setfreq (jf_freq, jiffies) )
         return 1;
      _jf.jiffies = jiffies;
      _jf.freq = jf_freq;
      _jf.jpus = jf_per_usec ();
      return 0;
   }
   return 1;
}

// Return the systems best approximation for jiffies per usec
jiffy_t jf_per_usec (void) {
   jiffy_t jf = _jf.freq / 1000000;

   if (jf <= _jf.jiffies)
      return jf;
   else
      // We can not count beyond timer's reload
      return 0;
}

/*!
 * \brief
 *    A code based delay implementation, using jiffies for timing.
 *    This is NOT accurate but it ensures that the time passed is always
 *    more than the requested value.
 *    The delay values are multiplications of 1 usec.
 * \param
 *    usec     Time in usec for delay
 */
void jf_delay_us (int32_t usec) {
   jiffy_t m, m2, m1 = *_jf.value;

   usec *= _jf.jpus;
   if (*_jf.value - m1 > usec) // Very small delays will return here.
      return;

   // Delay loop: Eat the time difference from usec value.
   while (usec>0) {
      m2 = *_jf.value;
      m = m2 - m1;
      usec -= (m>0) ? m : _jf.jiffies + m;
      m1 = m2;
   }
}

/*!
 * \brief
 *    A code based polling version delay implementation, using jiffies for timing.
 *    This is NOT accurate but it ensures that the time passed is always
 *    more than the requested value.
 *    The delay values are multiplications of 1 usec.
 * \param
 *    usec     Time in usec for delay
 */
 int jf_check_usec (int32_t usec) {
   static jiffy_t m1=-1, cnt;
   jiffy_t m, m2;

   if (m1 == -1) {
      m1 = *_jf.value;
      cnt = _jf.jpus * usec;
   }

   if (cnt>0) {
      m2 = *_jf.value;
      m = m2-m1;
      cnt-= (m>0) ? m : _jf.jiffies + m;
      m1 = m2;
      return 1;   // wait
   }
   else {
      m1 = -1;
      return 0;   // do not wait any more
   }
}
#include "driver.h"
#include "jiffy.h"

void do_some_job1 (void) {
   // job 1
}
void do_some_job2 (void) {
   // job 2
}
int main (void) {
   jf_link_setfreq ((jf_setfreq_pt)JF_setfreq);  // link with driver
   jf_link_value ((jiffy_t*)&JF_TIM_VALUE);
   jf_init (1000000, 1000);  // 1MHz timer, 1000 counts, 1 usec per count

   // use delay version
   do_some_job1 ();
   jf_delay_us (300);  // wait for at least 300 usec
   do_some_job1 ();

   // use polling version
   do_some_job1 ();
   while (jf_check_usec (300)) {
      do_some_job2 ();  // keep calling for at least 300 usec
   }
}

谢谢你给我这个令人惊讶的、宽泛的回答。
#include "jiffy.h"

static jf_t _jf;
#define JF_MAX_TIM_VALUE      (0xFFFF)    // 16bit counters

//Connect the Driver's Set frequency function 
void jf_link_setfreq (jf_setfreq_pt pfun) {
   _jf.setfreq = pfun;
}

// Connect the timer's value to jiffy struct
void jf_link_value (jiffy_t* v) {
   _jf.value = v;
}

// De-Initialize the jf data and un-connect the functions
// from the driver
void jf_deinit (void) {
   memset ((void*)&_jf, 0, sizeof (jf_t));
}

// Initialise the jf to a desired jiffy frequency f
int jf_init (uint32_t jf_freq, uint32_t jiffies) {
   if (_jf.setfreq) {
      if ( _jf.setfreq (jf_freq, jiffies) )
         return 1;
      _jf.jiffies = jiffies;
      _jf.freq = jf_freq;
      _jf.jpus = jf_per_usec ();
      return 0;
   }
   return 1;
}

// Return the systems best approximation for jiffies per usec
jiffy_t jf_per_usec (void) {
   jiffy_t jf = _jf.freq / 1000000;

   if (jf <= _jf.jiffies)
      return jf;
   else
      // We can not count beyond timer's reload
      return 0;
}

/*!
 * \brief
 *    A code based delay implementation, using jiffies for timing.
 *    This is NOT accurate but it ensures that the time passed is always
 *    more than the requested value.
 *    The delay values are multiplications of 1 usec.
 * \param
 *    usec     Time in usec for delay
 */
void jf_delay_us (int32_t usec) {
   jiffy_t m, m2, m1 = *_jf.value;

   usec *= _jf.jpus;
   if (*_jf.value - m1 > usec) // Very small delays will return here.
      return;

   // Delay loop: Eat the time difference from usec value.
   while (usec>0) {
      m2 = *_jf.value;
      m = m2 - m1;
      usec -= (m>0) ? m : _jf.jiffies + m;
      m1 = m2;
   }
}

/*!
 * \brief
 *    A code based polling version delay implementation, using jiffies for timing.
 *    This is NOT accurate but it ensures that the time passed is always
 *    more than the requested value.
 *    The delay values are multiplications of 1 usec.
 * \param
 *    usec     Time in usec for delay
 */
 int jf_check_usec (int32_t usec) {
   static jiffy_t m1=-1, cnt;
   jiffy_t m, m2;

   if (m1 == -1) {
      m1 = *_jf.value;
      cnt = _jf.jpus * usec;
   }

   if (cnt>0) {
      m2 = *_jf.value;
      m = m2-m1;
      cnt-= (m>0) ? m : _jf.jiffies + m;
      m1 = m2;
      return 1;   // wait
   }
   else {
      m1 = -1;
      return 0;   // do not wait any more
   }
}
#include "driver.h"
#include "jiffy.h"

void do_some_job1 (void) {
   // job 1
}
void do_some_job2 (void) {
   // job 2
}
int main (void) {
   jf_link_setfreq ((jf_setfreq_pt)JF_setfreq);  // link with driver
   jf_link_value ((jiffy_t*)&JF_TIM_VALUE);
   jf_init (1000000, 1000);  // 1MHz timer, 1000 counts, 1 usec per count

   // use delay version
   do_some_job1 ();
   jf_delay_us (300);  // wait for at least 300 usec
   do_some_job1 ();

   // use polling version
   do_some_job1 ();
   while (jf_check_usec (300)) {
      do_some_job2 ();  // keep calling for at least 300 usec
   }
}