PWM、PPM、SBUS、DSM2这四种协议到底是什么鬼?

本文深入探讨了航模世界中的四种关键通信协议:PWM、PPM、S.BUS和DSM2,解释了它们的工作原理、优缺点及适用场景,为航模爱好者提供了一份实用指南。

    最近跳进了航模这个魔坑,进出各个论坛,看各种各样的DIY,最终发现航模规模实在太大了,又是固定翼,又是多轴飞行器,又是穿越机,又是......要完全“玩转”航模,既得懂无线电通信,又得掌握飞行控制,既要懂机载电子电路,又要掌握空气动力学…… 

    说起设备,又是遥控器,又是飞行控制器,又是接收机,又是电调,又是FPV,又是头追,又是地面站.......

    不知道从哪学起,也不知道从哪开始看。听说接收机输出还有这四种协议,网络查找一番,现在转过来,以备以后学习用。

PWM, PPM(也叫CPPM,基本上可以理解为两者是同一个东西), S.BUS,DSM2都是接收机与其他设备通信的协议。请注意这里不要和遥控器和接收机之间的协议混淆。遥控器和接收机之间会采用某种协议来互相沟通,这些协议往往各个厂牌各自有一套且互不兼容。但接收机输出的信号是有通行标准的,我们这里讨论的就是接收机输出的信号。


PWM

 

PWM ,Pulse Width Modulation的缩写,英文意思是脉宽调制,在航模中主要用于舵机的控制。这是一种古老而通用的工业信号,是一种最常见的控制信号。该信号主要原理是通过周期性跳变的高低电平组成方波,来进行连续数据的输出。如下图所示:

640.webp.jpg

 

而航模常用的PWM信号,其实只使用了它的一部分功能,就是只用到高电平的宽度来进行信号的通信,而固定了周期,并且忽略了占空比参数。

 

PWM的优点很明显

 

由于传输过程全部使用满电压传输,非0即1,很像数字信号, 所以他拥有了数字信号的抗干扰能力。

脉宽的调节是连续的,使得它能够传输模拟信号。

PWM信号的发生和采集都非常简单,现在的数字电路则使用计 数的方法产生和采集PWM信号。

信号值与电压无关,这在电压不恒定的条件下非常有用,比如电 池电压会随消耗而降低,DCDC都会存在纹波等等,这些因素不会干扰信号的传输。

 

PWM因为处理简单,在航模圈至今仍然广泛用以驱动舵机和固定翼飞机的电调等。其相对于PPM等协议最大的不同在于,它每条物理连线里只传输1路信号。换句话说,需要传输几个通道,就需要几组物理连线。

 

PPM(CPPM)

 

全称是Pulse Position Modulation。

 

因为PWM每路只能传输一路信号,在分别直接驱动不同设备的时候(比如固定翼,每路各自驱动不同的舵机和电调)这没有任何问题。但在一些场合,我们并不需要直接驱动设备,而是需要先集中获取接收机的多个通道的值,再做其他用途时,比如将两个遥控器之间连接起来的教练模式,比如遥控器接电脑玩模拟器,当然还有我们玩多轴,要将接收机的信号传输给飞控时,每个通道一组物理连线的方式就显得非常的繁琐和没有必要。这时候PPM就是救星了。

 

航模使用的PWM信号,高电平的持续时间在整个时间轴上所占的空间其实是很小的(假设高电平是信号),绝大部分的时间都是空白的。PPM简单的将多个通道的数值一个接一个合并进一个通道,用2个高电平之间的宽度来表示一个通道的值。下图中的第一行。

640.jpg

 

因为每一帧信号的尾部必须加入一个足够长的空白(显著超过一个正常PWM信号的宽度)来分隔前后两个信号,每一帧能传输的信号通道最多只能到8个。这在大部分的场合已经足够了,比如刚才说的教练模式/模拟器/多轴等。且PPM是一个通行标准,绝大多数厂牌的遥控/接收都是支持的。

 

S.BUS(S-BUS/SBUS)

 

全称是Serial Bus。

 

S.BUS是一个串行通信协议,最早由日本厂商FUTABA(扶他爸~)引入,随后FrSky的很多接收机也开始支持,S.BUS是全数字化接口总线,数字化是指的该协议使用现有数字通信接口作为通信的硬件协议,使用专用的软件协议,这使得该设备非常适合在单片机系统中使用,也就是说适合与飞控连接。这也就是我为什么要将这个协议详细叙述的原因。总线是指他可以连接多个设备,这些设备通过一个Hub与这个总线相连,得到各自的控制信息。

 

S.bus使用RS232C串口的硬件协议作为自己的硬件运行基础。 使用TTL电平,即3.3V。  使用负逻辑,即低电平为“1”,高电平为“0”。 波特率:100000(100k),注意:不兼容波特率115200。

 

DSM2(DSMX)

 

DSM是Digital Spread Spectrum Modulation的缩写

 

DMS协议一共有三代: DSM、DSM2、DSMX。国内最常见的是DSM2,JR和Spectrum 的遥控器都支持。该协议也是一种串行协议,但是比S.BUS更加通用,使用的标准串口定义,所以市面上兼容接收机更加便宜,兼容的设备也更多,比如电直的三轴陀螺VBar就可以直接接受DSM2信号。

但是该协议并不是一种总线化的协议,要靠接收机取把协议变为PWM来驱动舵机,DSM2接口也只能连接接收机和卫星接收机,不过对于飞控来说这个无所谓,反正也是一个接口连接到飞控就可以了。

 

DSMX是DSM2的升级版,协议基本一样就是速率加快了。DSMX协议可以用于双向传输,即能够将飞机上的信息传回遥控器上在液晶屏显示,不过对于玩儿飞控这个功能不重要,有了电台和PC,这个意义不大。

 

那么到底该用什么呢?

 

★.如果你是固定翼玩家,也无意在固定翼飞机上加飞控,那么其实这个问题对你来说不是问题:PWM。

★.如果你需要配置无线教练机或者无线模拟器,那么一个支持PPM输出的接收机可以省去一团乱麻的连线。如果普通休闲玩多轴,无论是航拍还是穿越,PPM也足够胜任。

★.如果你开始追求极限的穿越机表现,那也许你会开始能感受到S.BUS的低延迟带来的优势。或者你涉足功能丰富的正经航拍机,除了控制飞机,还要控制云台等等一系列其他附加设备时,S.BUS的多通道会给你带来很大便利。然而你需要寻找支持S.BUS的遥控接收组合,这也许意味着额外的投入。

以上是我个人通过网上学习整理的,给大家一个参考。还有很多协议在国内普及度不是很高,这里就不一一阐述了,希望对大家初步认识有所帮助。

Channel 1: 1492 us Channel 2: 1495 us Channel 3: 1500 us Channel 4: 1492 us Channel 5: 1047 us Channel 6: 1048 us Channel 7: 1052 us Channel 8: 1044 us Channel 1: 1497 us Channel 2: 1495 us Channel 3: 1495 us Channel 4: 1496 us Channel 5: 1047 us Channel 6: 1048 us Channel 7: 1048 us Channel 8: 1046 us Channel 1: 1494 us Channel 2: 1496 us Channel 3: 1500 us Channel 4: 1493 us Channel 5: 1046 us Channel 6: 1047 us Channel 7: 1048 us Channel 8: 1048 us我们想要的PPM数据是1500,1500,1500.1500,1050,1050,1050,1050相差了5us,void* PPM_Rcv_thread(void* arg) { int gpio_fd; struct epoll_event ev, events[1]; int epoll_fd; char value_str[2]; struct timespec last_falling_time = {0, 0}; // 上次下降沿时间 struct timespec current_time; uint16_t local_channels[PPM_MAX_CHANNELS]; int channel_count = 0; struct timespec last_frame_time = {0, 0}; uint16_t crcval; // --- 使用 falling-only 模式 --- gpio_fd = setup_gpio_falling_only(PPM_GPIO); if (gpio_fd < 0) { fprintf(stderr, "Failed to setup GPIO %d with 'falling'\n", PPM_GPIO); return NULL; } epoll_fd = epoll_create1(0); if (epoll_fd == -1) { perror("epoll_create1"); close(gpio_fd); return NULL; } ev.events = POLLPRI; // 高优先级事件(边沿触发) ev.data.fd = gpio_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, gpio_fd, &ev) == -1) { perror("epoll_ctl: add"); close(epoll_fd); close(gpio_fd); return NULL; } printf("PPM Receiver Thread: Monitoring FALLING edges only on GPIO %d\n", PPM_GPIO); while (1) { int nfds = epoll_wait(epoll_fd, events, 1, -1); if (nfds == -1 && errno == EINTR) continue; if (nfds == -1) { perror("epoll_wait"); break; } for (int i = 0; i < nfds; ++i) { if (events[i].data.fd == gpio_fd) { clock_gettime(CLOCK_MONOTONIC, &current_time); // 必须读取 value 清除 pending 中断 lseek(gpio_fd, 0, SEEK_SET); if (read(gpio_fd, value_str, 1) != 1) { perror("read value"); continue; } // 第一次下降沿,只记录时间,不计算 if (last_falling_time.tv_sec == 0 && last_falling_time.tv_nsec == 0) { last_falling_time = current_time; continue; } // 计算本次与上次下降沿之间的时间差 → 即上一个通道的高电平宽度 int64_t pulse_width_us = timespec_to_us(&current_time) - timespec_to_us(&last_falling_time); // printf("Falling Edge: delta = %lld us\n", pulse_width_us); // 判断是否为同步帧(超长低电平) if (pulse_width_us > SYNC_THRESHOLD_US) { if (channel_count > 0) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); if (last_frame_time.tv_sec != 0) { int64_t frame_interval_us = timespec_to_us(&now) - timespec_to_us(&last_frame_time); // printf("Frame interval: %lld us (%.1f ms)\n", frame_interval_us, frame_interval_us / 1000.0); } last_frame_time = now; pthread_mutex_lock(&data_mutex); memcpy(g_ppm_channels, local_channels, sizeof(uint16_t) * channel_count); g_channel_count = channel_count; sbus_format_frame(g_ppm_channels, g_channel_count, g_sbus_frame); // print_hex_buffer("标准SBUS帧数据", g_sbus_frame, 25); // pthread_mutex_lock(&sbubs_mutex); memset(sky_sbus, 0, sizeof(sky_sbus)); sky_sbus[0] = 0xFC; sky_sbus[1] = 0xB1; sky_sbus[2] = 0x16; memcpy(&sky_sbus[3], &g_sbus_frame[1], SBUS_MSG_VALID_DATA_LEN); crcval = crc16_ccitt(&g_sbus_frame[1], SBUS_MSG_VALID_DATA_LEN); sky_sbus[25] = (crcval >> 8) & 0xFF; sky_sbus[26] = crcval & 0xFF; // print_hex_buffer("私有SBUS帧数据", sky_sbus, 27); // pthread_mutex_unlock(&sbubs_mutex); // printf("New Frame: %d channels\n", channel_count); pthread_mutex_unlock(&data_mutex); channel_count = 0; } } // 正常通道值 else if (pulse_width_us >= 800 && pulse_width_us <= 2200) { if (channel_count < PPM_MAX_CHANNELS) { local_channels[channel_count++] = (uint16_t)pulse_width_us; printf("Channel %d: %lld us\n", channel_count, pulse_width_us); } else { printf("Channel buffer full!\n"); } } else { printf("Invalid width: %lld us (ignored)\n", pulse_width_us); } // 更新上一次下降沿时间 last_falling_time = current_time; } } } close(epoll_fd); close(gpio_fd); unexport_gpio(PPM_GPIO); printf("PPM Receiver Thread Exited.\n"); return NULL; }怎么做到相差2us
最新发布
01-04
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值