Arduino如何输出特定的(即56 kHz)载波频率?


9

我正在研究一个自由空间光学项目,以在两点之间无线发送数据。为了实现这一点,我使用了连接到Arduino Uno的IR LED,该信号以发射器的56 kHz载波频率脉动,而第二个Arduino以接收器的56 kHz红外探测器模块脉动。

我尝试在引脚高电平和引脚低电平命令之间使用delayMicroseconds()来创建载波频率。这种工作方式有效,但是频率并不总是相同,脉冲信号的任何其他延迟(即调用函数和递减所需的时间)都可以改变它。

阅读ATmega328的数据表,似乎可以使用芯片的定时器来设置更精确的脉冲。这是否可能,如果可以,如何使用计时器创建一个56 kHz的脉冲?


您的56KHz频率需要多少精度?即您的项目可接受的频率范围是多少?我问,因为仅使用Arduino定时器,精度就有局限性。
jfpoilpret 2014年

55.5khz至56.5khz将是理想的,以便在检测器中保持较高的响应度。
jlbnjmn 2014年

Answers:


10

实际上,可以使用Arduino 定时器生成56 kHz信号。

实际上,计时器可以看作是MCU中的一个特殊寄存器,其中包含一个值(从0开始),该值以MCU时钟频率(在Arduino Uno上为16 MHz)的频率递增。预分频器。当该值达到您指定的称为Compare Match的限制时,就会发生两件事:

  • 定时器寄存器值重置为0。
  • 调用了一个ISR(中断服务例程)回调函数(您可以将其定义为指向您自己的代码)。

这个想法是使用该ISR来更改逻辑引脚的输出(每次被调用时(HIGH,然后LOW,然后是HIGH...))。

现在,为了生成一个56 kHz的方波,您需要56000 * 2每秒调用一次ISR (* 2因为您需要每个周期两次更改输出值)。

您可以在以下列表中选择计时器所需的预分频器值:

  • 1(时钟频率未分频,因此为16 MHz)
  • 8(时钟频率除以8,因此为2 MHz)
  • 64
  • 256
  • 1024

Arduino Uno有两种尺寸的计时器/计数器(实际上称为计时器/计数器):8位和16位。

在Arduino Uno(ATmega328P)上,您总共有三个计时器,但是Arduino核心库或草图中使用的其他库可能会使用其中的一些计时器(您必须自己检查一下):

  • 定时器0(8位)
  • 定时器1(16位)
  • timer2(8位):该定时器具有更多的预分频选项(1、8、32、64、128、256和1024)

现在您需要从16 MHz产生一个56 kHz的波,因此,如果不进行预缩放,则需要计算:

16000000 / (56000 * 2) - 1 = 141.857- 1因为计时器从0计数到该值,并且仅达到该值后才复位)

通过此计算,我们可以得出两个观察结果:

  1. 141.857 不是整数,因此您将无法生成正好为56 kHz的波形。
  2. 如果不进行预分频,则需要一个16位计时器,因为285不能表示为8位无符号整数。

从现在开始,您有两个选择:

  1. 使用16位定时器(timer1),使用预分频器= 1,并选择142作比较匹配;这将为您提供以下频率:16000000 / (2 * (142 + 1)) = 55944 Hz
  2. 使用8位定时器(timer0),使用预分频器= 8,并选择17作比较匹配;以下频率会降低精度:16000000 / (8 * 2 * (17 + 1)) = 55555 Hz仍在所需范围内。

现在,关于如何为此编写草图,我建议您检查一下此说明该说明非常完整且非常有趣。

当然,如果您想了解一点点细节,ATmega328P完整的数据表也很重要。

一些重要的注意事项:

  • ISR在禁用中断的情况下执行,因此必须尽可能短。特别是,Arduino库中有一些函数不能从ISR调用。
  • Arduino Uno时钟不是很准确(它使用陶瓷谐振器代替石英,这本来会更准确),所以这意味着输出频率将进一步偏移。

2
同样,当达到指定的限制时,硬件可以切换引脚。因此,根本不需要使用ISR。ISR 总是会产生抖动,因为指令一旦开始就无法中断。但是,硬件将始终以所需的速率切换引脚。
尼克·加蒙

Arduino Uno使用陶瓷谐振器有点令人惊讶,但其来源是Arduino UNO常见问题解答(在“ Uno是否使用谐振器或晶体作为处理器时钟?”附近)。
彼得·莫滕森

3

我发现tone()在任何引脚上产生高频脉冲都很有用。它应该能够处理56 KHz。(编辑:正如jfpoilpret所指出的,您实际上在16 MHz Arduino上可以获得的最接近频率约为55.944 KHz)

困难显然是将其与您的数据信号相结合。我认为您不依靠低级代码就不能在软件中做到这一点。由于它是数字的,因此在硬件上应该很容易。

您需要做的就是将数据信号输出到另一个引脚上,然后使用与门将其与载波组合。合并后的信号可以直接传到您的红外发射器。

如果没有“与”门,那么使用一对晶体管进行制造就非常简单。只需在线搜索“晶体管和栅极”。


接收器通常通常具有低电平有效输出。如果将LED的顶部连接至56khz,底部连接至数据引脚,则当数据引脚变为低电平时,您将获得IR输出,这将使接收器变为低电平。无需栅极,只需一个LED和一个电阻。唯一的问题是电流io引脚可以驱动的范围。
EternityForest 2014年

2

jfpoilpret的公认答案写得很好,非常有效,在99%的情况下,我都会按照他的解释做。他的解决方案完全在您定义的参数之内,因此它们应该运作良好。但是有什么比“ 很好 ” 更好的呢?完美!毕竟,问题在于要产生一个确切的值。正如所说的,在大多数情况下(可以说是所有情况),足够接近是好的,即使当以1秒钟需要1秒钟作为时钟处理某些东西时,您仍然必须遭受继承零件的缺陷。

我会建议的并非总是可能的。在某些情况下,这是可能的,但是比这种情况要麻烦和多得多。是否值得视情况而定。我的目标主要是为将来的参考提供替代方法,在某些情况下更好。本文是针对没有电子学丰富经验的Arduino新手编写的。

对于更高级的人来说,这可能看起来太冗长和愚蠢了。但我相信,这些人可能已经知道并且不需要此答案。这也适用于每个微控制器,每个制造商和体系结构。但是对于其他微控制器,您将需要查阅正确的数据手册以找到正确的寄存器以及预分频器的名称和值。

在您的情况下,您需要一个特定的频率,其好处是,实际上可以非常轻松地实现精确的56 kHz(不计算零件的实际缺陷)。因此,这也是一个完美的例子。

生成信号取决于微控制器的计时器和时钟源,如jfpoilpret所述。他的回答仅涉及一种观点问题,并且摆弄了定时器。但是,您也可以摆弄时钟源,甚至可以同时摆弄时钟,以获得协同作用和出色的结果。通过更改环境参数(在这种情况下,是对系统进行黑客攻击并更换时钟源),我们可以更加轻松,简单地处理特定问题。

首先要提醒的是,由于切换引脚状态,您需要执行比信号频率高两倍的ISR。这是每秒112,000次。正如已经指出的那样,56,000和16,000,000的总和并不十分理想。我们需要更改信号频率或间歇频率。现在让我们处理一个不变的信号频率,并找到更好的时钟速度。

选择一个比56 kHz(或112 kHz,但实际上是相同的)大一个数量级的时钟将是最简单的,因为您只需加上零,这种数学运算对于大多数人来说是最简单的。不幸的是,这个世界上的一切都是某种妥协。并非每个值都行得通。

第一个例子是发电机转速太低。

如果您选择56,000 Hz时钟,您将无能为力,因为您需要在每个周期调用ISR,并且无能为力。这是完全没有用的。如果选择10倍快的速度(560 kHz),则将有9个微控制器周期(计时器达到其最大值需要10个周期-调用ISR函数需要1个周期)来完成工作,而这完全是不够的。您只是经常需要更多的计算能力。

另一方面,如果您选择一个太大的值,则在56 MHz时,微控制器根本无法使用它。太快了。因此,仅选择商店中的最大价值也不会减少价值。

原始的Arduino Uno R3的备用时钟为16 MHz,因此可以保证任何较慢的工作。下一个大于56且小于16 MHz的数量级的值是5.6 MHz。这样可以每隔50个周期调用一次ISR,并创建一个理想的112,000 Hz定时器频率。您的信号将恰好是56 kHz。在ISR调用之间,您将有49个MCU周期来执行您的程序,但仍约为原始时钟速度的1/3。可以使用112作为基础,并使用11.2 MHz时钟,这将产生大约16MHz谐振器的2/3。每隔100个周期就会调用一次ISR函数,并且仍会生成一个完美的56 kHz信号。

但是,这些值存在两个主要问题。

  • 第一个问题严重取决于您的需求:为了获得使用易于查找的寄存器值(OCR iirc)的准确信号频率,您牺牲了大约1/3(使用11.2 MHz)的最大计算能力。您可能会满意,也可能不会。

  • 第二个问题是难以解决的问题:很难找到值,但是很多时候它们根本不作为制造的时钟源存在。这是Farnell的谐振器网页,只是缺少5.6 MHz和11.2 MHz。

为了避免这种情况,我们可以查看可用的谐振器值,并找出可以用于生成精确所需值的其他方法。如果将56除以4,我们得到14,幸运的是有一个14 MHz谐振器。这为我们提供了更高的速度和更多的功能,并且同样容易找到寄存器值。要每秒调用ISR 112,000次,我们需要在OCR寄存器中放入十进制124或十六进制0x7C的值,因此,通过计数124个周期+ 1来调用ISR,我们得到了理想的理想值。

NB

  1. ISR-中断服务程序(这是仅在生成的中断上执行的代码)
  2. 您的程序有多大取决于内存大小!它与时钟速度无关,与您调用ISR的频率无关。
  3. 当微控制器以程序命令启动时,计数器将递增。如果产生中断,则调用ISR,并将该值存储在特殊寄存器中。当ISR代码完成时,程序计数器的值将从该特殊寄存器中恢复,并且程序从中断的地方继续进行,就好像从未发生过一样。

    我将举一个非常愚蠢的例子。如果您是一个纯粹主义者,我警告您:鼻子和眼睛可能会流血。

    想象一下,您必须从某个地方走到另一个地方。分步路线说明是您的主程序及其命令。您走路或跑步的速度取决于您的“时钟速度”,而不取决于路线说明(前进30步,向左1旋转90度,前进10步,向右45度等),它们始终是相同的。现在想象一下,一个小孩或一个贪婪腐败的地方政客时不时解开鞋子。这是产生中断的事件。然后您在最后一步后停下来,跪下并再次系好鞋子。这是您的ISR程序。

    然后,您从停止的地方继续;您不是从头开始。当您在世间无时无刻不在走路时,即使您必须每隔一步都系鞋带,也不在乎。但是,如果您这样做有时间限制,例如在奥运会上奔跑100米(或从饥饿的食肉天敌中奔跑),停下来系鞋带可能会带来可怕的后果。微控制器也是如此。即使只执行一行代码,程序也会继续执行,尽管速度很慢。如果您根本不关心速度,那将不是问题。如果您必须做一些与时间有关的事情,例如使用其他与计时器有关的动作,那么干扰可能是非常不希望的并且会带来问题。

  4. 少即是多!更快的时钟并不总是更好。较慢的时钟设备消耗的功率要少得多。这可能是电池供电设备中的关键点。

  5. 从以下公式得出所需的周期:(
    时钟速度/(预分频器值*所需的ISR调用频率))-1


TLDR:解焊陶瓷16 MHz振荡器,并用另一个振荡器进行替换,该振荡器允许以整数分频(例如14 MHz并除以250)精确地达到 56 kHz。
Peter Mortensen

0

您只需在输出和输入之间切换载波引脚模式即可打开和关闭载波。我用它来通过37KHz红外(远程控制)端口控制热泵。


0

无需使用ISR创建载体。只需设置一个定时器即可在所需的载波频率上产生50%的PWM输出。然后,ISR负责调制载波(通常以0.5或1ms的间隔),这要舒适得多。以我的经验,大多数红外接收器可以容忍5%的载波频率误差。我使用的是Freetronics EtherMega 2560(具有大量的计时器),但我相信其他CPU也能做到。


那么如何精确地实现载波调制?在输入(载波关闭)和输出(载波开启)之间更改计时器输出捕获引脚的模式?
彼得·莫滕森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.