红外遥控实现回充原理&红外发射与调制信号_gpio-ir-tx-程序员宅基地

遥控红外通信原理

在实际的通信领域,发出来的信号一般有较宽的频谱,而且都是在比较低的频率段分布大量的能量,所以称之为基带信号,这种信号是不适合直接在信道中传输的。为便于传输、提高抗干扰能力和有效的利用带宽,通常需要将信号调制到适合信道和噪声特性的频率范围内进行传输,这就叫做信号调制。在通信系统的接收端要对接收到的信号进行解调,恢复出原来的基带信号。
我们平时用到的红外遥控器里的红外通信,通常是使用38K左右的载波进行调制的
调制:就是用待传送信号去控制某个高频信号的幅度、相位、频率等参量变化的过程,即用一个信号去装载另一个信号。比如我们的红外遥控信号要发送的时候,先经过38K调制,如图1所示。
在这里插入图片描述
图1 红外信号调制

原始信号就是我们要发送的一个数据“0”位或者一位数据“1”位,而所谓38K载波就是频率为38K的方波信号,调制后信号就是最终我们发射出去的波形。我们使用原始信号来控制38K载波,当信号是数据“0”的时候,38K载波毫无保留的全部发送出去,当信号是数据“1”的时候,不发送任何载波信号。
在这里插入图片描述
图2 红外发射原理图

38K载波,我们可以用455K晶振,经过12分频得到37.91K,也可以由时基电路NE555来产生,或者使用单片机的PWM来产生。当信号输出引脚输出高电平时,Q2截止,不管38K载波信号如何控制Q1,右侧的竖向支路都不会导通,红外管L1不会发送任何信息。当信号输出是低电平的时候,那么38K载波就会通过Q1释放出来,在L1上产生38K的载波信号。这里要说明的是,大多数家电遥控器的38K的占空比是1/3,也有1/2的,但是相对少一些。
正常的通信来讲,接收端要首先对信号通过监测、放大、滤波、解调等等一系列电路处理,然后输出基带信号。但是红外通信的一体化接收头HS0038B,已经把这些电路全部集成到一起了,我们只需要把这个电路接上去,就可以直接输出我们所要的基带信号了
在这里插入图片描述
 图3 红外接收原理图

由于红外接收头内部放大器的增益很大,很容易引起干扰,因此在接收头供电引脚上必须加上滤波电容,官方手册给的值是4.7uF,我们这里直接用的10uF,手册里还要求在供电引脚和电源之间串联100欧的电阻,进一步降低干扰。

图3所示的电路,用来接收图16-5电路发送出来的波形,当HS0038监测到有38K的红外信号时,就会在OUT引脚输出低电平,当没有38K的时候,OUT引脚就会输出高电平。那我们把OUT引脚接到单片机的IO口上,通过编程,就可以获取红外通信发过来的数据了。

大家想想,OUT引脚输出的数据是不是又恢复成为基带信号数据了呢?那我们单片机在接收这个基带信号数据的时候,如何判断接收到的是什么数据,应该遵循什么协议呢?像我们前边学到的UART、I2C、SPI等通信协议都是基带通信的通信协议,而红外的38K仅仅是对基带信号进行调制解调,让信号更适合在信号中传输。

由于我们的红外调制信号是半双工的,而且同时空间只能允许一个信号源,所以我们红外的基带信号不适合在I2C或者SPI通信协议中进行的,我们前边提到过UART虽然是2条线,但是通信的时候,实际上一条线即可,所以红外可以在UART中进行通信。当然,这个通信也不是没有限制的,比如在HS0038B的数据手册中标明,要想让HS0038B识别到38K的红外信号,那么这个38K的载波必须要大于10个周期,这就限定了我们红外通信的基带信号的比特率必须不能高于3800,那如果把串口输出的信号直接用38K调制的话,波特率也就不能高于3800。

常用红外遥控器协议

一、 NEC 协议

特征:

8 位地址和 8 位命令长度为提高可靠性每次传输两遍地址(用户码)和命令(按键值)通过脉冲串之间的时间间隔来实现信号的调制 38Khz 载波每位的周期为 1.12ms 或者 2.25ms
在这里插入图片描述
Note:对于测试红外接收头的信号来说,有脉冲信号的地方就是高电平。即逻辑“1” 为 0.56ms 高电平+1.69ms 低电平,逻辑“0”为 0.56ms 高电平+0.56ms 低电平。
在这里插入图片描述
上图为典型的 NEC 协议传输格式,起始位(引导码)为 9ms 高+4.5ms 低组成,有效数据为地址+地址反码+命令+命令反码。反码的作用是用来校准前面的地址和命令,如果对可靠性不感兴趣,也可以去掉取反的数据,或者将地址和命令扩展到 16 位

上图传输的地址数据为 10011010,需要注意的是先发低位地址再发高位地址,因此该波形的地址为 01011001=0X59,同理,命令为 00010110=0X16。
长按键时,如下图所示,每隔 110ms 重复发送一次,但是命令只发送一次,重复发送的是 9ms 高电平+2.25ms 低电平+0.56ms 高电平+低电平
在这里插入图片描述
  扩展协议:
  在这里插入图片描述
  扩展协议只是将地址改为 16 位,其他不变。

实测波形:

下面的波形是从红外接收头上得到的波形:(调制脉冲信号转变成高低电平了)
  在这里插入图片描述
   由于红外接收头在接收信号时(或者是发送的时候)将波形反向了,因此在读数据时可以将示波器的反向功能打开,就能读到有效数据了。

下面实例是已知 NEC 类型遥控器所截获的波形:

遥控器的识别码是 Address=0xDD20;其中一个键值是 Command=0x0E
  在这里插入图片描述

  rk3308:
MODULE_PARM_DESC 对模块的描述信息
module_param_named  加载模块可修改的参数

查看模块信息:
modinfo  *.ko
parm:(MODULE_PARM_DESC中的信息)

insmod *.ko module_param_named设置的变量=xxx
insmod hello.ko  watchdog=1000

IR发射
compatible = "pwm-ir-tx"
    pwm_ir_probe
        devm_pwm_get
        devm_rc_register_device(rc1)
        device_create_file(&rcdev->dev, &ir_attrs[i])
        
debugfs
    duty_ratio_store duty_ratio_show 
        pwm_ir->duty_cycle
    frequency_store frequency_show
        pwm_ir->carrier
    transmit_store transmit_show
        pwm_ir_tx(rcd, patterns, index);
    
echo 25000 > /sys/class/rc/rc1/frequency 频率
echo 90 > /sys/class/rc/rc1/duty_ratio 占空比
cat /tmp/ir_recv > /sys/class/rc/rc1/transmit 发送

IR接收
compatible = "gpio-ir-receiver"
    gpio_ir_recv_probe
        devm_gpiod_get
        gpiod_to_irq
        devm_rc_register_device(rc0)
        device_create_file(&rcdev->dev, &ir_attrs[i])
        gpio_ir_recv_irq
            gpiod_get_value
            ir_raw_event_store_edge

            
波形   1T=560us 载波频率为:38kHz
1.头码由9ms高电平加4.5ms低电平表示
2.数据 “1” 由1T高电平加3T低电平表示数据“13.数据 “0” 由1T高电平加1T低电平表示数据“0

红外发射与调制信号

在这里插入图片描述

NE555

使用NE555产生38kHz, 占空比为1/3的方波信号。
在这里插入图片描述
产生方波的频率=0.693((RA+2RB)*C)
占空比=RB/(RA+2RB)
因为红外发射管最佳的占空比是1/3,C一般为0.01uf,所以计算之后RA=RB=1.2k

使用三极管来增强红外发射管的发射功率。

在这里插入图片描述
直径3mm 5mm为小功率红外发射管,正向电压: 1.1~1.5V,电流20mA
直径8mm为中功率红外发射管,正向电压: 1.4~1.65V,电流50~100mA
直径10mm为大功率红外发射管,正向电压: 1.51.9V,电流200350mA

以为中功率红外发射管为例:基极电流IB=(5v-0.7v-1.4v)/R1=2.9v/R1
三极管β为30,则发射极流过红外发射管的电流为(1+β)*IB=89.9mA
在这里插入图片描述
正常情况下IR一体化接收头未收到38kHz的调制信号时为管脚信号为高电平,收到38kHz的调制信号时管脚信号为低电平。

TIM3 PWM输出 4路

一、设置TIM3的GPIO为推挽输出
void TIM3_IOConfig(void)
{
    
        GPIO_InitTypeDef  GPIO_InitStructure;
  
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);

        //这里TIM3的通道1是GPIOA_Pin_6,通道2是GPIOA_Pin_7;通道3是GPIOB_Pin_0;
        //这里TIM3的通道4是GPIOB_Pin_1;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        //使用PWM的功能需要设置成AF_PP模式
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
        GPIO_Init(GPIOA,&GPIO_InitStructure);
        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
        GPIO_Init(GPIOB,&GPIO_InitStructure);
}
 

二、设置TIM3PWM1方式4路输出
//arr:自动重装值 psc:时钟预分频数
//定时器溢出时间计算:Tout=((arr+1)*(psc+1))/Ft   
void TIM3_PWM_Init(void)
{
    
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStructure;//TIM定时器的配置
        TIM_OCInitTypeDef        TIM_OCInitStructure;//TIMPWM的
        
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
        //使能TIM3的时钟线;
        
        //复位定时器
        TIM_DeInit(TIM3);
    
        //TIM_Prescaler:72,TIM_Period:20000周期为20ms
        TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;                        //定时器周期
        TIM_TimeBaseInitStructure.TIM_Period = 20000-1;                        //定时器更新的重装载值0-65535
        TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
        TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
        TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);            //初始化TIM3      
        
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
        TIM_OCInitStructure.TIM_Pulse = 1000;                                //预设值
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;    
        
        TIM_OC1Init(TIM3,&TIM_OCInitStructure);
        TIM_OC2Init(TIM3,&TIM_OCInitStructure);
        TIM_OC3Init(TIM3,&TIM_OCInitStructure);
        TIM_OC4Init(TIM3,&TIM_OCInitStructure);

        TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);
        TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
        TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);
        TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);
      
        TIM_CtrlPWMOutputs(TIM3, ENABLE);
//        TIM_ITConfig(TIM3,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);
        //使能标志位CC1,CC2,CC3,CC4
        TIM_Cmd(TIM3,ENABLE);
        //使能TIM;
}
上面的TIM周期是20ms

三、修改PWM的方法

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

STM32定时器初始化完成后修改频率问题

问题代码
void timer_PWM_init(TIM_TypeDef *TIMx, uint32_t tim_ch)
{
    
   LL_TIM_InitTypeDef TIM_InitStruct = {
    0};
   timer_clk_irq_config(TIMx, 0);
   TIM_InitStruct.Prescaler = 71;
    TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
    TIM_InitStruct.Autoreload = 999;
    TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
    LL_TIM_Init(TIMx, &TIM_InitStruct);
    
    LL_TIM_DisableARRPreload(TIMx);
    LL_TIM_OC_EnablePreload(TIMx, tim_ch);
    TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
    TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
    TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
    TIM_OC_InitStruct.CompareValue = 500;
    TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
    
    LL_TIM_OC_Init(TIMx, tim_ch, &TIM_OC_InitStruct);

    LL_TIM_OC_DisableFast(TIMx, tim_ch);
    LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_RESET);
    LL_TIM_DisableMasterSlaveMode(TIMx);

    timer_io_init();
}
void timer_PWM_freq_set(TIM_TypeDef *TIMx, uint32_t tim_ch, uint16_t freq)
{
    
	LL_TIM_DisableCounter(TIMx);
	LL_TIM_SetPrescaler(TIMx, 7200-1);	// 10KHz
	LL_TIM_SetAutoReload(TIMx, (uint16_t)((10000/freq))-1);
}

问题现象:
调用:timer_PWM_freq_set(TIM3, LL_TIM_CHANNEL_CH4, 1000)
PWM没问题,1KHz,占空比50%
但是,调用:timer_PWM_freq_set(TIM3,LL_TIM_CHANNEL_CH4,100)
出问题了,PWM一直是高定平。

问题解决

因为初始化PWM的频率为1K,计数周期为500,占空比为50%
但是在后面修改频率后并未修改计数周期,也就是频率为100HZ,计数周期还是500,计数周期大于频率了,于是就出现一直高电平的现象。(个人分析的,不知正确与否,应该是这样的。。。。)

解决方法:
修改频率的同时也要修改计数周期,要保证计数周期小于频率,不然占空比比周期还长,自然就一直高电平或者低电平了
代码修改

**
  * @brief  设置PWM输出频率
  * @param  fre取值范围(1-10000)Hz
  * @retval 设置完成频率之后不启动定时器
  */
void timer_PWM_freq_set(TIM_TypeDef *TIMx, uint32_t tim_ch, uint16_t freq)
{
    
	LL_TIM_DisableCounter(TIMx);
	LL_TIM_SetPrescaler(TIMx, 7200-1);	// 10KHz
	LL_TIM_SetAutoReload(TIMx, (uint16_t)((10000/freq))-1);
	LL_TIM_OC_SetCompareCH4(TIMx, (uint16_t)((10000/freq)/2))
}

抄写与 https://blog.csdn.net/yangyang_1024/article/details/82999694?ops_request_misc=&request_id=&biz_id=102&utm_term=红外信号&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-82999694

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44547715/article/details/107404402

智能推荐

JavaScript学习笔记_curry函数未定义-程序员宅基地

文章浏览阅读343次。五种原始的变量类型1.Undefined--未定义类型 例:var v;2.String -- ' '或" "3.Boolean4.Number5.Null--空类型 例: var v=null;Number中:NaN -- not a number非数本身是一个数字,但是它和任何数字都不相等,代表非数,它和自己都不相等判断是不是NaN不能用=_curry函数未定义

兑换码编码方案实践_优惠券编码规则-程序员宅基地

文章浏览阅读1.2w次,点赞2次,收藏17次。兑换码编码设计当前各个业务系统,只要涉及到产品销售,就离不开大大小小的运营活动需求,其中最普遍的就是兑换码需求,无论是线下活动或者是线上活动,都能起到良好的宣传效果。兑换码:由一系列字符组成,每一个兑换码对应系统中的一组信息,可以是优惠信息(优惠券),也可以是相关奖品信息。在实际的运营活动中,要求兑换码是唯一的,每一个兑换码对应一个优惠信息,而且需求量往往比较大(实际上的需求只有预期_优惠券编码规则

c语言周林答案,C语言程序设计实训教程教学课件作者周林ch04结构化程序设计课件.ppt...-程序员宅基地

文章浏览阅读45次。C语言程序设计实训教程教学课件作者周林ch04结构化程序设计课件.ppt* * 4.1 选择结构程序设计 4.2 循环结构程序设计 4.3 辅助控制语句 第四章 结构化程序设计 4.1 选择结构程序设计 在现实生活中,需要进行判断和选择的情况是很多的: 如果你在家,我去拜访你 如果考试不及格,要补考 如果遇到红灯,要停车等待 第四章 结构化程序设计 在现实生活中,需要进行判断和选择的情况..._在现实生活中遇到过条件判断的问

幻数使用说明_ioctl-number.txt幻数说明-程序员宅基地

文章浏览阅读999次。幻数使用说明 在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情。 因为设备都是特定的,这里也没法说。关键在于怎样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径 。 命令码的组织是有一些讲究的,因为我们一定要做到命令和设备是一一对应的,利_ioctl-number.txt幻数说明

ORB-SLAM3 + VScode:检测到 #include 错误。请更新 includePath。已为此翻译单元禁用波浪曲线_orb-slam3 include <system.h> 报错-程序员宅基地

文章浏览阅读399次。键盘按下“Shift+Ctrl+p” 输入: C++Configurations,选择JSON界面做如下改动:1.首先把 “/usr/include”,放在最前2.查看C++路径,终端输入gcc -v -E -x c++ - /usr/include/c++/5 /usr/include/x86_64-linux-gnu/c++/5 /usr/include/c++/5/backward /usr/lib/gcc/x86_64-linux-gnu/5/include /usr/local/_orb-slam3 include 报错

「Sqlserver」数据分析师有理由爱Sqlserver之十-Sqlserver自动化篇-程序员宅基地

文章浏览阅读129次。本系列的最后一篇,因未有精力写更多的入门教程,上篇已经抛出书单,有兴趣的朋友可阅读好书来成长,此系列主讲有理由爱Sqlserver的论证性文章,希望读者们看完后,可自行做出判断,Sqlserver是否真的合适自己,目的已达成。渴望自动化及使用场景笔者所最能接触到的群体为Excel、PowerBI用户群体,在Excel中,我们知道可以使用VBA、VSTO来给Excel带来自动化操作..._sqlsever 数据分析

随便推点

智慧校园智慧教育大数据平台(教育大脑)项目建设方案PPT_高校智慧大脑-程序员宅基地

文章浏览阅读294次,点赞6次,收藏4次。教育智脑)建立学校的全连接中台,对学校运营过程中的数据进行处理和标准化管理,挖掘数据的价值。能:一、原先孤立的系统聚合到一个统一的平台,实现单点登录,统一身份认证,方便管理;三、数据共享,盘活了教育大数据资源,通过对外提供数。的方式构建教育的通用服务能力平台,支撑教育核心服务能力的沉淀和共享。物联网将学校的各要素(人、机、料、法、环、测)全面互联,数据实时。智慧校园解决方案,赋能教学、管理和服务升级,智慧教育体系,该数据平台具有以下几大功。教育大数据平台底座:教育智脑。教育大数据平台,以中国联通。_高校智慧大脑

编程5大算法总结--概念加实例_算法概念实例-程序员宅基地

文章浏览阅读9.5k次,点赞2次,收藏27次。分治法,动态规划法,贪心算法这三者之间有类似之处,比如都需要将问题划分为一个个子问题,然后通过解决这些子问题来解决最终问题。但其实这三者之间的区别还是蛮大的。贪心是则可看成是链式结构回溯和分支界限为穷举式的搜索,其思想的差异是深度优先和广度优先一:分治算法一、基本概念在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两_算法概念实例

随笔—醒悟篇之考研调剂_考研调剂抑郁-程序员宅基地

文章浏览阅读5.6k次。考研篇emmmmm,这是我随笔篇章的第二更,原本计划是在中秋放假期间写好的,但是放假的时候被安排写一下单例模式,做了俩机试题目,还刷了下PAT的东西,emmmmm,最主要的还是因为我浪的很开心,没空出时间来写写东西。  距离我考研结束已经快两年了,距离今年的考研还有90天左右。  趁着这个机会回忆一下青春,这一篇会写的比较有趣,好玩,纯粹是为了记录一下当年考研中发生的有趣的事。  首先介绍..._考研调剂抑郁

SpringMVC_class org.springframework.web.filter.characterenco-程序员宅基地

文章浏览阅读438次。SpringMVC文章目录SpringMVC1、SpringMVC简介1.1 什么是MVC1.2 什么是SpringMVC1.3 SpringMVC的特点2、HelloWorld2.1 开发环境2.2 创建maven工程a>添加web模块b>打包方式:warc>引入依赖2.3 配置web.xml2.4 创建请求控制器2.5 创建SpringMVC的配置文件2.6 测试Helloworld2.7 总结3、@RequestMapping注解3.1 @RequestMapping注解的功能3._class org.springframework.web.filter.characterencodingfilter is not a jakart

gdb: Don‘t know how to run. Try “help target“._don't know how to run. try "help target".-程序员宅基地

文章浏览阅读4.9k次。gdb 远程调试的一个问题:Don't know how to run. Try "help target".它在抱怨不知道怎么跑,目标是什么. 你需要为它指定target remote 或target extended-remote例如:target extended-remote 192.168.1.136:1234指明target 是某IP的某端口完整示例如下:targ..._don't know how to run. try "help target".

c语言程序设计教程 郭浩志,C语言程序设计教程答案杨路明郭浩志-程序员宅基地

文章浏览阅读85次。习题 11、算法描述主要是用两种基本方法:第一是自然语言描述,第二是使用专用工具进行算法描述2、c 语言程序的结构如下:1、c 语言程序由函数组成,每个程序必须具有一个 main 函数作为程序的主控函数。2、“/*“与“*/“之间的内容构成 c 语言程序的注释部分。3、用预处理命令#include 可以包含有关文件的信息。4、大小写字母在 c 语言中是有区别的。5、除 main 函数和标准库函数以..._c语言语法0x1e