【问题标题】:How to make a microsecond-precise timer on the STM32L-Discovery ARM board?如何在STM32L-Discovery ARM板上制作微秒级精度的定时器?
【发布时间】:2015-03-12 13:16:51
【问题描述】:

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

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

【问题讨论】:

    标签: timer stm32 1wire


    【解决方案1】:

    首先我必须告诉你,没有办法使用软件来实现精确的 usec 延迟。即使您使用基于中断的系统,您也会有延迟。当然,您可以使用更大的 CPU 频率获得更高的精度。

    为了连接 1-Wire 设备,您可以使用:

    • DS2482-100这样的外部接口
    • 使用引脚轮询的软件 1-wire 实现。

    对于第二种解决方案,您必须调用基于软件的延迟。您可以进行标志轮询延迟或基于中断的标志轮询延迟。在这两种情况下,您都会确定已经过去了一定的时间,但您无法确定更多的时间已经过去了多少。这是因为 CPU 延迟、CPU 时钟等...

    例如考虑以下实现。我们对 HW TIMER 进行编程以连续计数并检查 TIMER 的值。我们将每个 TIMER 滴答之间的时间命名为“jiffy”,并将 TIMERS 最大值称为“jiffy”:

    低级驱动部分(例如:driver.h)

    // ...
    #define  JF_TIM_VALUE         (TIM7->CNT)
    int JF_setfreq (uint32_t jf_freq, uint32_t jiffies);
    // ...
    

    低级驱动部分(例如:driver.c)

    // ...
    #include <stm32l1xx.h>
    #include <misc.h>
    #include <stm32l1xx_rcc.h>
    #include <stm32l1xx_tim.h>
    /*
     * Time base configuration using the TIM7
     * \param jf_freq  The TIMER's frequency
     * \param jiffies  The TIMER's max count value
     */
    int JF_setfreq (uint32_t jf_freq, uint32_t jiffies) {
       uint32_t psc=0;
    
       RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
       SystemCoreClockUpdate ();
    
       if (jf_freq)
          psc = (SystemCoreClock / jf_freq) - 1;
    
       if (psc < 0xFFFF)       TIM7->PSC = psc;
       else                    return 1;
    
       if (jiffies < 0xFFFF)   TIM7->ARR = jiffies;
       else                    return 1;
       TIM7->CR1 |= TIM_CR1_CEN;
       return 0;
    }
    // ...
    

    具有一些延迟实现的中间件 jiffy 系统 jiffy.h:

    #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);
    

    jiffy.c:

    #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
       }
    }
    

    嗯,你做到了。不错

    所以现在您可以像这样在您的应用程序中使用它: ma​​in.c:

    #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
       }
    }
    

    【讨论】:

    • 感谢您提供这个惊人而广泛的答案。
    猜你喜欢
    • 1970-01-01
    • 2015-12-05
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    相关资源
    最近更新 更多