EV1527协议应用

EV1527协议应用

  • EV1527 帧结构
  • 解码原理
  • 实现代码

EV1527 帧结构

EV1527 每帧数据由同步码和 24 位的数据码组成,数据码又分为地址码(20 位)和按键码(4 位)。
以 433Mhz EV1527 遥控器为例,遥控波形如下。
在这里插入图片描述

bit0:400us 高电平+800us 低电平
bit1:1ms 高电平+200us 低电平

同步码数据码(20位)D0D1D2D3

同步码(黑色线条部分):高电平 400us+低电平 9ms。
地址码(橙色线条部分):20 个数据位,共 24ms。
按键码(红色线条部分):4 个数据位,共 4.8ms。

解码原理

同步码和 bit1、bit0 的低电平持续时间都不一样。通过定时器计算低电平时间来判断同步码、bit1、bit0。

实现代码

本次使用STC8作为底板

/****************************************************************************
支持433M、315M等常用频段的1527协议的接收以及解析
 ****************************************************************************/
#ifndef _ASR_RCSWITCH_H
#define _ASR_RCSWITCH_H

#include "asr.h"
#ifndef TW_ASR_PRO
#include "asr_delay.h"
#endif

// Number of maximum _high/Low changes per packet.
// We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync
#define RCSWITCH_MAX_CHANGES    67

static uint8_t nReceiverInterrupt;

class RCSwitch 
{
public:
    // RCSwitch(uint8_t tx_pin, timer_base_t timer);
    RCSwitch();

    void enableReceive(uint8_t interrupt);
    void disableReceive();
    bool available();
    void resetAvailable();

    uint32_t getReceivedValue();
    unsigned int getReceivedBitlength();
    unsigned int getReceivedDelay();
    unsigned int getReceivedProtocol();
    unsigned int* getReceivedRawdata();

    void setReceiveTolerance(int nPercent);

    /**
     * Description of a single pule, which consists of a _high signal
     * whose duration is "_high" times the base pulse length, followed
     * by a _low signal lasting "_low" times the base pulse length.
     * Thus, the pulse overall lasts (_high+_low)*pulseLength
     */
    struct HighLow {
        uint8_t _high;
        uint8_t _low;
    };

    struct Protocol {
        uint16_t pulseLength;  //同步头高电平脉宽(us)
        HighLow syncFactor; /* 同步头的高电平和低电平脉宽比 */
        HighLow zero;  /* 数据0的高电平和低电平脉宽比 */
        HighLow one;   /* 数据1的高电平和低电平脉宽比 */

        /**
         * If true, interchange _high and _low logic levels in all transmissions.
         *
         * By default, RCSwitch assumes that any signals it sends or receives
         * can be broken down into pulses which start with a _high signal level,
         * followed by a a _low signal level. This is e.g. the case for the
         * popular PT 2260 encoder chip, and thus many switches out there.
         *
         * But some devices do it the other way around, and start with a _low
         * signal level, followed by a _high signal level, e.g. the HT6P20B. To
         * accommodate this, one can set invertedSignal to true, which causes
         * RCSwitch to change how it interprets any HighLow struct FOO: It will
         * then assume transmissions start with a _low signal lasting
         * FOO._high*pulseLength microseconds, followed by a _high signal lasting
         * FOO._low*pulseLength microseconds.
         */
        bool invertedSignal;  //如果为真,则交换所有传输中_high和_low的逻辑。
    };

private:
    static void handleInterrupt();
    static bool receiveProtocol(const int p, unsigned int changeCount);

    int nTransmitterPin;
    int nRepeatTransmit;
    
    Protocol protocol;

    static int nReceiveTolerance;
    volatile static uint32_t nReceivedValue;
    volatile static unsigned int nReceivedBitlength;
    volatile static unsigned int nReceivedDelay;
    volatile static unsigned int nReceivedProtocol;
    const static unsigned int nSeparationLimit;
    /* 
    * timings[0] contains sync timing, followed by a number of bits
    */
    static unsigned int timings[RCSWITCH_MAX_CHANGES];
};

static const RCSwitch::Protocol recv_proto[] = {
  { 350, {  1, 31 }, {  1,  3 }, {  3,  1 }, false },    // protocol 1
  { 650, {  1, 10 }, {  1,  2 }, {  2,  1 }, false },    // protocol 2
  { 100, { 30, 71 }, {  4, 11 }, {  9,  6 }, false },    // protocol 3
  { 380, {  1,  6 }, {  1,  3 }, {  3,  1 }, false },    // protocol 4
  { 500, {  6, 14 }, {  1,  2 }, {  2,  1 }, false },    // protocol 5
  { 450, { 23,  1 }, {  1,  2 }, {  2,  1 }, true },     // protocol 6 (HT6P20B)
  { 150, {  2, 62 }, {  1,  6 }, {  6,  1 }, false },    // protocol 7 (HS2303-PT, i. e. used in AUKEY Remote)
  { 200, {  3, 130}, {  7, 16 }, {  3,  16}, false},     // protocol 8 Conrad RS-200 RX
  { 200, { 130, 7 }, {  16, 7 }, { 16,  3 }, true},      // protocol 9 Conrad RS-200 TX
  { 365, { 18,  1 }, {  3,  1 }, {  1,  3 }, true },     // protocol 10 (1ByOne Doorbell)
  { 270, { 36,  1 }, {  1,  2 }, {  2,  1 }, true },     // protocol 11 (HT12E)
  { 320, { 36,  1 }, {  1,  2 }, {  2,  1 }, true },     // protocol 12 (SM5212)
  { 400, {  1, 31 }, {  1,  3 }, {  3,  1 }, false },    // protocol 13 公牛无线开关
  {  20, { 239, 78}, { 20, 35 }, { 35, 20 }, false },    // protocol 14 Dooya DC1600
};

enum {
   numRecvProto = sizeof(recv_proto) / sizeof(recv_proto[0])
};

volatile uint32_t RCSwitch::nReceivedValue = 0;
volatile unsigned int RCSwitch::nReceivedBitlength = 0;
volatile unsigned int RCSwitch::nReceivedDelay = 0;
volatile unsigned int RCSwitch::nReceivedProtocol = 0;
int RCSwitch::nReceiveTolerance = 60;
const unsigned int RCSwitch::nSeparationLimit = 4300;
// separationLimit: minimum microseconds between received codes, closer codes are ignored.
// according to discussion on issue #14 it might be more suitable to set the separation
// limit to the same time as the '_low' part of the sync signal for the current protocol.
unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES];


RCSwitch::RCSwitch() 
{
  nReceiverInterrupt = -1;
  this->setReceiveTolerance(100);
  RCSwitch::nReceivedValue = 0;
}

/*************************************************************************
 * @fn      setReceiveTolerance
 * @brief   设置接收的容许公差.
 * @param   interrupt - 中断号        
 * @return  none
 *************************************************************************/
void RCSwitch::setReceiveTolerance(int nPercent) {
  RCSwitch::nReceiveTolerance = nPercent;
}

/*************************************************************************
 * @fn      enableReceive
 * @brief   使能接收.
 * @param   interrupt - 中断号        
 * @return  none
 *************************************************************************/
void RCSwitch::enableReceive(uint8_t interrupt) {
    nReceiverInterrupt = interrupt;
    if (nReceiverInterrupt != -1) 
    {
        RCSwitch::nReceivedValue = 0;
        RCSwitch::nReceivedBitlength = 0;
        pinMode(interrupt,input);
        Set_GPIO_irq(nReceiverInterrupt,both_edges_trigger,(int32_t)(handleInterrupt));
    }
}

/*************************************************************************
 * @fn      disableReceive
 * @brief   失能接收.
 * @param   none       
 * @return  none
 *************************************************************************/
void RCSwitch::disableReceive() 
{
    nReceiverInterrupt = -1;
}

/*************************************************************************
 * @fn      available
 * @brief   是否接收到数据.
 * @param   none       
 * @return  true:接收到数据;false:没有接收到数据
 *************************************************************************/
bool RCSwitch::available() 
{
  return RCSwitch::nReceivedValue != 0;
}

/*************************************************************************
 * @fn      resetAvailable
 * @brief   接收到数据标志清零.
 * @param   none       
 * @return  none
 *************************************************************************/
void RCSwitch::resetAvailable() 
{
  RCSwitch::nReceivedValue = 0;
}

/*************************************************************************
 * @fn      getReceivedValue
 * @brief   获取接收到的数据.
 * @param   none       
 * @return  接收到的数据
 *************************************************************************/
uint32_t RCSwitch::getReceivedValue() 
{
  return RCSwitch::nReceivedValue;
}

/*************************************************************************
 * @fn      getReceivedBitlength
 * @brief   获取接收到的数据位数长度.
 * @param   none       
 * @return  数据长度
 *************************************************************************/
unsigned int RCSwitch::getReceivedBitlength() 
{
  return RCSwitch::nReceivedBitlength;
}

/*************************************************************************
 * @fn      getReceivedDelay
 * @brief   获取接收
 * @param   none       
 * @return  none
 *************************************************************************/
unsigned int RCSwitch::getReceivedDelay() 
{
  return RCSwitch::nReceivedDelay;
}

/*************************************************************************
 * @fn      getReceivedProtocol
 * @brief   获取接收
 * @param   none       
 * @return  none
 *************************************************************************/
unsigned int RCSwitch::getReceivedProtocol() 
{
  return RCSwitch::nReceivedProtocol;
}

/*************************************************************************
 * @fn      getReceivedRawdata
 * @brief   获取接收
 * @param   none       
 * @return  none
 *************************************************************************/
unsigned int* RCSwitch::getReceivedRawdata() 
{
  return RCSwitch::timings;
}


/*************************************************************************
 * @fn      diff
 * @brief   获取两个数相减的绝对值
 * @param   none       
 * @return  none
 *************************************************************************/
static inline unsigned int diff(int A, int B) 
{
  return abs(A - B);
}

/*************************************************************************
 * @fn      receiveProtocol
 * @brief   接收处理函数
 * @param   none       
 * @return  none
 *************************************************************************/
bool RCSwitch::receiveProtocol(const int p, unsigned int changeCount) 
{
	const Protocol &pro = recv_proto[p-1];
	uint32_t code = 0;

	//获取同步头中较长的脉宽
	const unsigned int syncLengthInPulses =  ((pro.syncFactor._low) > (pro.syncFactor._high)) ? (pro.syncFactor._low) : (pro.syncFactor._high);
	//得到同步头比值中一份比值的脉宽(比如对于DOOYA为:timings[0]/31)
	volatile unsigned int delay = RCSwitch::timings[0] / syncLengthInPulses;
  //设置容许误差,此处为delay*1
	volatile unsigned int delayTolerance = delay * ((float)RCSwitch::nReceiveTolerance / 100.0);
	//如果pro.invertedSignal为真,则交换所有传输中_high和_low的逻辑。
  const unsigned int firstDataTiming = (pro.invertedSignal) ? (2) : (1);

	for (unsigned int i = firstDataTiming; i < changeCount - 1; i += 2) {
		code <<= 1;
		if (diff(RCSwitch::timings[i], delay * pro.zero._high) < delayTolerance &&
			diff(RCSwitch::timings[i + 1], delay * pro.zero._low) < delayTolerance) {
			// zero

		} else if (diff(RCSwitch::timings[i], delay * pro.one._high) < delayTolerance &&
					diff(RCSwitch::timings[i + 1], delay * pro.one._low) < delayTolerance) {
			// one
			code |= 1;
		} else {
			// Failed
			return false;
		}
	}

	if (changeCount > 7) {    // ignore very short transmissions: no device sends them, so this must be noise
		RCSwitch::nReceivedValue = code;
		RCSwitch::nReceivedBitlength = (changeCount - 1) / 2;
		RCSwitch::nReceivedDelay = delay;
		RCSwitch::nReceivedProtocol = p;
		return true;
	}
	return false;
}


/*************************************************************************
 * @fn      handleInterrupt
 * @brief   接收中断函数
 * @param   none       
 * @return  none
 *************************************************************************/
void RCSwitch::handleInterrupt() 
{
	if(gpio_get_irq_status(nReceiverInterrupt))
	{
		Clear_GPIO_irq(nReceiverInterrupt);
		static unsigned int changeCount = 0;
		static unsigned long lastTime = 0;
		static unsigned int repeatCount = 0;
		const uint64_t tim = micros();
		const unsigned int duration = tim - lastTime;
		

		//长时间没有电平变化
		if (duration > RCSwitch::nSeparationLimit)
		{
			//如果当前这个长信号的长度和上次记录到的长信号值大致接近
			//这代表这个确实可能是同步头或者2个传输之间的间隙
			//发送方需要多次发送该信号,且两者之间间距大致相同
			if ((repeatCount==0) || (diff(duration, RCSwitch::timings[0]) < 200))
			{
				repeatCount++;
				if (repeatCount == 2) {
					for(unsigned int i = 1; i <= numRecvProto; i++) {
					  if (receiveProtocol(i, changeCount)) {
					    break;
					  }
					}
					repeatCount = 0;
				}
			}
      changeCount = 0;  
		}
	
		//数据溢出
		if (changeCount >= RCSWITCH_MAX_CHANGES) 
		{
			changeCount = 0;
			repeatCount = 0;
		}
		RCSwitch::timings[changeCount++] = duration;
		lastTime = tim;  
	}
}

#endif











本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/376727.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TOP100 二叉树(二)

7.108. 将有序数组转换为二叉搜索树 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 示例 1&#xff1a; 输入…

闲聊电脑(6)装个 Windows(二)

闲聊电脑&#xff08;6&#xff09;装个 Windows&#xff08;二&#xff09; 夜深人静&#xff0c;万籁俱寂&#xff0c;老郭趴在电脑桌上打盹&#xff0c;桌子上的小黄鸭和桌子旁的冰箱又开始窃窃私语…… 小黄鸭&#xff1a;冰箱大哥&#xff0c;上次说的镜像文件到底长啥样…

【华为 ICT HCIA eNSP 习题汇总】——题目集13

1、以下在项目规划阶段中需要完成的工作是&#xff08;&#xff09;。 A、确定技术方案 B、了解项目背景 C、选择网络产品 D、规划 IP 地址 考点&#xff1a;网络规划与设计 解析&#xff1a;&#xff08;B&#xff09; 确定技术方案是在网络规划的设计阶段完成的工作&#xff…

Anaconda的安装及其配置

一、简介 Anaconda是一个开源的包、环境管理器&#xff0c;主要具有以下功能和特点&#xff1a; 提供conda包管理工具&#xff1a;可以方便地创建、管理和分享Python环境&#xff0c;用户可以根据自己的需要创建不同的环境&#xff0c;每个环境都可以拥有自己的Python版本、库…

c#cad 创建-直线(五)

运行环境 vs2022 c# cad2016 调试成功 一、代码说明 这段代码是用于在AutoCAD中创建一条直线。首先获取当前活动文档和数据库的引用&#xff0c;然后创建一个编辑器对象用于提示用户输入。接下来&#xff0c;在一个事务中获取模型空间的块表记录&#xff0c;并定义直线的长度…

【Git】06 常用场景

文章目录 前言一、场景11.1 删除分支1.2 修改message信息1.2.1 最新一次commit的message1.2.2 过去commit的message 1.3 合并commit1.3.1 多个连续commit合并1.3.2 不连续commit合并 二、场景22.1 比较暂存区和HEAD所含文件的差异2.2 比较工作区和暂存区所含文件的差异2.3 将暂…

113.乐理基础-五线谱-五线谱的调号(二)

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;五线谱的调号&#xff08;一&#xff09;-CSDN博客 调号一共有15个&#xff1a;如下图 上一个内容里写了&#xff0c;C、D、E、F、G、A、B这七个调号&#xff0c;如下图 然后所有调号的五线谱版本&#xff1a; 然后…

单片机向PC发送数据

#include<reg51.h> //包含单片机寄存器的头文件 unsigned char code Tab[ ]{0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F}; //流水灯控制码&#xff0c;该数组被定义为全局变量 /***************************************************** 函数功能&#xff1a;向PC发送…

html从零开始3-css【搬代码】

css简介 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, in…

代码随想录算法训练营第45天|139.单词拆分、多重背包、背包问题总结

文章目录 139.单词拆分思路代码 多重背包思路代码 背包问题总结思路代码 139.单词拆分 题目链接&#xff1a;139.单词拆分 文章讲解&#xff1a;代码随想录|139.单词拆分 视频讲解&#xff1a;139.单词拆分 思路 按照双指针思路直接想这题更好理解&#xff0c;用动态规划五部曲…

StringBuilder类常用方法(Java)

StringBuilder类常用方法 StringBuilder 是 Java 中常用的字符串缓冲区类&#xff0c;适用于频繁修改字符串的场景。 1. append(): 将指定字符串、字符、布尔值或其他数据类型的表示追加到字符串缓冲区的末尾。 StringBuilder sb new StringBuilder("Hello"); sb.…

《数电》理论笔记-第2章-组合逻辑电路

一&#xff0c;集成门电路 1TTL门电路 TTL门电路中双极型三极管构成,它的特点是速度快、抗静电能力强集成度低、功耗大&#xff0c; 目前广泛应用于中、小规模集成电路中。 TTL门电路有 74 (商用) 和 54 (军用) 两大系列&#xff0c;每个系列中又有若干子系列。 2 CMOS门电路 …

雨云EPYC7702服务器上线了!适合幻兽帕鲁开服的VPS!雨云EPYC7702高防VPS性能测评

雨云游戏云上线了AMD EPYC 7702的VPS服务器&#xff0c;中等水平的单核性能&#xff0c;适合开幻兽帕鲁和我的世界1.17以下版本的服务器。 AMD Epyc 7702是一款64核心128线程&#xff0c;基础频率2.00 GHz加速频率高达3.35 GHz处理器&#xff0c;凭借着7 nm工艺及新一代Rome (…

CopyOnWriteArrayList底层原理全面解析【建议收藏】

简介 CopyOnWriteArrayList是Java中的一个线程安全的集合类&#xff0c;是ArrayList线程安全版本&#xff0c;主要通过Copy-On-Write&#xff08;写时复制&#xff0c;简称COW&#xff09;机制来保证线程安全。 Copy-On-Write机制核心思想&#xff1a;向一个数组中添加数据时…

Android中的MVVM

演变 开发常用的框架包括MVC、MVP和本文的MVVM&#xff0c;三种框架都是为了分离ui界面和处理逻辑而出现的框架模式。mvp、mvvm都由mvc演化而来&#xff0c;他们不属于某种语言的框架&#xff0c;当存在ui页面和逻辑代码时&#xff0c;我们就可以使用这三种模式。 model和vie…

1、卷积分类器

用 Kera 创建你的第一个计算机视觉模型。 数据集下载地址:链接:https://pan.quark.cn/s/f9a1428cf6e3 提取码:XJcv 文章目录 欢迎来到计算机视觉!简介卷积分类器训练分类器示例 - 训练一个卷积分类器步骤1 - 加载数据步骤2 - 定义预训练基步骤3 - 附加头步骤4 - 训练结论欢…

pycharm像jupyter一样在控制台查看后台变量

更新下&#xff1a;这个一劳永逸不用一个一个改 https://blog.csdn.net/Onlyone_1314/article/details/109347481 右上角运行

1Panel面板如何安装并结合内网穿透实现远程访问本地管理界面

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器&#xff0c;包括主机监控、…

牛客网SQL:查询每个日期新用户的次日留存率

官网链接&#xff1a; 牛客每个人最近的登录日期(五)_牛客题霸_牛客网牛客每天有很多人登录&#xff0c;请你统计一下牛客每个日期新用户的次日留存率。 有一个登录(login。题目来自【牛客题霸】https://www.nowcoder.com/practice/ea0c56cd700344b590182aad03cc61b8?tpId82 …

HCIA-HarmonyOS设备开发认证V2.0-3.轻量系统内核基础

目录 一、前言二、LiteOS-M系统概述三、内核框架3.1、CMSIS 和 POSIX 整体架构3.2、LiteOS-M内核启动流程 四、内核基础4.1、任务管理4.2、时间管理(待续)4.3、中断管理(待续)4.4、软件定时器(待续) 五、内存管理5.1、静态内存(待续)5.2、动态内存(待续) 六、内核通信机制6.1、…