PPM通信测试—FS-i6X+FS-A8S接收机+ESP32

提示:本文内容仅供学习参考。Author: Jonnie Walker CGC

目录

前言

一、PPM是什么?

二、测试步骤

1.硬件

(1).FS-A8S接收机:

(2).FS-i6X遥控器

[(3).E SP32-C3连接电路](#(3).E SP32-C3连接电路)

2.软件

总结


前言

前一段时间在使用FS-i6X遥控器,测试过程将其记录下来,分享给需要的朋友。

这段时间在DIY仿生蝴蝶,使用到这个遥控器所以将其记录一下!


一、PPM是什么?

1.脉冲位置调制 (Pulse Position Modulation) 。

(1).核心概念 :在一个固定的时间周期内,通过改变脉冲出现的时间位置来携带和传递数据信息。

(2).常见应用

  • 无线电遥控 (RC):在无人机、航模或机器人的遥控器和接收机之间,PPM 是一种经典的模拟信号协议,可以用一根信号线同时传输多个通道的控制数据。

  • 光通信:常用于深空通信或红外线遥控中。

二、测试步骤

1.硬件

(1).FS-A8S接收机:

图1 正 图2 反

(2).FS-i6X遥控器

在测试前先进行遥控器PPM设置:

图1 进入系统

图2 选择接收机设置

图3 进入后选择输出模式

图4 选择本次测试的PPM输出模式

图5 回到系统,然后选择辅助开关设置

图6 通道数设置为8,然后将所有开关打开

图7 然后退出系统选择进入功能

图8 选择辅助通道

图9 设置对应通道,不知道怎么设置,就按照图设置进行

图10

遥控器的设置到此就完成。

(3).E SP32-C3连接电路

图1

2.软件

以下为本次测试的程序1,8通道输出。阻塞版本:

cpp 复制代码
/*
 * PPM信号读取测试程序 - ESP32-版本
 * 文件名:PPM_Reader_Test.ino
 * 作者:iTEM-CGC
 * 版本:1.0
 * 日期:2025-12-1
 * 
 * 功能描述:
 * - 读取8通道PPM信号
 * - 通过串口输出原始脉冲宽度(微秒)
 * - 简单的数据格式输出
 * 
 * 硬件连接:
 * - PPM信号线 -> GPIO6
 * - PPM GND   -> ESP32 GND
 * - PPM VCC   -> ESP32 3.3V (如果需要)
 */

/*******************************
 *          宏定义             *
 *******************************/
#define PIN_PPM_INPUT        2      // PPM信号输入引脚 (GPIO2)
#define BAUD_RATE           115200  // 串口波特率
#define CHANNEL_COUNT       8       // 要读取的通道数量

// PPM信号参数
#define PPM_SYNC_THRESHOLD   5000   // 同步脉冲阈值(微秒)
#define PPM_MIN_PULSE        500    // 最小脉冲宽度(微秒)
#define PPM_MAX_PULSE        2500   // 最大脉冲宽度(微秒)

/*******************************
 *         全局变量            *
 *******************************/
int ppmChannels[CHANNEL_COUNT] = {0};  // 存储通道数据
unsigned long frameCount = 0;          // 帧计数器
unsigned long lastFrameTime = 0;       // 上一帧时间
float frameRate = 0.0;                 // 帧率

/*******************************
 *          主程序             *
 *******************************/

/**
 * @brief 初始化
 */
void setup() {
  // 初始化串口
  Serial.begin(BAUD_RATE);
  while (!Serial) {
    ; // 等待串口连接
  }
  
  // 初始化PPM输入引脚
  pinMode(PIN_PPM_INPUT, INPUT);
  
  Serial.println("========================================");
  Serial.println("PPM信号读取测试程序");
  Serial.println("========================================");
  Serial.println("等待PPM信号...");
  Serial.println();
}

/**
 * @brief 主循环
 */
void loop() {
  // 读取PPM数据
  bool success = readPPMChannels();
  
  // 帧率统计
  frameCount++;
  unsigned long currentTime = millis();
  if (currentTime - lastFrameTime >= 1000) {
    frameRate = frameCount * 1000.0 / (currentTime - lastFrameTime);
    frameCount = 0;
    lastFrameTime = currentTime;
  }
  
  // 如果成功读取到数据,则输出
  if (success) {
    printPPMData();
  }
}

/**
 * @brief 读取PPM通道数据
 * @return true: 读取成功, false: 读取失败
 */
bool readPPMChannels() {
  // 等待同步脉冲(长脉冲)
  unsigned long syncStart = micros();
  while (pulseIn(PIN_PPM_INPUT, HIGH) < PPM_SYNC_THRESHOLD) {
    // 超时检测(100ms超时)
    if (micros() - syncStart > 100000) {
      return false;
    }
  }
  
  // 读取所有通道数据
  for (int i = 0; i < CHANNEL_COUNT; i++) {
    unsigned long pulseStart = micros();
    ppmChannels[i] = pulseIn(PIN_PPM_INPUT, HIGH);
    
    // 超时检测(5ms超时)
    if (micros() - pulseStart > 5000) {
      ppmChannels[i] = 0;
      return false;
    }
  }
  
  return true;
}

/**
 * @brief 打印PPM数据
 */
void printPPMData() {
  static unsigned long lastPrintTime = 0;
  unsigned long currentTime = millis();
  
  // 控制输出频率(每100ms输出一次)
  if (currentTime - lastPrintTime < 100) {
    return;
  }
  
  lastPrintTime = currentTime;
  
  // 输出时间戳和帧率
  Serial.print("[");
  Serial.print(currentTime / 1000);  // 转换为秒
  Serial.print(".");
  Serial.print(currentTime % 1000);
  Serial.print("s] FR:");
  Serial.print(frameRate, 1);
  Serial.print("Hz | ");
  
  // 输出所有通道数据
  for (int i = 0; i < CHANNEL_COUNT; i++) {
    Serial.print("CH");
    if (i+1 < 10) Serial.print("0");
    Serial.print(i+1);
    Serial.print(":");
    Serial.print(ppmChannels[i]);
    Serial.print("us ");
  }
  
  // 输出通道状态指示
  Serial.print("| [");
  for (int i = 0; i < CHANNEL_COUNT; i++) {
    if (ppmChannels[i] >= PPM_MIN_PULSE && ppmChannels[i] <= PPM_MAX_PULSE) {
      Serial.print("✓");
    } else if (ppmChannels[i] == 0) {
      Serial.print("-");
    } else {
      Serial.print("✗");
    }
  }
  Serial.println("]");
}

/**
 * @brief 可选:简洁输出模式(每帧都输出)
 */
void printPPMDataSimple() {
  // 简洁输出格式
  Serial.print("[");
  Serial.print(millis());
  Serial.print("] ");
  
  for (int i = 0; i < CHANNEL_COUNT; i++) {
    Serial.print(ppmChannels[i]);
    if (i < CHANNEL_COUNT - 1) {
      Serial.print(",");
    }
  }
  
  Serial.println();
}

/**
 * @brief 可选:详细输出模式
 */
void printPPMDataDetailed() {
  static unsigned long lastDetailedPrint = 0;
  unsigned long currentTime = millis();
  
  // 每500ms输出一次详细报告
  if (currentTime - lastDetailedPrint < 500) {
    return;
  }
  
  lastDetailedPrint = currentTime;
  
  Serial.println("\n=== PPM数据详细报告 ===");
  Serial.println("通道   原始值(us)   状态");
  Serial.println("-----------------------");
  
  for (int i = 0; i < CHANNEL_COUNT; i++) {
    Serial.print("CH");
    if (i+1 < 10) Serial.print("0");
    Serial.print(i+1);
    Serial.print(": ");
    Serial.print(ppmChannels[i]);
    Serial.print("us\t");
    
    // 判断状态
    if (ppmChannels[i] == 0) {
      Serial.println("无信号");
    } else if (ppmChannels[i] < PPM_MIN_PULSE) {
      Serial.println("过低");
    } else if (ppmChannels[i] > PPM_MAX_PULSE) {
      Serial.println("过高");
    } else if (abs(ppmChannels[i] - 1500) < 50) {
      Serial.println("中位");
    } else if (ppmChannels[i] > 1500) {
      Serial.print("高位 +");
      Serial.print(ppmChannels[i] - 1500);
      Serial.println("us");
    } else {
      Serial.print("低位 ");
      Serial.print(ppmChannels[i] - 1500);
      Serial.println("us");
    }
  }
  
  Serial.print("帧率: ");
  Serial.print(frameRate, 1);
  Serial.println(" Hz");
  Serial.println("=======================\n");
}

以下为本次测试的程序2,8通道输出。外部中断版本:

cpp 复制代码
/*
 * PPM信号读取测试程序 - ESP32-版本
 * 文件名:PPM_Reader_Test.ino
 * 作者:iTEM-CGC
 * 版本:1.0
 * 日期:2025-12-1
 * 
 * 功能描述:
 * - 读取8通道PPM信号
 * - 通过串口输出原始脉冲宽度(微秒)
 * - 简单的数据格式输出
 * 
 * 硬件连接:
 * - PPM信号线 -> GPIO6
 * - PPM GND   -> ESP32 GND
 * - PPM VCC   -> ESP32 3.3V (如果需要)
 */


#define PPM_INPUT_PIN         2   // 定义PPM输入引脚
#define MAX_CHANNELS          8   // 定义最大通道数(通常航模遥控器为6-8通道)
#define SYNC_PULSE_MIN_LENGTH 3000// 同步脉冲的最小长度(微秒),通常大于3000us

// 声明 volatile 变量,因为它们将在中断服务程序(ISR)和主循环中共享
volatile uint16_t ppmValues[MAX_CHANNELS]; // 存放各个通道的值
volatile uint8_t currentChannel = 0;       // 当前正在读取的通道索引
volatile uint32_t lastMicros = 0;          // 上次触发中断的时间戳
volatile bool newFrameReady = false;       // 标记是否成功接收到一帧完整的数据

// IRAM_ATTR 宏将该函数放入内部RAM中执行,这对于ESP32的高频中断非常重要
void IRAM_ATTR ppmInterrupt() {
  uint32_t currentMicros = micros();            // 获取当前时间(微秒)
  uint32_t delta = currentMicros - lastMicros;  // 计算与上次中断的时间差
  lastMicros = currentMicros;                   // 更新上次中断时间

  // 判断是否为同步帧(长脉冲)
  if (delta > SYNC_PULSE_MIN_LENGTH) {
    currentChannel = 0;         // 遇到同步帧,通道计数器清零,准备接收通道1
    newFrameReady = true;       // 标记上一帧数据已经完整接收
  } 
  // 否则,如果通道数没有超过最大值,则记录通道数据
  else if (currentChannel < MAX_CHANNELS) {
    ppmValues[currentChannel] = delta;
    currentChannel++;
  }
}

void setup() {
  Serial.begin(115200);
  
  // 设置PPM引脚为输入模式。如果接收机信号比较弱,可以尝试使用 INPUT_PULLUP
  pinMode(PPM_INPUT_PIN, INPUT); 

  // 将中断附加到引脚上,大多数PPM信号通过计算两个上升沿(RISING)之间的宽度来获取数值
  attachInterrupt(digitalPinToInterrupt(PPM_INPUT_PIN), ppmInterrupt, RISING);

  Serial.println("ESP32-C3 PPM 通信测试程序已启动...");
  Serial.println("等待PPM信号...");
}

void loop() {
  // 如果成功接收到一帧新的PPM数据
  if (newFrameReady) {
    // 临时数组,用于安全复制数据
    uint16_t safePpmValues[MAX_CHANNELS];

    // 禁用中断,防止在复制数据的过程中发生中断导致数据被覆盖(数据撕裂现象)
    noInterrupts();
    for (int i = 0; i < MAX_CHANNELS; i++) {
      safePpmValues[i] = ppmValues[i];
    }
    newFrameReady = false; // 重置标志位
    interrupts(); // 恢复中断

    // 在串口监视器打印所有通道的值
    Serial.print("PPM 帧数据: ");
    for (int i = 0; i < MAX_CHANNELS; i++) {
      Serial.print("CH");
      Serial.print(i + 1);
      Serial.print(": ");
      // 输出数值,正常情况应在 1000 到 2000 之间
      Serial.print(safePpmValues[i]); 
      Serial.print(" \t");
    }
    Serial.println();

    // 延迟一小段时间,避免串口输出过快(一帧PPM通常大约20ms)
    delay(20); 
  }
}

程序运行后打开串口工具,然后窗口会输出8个通道数据。在结合操作遥控器观察其数据的变化!


总结

本文详细描述比较少!希望能对你有帮助。

相关推荐
Full Stack Developme2 小时前
Java Simple Serial Connector 教程
java·stm32·单片机
youcans_2 小时前
【FOC-MBD】(20)矢量空间脉宽调制 (SVPWM)输出
stm32·单片机·嵌入式硬件·matlab·代码生成
点灯小铭2 小时前
基于单片机的全自动洗衣机控制器设计
单片机·嵌入式硬件
Flamingˢ2 小时前
ZYNQ + OV5640 + HDMI 视频系统调试记录:一次 RGB888 与 RGB565 引发的黑屏问题
arm开发·嵌入式硬件·fpga开发·vim·音视频
Strange_Head4 小时前
《Linux系统编程篇》Linux Socket 网络编程03(Linux 进程间通信(IPC))——基础篇
linux·网络·单片机
搁浅小泽4 小时前
大电流焊点补焊要求
单片机·嵌入式硬件·可靠性工程师
Linux猿4 小时前
基于单片机浴室窗帘控制系统 | 附源码
单片机·嵌入式硬件·毕业设计·源码·课程设计·项目·基于单片机于是窗帘控制系统
清风6666664 小时前
基于51单片机的的智能电动车充电桩系统设计
单片机·嵌入式硬件·毕业设计·51单片机·课程设计·期末大作业
Flamingˢ4 小时前
YNQ + OV5640 视频系统开发(二):OV5640_Data IP 核源码解析
arm开发·嵌入式硬件·网络协议·tcp/ip·fpga开发·vim·音视频