主题中讨论的其他器件: TM4C123、 TMP20
大家下午好、
我想使用计时器在 tm4c123gh6pge 微控制器中创建延迟函数10分钟。 我请求您建议我如何使用以下时钟频率编写简单的延迟函数。
SysCtlClockSet (SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN| SYSCTL_XTAL_16MHz);
谢谢、
此致、
Srinu.V
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.
大家下午好、
我想使用计时器在 tm4c123gh6pge 微控制器中创建延迟函数10分钟。 我请求您建议我如何使用以下时钟频率编写简单的延迟函数。
SysCtlClockSet (SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN| SYSCTL_XTAL_16MHz);
谢谢、
此致、
Srinu.V
您好 Srinu、
我将尝试制作一份通用指南:下面简要总结一下如何为您的目标组织项目:
1) 1) TM4C MCU 上的计时器计算内部"咔嗒声"或时钟周期。 通常、这些咔嗒声来自主处理器的时钟。 因此、基本而言、计数时间(等待10分钟)是等待特定数量的周期发生的问题。 使用定时器有一种"特殊的方法"、其中的咔嗒声来自慢速晶振(32768Hz)、但除非您为此专门配置定时器、否则不会出现这种情况。
2) 2)您需要知道在一秒内 CPU 中发生了多少个时钟周期。 您提到的函数 SysCtlClockSet()用于配置它。 对于 TM4C123系列、有一个读取此值的函数、即 SysCtlClockGet ()。
3) 3)在程序期间、您可能需要几次系统时钟值。 因此、最好读取它并将其存储在全局变量中。
uint32_t mySystemClock;
mySystemClock = SysCtlClockGet ();
4) 4)那么、例如、现在假设您的时钟配置为50MHz、变量的值为50、000 000。 这是您的计时器将在一秒内计数的时钟周期数。
5) 5)您希望计数到10分钟... 即10 x 60秒或600秒。 足够简单。 它指的是多少个时钟周期?
600 x mySystemClock = 30 000 000 000。
如果您可以使用该数量的周期配置定时器负载、则任务完成。
6)问题:ARM M4的"正常"计时器为32位。 这意味着它可以计数的最大数量是2^32 - 1、或4 294 967 295。 如您所述、计算10分钟的 CPU 时钟需要一个大于该值的数字。 以下是两个选项:
7) OPTION1:累积辅助计数器。 例如、您可以使用计时器每秒触发一次、并在外部变量中计算秒数。 这意味着将您的计时器配置为在50、000个周期结束后触发(并自动从零开始)、并且在计时器中断例程中、添加一秒:
空 interruptRoutineWhenTimer1触发器(空)
{
globalElapsedSeconds +;
if (globalElapsedSeconds >= 600)
{
globalElapsedSeconds = 0;
doSomeing();
}
}
不过、请注意、即使是5、000万也是一个很大的数字。 某些计时器(实际上是"半计时器")只能计数到2^16 - 1、因此如果这是您的策略、请确保使用320位计时器。
8)选项2:特别是对于 TM4C123系列、有宽定时器、它可以计数到2^64 -1、这是一个很大的数字。 如果使用宽范围计时器、您只需将计时器加载到该大数字、即10分钟。
9) 9)无论采用哪种方式、您都需要配置一次计时器。 以下是32位计时器的配置示例:
TimerConfigure (Timer1_base、timer_CFG_PERIODICRACRACASE);
TimerLoadSet (Timer1_base、timer_A、50000000);
TimerIntEnable (Timer1_base、timer_TINA_TIMEOUT);
TimerEnable (Timer1_base、timer_A);
(此示例将反复调用 ISR、每秒一次)
10) 10)更多指针:
-在尝试配置 Timer1外设之前,必须启用该外设。
-必须启用系统中断。
-您需要编辑项目的启动文件,以便将 interruptRoutineWhenTimer1Triggers()设置为 Timer1 ISR。
-在中断例程(ISR)内,您需要清除中断标志(对于大多数外设中断类型来说是真的)。
-从 ISR 内部调用 doSomething()并不是一个好的做法。 最好的方法是设置一个标志、例如 thingCanHappen = true;在主循环中、持续检查是否会发生这种情况(运行后不要忘记将 thingCanHappen 设置为 false)。
时间调度有一种特别不同的方式:一个被称为 SysTick 的更简单 ARM 定时器被配置为每1毫秒触发一次、而一个全局变量累积已经历的 ms。 将来、只要您需要在给定的时间发生某种情况、就计算将是哪个 msCounter 值、并定期检查是否已达到此"时间限制"、然后调用您的事件。
希望本基本指南对您自己和他人有用。
布鲁诺
Srinu
我看到您没有为计时器创建中断服务例程。 查看 TIVAARE 示例文件夹、您必须了解中断的工作原理、并且需要能够在代码中包含工作 ISR。 您知道如何从 Tivaware 导入示例吗? 研究中断示例(以及计时器示例、当您在其中时)。
我无法检查您的代码以了解详细信息、但为了实现您的目标、您需要首先确定这是连续循环(10分钟打开、10分钟关闭)还是仅一次?
一次性建议:
-在 while (1)例程之前、只执行一次"led = on"。
-每次触发计时器时、使"secondsCounter"全局变量递增1。
-在 while ()中,检查以下内容:
if (secondsCounter >= 600){LedOff ();}
此致
布鲁诺
希望 "加速、放松和(甚至)加强"这种"成功实施"--提出以下意见。
我认为 、一个有用/简化的简化是由"简单" (7计数对600)的认可产生的。 (“ 3个以上”后列出了6000个计数- 600个是指。)
七(7)个简单的32位计数器"翻转"产生10分钟的计时持续时间、 精度为"0.22%"! 这应该证明:"对 G'Ovt 工作有好处-至少对于"概念证明!" (这一直是一个好主意...)
先前-在另一个帖子中-出现了单词用法。 ("部分"-适用于任何关心的人。)
先前的帖子-此主题-已列出、 "7) OPTION1: 累加辅助计数器"
现在、我完全同意 Robert 的评估-我们的海报 Bruno 在这里做得很好。 然而-"累加 -正如这里使用的-更好地描述了"所选定时器的功能"-辅助计数器应该-更正确-被"选择和/或使用"-它的任务-是 "累加(其它)定时器 (理想的32 位定时器)的溢出。"
拉尔夫太棒了! 因此需要说。
海报必须接受(部分)责任-并致力于自己的"专注工作!" 这种"实践"往往是"试验和错误",甚至是许多此类试验,是真正学习的途径。
布鲁诺的详细帖子涵盖了海报所寻求的"确切"内容。 以及您提供的附加/链接代码指南-当然-"指南好、直接!"
海报上的几个"布鲁诺帖子重读"、然后 是"他自己的"编码尝试、证明了" 提高海报理解"的最佳方法(实际上是唯一的方法)、并实现 了真正的学习!
通过(请求的指导手册)[努力-轻装] -"剪切和粘贴!"获取"
这种情况
[引用 USER="Srinu Vakada]while (1)
{
function_1 ();
function_2 ();
延迟(10分钟);-------- 我想在这里延迟10分钟、并在 while 循环中重复执行这两个函数。
}[/报价]
与此相矛盾
[引用 user="Srinu Vakada"]但现在我想使用计时器中断来延迟
对于您所说的内容、中断有什么好处?
顺便说一句、您几乎肯定不希望处理器停止响应10分钟、但这就是您想要的。
Robert
[引用 user="Srinu Vakada">实际我的应用是使用 GSM 模块将数据发送到服务器。 在这里、我使用两个函数将数据发送到服务器、使用另一个函数从服务器读取数据。
10分钟内不需要其他任何东西似乎是很不可能的。
[引用 USER="Srinu Vakada]while (1)
{
data_send();
data_receive();
延迟(10分钟);-------- 我想在这里延迟10分钟、并在 while 循环中重复执行这两个函数。
}
我请求您向我提供建议。[/引述]
布鲁诺在第一次答复中就这样做了。
您可以雇用顾问。
Robert
[报价 USER="CB1_MOBIT"]为什么您认为7个滚动需要(大于) 600次的护理?[/QUERPLET]
因为一旦您创建了等待 X 秒的解决方案、您就可以在需要时立即使用它。
另一方面、当您创建等待10分钟的解决方案时、您只能在等待10分钟时使用该解决方案、更具体地说、就是将时钟设置为50MHz (这甚至不是该 MCU 上可用的最快时钟)。
因此、如果我要对秒进行计数、我宁愿让全局变量对这些秒进行计数、实现一个简单的 ISR、该 ISR 每秒调用一次、使此类变量递增一次。 超时控制本身只是与时间限制值的比较、这对于任何秒数都很有用。 当然、1秒的计时器负载会在初始配置期间验证 CPU 时钟、因此当您以16MHz 运行 TM4C123或以120MHz 运行更快的 TM4C 时、该工具仍然是很好的...
但是、为了使生活变得比上述描述更简单、甚至"效率更低": 在一个典型的项目中会发生很多事情、其中很多事情取决于经过的时间-所以在这里完成的任何项目的基础包括一个1ms 的粒度计数器由 SysTick 递增而不是由计时器递增(SysTick 不需要清除中断标志、 这简化了 ISR 内容)。 为了简化编码、宏 system_counter_MS 指向该全局变量。
OP 的任务将通过以下代码完成:
{
uint32_t timeExpire = 0;
DataSend();
DataReceive();
timeExpire = system_counter_MS + 600000;
while (system_counter_MS < timeExpire){}
}
运行 CPU @50MHz 时、我们每毫秒有50、000个周期。 "浪费"大约30个周期绕道/递增全局计数器、相当于系统中永久毫秒计数的0.06%处理能力! 好还是坏?
此致
布鲁诺
您好 CB1
[引用 user="CB1_MOBIST"]不受"不可避免的时间变化"[/引用]影响
这话什么意思???
SysTick 由系统时钟以及常规计时器驱动。
配置为每50、000个周期生成一次中断的 SysTick 将在... 嗯... 50、000个周期。 不多一个、少一个。 默认情况下、它设置为更高的优先级、因此其 ISR 内部发生的任何事情都更有可能是"实时"。
也许你在想 SysCtlDelay()?
布鲁诺
我们在 ARM 文档中注意到了此类 SysTick 问题-千禧一代员工立即搜索。
我们在过去2个月内通过另一个 ARM M7确认了这种情况、这就是我"回忆"的方式。 我将在 n ü crüe 找到后发布-并展示他们的调查结果...
我是个有脚受伤的家——“受影响”,但足够警惕,不要与“SysCtlDelay()”混淆! (也许,有人希望... 我被警告说,“不要操作重型机械”(像任何有声的人都会相信 Moi -有重型机械),但你对类似名称的“SysCtlDelay()"的建议却受到了启发...) *** 类似 ***
Srinu、
虽然我可以看到您的代码起作用、但以下是一些改进代码的建议:
-不要将 Timer0 ISR 命名为"SysTickInt()"。 该名称建议使用不同的计时器。 您应该将其命名为类似于 Timer0ISR()的名称。
-不要将秒变量的名称命名为“毫秒”。 此类变量会统计秒数、而不是毫秒、因此所选名称不是那么方便。
-不要在 SysCtlPeripheralEnable 之后使用 SysCtlDelay。 外设确实需要一些时间才能准备就绪、但等待它准备就绪的正确方法是使用 while (SysCtlPeripheralReady())。
-在 TimerLoadSet 上,您使用的是50000000的硬编码值。 该数字对于您的特定时钟设置是正确的、但如果您将时钟设置存储在代码开头的变量中(类似于 systemClockHz)、则会更巧妙、 并在您的定时器负载上使用该变量(同样、它不会产生任何功能差异-但是、如果您决定将 CPU 重新配置为在80MHz 下工作、代码仍能完美运行、而无需再担心)。
在等待函数内,您可以在 while 之前进行一次计算。 例如"expireTime = millis + temp"、然后是"while (millis < expireTime)"。 此外、这在这里没有什么不同、因为您的 MCU 不执行任何其他操作、但这只是一个"良好做法"、需要更少的处理、计算值不需要具有易失性。
此致
布鲁诺
您必须首先回答几个问题。
例如、如果您预计代码将被阻止。 这似乎是可以的、但这么长时间的阻断并不是非常明智的。
然后、您会遇到使用哪个时间的问题。 在我的应用中、我通常会让 SysTick 运行以提供自由时间戳。 可在此处使用。
您还有16、32或64位计时器以及预分频器。 使用32位或更多位计时器可以轻松完成任务。 对于16位计时器、它需要一点编程。 但没有什么特别的。
然后是 dwt -一个32位自由运行计数器。 很少被人们使用、但非常有用。
然后、您会遇到一个有关您需要多少抖动的问题。 您可以对周期精确延迟进行编程。 或者、您可以在更短的延迟时间内构建代码。 例如、您可以编写一个延迟一秒的片段。 然后、写一个延迟一分钟的片段、然后得到10个延迟...
很高兴为您提供一些代码示例。
我要做的是、通过16位拆分定时器(在本例中为 TIMER2)实现"阻断"延迟例程。
1.将 timer2初始化为16位拆分定时器。 我选择了1个预分频器、顶部为0xFFFF -这对我们的应用没有影响。
//global define #define TMRxTIMER2//我们使用 timer2作为时基 //全局变量 //重置 timer2a - split计时 器 //16/32位计时器:8位预分频器,16位周期寄存 器 void tmr2a_init (uint32_t ps、uint32_t pr){//_ispr->RC1 –/syrtr2</>RCr=/r1</syrtscr&r1<r=/rtrl=/rtrl=/rtrl=/rtscr1&trl=/rtrl=/rtscr1&trl=/r&trl=/rt TIMER0/1/2/3/4/5 //停止计时器 TMRx->CTL &=~(1<0);//0->禁用计时器,1->启用计时器 //配置计时器,拆分,递增计数 器 TMRx->CFG = 0x04;//0x00->32/64位、0x01->RTC、0x02-0x03->保留、0x04->16/32位拆分定时器 TMRx-> Tamr =(0<11)|//0->传统操作 (0<10)|//0->在下一个周期更新匹配寄存器、1 ->立即更新匹配寄存器 (0)|禁用 PWM<9 |中断0 | 0 1>立即更新加载寄存 器(0<<7)|//0->禁用快照,1>启用快照 (0<<6)|//0->立即计数计时器; 1->计数前计时器等待触发 (0<5)|/0->匹配中断被禁用,1->匹配中断被启用 (1<4)|//0->倒计数,1->递增计数 (0<3)|//0->捕获/比较启用1->PWM (0<2|/0->0x02- >捕获、0x02->边沿计数;0x02->保留0x02->捕获时间;0x02>0x02->捕获 //设置预分频 器 TRXM->TAPR =(ps - 1)& 0xff;//设置预分频 器 TRXMX->TAILR=(pr - 1)& 0xFFFF;//设置顶部 //重置计数 器 TRXM-> TAR = 0; //清除 ISR 标志,禁用 所有中断 TRXM</>IMR<>0XM0 ;~//启用中断0XM1-中断 //1->清除中断标志,0->无效 //启动计时器 TMRx->CTL |=(1<0);//0->禁用计时器,1->启用计时 器}
2. delay64()使用64位参数提供精确到周期的延迟。 实际上限制更少。
//用户 tmrx 延迟64位 //由上方的 TMRx 指定的时基 //假设初始化为16位计时器 char delay64 (uint64_t tick){ TMRx ->TAR = 0;//重置计时器计数 器 TMRx->TAILR=(ticks & 0xFFFF);//设置顶部 TRX->ICR = 0<01- (TRX0);中断= 0<TRIS = 0X0<TRIS =(CONTINUE);//继续)//等待计时器溢出 滴答=滴答和~0xFFFF;//屏蔽最低的16位 TMRx->TAILR= 0xFFFF;//顶部在0x10000 ,而(滴答= 0x10000ul){ TMRx->ICR =(1<0);//在 (((TMRx-> RIS 和0x01))时复位标志;//等待计时器返回 1);//继续溢出
3.将64位参数传递到 delay64()将提供所需的延迟。 每个"SystemCoreClock"相当于1秒的延迟。 "SystemCoreClock"被定义为 CMSIS 框架的一部分。
//示例:延迟2.5秒 #define LED_DLY(SystemCoreClock * 2.5)//长延迟周期计数,1 SystemCoreClock = 1秒 //主循环 //配置 tmrx tmr2a_init (1、0xFFFF);//将 tmr2a 初始化为16位拆分计时器 // ei ();//启用中断 while ( time/dlp_LED)/delayePort (1、dlpy);// dlp_yt (1)/delayeLp (64)
经测试可在 LM4F120上工作。
你在这里做了很好的努力-谢谢。 不过、在本主题的(现在已发布了35个深帖子)案例中、海报已 "表明他接受 了"同行海报 Bruno 的方向"、甚至授予了"已解决我的问题" 帖子亮点。
请注意、您(我认为是正确的)似乎对"阻止延迟"提出质疑(在大多数"现实世界"应用中都是不可接受的)。 -然后继续演示(另一个)阻塞代码示例。
您的大部分代码都采用了" 长时间不喜欢这里/其他地方-"DRM"编码方法"、该方法被称为低效和错误诱导。 当然、这家供应商 的广泛 API 从未如此有用、而且 API "久经考验、真实可靠、经过严格测试"。 (虽然您注意到"您的测试"(在 EOL MCU 上)、但"一人测试"( 不能"希望比较") 与供应商(广泛) API 所经历的"千"(可能成千上万)的测试相比。
我们注意到您的专业知识和"愿意提供帮助"、但(确实存在)有多个"未回答的帖子"、这似乎"更值得关注..." (再次-此主题已(标记很长)"已解决!")
此处是有关使用 DWT 计时器的快速跟进:
//使用 DWT 进行定时 //返回标志:1>时间已满,0->尚未耗尽的时间 char delay64DWT (uint64_t ticks64){ static uint64_t ticks64_prev = 0;//以前的 coreticks,64位 静态 uint64_t ticks64_now = 0; /t ticks64_eons/eons/eone-now; //32位时钟 //时间戳当前时钟- 32位 时钟现在= DWT->2a CCNT;//coreticks();//读取 DWT 周期计数器(32位) ticks64_now +=(MRx_NOW > ticks_prev)?(ticks_now - ticks_prev):(0x1ticks_now = 32位时钟周期/ ticks/ ticks-现在用作32位时钟周期前/时钟)//16位 timer2a 作为时基 //ticks64_now +=(ticks_now > ticks_prev)?(ticks_now - ticks_prev):((0x1ull << 16)-(ticks_prev - ticks_now));//对于32位翻转 //备选2:使用 Syticks+(ntick_ticks) –ticksprise_ticks=24 (ticksprick (ticks_now)//对于32位翻转 //时间戳电流节拍- 64位 ticks_prev = ticks_now;//更新 ticks_prev if ((ticks64_now - ticks64_prev)< ticks64)返回0; 否则{ticks64_prev = ticks64_now;return 1;}//时间为 up }
上述代码提供三种替代方法:
使用 DWT 作为计数器。 它是一个32位计数器;
2.使用16位定时器作为计数器;代码已注释掉
3.使用 SysTick 作为转换器;24位宽。
当所需的时间(以节拍为单位)过去时、该函数返回1。 0否则。
IF (delay64DWT (LED_DLY)) IO_FLP (LED_PORT、LED);//翻转 LED
在这种情况下、当 LED_DLY 过期时、它会翻转 LED。
经测试可在 LM4F120上工作。 并且应该能够处理任何具有 DWT 的 cm3/4/4F/7芯片、或者任何 CMx 芯片(如果 SysTick 被用作时基)。
重点是要说明有许多方法可以做同样的事情。 没有理由将自己局限于一种特定的方法。