【问题标题】:Repeat an instruction a certain number of times without a "loop"?在没有“循环”的情况下重复指令一定次数?
【发布时间】:2017-07-20 08:28:18
【问题描述】:

我以前用过 PIC,现在我用的是 STM32F415。 在我的代码的时间关键部分,我需要设置一个非常精确的延迟来调整 DAC-DMA 的周期,它们一起工作以创建周期性模拟信号。

我想添加的延迟从 0 到 63 个时钟周期(如果我能够做到 10-63 个时钟周期也可以)。在 PIC24F 汇编中,有一条指令“REPEAT”允许我重复下一条指令一定次数。这对我很有用,因为我可以这样做:

REPEAT #0xNUMBER
NOP

我试图找到与 STM32F4 类似的东西,但我在指令集、参考手册和一般互联网上搜索都没有运气。

我已经尝试在 C 中使用 for/while 循环和专用的计时器,但是所需的额外指令消耗了太多时间(40-50 个循环,具体取决于我的编程方式)。

如果有人有想法或知道怎么做,这对我来说非常有用。

非常感谢。

英语不是我的母语,所以对于任何可能的错误,我深表歉意。让我知道,我会努力改进它:)

编辑 1(2017 年 7 月 23 日)

感谢大家的回答,我一直很忙,无法单独回答你们每一个人。 我会尝试门控时钟的@berendi 解决方案,它似乎最适合我的应用程序。 学习了很多STM32不知道的东西,谢谢大家!

【问题讨论】:

  • 您能提供更多关于您的系统如何工作的信息吗?这是一个周期性循环,您想添加一些受控抖动吗?
  • 此代码进入 de DMA ISR。系统在每次半传输和每次传输完成时输入 ISR。因此,每次半传输我想添加一个非常小的延迟来将信号的周期调整到我想要的确切值。我停止使 de DMA-DAC 工作的定时器,进行延迟,然后再次启用它。代码有效,我可以延迟,但 C 代码指令所需的周期太多,所以我想要一个更有效的解决方案,比如我提到的 REPEAT-NOP。
  • 我认为使用 DMA-DAC 和 TIMERS 组合来产生您想要的信号是一个更好的主意。手动延迟可能效率低下。
  • @ctasdemir 问题是在计算周期时,我从除法中得到错误。所以我计算了我在整个周期中“丢失”了多少个周期(因为周期分为 64 个值来构造信号),然后尝试手动“添加”它们。信号的后半部分为 0,因此再增加几个周期不会失真。
  • 好的,除法运算需要可变数量的周期,您是否试图弥补这一点?是否可以在除法之前启动一个计时器,然后在除法之后对计时器进行忙碌等待?通过这种方式,硬件可以为您管理延迟。一个更好的步骤是,如果您的下一个阶段可以由定时器中断驱动,即延迟后的动作实际上是在定时器中断中?

标签: arm embedded stm32 stm32f4


【解决方案1】:

我停止使 de DMA-DAC 工作的计时器,做延迟,然后 再次启用它。

所以,如果我理解正确的话,您有定时器 A 控制您的 DAC,在每次计数器溢出时触发转换,并且您希望将其延迟可变数量的时钟周期。

STM32F4 的大多数(如果不是全部)定时器都支持门控模式从机操作,您可以在其中选择另一个定时器(定时器 B)作为主定时器,定时器 A 只在触发时计数定时器 B 的输出为低电平。换句话说,定时器 A 将在定时器 B 的上升沿停止计数,并在下降沿恢复计数。现在,将定时器 B 配置为在启用时输出一个单脉冲,其中脉冲宽度是您想要的延迟,然后定时器 A 将延迟脉冲的确切持续时间。

请参阅参考手册中有关单脉冲模式、定时器和外部触发同步的章节以及对 CR1、CR2 和 SMCR 寄存器的说明。

【讨论】:

  • 感谢您的帮助,这是我现在尝试应用的解决方案。在参考手册中有关于使用一个定时器启用另一个定时器单脉冲模式的解释。我实际上是在尝试使用另一个计时器禁用一个计时器,我想我知道如何设置单个脉冲。我现在的问题是,一旦主人完成了它的脉冲,它会保持在“1”还是会回到“0”?因为我希望它始终为“1”,除了“0”中的脉搏,我告诉他在某个时间点做。
  • 我想补充一点,我不确定最好的方法是使用 Trigger Output 还是 Output compare 模式,看起来两者都可以配置为驱动从站,但我没有找到将它们放在一起做我想让他们做的事情的方法。
【解决方案2】:

NOP 不是一个很好的延迟解决方案。请改用屏障指令,因为执行时间与 ARM 文档中的说明完全相同(3、4 或 5 个周期,具体取决于指令和内核版本)。您可以设置 n 个连续的障碍来存档您需要的延迟

【讨论】:

    【解决方案3】:

    您可以在 PIC 上执行此操作,这是一个非常常见的解决方案,执行时间是确定性的。像这样的外部架构,以及也是确定性的旧芯片(在克隆出来之前)也可以。但总的来说,这不是你如何做延迟,它不是确定性的,你可以得到一个“至少这么长”的调谐循环,但你不能得到“正好这么长”,甚至调谐,或者永远不应该期望。这就是为什么在 MCU 设计中通常有多个计时器,这就是您用来测量时间的原因。对于您要解决的问题,这里的解决方案是一个计时器或级联计时器,如果您真的需要的话。

    Arm 没有类似 x86 的重复指令,您的最小循环将是两条指令,我已经无数次证明,在同一个芯片上,这个循环的速度会有所不同,所以调整它,添加一行代码,然后此循环的延迟属性更改

    here:
      sub r0,#1
      bne here
    

    对于经典(gas)语法,对于统一语法使用 subs 而不是 sub。

    你也在一个 stm32 上,他们在指令端有一个隐藏的缓存,你不能关闭也不能控制,它通常不会给你等待状态的性能,当然对于这样的事情,但显然他们没有缓存大小闪存,因此预取循环必须在某个地方发生,并且您必须期望有时当您跳入此循环时必须感觉到预取。

    【讨论】:

    • 如果您可以容忍这里和那里的几个时钟倾斜,您可以使用循环或调整的 nop 数量用于具有特定时钟和等待状态设置的特定芯片,但如果您想要一个确切的数字时钟 1) 不一定可能 2) 使用计时器。
    • 可以通过将代码的关键部分放在内存中来避免闪存问题。在许多 Cortex 设备上,您甚至拥有核心耦合 ram,以避免与 DMA 冲突,有些设备将指令和数据分开,您可以在最高效的哈佛架构中工作。
    • 在 stm32 上更容易展示 ram 中的性能差异,cortex-m 上的主要问题是获取长度是 32 还是 16,并且是在内核编译时,所以那个简单的循环你做了多少额外的提取。将对齐更改一个字,您可以将每个循环的循环时间增加一到两个时钟。但是是的,一般来说非 stm32,flash 的速度通常是 cpu 的一半或更差,ram 是 1x(你仍然可以对齐,但它会自动更快)
    • 具有较大的臂,例如提取长度为 8 个字,如果启用分支预测器,则工作方式不同,存在最佳提取的最佳点,并且不必在每个循环中获取额外的提取行,并且分支预测器有效与无效的最佳点。我记得的 cortex-m 上的分支预测器不是管道类型,而是缓存的,我记得上次这个地址是一个分支,所以我假设它仍然是一个分支。
    • 抛出中断或其他类似的事情会影响下一个调整循环的执行,但之后的第二个可能会回到调整时间
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-18
    • 1970-01-01
    • 1970-01-01
    • 2014-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多