亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

读取 PWM,解码 RC 接收器输入,并应用故障保护

组件和用品

Arduino UNO
× 1
SG90 微伺服电机
× 2

关于这个项目

该项目包含通用但高效的代码,可用于简单地读取任何 Arduino 输入引脚上的 RC 接收器(或任何其他 PWM 信号),并在发射器信号丢失的情况下应用故障安全。

下面的视频显示了 Arduino uno 使用本页底部提供的代码 PWMread_RCfailsafe.ino 充当伺服混合器。

PWMread_RCfailsafe.ino 中的函数帮助您管理中断寄存器,并且可以在使用不同引脚的不同项目之间轻松移动。

在下面的视频示例中:

  • 当接收器没有来自发射器的信号时,故障安全功能会激活。升降舵通道设置为满载,副翼通道设置为空档
  • 蓝色舵机的方向由油门位置设置
  • 蓝色舵机的移动范围(速率)由发射器侧面的滑块设置
  • 使用发射器上的齿轮开关打开和关闭混合

内容

  • PWM 如何控制舵机
  • Arduino 在 RC 模型/机器人中的使用示例
  • 代码概览:从带有故障安全功能的 RC 接收器解码 PWM
  • 如何使用 PWMread_RCfailsafe.ino
  • 显示接收器帧率和频率
  • 伺服混合示例
  • 所采取方法的理由
  • 限制

PWM 是如何控制舵机和速度控制器的?

在本项目的其余部分中,假设您了解用于控制伺服系统和速度控制器的 PWM 信号。这是一个很好的视频,解释了这些脉冲宽度调制 (PWM) 信号的工作原理。

您还应该具备以下工作知识:

  • Arduino IDE
  • Float、boolean 和 int 变量
  • 如果循环
  • For 循环
  • 数组
  • 伺服库

Arduino 在 RC 模型/机器人中的使用示例

你只会受到你的想象力的限制:

  • 应用伺服混合、开/关灯、控制泵/阀门、设置定制序列...
  • 创建一个控制器(即飞行稳定/自动驾驶仪、航向保持、高度/深度保持、自动水平仪、感知和避免、返航......)
  • 让您的 RC 模型响应信号丢失或电池电压低...
  • 为多个模型/项目使用同一个发射器,而无需更改任何设置或使用模型记忆功能。

代码概述:使用故障安全从 RC 接收器解码 PWM

此代码使用引脚更改中断测量 PWM(脉冲宽度调制)信号。所使用的功能在 Arduino Uno、Nano 或 Pro Mini 上自动设置中断并从任何数字或模拟引脚(不包括 A6 和 A7)提取数据。这使得代码即使对于初学者也易于使用。

该项目的主要目的是创建一个具有故障安全“模块”的通用 RC 接收器,可以在项目之间快速移动。因此,“如何使用”部分中显示的示例代码只能用作达到目的的手段。

注意: 此代码不适用于软件串行或任何其他使用引脚更改中断的库。

对于那些对代码如何工作感兴趣的人:

  • 输入引脚在数组中标识。这个数组可以是任意长度。
  • 设置函数通过为引脚阵列中列出的每个引脚设置适当的寄存器来启用引脚更改中断。
  • 任何选定引脚上的电压变化将触发三个中断服务路由 (ISR) 之一,具体取决于引脚所属的端口寄存器 ISR(PCINT0_vect) -> 端口 B、ISR(PCINT1_vect) -> 端口 C 或ISR(PCINT2_vect) -> 端口 D。
  • 在每个 ISR 中,FOR 循环和 IF 语句用于确定哪个引脚发生了变化,以及它属于哪个 RC 通道。在返回主循环()之前,通过使用 micros() 来记录中断的时间。
  • 引脚变化之间的时间间隔用于计算脉冲宽度和重复周期。
  • 在每个 ISR 中设置标志以指示何时接收到新脉冲
  • 其余功能随后使用这些标志来提取和处理 ISR 收集的数据

以下 YouTube 视频制作者 Joop Brokking 谈到了一个不同的项目,该项目使用相同的方法将 RC 接收器连接到 arduino。在前 8 分钟内,Joop 清楚地解释了如何 使用引脚变化中断来测量来自 RC 接收器的 PWM 信号。

所有这些细节都由 PWMread_RCfailsafe.ino 负责,可在本页底部下载。

还可以在此处找到有关端口操作的一些有用信息: https://tronixstuff.com/2011/10/22/tutorial-arduino-port-manipulation/

除了引脚变化中断处理之外,还编写了一个专用函数 RC_decode() 以将脉冲宽度 (1000-2000uS) 转换为来自发送器的 +-100% 控制信号。故障安全使用 10-330Hz 和 500-2500uS 的信号容差检查有效的发射器信号。如果信号丢失,则 RC_decode() 返回一个预先确定的故障安全值。

可以在 PWMread_RCfailsafe.ino 中为每个通道设置特定变送器的校准值和故障安全位置

如何使用PWMread_RCfailsafe.ino

第 1 步: 示例 硬件 设置 阿杜诺 乌诺

如果按照使用 Arduino Uno 的示例设置,请按如下方式连接您的接收器:(否则,如果您使用自己的项目,请直接跳到第 2 步)

  • 使用 5v 和 GND 引脚为接收器供电
  • 使用母对公跳线将接收器的信号引脚连接到 Arduino 上的引脚 2 到 7。 (如果您有一个 2 通道接收器,则仅连接到引脚 2 和 3)
  • 对于伺服混合器示例,将一根伺服信号线连接到引脚 9,将另一根连接到引脚 10。

第 2 步:将 PWMread_RCfailsafe.ino 复制到草图文件夹中

页面底部包含一个示例草图 RC_Read_Example 供下载。按照以下步骤操作时,您可以将其用作主要草图。

将 PWMread_RCfailsafe.ino 文件复制并粘贴到包含主草图的文件夹中。下次在 IDE 中打开草图时,将出现第二个选项卡,其中包含 PWMread_RCfailsafe.ino 中的代码。

第 3 步:指定 输入 别针

在 Arduino IDE 中打开或重新打开主草图。

单击 PWMread_RCfailsafe 选项卡,向下滚动到“USER DEFINED VARIABLES”标题并在数组 pwmPIN[] 中输入输入引脚。

注意: 可以使用任意数量的引脚,并且可以按任意顺序使用。请注意,您拥有的输入越多,代码用于处理中断例程的时间就越多。注意 A6 和 A7 是仅模拟引脚,不能使用。

目前不支持 Arduino MEGA,但如果有兴趣,这可以很容易地解决。

注意: pwmPIN[] 中的第一个元素是通道 1,第二个元素是通道 2,等等...如果您使用来自接收器的所有通道,最好确保接收器通道 1 对应于通道1 在 pwmPIN[]...

第 4 步: 评论 可用 功能 PWMread_RCfailsafe.ino

第 5 步: 打印 脉冲 宽度 数据 串行

上传 RC_Read_Example 代码,打开发射器并将原始脉冲宽度数据打印到串行。

应使用 RC_avail() 函数检查所有通道何时接收到新数据,然后使用 print_RCpwm() 将脉宽数据发送到串行。

第 6 步: 校准发射器

使用通过 print_RCpwm() 打印到串行的脉冲宽度数据手动修改数组 RC_min[]、RC_mid[] 和 RC_max[] 中的值,以便将每个通道校准到 +-100% 的范围内。

第 7 步:打印校准通道 串行

注释掉print_RCpwm()函数

使用 RC_decode(channel) 函数将每个通道校准到 +-1 的范围内。

然后使用decimal2percentage() 函数和Serial.println("") 将每个校准的通道打印到串行

第 8 步: 设置故障安全

为每个通道调整 RC_failsafe[] 数组中的故障安全位置(范围 +-1)。

打开和关闭变送器以检查故障安全装置是否按要求运行。

现在可以在您的草图中使用 RC 输入。

注意: 您可能必须停用接收器中的任何故障安全功能,否则 arduino 将无法响应发射器信号的丢失。

显示接收器帧率和频率

接收器脉冲重复周期和频率可以串行打印。在使用 PWM_period() 和 PWM_freq() 提取用于打印的数据之前,使用函数 PWM_read(channel number) 检查所选通道上的新数据是否可用。 RC_FrameRate.ino 中提供了示例代码。

最好使用第一个通道,因为这将是每个接收器帧中发送的第一个脉冲。 PWM_read() 使用与 RC_decode(CH) 相同的标志,因此请确保首先调用 PWM_read()。

请参阅下面的屏幕截图:

了解接收器周期很有用,因为它告诉您在下一组数据到达之前代码还有多长时间。如果 RC_avail() 在预定时间(即 21 毫秒)后没有检测到新的 RC 数据,则运行 RC_decode() 以触发故障安全和/或以稳定频率继续运行程序(可能是 PID 控制器)。

这是通过以下 if 语句在 RC_Read_Example.ino 中实现的。

now =millis();if(RC_avail() || now - rc_update> 21) rc_update =now; // 使用 RC_decode() 更新 RC 输入数据 // 运行 PID 控制器 // 应用伺服混合 // 定位伺服} 

伺服混合示例

我已经包含了 RC_ServoMixer_Example.ino 来展示如何混合两个接收器通道(在这种情况下,通道 2 和 3,升降舵和副翼)。该草图还显示了一种设置伺服方向、速率和子微调的方法。舵机库用于通过引脚 9 和 10 控制舵机。

下面是代码的伺服混合部分的屏幕截图:

混合是通过简单地将两个通道相加和相减并将输出限制在 -1 到 +1 的范围内来实现的。当应用升降舵和副翼混合时,您为每个伺服系统创建两个输出。

mix1 =channel 2 - channel3 (elv - ail)

mix2 =channel 2 + channel3 (elv - ail)

在定位舵机之前,您需要将 +-100% (+-1) 信号转换为舵机的等效脉冲宽度(以微秒为单位)。在 RC_ServoMixer_Example.ino 中,我使用函数 calc_uS() 来执行此操作。此功能位于草图的底部,如下面的屏幕截图所示。

为每个舵机指定的方向、速率和子微调用于计算适合舵机的脉冲宽度。

标准中性脉冲为1500uS,中性两侧正常范围为+-500uS。这给出了 1000uS (-100%) 的最小脉冲宽度和 2000uS (+100%) 的最大值。因此,应用了速率、方向和子微调的脉冲可以计算如下。

脉冲,uS =1500 + (servo_position_% * rate * direction + sub trim) * 500

伺服方向、速率和子微调可以是静态的,也可以由草图动态修改,以响应来自另一个接收器通道的输入,或通过其他方式进行修改。

所采取方法的理由

可以使用pulseIn(PIN, HIGH) 函数读取RC 接收器,但是pulseIn() 在等待脉冲开始和结束时阻塞loop() 中的代码,浪费了宝贵的处理时间。如果有多个输入数据也可能丢失。

为了速度,最好使用 Arduino 的引脚更改中断功能以及直接端口操作,以允许 loop() 中的代码以最小的延迟运行。然而,这比简单地调用pulseIn(PIN, HIGH)更复杂、更耗时。

因此,我想通过编写一些可以在项目之间移动的通用代码来获得两个世界的优势。所需要做的就是将.ino 文件(包含函数和中断例程)复制并粘贴到主草图文件夹中,指定输入引脚,然后使用草图中的函数。

限制

micros() 函数

arduino 上的微秒计时是使用 micros() 函数进行的。此功能以 4uS 步长计算。这意味着当我们测量 1000-2000uS 脉冲时,我们有 4 微秒级的精度。从实用的角度来看,这已经绰绰有余了。

如果需要,可以通过使用定时器中断将此分辨率提高到 0.5uS。见以下链接:

https://www.instructables.com/id/How-to-get-an-Arduino-micros-function-with-05us-pr/

PWMread_RCfailsafe.ino 的效率

如果您使用 PWMread_RCfailsafe.ino 读取 6 或 9 通道接收器,则有 1.4-2.0% 的处理时间用于运行引脚更改中断例程,我认为这超出了可接受的范围。

但是,了解代码的局限性以及在需要时如何加快速度总是有好处的。

下面是运行每个 ISR 所需时间的列表,具体取决于所选输入通道的数量。

1通道<8uS

2通道<12uS

3通道<16uS

4通道<20uS

5通道<20uS

6通道<24uS

注意: 使用的通道越多,每个 ISR 运行的时间就越长。这是因为每次调用 ISR 时都会有一个 for 循环通过每个通道。

在测量低频(即 50hz)RC 信号时,这种额外的时间(低效率)可以忽略不计。

除上述内容外,进入和退出 ISR 需要大约 4uS。对于一个脉冲,ISR 运行两次,一次在脉冲开始时(从低到高),然后在结束时(从高到低)。

使用 6 个 RC 输入时测量 1 个脉冲所需的时间为

2 *(4us 进入 ISR + 24uS 运行 ISR)=2 * 28 =48uS。

注意: 这是可以测量的最小脉冲宽度。

读取全部6个通道所用的时间为288uS(6 * 48uS)

假设接收器重复周期为 20 毫秒,则中断将运行 1.44% (0.000288/0.02) 的时间。这比使用pulseIn()函数要好得多。对于每个引脚,pulseIn() 会阻塞代码长达 20 毫秒。

仅供参考: 如果 arduino 只有 2 个 RC 输入,那么 ISR 的运行时间仅为 0.16% (0.000032/0.02)

最大实用频率 (赫兹)

如果将此代码用于任何其他目的,我建议最大实际频率为 2.5kHz。这给出了来自 micros() 函数的 100 步分辨率(+- 0.025kHz)。

如果在该频率下使用一个输入引脚,则有 3% 的时间用于中断,这意味着可以测量的最小占空比为 0.03。这相当于 12uS 的最小脉冲。

对于更高的频率,重写 ISR 以适合您的应用。

代码

  • PWMread_RCfailsafe
  • RC_Read_Example
  • RC_FrameRate
  • RC_ServoMixer_示例
PWMread_RCfailsafeArduino
此 .ino 文件包含用于解码 RC 接收器并在发送器信号丢失时应用故障安全的函数和引脚更改中断例程 (ISR)。将此文件复制并粘贴到与主草图相同的文件夹中(当您打开草图时,此代码将显示为 arduino IDE 中的第二个选项卡)。然后按照文件中的说明进行操作。
/* Kelvin Nelson 24/07/2019 * * 带有故障保护功能的 RC 接收器的脉宽调制 (PWM) 解码 * * 此代码包含易于使用的功能,可在任何设备上测量方波信号arduiuno pro mini、nano 或 uno 引脚,不包括 A6 和 A7。 * 该代码旨在与 RC 接收器一起使用,但也可用于大多数其他 PWM 测量应用,作为pulseIn(PIN, HIGH)的直接替代品。 *(迄今为止,它还没有在大于 1khz 的频率或在 arduino mega 上进行测试)* * RC 信号脉冲可以从每个输入引脚的脉冲宽度持续时间 (1000-2000uS) 转换为 -+100 % (-+1.0) 输出用于草图。 * 可以为每个通道设置此转换的校准以及故障保护设置。 (故障安全容差 10-330Hz 和 500-2500uS)。 * * 还可以提取每个引脚的原始数据,即脉冲时间、脉冲宽度、帧长、占空比和频率。 * * 设置很快,该文件的组织如下: * * - 代码概览 * - 功能列表 * - 如何使用,包括示例草图 * - 用户定义的变量 -> 指定输入引脚,发送器校准和故障保护。 * - 全局变量和函数 * * 代码概述:* * 该代码通过设置适当的寄存器来启用所选引脚上的引脚更改中断。 * * 任何选定引脚上的电压变化将触发三个中断服务程序之一,具体取决于引脚所属的寄存器。 * - ISR(PCINT0_vect)、ISR(PCINT1_vect) 或ISR(PCINT2_vect) * * 在每个ISR 中,代码确定哪个引脚发生了变化,并在返回主循环之前记下时间()。 * * 引脚变化之间的时间间隔用于计算脉冲宽度和帧长度。 * * 标志由 ISR 设置以指示何时接收到新脉冲。 * * 然后使用标志来提取和处理每个 ISR 收集的数据。 * * 虽然不完全相同,但此代码遵循与此视频中解释的原则相似的原则:https://youtu.be/bENjl1KQbvo * */// 函数列表:// 函数注释的输出类型名称// 无效setup_pwmRead() 使用引脚更改中断初始化 PWM 测量// RC 接收器解码// 布尔型 RC_avail() 在新的 RC 数据可用时返回一个高电平 // 浮动 RC_decode(通道编号)将选定的 RC 通道解码到 +-100 范围内%,并应用故障保护。// void print_RCpwm() 将 RC 通道原始数据打印到串行端口(用于校准)。// GENERIC PWM MEASUREMENTS// 布尔型 PWM_read(通道编号)在有新脉冲时返回高电平在特定频道上检测到。 // 该函数将脉冲数据保存到中断例程之外的变量中// 并且必须在使用其余 PWM 函数之前调用。// unsigned long PWM_time() 返回脉冲开始时的时间 // float PWM()返回脉冲宽度// float PWM_period() 返回脉冲之间的时间// float PWM_freq() 计算频率// float PWM_duty() 计算占空比// 注意:PWM_read(CH) 和RC_decode(CH) 使用相同的标志检测何时有新数据可用,这意味着如果两者同时在同一通道上使用,数据可能会丢失。// SUGESTION:如果您想使用 PWM_read(CH) 来查找 RC 通道的帧速率,请调用它在 RC_decode(CH) 之前。来自 RC_decode(CH) 的输出将默认为故障保护。// 如何使用,包括示例草图// 在下面代码中的“用户定义变量”标题下//// 步骤 1:将输入引脚输入到数组 pwmPIN[] ={}。 //// - 可以在 pwmPIN[] 中输入任意数量的引脚(引脚可用 0 - 13 和 A0 - A5)// - 引脚不需要按数字顺序排列,例如 pwmPIN[] ={A0, 5,6,10,8} 用于 5 个通道,或 pwmPIN[] ={A0,5} 用于 2 个通道// - 数组中的第一个元素是“通道 1”的引脚编号,第二个元素是引脚“通道 2”的编号……等等。// - 连接到 RC 接收器的所有引脚都需要位于阵列的开头。即前 2 个通道可以是 RC 输入,第 3 个通道可以连接到另一个设备,如超声波传感器的回声引脚。//// 第 2 步:如果 RC 接收器连接到所有输入,则将 RC_inputs 设置为0,如果没有指定连接到接收器的通道数,即 RC_inputs =2;//// 第 3 步:通过使用包含在草图文件夹中的这个 .ino 文件上传一个简单的草图来校准您的发射器,并打印原始 PWM 值到串行(或者将所需的功能复制并粘贴到草图中)。// 使用串行监视器中的信息手动更新数组 RC_min[]、RC_mid[]、RC_max[] 中的值以适合您的发射器(使用全速率获得最佳分辨率)。 // 将 RC 通道 PWM 数据打印到串行的示例草图。 /* void setup() { setup_pwmRead(); Serial.begin(9600); } void loop() { if(RC_avail()) print_RCpwm(); } */// 步骤4:为每个通道选择一个故障安全位置,范围在-1.0到+1.0,并将其输入数组RC_failsafe[] ={}//注意:如果您希望arduino响应发射器信号丢失,您可能需要禁用接收器上的故障保护功能(如果有的话)。// 检查故障保护操作的示例草图,并将校准通道打印到串行:/* unsigned long now; // 定时变量以固定间隔更新数据 unsigned long rc_update; const int 通道 =6; // 指定接收器通道数 float RC_in[channels]; // 一个数组,用于存储来自接收器的校准输入 void setup() { setup_pwmRead(); Serial.begin(9600); } void loop() { now =millis(); if(RC_avail() || now - rc_update> 25){ // 如果 RC 数据可用或自上次更新以来已经过去 25 毫秒(调整以适应接收器的帧速率) rc_update =now; //print_RCpwm(); // 取消将原始数据从接收器打印到串行的注释 for (int i =0; i=0 &&pwmPIN[i] <=7) pwmPIN_port[i] =2; // pin属于PCINT2_vect (PORT D) else if (pwmPIN[i]>=8 &&pwmPIN[i] <=13) pwmPIN_port[i] =0; // 引脚属于 PCINT0_vect (PORT B) // 将引脚编号(即引脚 11 或引脚 A0)转换为端口寄存器中的引脚位置。使用宏很可能有更好的方法... //(直接从端口寄存器读取引脚状态可加快 ISR 中的代码速度) if(pwmPIN[i] ==0 || pwmPIN[i ] ==A0 || pwmPIN[i] ==8) pwmPIN_reg[i] =0b00000001;否则 if(pwmPIN[i] ==1 || pwmPIN[i] ==A1 || pwmPIN[i] ==9) pwmPIN_reg[i] =0b00000010;否则 if(pwmPIN[i] ==2 || pwmPIN[i] ==A2 || pwmPIN[i] ==10) pwmPIN_reg[i] =0b00000100;否则 if(pwmPIN[i] ==3 || pwmPIN[i] ==A3 || pwmPIN[i] ==11) pwmPIN_reg[i] =0b00001000;否则 if(pwmPIN[i] ==4 || pwmPIN[i] ==A4 || pwmPIN[i] ==12) pwmPIN_reg[i] =0b00010000;否则 if(pwmPIN[i] ==5 || pwmPIN[i] ==A5 || pwmPIN[i] ==13) pwmPIN_reg[i] =0b00100000;否则如果(pwmPIN[i] ==6)pwmPIN_reg[i] =0b01000000;否则如果(pwmPIN[i] ==7)pwmPIN_reg[i] =0b10000000; }}// PIN CHANGE INTERRUPTSvoid setup_pwmRead(){ for(int i =0; i  num_ch) RC_inputs =num_ch; // 定义连接到 RC 接收器的引脚数。 } // 用于读取 PWM 输入的中断服务程序 (ISR)// PCINT0_vect(B 端口寄存器)对引脚 D8-13 上的任何更改做出反应。// PCINT1_vect(C 端口寄存器)“”“”A0-A5。 // PCINT2_vect (D 端口寄存器) "" "" D0-7.// 端口寄存器用于加速 ISR 代码中的 if 语句:// https://www.arduino.cc/en/Reference/PortManipulation http ://tronixstuff.com/2011/10/22/tutorial-arduino-port-manipulation/// http://harperjiangnew.blogspot.co.uk/2013/05/arduino-port-manipulation-on-mega-2560 .html// READ INTERRUPTS ON PINS D8-D13:ISR 例程检测哪个引脚发生了变化,并返回 PWM 脉冲宽度和脉冲重复周期。ISR(PCINT0_vect){ // 如果在端口 B 上检测到引脚变化,则该函数将运行pciTime =micros(); // 以微秒为单位记录 PIN 变化的时间 for (int i =0; i  RC_inputs) return 0; // if channel number is out of bounds return zero. int i =CH - 1; // determine the pulse width calibration for the RC channel. The default is 1000, 1500 and 2000us. int Min; if(CH <=size_RC_min) Min =RC_min[CH-1]; else Min =1000; int Mid; if(CH <=size_RC_mid) Mid =RC_mid[CH-1]; else Mid =1500; int Max; if(CH <=size_RC_max) Max =RC_max[CH-1]; else Max =2000; float CH_output; if(FAILSAFE(CH) ==HIGH){ // If the RC channel is outside of failsafe tolerances (10-330hz and 500-2500uS) if(CH> size_RC_failsafe) CH_output =0; // and if no failsafe position has been defined, set output to neutral else CH_output =RC_failsafe[i]; // or if defined set the failsafe position } else{ // If the RC signal is valid CH_output =calibrate(PW[i],Min,Mid,Max); // calibrate the pulse width to the range -1 to 1. } return CH_output; // The signal is mapped from a pulsewidth into the range of -1 to +1, using the user defined calibrate() function in this code. // 0 represents neutral or center stick on the transmitter // 1 is full displacement of a control input is one direction (i.e full left rudder) // -1 is full displacement of the control input in the other direction (i.e. full right rudder)}/* * Receiver Calibration */ // NEED TO SPEED UPfloat calibrate(float Rx, int Min, int Mid, int Max){ float calibrated; if (Rx>=Mid) { calibrated =map(Rx, Mid, Max, 0, 1000); // map from 0% to 100% in one direction } else if (Rx ==0) { calibrated =0; // neutral } else { calibrated =map(Rx, Min, Mid, -1000, 0); // map from 0% to -100% in the other direction } return calibrated * 0.001;}// Basic Receiver FAIL SAFE// check for 500-2500us and 10-330Hz (same limits as pololu)boolean FAILSAFE(int CH){ int i =CH-1; boolean failsafe_flag =LOW; if(pwmFlag[i] ==1) // if a new pulse has been measured. { pwmFlag[i] =0; // set flag to zero if(pwmPeriod[i]> 100000) // if time between pulses indicates a pulse rate of less than 10Hz { failsafe_flag =HIGH; } else if(pwmPeriod[i] <3000) // or if time between pulses indicates a pulse rate greater than 330Hz { failsafe_flag =HIGH; } if(PW[i] <500 || PW[i]> 2500) // if pulswidth is outside of the range 500-2500ms { failsafe_flag =HIGH; } } else if (micros() - pwmTimer[i]> 100000) // if there is no new pulswidth measurement within 100ms (10hz) { failsafe_flag =HIGH; } return failsafe_flag; }/* * Quick print function of Rx channel input */void print_RCpwm(){ // display the raw RC Channel PWM Inputs for (int i =0; i =0) Serial.print(" "); if (abs(pc) <100) Serial.print(" "); if (abs(pc) <10) Serial.print(" "); Serial.print(" ");Serial.print(pc);Serial.print("% ");}/* * GENERIC PWM FUNCTIONS */unsigned long pin_time;float pin_pwm;float pin_period;boolean PWM_read(int CH){ if(CH <1 &&CH> num_ch) return false; int i =CH-1; boolean avail =pwmFlag[i]; if (avail ==HIGH){ pwmFlag[i] =LOW; noInterrupts(); pin_time =pwmTimer[i]; pin_pwm =PW[i]; pin_period =pwmPeriod[i];中断(); } return avail;}unsigned long PWM_time(){return pin_time;}float PWM_period(){return pin_period;}float PWM(){return pin_pwm;}float PWM_freq(){ float freq; return freq =1000000 / pin_period; // frequency Hz}float PWM_duty(){ float duty; duty =pin_pwm/pin_period; return duty;}
RC_Read_ExampleArduino
An example sketch used to display raw data in order to calibrate your RC receiver and set your the fail safe. The PWMread_RCfailsafe.ino file should be copied into the same folder in order for the functions to be available.
unsigned long now; // timing variables to update data at a regular interval unsigned long rc_update;const int channels =6; // specify the number of receiver channelsfloat RC_in[channels]; // an array to store the calibrated input from receiver void setup() { setup_pwmRead(); Serial.begin(9600);}void loop() { now =millis(); if(RC_avail() || now - rc_update> 25){ // if RC data is available or 25ms has passed since last update (adjust to be equal or greater than the frame rate of receiver) rc_update =now; print_RCpwm(); // uncommment to print raw data from receiver to serial for (int i =0; i 
RC_FrameRateArduino
Example sketch that prints the frame rate and frequency of an RC Receiver. The PWMread_RCfailsafe.ino file should be copied into the same folder in order for the functions to be available.
void setup() { setup_pwmRead(); Serial.begin(9600);}void loop() { // Print RC receiver frame length and frame rate if (PWM_read(1)){ // if a new pulse is detected on channel 1 Serial.print(PWM_period(),0);Serial.print("uS "); Serial.print(PWM_freq());Serial.println("Hz"); }}
RC_ServoMixer_ExampleArduino
An servo mixing example. Two channels from a 6 channel are receiver are mixed and sent to two servos controlled using the servo library. The PWMread_RCfailsafe.ino file should be copied into the same folder in order for the functions to be available.
// servo variables#include  // include the servo library to control the servosServo servo1; // name each servo output for use with the servo library Servo servo2; // Each servo must be attached to a pin that has a PWM output// on the arduino uno, nano and pro mini these pins are 3, 5, 6, 9, 10 and 11const int servo1_pin =9; // identify the pins that each servo signal wire is connected toconst int servo2_pin =10;// Select Servo Direction, Rates and Sub-trim (the size of each array must match the number of servos)boolean servo_dir[] ={0,1}; // Direction:0 is normal, 1 is reversefloat servo_rates[] ={1,0.5}; // Rates:range 0 to 2 (1 =+-500us (NORMAL), 2 =+-1000us (MAX)):The amount of servo deflection in both directionsfloat servo_subtrim[] ={0.0,0.0}; // Subtrimrange -1 to +1 (-1 =1000us, 0 =1500us, 1 =2000us):The neutral position of the servoboolean servo_mix_on =true;unsigned long now; // timing variables to update data at a regular interval unsigned long rc_update;// Receiver variablesconst int channels =6; // specify the number of receiver channelsfloat RC_in[channels]; // an array to store the calibrated input from receiver void setup() { servo1.attach(servo1_pin, 500, 2500); // attach the servo library to each servo pin, and define min and max uS values servo2.attach(servo2_pin, 500, 2500); setup_pwmRead(); Serial.begin(9600);}void loop() { now =millis(); if(RC_avail() || now - rc_update> 25){ // if RC data is available or 25ms has passed since last update (adjust to> frame rate of receiver) rc_update =now; print_RCpwm(); // uncommment to print raw data from receiver to serial for (int i =0; i 1) mix1 =1; // limit mixer output to +-1 else if(mix1 <-1) mix1 =-1; if(mix2> 1) mix2 =1; // limit mixer output to +-1 else if(mix2 <-1) mix2 =-1; // Calculate the pulse widths for the servos servo1_uS =calc_uS(mix1, 1); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) servo2_uS =calc_uS(mix2, 2); // apply the servo rates, direction and sub_trim for servo 2, and convert to a RC pulsewidth (microseconds, uS) } else{ // MIXING OFF servo1_uS =calc_uS(RC_in[1],1); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) servo2_uS =calc_uS(RC_in[2],2); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) } servo1.writeMicroseconds(servo1_uS); // write the pulsewidth to the servo. servo2.writeMicroseconds(servo2_uS); // write the pulsewidth to the servo. }}int calc_uS(float cmd, int servo){ // cmd =commanded position +-100% // servo =servo num (to apply correct direction, rates and trim) int i =servo-1; float dir; if(servo_dir[i] ==0) dir =-1; else dir =1; // set the direction of servo travel cmd =1500 + (cmd*servo_rates[i]*dir + servo_subtrim[i])*500; // apply servo rates and sub trim, then convert to a uS value if(cmd> 2500) cmd =2500; // limit pulsewidth to the range 500 to 2500us else if(cmd <500) cmd =500; return cmd;}

示意图

This RC Receiver is powered by 5v and ground from the ICSP pins with the 6 signal outputs connected to pins 2-7
Micro servo 1 is powered by 5v pin and ground, with signal wire connected to pin 9
Micro servo 2 powered by 3.3v pin and ground, with signal wired connected to pin 10

制造工艺

  1. C - 输入和输出
  2. LCD 动画和游戏
  3. 使用 Arduino 和智能手机的 DIY 电压表
  4. 温湿度数据记录仪
  5. 如何使用 DHT11 在 Blynk 上读取温度和湿度
  6. Python3 和 Arduino 通信
  7. 基于 Arduino 和 OLED 的元胞自动机
  8. 使用 Arduino 和 RDA8057M 的 FM 收音机
  9. Arduino TEA5767 FM 收音机接收器
  10. 基于Arduino和谷歌电子表格的考勤系统
  11. 自制 Arduino、Si4730 全波段接收器(LW、MW、SW、FM)
  12. Arduino 的隔离模拟输入