2024年红外遥控学习,万能遥控解决方案_红外学习电路,大厂内部资料

总结

三个工作日收到了offer,头条面试体验还是很棒的,这次的头条面试好像每面技术都问了我算法,然后就是中间件、MySQL、Redis、Kafka、网络等等。

  • 第一个是算法

关于算法,我觉得最好的是刷题,作死的刷的,多做多练习,加上自己的理解,还是比较容易拿下的。

而且,我貌似是将《算法刷题LeetCode中文版》、《算法的乐趣》大概都过了一遍,尤其是这本

《算法刷题LeetCode中文版》总共有15个章节:编程技巧、线性表、字符串、栈和队列、树、排序、查找、暴力枚举法、广度优先搜索、深度优先搜索、分治法、贪心法、动态规划、图、细节实现题

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

《算法的乐趣》共有23个章节:

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

  • 第二个是Redis、MySQL、kafka(给大家看下我都有哪些复习笔记)

基本上都是面试真题解析、笔记和学习大纲图,感觉复习也就需要这些吧(个人意见)

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

  • 第三个是网络(给大家看一本我之前得到的《JAVA核心知识整理》包括30个章节分类,这本283页的JAVA核心知识整理还是很不错的,一次性总结了30个分享的大知识点)

最新出炉,头条三面技术四面HR,看我如何一步一步攻克面试官?

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

1. 原理

目前电视机、空调等家电大部分还是采用的红外遥控的,有时项目需要把遥控嵌入到自己的设备中,或者又是物联网需要控制家电,此时就需要智能学习和发送了,红外遥控电路图如下:

在这里插入图片描述
左侧为红外发送电路,右侧有红外接收电路。

● 发送端:

以NEC协议为例(实际测试中遵循NEC协议的不多),信息传输是基于38K载波,也就是说红外线是以载波的方式传递。

发送协议数据“0” = 发送载波560us + 不发送载波560us

发送协议数据“1” = 发送载波560us+ 不发送载波1680us

发送的波形如下图所示,下图中为 0 0 0 0 1 0 1
在这里插入图片描述
● 接收端:

在红外接收端,如果接收到红外38K载波,则IR输出为低电平,如果不是载波包括固定低电平和固定高电平则输出高电平。在IR端接收的信号如下所示:
在这里插入图片描述
NEC协议规定:

发送协议数据“0” = 发送载波560us + 不发送载波560us

发送协议数据“1” = 发送载波560us+ 不发送载波1680us

发送引导码 = 发送载波9000us + 不发送载波4500us

● 总结:

1、接收端收到38K载波脉冲为低电平,没有则为高电平;所以在设计发送的时候,发送低电平应该开启38KHZ的PWM,发送高电平则关闭PWM(默认为高电平)

2、数据“0” 和数据“1” 是由接收到的一个高电平和一个低电平组合而来,一般来说高电平时间等于低电平时间为数据0,其它则为1

参考: STM32之红外遥控信号自学习实现

2. 思路

思路简单,简述为: 捕获——>保存——>发射

先捕获一个红外遥控按键的全部信息,不管内容是什么,再保存到flash中,就是多个高低电平的持续时间,然后再一一发射出去。这样不管是什么协议都能成功。

3. 红外遥控接收

3.1 初始化定时器

具体见代码和详细的注释:

// ir\_inputCapture\_send.c
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
 \* @brief 定时器16输入捕获初始化/红外遥控初始化、设置IO以及TIM16\_CH1的输入捕获、10ms溢出
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
void TIM16\_Remote\_Init(void)
{  
    TIM_IC_InitTypeDef TIM16_CH1Config;  
    
    TIM16_Handler.Instance = TIM16;                          			//通用定时器16
    TIM16_Handler.Init.Prescaler=(48-1);                	 			//预分频器,1M的计数频率,1us加1.
    TIM16_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;      			//向上计数器
    TIM16_Handler.Init.Period = (60000-1);                      		//自动装载值,16位最大65536,此处设置60ms的计时
    TIM16_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;			//时钟分频因子
    HAL\_TIM\_IC\_Init(&TIM16_Handler);
    
    //初始化TIM1输入捕获参数
    TIM16_CH1Config.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;    	//注意下降沿捕获 
    TIM16_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI;				//映射到TI4上
    TIM16_CH1Config.ICPrescaler=TIM_ICPSC_DIV1;          				//配置输入分频,不分频
    TIM16_CH1Config.ICFilter=0x03;  // 0 //IC4F=0003 8个定时器时钟周期滤波
    HAL\_TIM\_IC\_ConfigChannel(&TIM16_Handler, &TIM16_CH1Config, TIM_CHANNEL_1);//配置TIM4通道4
    \_\_HAL\_TIM\_ENABLE\_IT(&TIM16_Handler,TIM_IT_UPDATE);   				//使能更新中断 
    HAL\_TIM\_IC\_Stop\_IT(&TIM16_Handler,TIM_CHANNEL_1);
}

// stm32f0xx\_hal\_msp.c.c
//定时器16底层驱动,时钟使能,引脚配置
//此函数会被上述的HAL\_TIM\_IC\_Init()调用
//htim:定时器句柄
void HAL\_TIM\_IC\_MspInit(TIM_HandleTypeDef \*htim)
{
    GPIO_InitTypeDef   GPIO_InitStruct;
    if(htim->Instance==TIM16)
    {
        /\*\*\*\*\*\*\*\*\*\*\*\*TIM16\_CH1\*\*\*\*\*\*\*\*\*\*\*\*/
        GPIO_InitTypeDef GPIO_Initure;
        \_\_HAL\_RCC\_TIM16\_CLK\_ENABLE();            			//使能TIM16时钟
        \_\_HAL\_RCC\_GPIOA\_CLK\_ENABLE();			 			//开启GPIOA时钟
        
        GPIO_Initure.Pin =          GPIO_PIN_6;            	//PB9
		GPIO_Initure.Mode =         GPIO_MODE_AF_PP;  		//复用输入
        GPIO_Initure.Pull =         GPIO_PULLUP;           	//上拉
        GPIO_Initure.Speed =        GPIO_SPEED_FREQ_HIGH; 	//高速
        GPIO_Initure.Alternate =    GPIO_AF5_TIM16;
        HAL\_GPIO\_Init(GPIOA,&GPIO_Initure);
        HAL\_NVIC\_SetPriority(TIM16_IRQn,2,0); 				//设置中断优先级,抢占优先级2,子优先级0
        HAL\_NVIC\_EnableIRQ(TIM16_IRQn);       				//开启ITM16中断
    }
}

3.2 定时器输入捕获

红外编码是由多个字节编码组成的,根据持续的高低电平时间不同,而形成的编码信号,此时可以通过定时器捕获来实现。

定时器输入捕获中断回调函数如下,红外信号基本都是低电平起,可先用下降沿捕获:

// ir\_inputCapture\_send.c
void HAL\_TIM\_IC\_CaptureCallback(TIM_HandleTypeDef \*htim)//捕获中断发生时执行
{
	if(htim->Instance==TIM16)     
	{
		if(READ\_IR\_GPIO())  														        //上升沿捕获 
		{
            Dval = HAL\_TIM\_ReadCapturedValue(&TIM16_Handler, TIM_CHANNEL_1);                //读取CCR1也可以清CC4IF标志位
			TIM\_RESET\_CAPTUREPOLARITY(&TIM16_Handler, TIM_CHANNEL_1);   					//一定要先清除原来的设置!!
			TIM\_SET\_CAPTUREPOLARITY(&TIM16_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //CC1P=1 设置为下降沿捕获
            
            if(0 == irRun.receive.cnt)    { 
                \_\_HAL\_TIM\_SET\_COUNTER(&TIM16_Handler,0);  						//清空定时器值 
                return ;
            } 
            
            if(irRun.receive.cnt<(MAX_IR_LEARN_DATA_SIZE - 1))  {  				// 保存在数组中的 0 2 4 6 8 中
                irRun.buf[irRun.receive.cnt++] = Dval;
            } 
            else {                                  
                RcvRemoteFinish();
            }
            \_\_HAL\_TIM\_SET\_COUNTER(&TIM16_Handler,0);  	//清空定时器值 
		}
        else //下降沿捕获 
        {
			Dval = HAL\_TIM\_ReadCapturedValue(&TIM16_Handler, TIM_CHANNEL_1);
			TIM\_RESET\_CAPTUREPOLARITY(&TIM16_Handler,TIM_CHANNEL_1);       
			TIM\_SET\_CAPTUREPOLARITY(&TIM16_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);	//配置TIM16通道1上升沿捕获
            
            //当前这一次,不计数,下一次上升沿才计数
            if(0 == irRun.receive.cnt)    {  
                \_\_HAL\_TIM\_SET\_COUNTER(&TIM16_Handler,0);  		    			//清空定时器值 
                return;
            } 

            if(irRun.receive.cnt < (MAX_IR_LEARN_DATA_SIZE-1))   				//1 3 5 7 9
            {
                irRun.buf[irRun.receive.cnt++] = Dval;
                if(irRun.receive.cnt>=20) {
                    irRun.receive.dataComplete = 1;						     
                }
            }
            else 
            {
                RcvRemoteFinish();
            }
            \_\_HAL\_TIM\_SET\_COUNTER(&TIM16_Handler,0);  		    		//清空定时器 放最后
		}
	}
}

// ir\_inputCapture\_send.c
//60m定时器计时后溢出中断的回调函数:
void HAL\_TIM\_PeriodElapsedCallback(TIM_HandleTypeDef \*htim)
{
	if(htim->Instance==TIM16)  						//定时器更新(溢出)中断回调函数 10ms溢出
	{  
 		if(irRun.receive.dataComplete)				//至少已经收到了20个字节,才算接收到一帧信息
		{	
            RcvRemoteFinish();
		}	
        else {
            irRun.receive.cnt = 0;					//重新来过,此帧数据无效
        }
	}
}

// ir\_inputCapture\_send.c
/\* 当前红外遥控一帧结束。
 触发条件:① 接收到的红外字节个数已经超过最大允许的长度 (HAL\_TIM\_IC\_CaptureCallback)
 ② 如上述的定时器计时后溢出中断的回调函数 (HAL\_TIM\_PeriodElapsedCallback)
\*/
static void RcvRemoteFinish(void)
{
    //1. 先失能 避免重洗掉前面保存的一帧完整数据
    TIM16\_Remote\_Disable();

    //2. 重新设置下降沿捕获,以防上升沿捕获的
    TIM\_SET\_CAPTUREPOLARITY(&TIM16_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //CC1P=1 设置为下降沿捕获

    //3-1. 清零数据存在的标志
    irRun.receive.dataComplete = 0; //先清零

    //4-2. (多等待了60ms),不管如何都算完成了
    irRun.receive.complete = 1;    // irRun.receive.complete 为一帧接收的完整标志
                
    irRun.receive.len = irRun.receive.cnt - 1;   //一帧接收的最大长度,irRun.receive.cnt 多了1个 
    
	//清零接收长度 缓冲数据
    irRun.receive.cnt = 0;    
}

至此为止 数据保存在 irRun.buf 中, 个数为 irRun.receive.len

3.3 获取数据

具体见代码和详细的注释:

// 获取一帧数据在上层调用
// 返回值: 0, 没有任何按键按下
// 1 ,按下的按键键值. 
// ir\_inputCapture\_send.c
u8 GetRemoteRcvBuf(uint16\_t \*buf, uint32\_t \*len, uint8\_t isRecord)
{        
    if(irRun.receive.complete && !irRun.receive.dataComplete) { //完成一帧的标记, 且现在没有正在捕获的信号
        TIM\_SET\_CAPTUREPOLARITY(&TIM16_Handler, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //CC1P=1 设置为下降沿捕获
        
        //最多拷贝MAX\_IR\_LEARN\_DATA\_SIZE个, 第二次保护,前有一次保护措施 
        if(irRun.receive.len >= MAX_IR_LEARN_DATA_SIZE-1) 
           irRun.receive.len = MAX_IR_LEARN_DATA_SIZE-1; 
        if(irRun.receive.len >= GetFuncLen() && GetFuncLen()>20 && isRecord) {
            irRun.receive.len = GetFuncLen();


## 总结

至此,文章终于到了尾声。总结一下,我们谈论了简历制作过程中需要注意的以下三个部分,并分别给出了一些建议:

1.  技术能力:先写岗位所需能力,再写加分能力,不要写无关能力;
2.  项目经历:只写明星项目,描述遵循 STAR 法则;
3.  简历印象:简历遵循三大原则:清晰,简短,必要,要有的放矢,不要海投;

> 以及最后为大家准备的福利时间:简历模板+Java面试题+热门技术系列教程视频

![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/b7d76945bb193f1c25a68df9e5ee86cc.webp?x-oss-process=image/format,png)

![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/11936895be0f437413979acdf320c971.webp?x-oss-process=image/format,png)

![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/65b610f6a400ca471a5a7db2c6d6487d.webp?x-oss-process=image/format,png)

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

(img-HI2I2ub3-1715267539063)]

[外链图片转存中...(img-RQhqpYIZ-1715267539063)]

[外链图片转存中...(img-wE0KfXXn-1715267539063)]

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值