This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TM4C1230E6PM:用于中断的 TivaWare 计时器

Guru**** 2386350 points
Other Parts Discussed in Thread: TM4C1230E6PM
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1433385/tm4c1230e6pm-tivaware-timer-for-interrupts

器件型号:TM4C1230E6PM

工具与软件:

您好!

Im 尝试创建一个计时器、以定期触发 TM4C1230E6PM 上的中断。

下面是我提出的最小示例:

// --- Includes
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_memmap.h"
#include "driverlib/timer.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"

// --- Typedefs
typedef void (*pFctHandler)(void);

typedef const struct
{
  uint8_t   TIMER_NR;           
  uint32_t  SYSCTL_TIMER;       
  uint32_t  TIMER_BASE;         
  uint32_t  TIMER_CFG;          
  uint32_t TIMER_A_OR_B;        
  uint32_t TIMER_INTERVAL_US;   
  uint32_t INT_FLAGS;           
  pFctHandler callbackFct;      
} timerHW_t;

// --- Local Function Prototypes
void test_tick(void);

// --- Variables
timerHW_t tmr =
{
        .TIMER_NR = 0,
        .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
        .TIMER_BASE = TIMER2_BASE,
        .TIMER_CFG = TIMER_CFG_PERIODIC,
        .TIMER_A_OR_B = TIMER_BOTH,
        .TIMER_INTERVAL_US = 1000, // not implemented
        .callbackFct = test_tick, 
        .INT_FLAGS = TIMER_TIMA_TIMEOUT,
};


int main(void)
{
  // Set Clock
  SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //80MHz SysClk

  // Enable Interrupts
  IntMasterEnable();

  // Enable the peripheral
  SysCtlPeripheralEnable(tmr.SYSCTL_TIMER);

  // Wait for the module to be ready.
  while(!SysCtlPeripheralReady(tmr.SYSCTL_TIMER))
  {
  }

  // configure Timer 
  TimerConfigure(tmr.TIMER_BASE, tmr.TIMER_CFG);
  TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, SysCtlClockGet());

  // Register the interrupt handler
  TimerIntRegister(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, tmr.callbackFct);

  // Start the timer
  TimerEnable(tmr.TIMER_BASE, tmr.TIMER_A_OR_B);
  TimerIntEnable(tmr.TIMER_BASE, tmr.INT_FLAGS);

  while(1)
  {
    // do nothing
  ;
  }
}

void test_tick(void)
{
  TimerIntClear(tmr.TIMER_BASE, tmr.INT_FLAGS);
  
  // Why is this line needed?
 TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, SysCtlClockGet());
}

与此相关的问题如下:

  • 为什么需要81号线? 当达到负载0时、带有 TIMER_CFG_PERIODICENT 的计时器是否应该不会自行复位? 没有这一行,我基本上卡在 test_tick ()
  • 当第81行处于活动状态时、我注意到第78行之间的时钟计数不一致。 它通常在预期的80 000 000 (80 000 192)左右、但在两者之间偏离显示[0 (???)、70 000 000、68 000、79 000 ]。 计时器不应该非常精确地确定调用之间发生的时钟周期数吗?

提前感谢您的任何帮助!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

     您的配置不一致。 请参阅以下注释。   

     . Timer_NR = 0、
    . SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2
    . TIMER_BASE = TIMER2_BASE
    . Timer_CFG = TIMER_CFG_PERIODICENT、//这表明您是将两个16位计时器串联成一个32位计时器  
    . TIMER_A_OR_B = TIMER_BOTH       //这表明您正在尝试同时使用16位 TIMER_A 和 TIMER_B、而不是将它们组合成一个32位计时器
    . TIMER_INTERVAL_US = 1000、//未实现
    .callbackFct = test_tick、
    . INT_FLAGS = TIMER_TIMA_TIMEOUT、 //建议您仅为 TIMER_A 生成中断、并且仅将 TIMER_A 用作16位计时器。

    如果您只想将 Timer_A 用作16位计时器、可以使用以下配置。

    . Timer_NR = 0、
    . SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2
    . TIMER_BASE = TIMER2_BASE
    . Timer_CFG = TIMER_CFG_A_PERIOD | TIMER_CFG_SPLIT_PAIR
    . Timer_A_OR_B = TIMER_A
    . TIMER_INTERVAL_US = 1000、//未实现
    .callbackFct = test_tick、
    . INT_FLAGS = TIMER_TIMA_TIMEOUT、

    有关详细信息、请参阅驱动程序用户指南。  

    [报价用户 id="622437" url="~/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1433385/tm4c1230e6pm-tivaware-timer-for-interrupts "]
    • 为什么需要81号线? 当达到负载0时、带有 TIMER_CFG_PERIODICENT 的计时器是否应该不会自行复位? 没有这一行,我基本上卡在 test_tick ()
    [报价]

    在配置中、您 尝试使用 TIMER_BOTH 来启用 Timer_A 和 Timer_B 的中断、而不使用配置 TIMER_B 我建议您首先确定应用中使用的定时器(定时器_A、定时器_B、或者同时使用这两个定时器)并相应地启用/配置所需的定时器。  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Charles:

    感谢您的答复。 这种钙化了很多! 我将 TIMER_BOTH 误解为对组合全宽度计时器的引用、在这里它似乎是对"两个半宽计时器"的引用

    如果我要将两个16位定时器串联、配置是否正确?

    . Timer_NR = 0、
    . SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2
    . TIMER_BASE = TIMER2_BASE
    . TIMER_CFG = TIMER_CFG_PERIODICENT
    . Timer_A_OR_B = TIMER_A       
    . TIMER_INTERVAL_US = 1000、//未实现
    .callbackFct = test_tick、
    . INT_FLAGS = TIMER_TIMA_TIMEOUT、  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我尝试了你的建议与这个代码:

    // --- Includes
    #include <stdio.h>
    #include <stdbool.h>
    #include <stdint.h>
    
    //from global
    #include "inc/hw_memmap.h"
    #include "driverlib/timer.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/interrupt.h"
    
    // --- Typedefs
    typedef void (*pFctHandler)(void);
    
    typedef const struct
    {
      uint8_t   TIMER_NR;           
      uint32_t  SYSCTL_TIMER;       
      uint32_t  TIMER_BASE;         
      uint32_t  TIMER_CFG;          
      uint32_t TIMER_A_OR_B;        
      uint32_t TIMER_INTERVAL_US;   
      uint32_t INT_FLAGS;           
      pFctHandler callbackFct;      
    } timerHW_t;
    
    // --- Local Function Prototypes
    void test_tick(void);
    
    // --- Variables
    timerHW_t tmr =
    {
            .TIMER_NR = 0,
            .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
            .TIMER_BASE = TIMER2_BASE,
            .TIMER_CFG = TIMER_CFG_A_PERIODIC | TIMER_CFG_SPLIT_PAIR,
            .TIMER_A_OR_B = TIMER_A,
            .TIMER_INTERVAL_US = 1000,
            .callbackFct = test_tick, 
            .INT_FLAGS = TIMER_TIMA_TIMEOUT,
    };
    
    
    int main(void)
    {
      // Set Clock
      SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //80MHz SysClk
    
      // Enable Interrupts
      IntMasterEnable();
    
      // Enable the peripheral
      SysCtlPeripheralEnable(tmr.SYSCTL_TIMER);
    
      // Wait for the module to be ready.
      while(!SysCtlPeripheralReady(tmr.SYSCTL_TIMER))
      {
      }
    
      // configure Timer 
      TimerConfigure(tmr.TIMER_BASE, tmr.TIMER_CFG);
      TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, 10000);
    
      // Register the interrupt handler
      TimerIntRegister(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, tmr.callbackFct);
    
      // Start the timer
      TimerEnable(tmr.TIMER_BASE, tmr.TIMER_A_OR_B);
      TimerIntEnable(tmr.TIMER_BASE, tmr.INT_FLAGS);
    
      while(1)
      {
        // do nothing
      ;
      }
    }
    
    void test_tick(void)
    {
      TimerIntClear(tmr.TIMER_BASE, tmr.INT_FLAGS);
    }
    

    但是我仍然得到非常不一致的时钟周期计数与第80行上的"计数事件-时钟周期"。 我希望每次都接近10000?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我想我需要在每个 TimerIntClear 之后使用 TimerLoadSet。 如果我这样做:

    // --- Includes
    #include <stdio.h>
    #include <stdbool.h>
    #include <stdint.h>
    
    //from global
    #include "inc/hw_memmap.h"
    #include "driverlib/timer.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/interrupt.h"
    
    
    // --- Typedefs
    typedef void (*pFctHandler)(void);
    
    typedef struct
    {
      uint8_t   TIMER_NR;             //!< A value between 0 and 11 (Timer0 = 0, ..., Timer5 = 5, WTIMER0 = 6, ..., WTIMER5 = 11)
      uint32_t  SYSCTL_TIMER;         //!< system-control of the peripheral
      uint32_t  TIMER_BASE;           //!< TIMER Base
      uint32_t  TIMER_CFG;           //!< TIMER Base
      uint32_t TIMER_A_OR_B;         //!< TIMER_A or TIMER_B or TIMER_BOTH
      uint32_t TIMER_INTERVAL_US;       //!< Timer interval in microseconds
      uint32_t TIMER_INTERVAL_CLOCKCYCLES;        // will be calculated in hal_timer_init
      uint32_t INT_FLAGS;           //!< Interrupt flags
      pFctHandler callbackFct;        //!< Pointer to the interrupt handler
    } timerHW_t;
    
    // --- Local Function Prototypes
    void test_tick(void);
    
    // --- Variables
    timerHW_t tmr =
    {
            .TIMER_NR = 0,
            .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
            .TIMER_BASE = TIMER2_BASE,
            .TIMER_CFG = TIMER_CFG_PERIODIC,
            .TIMER_A_OR_B = TIMER_A,
            .TIMER_INTERVAL_US = 1000,
            .callbackFct = test_tick, 
            .INT_FLAGS = TIMER_TIMA_TIMEOUT,
    };
    
    timerHW_t* ptimerHW = &tmr;
    
    
    int main(void)
    {
      // Set Clock
      SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //80MHz SysClk
    
      // Enable Interrupts
      IntMasterEnable();
    
      // Enable the peripheral
      SysCtlPeripheralEnable(ptimerHW->SYSCTL_TIMER);
    
      // Wait for the Timer0 module to be ready.
      while(!SysCtlPeripheralReady(ptimerHW->SYSCTL_TIMER))
      {
      }
    
      // configure Timer timing
      TimerConfigure(ptimerHW->TIMER_BASE, ptimerHW->TIMER_CFG);
      ptimerHW->TIMER_INTERVAL_CLOCKCYCLES = (ptimerHW->TIMER_INTERVAL_US * (SysCtlClockGet() / (1000*1000))); 
      TimerLoadSet(ptimerHW->TIMER_BASE, ptimerHW->TIMER_A_OR_B, ptimerHW->TIMER_INTERVAL_CLOCKCYCLES); 
    
      // Register the interrupt handler
      TimerIntRegister(ptimerHW->TIMER_BASE, ptimerHW->TIMER_A_OR_B, ptimerHW->callbackFct);
    
      // Enable the Timer
      TimerEnable(tmr.TIMER_BASE, tmr.TIMER_A_OR_B);
      TimerIntEnable(tmr.TIMER_BASE, tmr.INT_FLAGS);
    
      while(1)
      {
        // do nothing
      ;
      }
    }
    
    void test_tick(void)
    {
      TimerIntClear(tmr.TIMER_BASE, tmr.INT_FLAGS);
      TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, tmr.TIMER_INTERVAL_CLOCKCYCLES);
    }
    

    当我在第85行(TimerIntClear)设置断点时、确实可以获得一致的时钟计数和预期的时钟计数。

    但是、如果我在第86行(TimerLoadSet、额外设置或作为唯一的断点)上设置了断点、我似乎卡在 TEST_TICK 中、因为程序在~60个周期后再次停止。 这里发生了什么?

    这种行为也是可逆的、当删除第86行的断点后、它每~80000个时钟周期恢复调用 TEST_TICK。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    [报价 userid="622437" url="~/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1433385/tm4c1230e6pm-tivaware-timer-for-interrupts/5497241 #5497241"]

    如果我要将两个16位定时器串联、配置是否正确?

    . Timer_NR = 0、
    . SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2
    . TIMER_BASE = TIMER2_BASE
    . TIMER_CFG = TIMER_CFG_PERIODICENT
    . Timer_A_OR_B = TIMER_A       
    . TIMER_INTERVAL_US = 1000、//未实现
    .callbackFct = test_tick、
    . INT_FLAGS = TIMER_TIMA_TIMEOUT、  

    [报价]

    您好、Dominic:

     有。 如果您希望将两个计时器组合成一个32位计时器、这将是配置。  

     我来尝试粘贴您的代码以了解会发生什么。  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    [报价 userid="622437" url="~/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1433385/tm4c1230e6pm-tivaware-timer-for-interrupts/5497923 #5497923"]

    当我在第85行(TimerIntClear)设置断点时、确实可以获得一致的时钟计数和预期的时钟计数。

    但是、如果我在第86行(TimerLoadSet、额外设置或作为唯一的断点)上设置了断点、我似乎卡在 TEST_TICK 中、因为程序在~60个周期后再次停止。 这里发生了什么?

    [报价]

    您能解释一下您是如何测量计数的吗? 我在你的代码中没有看到你调用 TimerValueGet 来获取计数。 您只是查看寄存器浏览器来查看值是什么吗?  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Charles:

    感谢您的答复。

    Im 不是直接在代码中测量此处所述的时钟周期。 我希望此值直接链接到 TimerLoadSet 值、是不是错?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Dominic:

     我从不在 CCS 中使用周期数方法、并且对此没有任何经验。 我认为它不会得出准确的结果。 由于您使用调试器来读取从一个断点到下一个断点的周期、它将受到外部影响。 如果您打开了存储器窗口或寄存器窗口、调试器在其中需要读取存储器以刷新存储器窗口、会出现什么情况? 调试器在幕后执行的其他操作可能会影响其精度。  

     我一直建议客户采用两种方法。 首先、在 ISR 中、切换 GPIO 引脚。 根据超时设置、您可以在示波器上测量周期是否准确。 在我看来、这是一种最准确的硬件测量、不会受到任何工具不准确的影响。  最后、您需要关注的是硬件计时器精度。 第二种方法是使用  TimerValueGet ()简单地读取当前的计数值、并从新的计数值中减去旧的计数值。 如果您坚持使用 CCS 来测量周期、我会将您的问题转发给 CCS 专家。 但在此之前,你为什么不尝试我提到的两种方法。  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Charles:

    非常感谢您的意见。 我曾尝试使用 GPIO 和示波器来测量计时、并且工作正常!

    因此、正如您所说的、计数周期不能用于验证时序。

    为了便于将来参考、我在此处发布了用于切换 GPIO 引脚 D6的代码:

    // --- Includes
    #include <stdio.h>
    #include <stdbool.h>
    #include <stdint.h>
    
    //from global
    #include "inc/hw_memmap.h"
    #include "driverlib/timer.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/gpio.h"
    
    
    
    // --- Typedefs
    typedef void (*pFctHandler)(void);
    
    typedef struct
    {
      uint8_t   TIMER_NR;             //!< A value between 0 and 11 (Timer0 = 0, ..., Timer5 = 5, WTIMER0 = 6, ..., WTIMER5 = 11)
      uint32_t  SYSCTL_TIMER;         //!< system-control of the peripheral
      uint32_t  TIMER_BASE;           //!< TIMER Base
      uint32_t  TIMER_CFG;           //!< TIMER Base
      uint32_t TIMER_A_OR_B;         //!< TIMER_A or TIMER_B or TIMER_BOTH
      uint32_t TIMER_INTERVAL_US;       //!< Timer interval in microseconds
      uint32_t TIMER_INTERVAL_CLOCKCYCLES;        // will be calculated in hal_timer_init
      uint32_t INT_FLAGS;           //!< Interrupt flags
      pFctHandler callbackFct;        //!< Pointer to the interrupt handler
    } timerHW_t;
    
    // --- Local Function Prototypes
    void test_tick(void);
    
    // --- Variables
    timerHW_t tmr =
    {
            .TIMER_NR = 0,
            .SYSCTL_TIMER = SYSCTL_PERIPH_TIMER2,
            .TIMER_BASE = TIMER2_BASE,
            .TIMER_CFG = TIMER_CFG_PERIODIC,
            .TIMER_A_OR_B = TIMER_A,
            .TIMER_INTERVAL_US = 50,
            .callbackFct = test_tick, 
            .INT_FLAGS = TIMER_TIMA_TIMEOUT,
    };
    
    timerHW_t* ptimerHW = &tmr;
    uint8_t gpio_state = 0;
    
    
    int main(void)
    {
      // Set Clock
      SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //80MHz SysClk
    
      // Enable Interrupts
      IntMasterEnable();
    
      // Enable the peripheral
      SysCtlPeripheralEnable(ptimerHW->SYSCTL_TIMER);
    
      // Wait for the Timer0 module to be ready.
      while(!SysCtlPeripheralReady(ptimerHW->SYSCTL_TIMER))
      {
      }
    
      // Setup GPIO (D6 for LED)
      SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
      while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOD))
      {
      }
      GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_6);
    
      // configure Timer timing
      TimerConfigure(ptimerHW->TIMER_BASE, ptimerHW->TIMER_CFG);
      ptimerHW->TIMER_INTERVAL_CLOCKCYCLES = (ptimerHW->TIMER_INTERVAL_US * (SysCtlClockGet() / (1000*1000))); 
      TimerLoadSet(ptimerHW->TIMER_BASE, ptimerHW->TIMER_A_OR_B, ptimerHW->TIMER_INTERVAL_CLOCKCYCLES); 
    
      // Register the interrupt handler
      TimerIntRegister(ptimerHW->TIMER_BASE, ptimerHW->TIMER_A_OR_B, ptimerHW->callbackFct);
    
      // Enable the Timer
      TimerEnable(tmr.TIMER_BASE, tmr.TIMER_A_OR_B);
      TimerIntEnable(tmr.TIMER_BASE, tmr.INT_FLAGS);
    
      while(1)
      {
        // do nothing
      ;
      }
    }
    
    void test_tick(void)
    {
      TimerIntClear(tmr.TIMER_BASE, tmr.INT_FLAGS);
      TimerLoadSet(tmr.TIMER_BASE, tmr.TIMER_A_OR_B, tmr.TIMER_INTERVAL_CLOCKCYCLES); 
    
      // toggle GPIO Pin
      if (gpio_state==0xFF)
      {
        gpio_state = 0;
      }
      else
      {
        gpio_state = 0xFF; // off
      }
      GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_6, gpio_state);
    }
    

    感谢您的支持!