前言

本文带你无前提条件,从零模仿破解美的RN02D/BG型号空调遥控器发送原理。

1.教程的直接目的:使用stm32+远红外发射管开启空调。
2.包含学习知识:通过本文你可以了解stm32hal库使用定时器提供us级延迟方法,使用定时器pwm功能方法,美的空调驱动的编写思想。以及一些硬件相关的介绍。

开始!

1基础入手

首先我们要清晰我们的最终目的是模仿空调的红外头,发出一样的波形来骗过空调接收,空调只是忠实的执行监测程序,一样的波形没有不接受的道理。如果没反应说明你波形还是有问题。

作者拆开了遥控器,简单查看了一下,如图。

背板布线还是挺漂亮的,mcu部分点了较,也可以做到防护作用。直接掏出逻辑分析仪!简单试了试发现应该是红外二极管的阴极电平会变化,阳极直接接到vcc了。这样我们就可以抓到一些波形了。
通过查找网上信息,这种红外的编码一般都有帧头帧尾和中间的数据位等部分。但实际网上的协议时序与本品牌的遥控器并不能对上,只是类似。所以只能自己观察,扒一下协议。

2详细分析&注意事项

2.1软件部分

这里主要参考这个博主的文章,可以说协议格式是相同的,但是细节是不同的,没关系,逻辑分析仪抓好自己慢慢数一数。
通过波形可以看出,如果我们想模拟遥控器,我们应当模拟一个载波,然后再想办法根据协议控制载波的开关时间。

额外插一句:载波是固定评率的波形,然后他的开启与关闭时间不同构成了01的区别,这其实是编码方式,对编码方式感兴趣的同学可以搜索m2编码或者米勒编码学习。顺便博主最近在研究计网。这些都是属于物理层的协议哦!不是简单的高电平表示1 低电平表示0哦!

这里要注意的是,没有必要开启定时器频繁进入中断开关io电平,以达到模拟载波的目的。因为stm32已经给我们提供了定时器的pwm模式。这里通过cubemx开启一个定时器,设置好他的pwm模式如图。这里使用的定时器16只有一个通道ch1 可以复用a6引脚,目前我们的小熊派上a6并没有使用,那就用他作为输出吧。本文我是用的是基于stm32l431rct6芯片的小熊派开发板。cubemx开启tim16,参数的设置如图,注意!我的定时器总线时钟设置为最高的80Mhz,这里预分频系数选择80-1 = 79,这样tim16的时钟频率就是1m,也就是每1us一个时钟。这样方便计算。下面两个箭头指示的值设置为26与8,
这样我们就可以获得一个周期是26us,其中9us是高电平的pwm载波波形了!

其实cubemx帮我们借助上面的图形化界面生成了三个主要函数:
void MX_TIM16_Init(void);
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle);
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle);

注意事项1:tim使用时,或者说cubemx使用时,我们必须清醒的认识到它帮助我们生成的函数其实包括两种:一种是MX_xxinit这种函数,是他帮你整理好的函数,你可以随意改动,因为只是里面调用的hal库函数,名字无所谓。另外一种是如msp函数。此类函数名字是不可以修改的,因为他是一个_weak 修饰的函数,hal规定了必须叫这个名字。cubemx帮我们强实现了,这个回调函数乱改名字hal是不承认的,他会继续掉_weak 函数用来避免编译错误,但其实什么也不做。

补充一点基础知识,回到注意事项,cubemx只会帮你做好一定的初始化任务,他不会帮你跑起来,所以如果你的定时器没有跑起来,请检查你是否使用了start函数。不同的功能需要采用不同的start函数
注意事项2:tim的pwm功能使用时要注意配置好正确的参数。请务必深刻理解预分频系数,重载值等概念。否则你得不到合适的pwm波。

下一步我们需要在开一个定时器用来实现us级别的延时。这里开启tim2定时器。同样给出cubemx的配置截图:

此外我们要在tim.c文件中实现一个us级别的延时供上层使用。这里一并贴出tim.c文件全部代码。
注意事项3:别忘记在tim头文件中声明延时函数哦!

/* USER CODE BEGIN Header */
/********************************************************************************* @file    tim.c* @brief   This file provides code for the configuration*          of the TIM instances.******************************************************************************* @attention** Copyright (c) 2022 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "tim.h"/* USER CODE BEGIN 0 */
#include <stdio.h>
int timecnt = 0;
/* USER CODE END 0 */TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim16;/* TIM2 init function */
void MX_TIM2_Init(void)
{/* USER CODE BEGIN TIM2_Init 0 *//* USER CODE END TIM2_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};/* USER CODE BEGIN TIM2_Init 1 *//* USER CODE END TIM2_Init 1 */htim2.Instance = TIM2;htim2.Init.Prescaler = 79;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 65535;htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim2) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM2_Init 2 */HAL_TIM_Base_Start_IT(&htim2);                     //重点关注 这块不加使能中断就不会跑起来/* USER CODE END TIM2_Init 2 */}
/* TIM16 init function */
void MX_TIM16_Init(void)
{/* USER CODE BEGIN TIM16_Init 0 *//* USER CODE END TIM16_Init 0 */TIM_OC_InitTypeDef sConfigOC = {0};TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};/* USER CODE BEGIN TIM16_Init 1 *//* USER CODE END TIM16_Init 1 */htim16.Instance = TIM16;htim16.Init.Prescaler = 79;htim16.Init.CounterMode = TIM_COUNTERMODE_UP;htim16.Init.Period = 25;htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim16.Init.RepetitionCounter = 0;htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim16) != HAL_OK){Error_Handler();}if (HAL_TIM_PWM_Init(&htim16) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 8;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;if (HAL_TIM_PWM_ConfigChannel(&htim16, &sConfigOC, TIM_CHANNEL_1) != HAL_OK){Error_Handler();}sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;sBreakDeadTimeConfig.DeadTime = 0;sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;if (HAL_TIMEx_ConfigBreakDeadTime(&htim16, &sBreakDeadTimeConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM16_Init 2 *//* USER CODE END TIM16_Init 2 */HAL_TIM_MspPostInit(&htim16);}void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM2){/* USER CODE BEGIN TIM2_MspInit 0 *//* USER CODE END TIM2_MspInit 0 *//* TIM2 clock enable */__HAL_RCC_TIM2_CLK_ENABLE();/* TIM2 interrupt Init *///HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);//HAL_NVIC_EnableIRQ(TIM2_IRQn);/* USER CODE BEGIN TIM2_MspInit 1 *//* USER CODE END TIM2_MspInit 1 */}else if(tim_baseHandle->Instance==TIM16){/* USER CODE BEGIN TIM16_MspInit 0 *//* USER CODE END TIM16_MspInit 0 *//* TIM16 clock enable */__HAL_RCC_TIM16_CLK_ENABLE();/* USER CODE BEGIN TIM16_MspInit 1 *//* USER CODE END TIM16_MspInit 1 */}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(timHandle->Instance==TIM16){/* USER CODE BEGIN TIM16_MspPostInit 0 *//* USER CODE END TIM16_MspPostInit 0 */__HAL_RCC_GPIOA_CLK_ENABLE();/**TIM16 GPIO ConfigurationPA6     ------> TIM16_CH1*/GPIO_InitStruct.Pin = GPIO_PIN_6;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Alternate = GPIO_AF14_TIM16;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USER CODE BEGIN TIM16_MspPostInit 1 *//* USER CODE END TIM16_MspPostInit 1 */}}void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM2){/* USER CODE BEGIN TIM2_MspDeInit 0 *//* USER CODE END TIM2_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_TIM2_CLK_DISABLE();/* TIM2 interrupt Deinit */HAL_NVIC_DisableIRQ(TIM2_IRQn);/* USER CODE BEGIN TIM2_MspDeInit 1 *//* USER CODE END TIM2_MspDeInit 1 */}else if(tim_baseHandle->Instance==TIM16){/* USER CODE BEGIN TIM16_MspDeInit 0 *//* USER CODE END TIM16_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_TIM16_CLK_DISABLE();/* USER CODE BEGIN TIM16_MspDeInit 1 *//* USER CODE END TIM16_MspDeInit 1 */}
}/* USER CODE BEGIN 1 */
void delayXus(uint16_t us){uint16_t differ=0xffff-us-5;                    //设定定时器计数器起始值__HAL_TIM_SET_COUNTER(&htim2,differ);HAL_TIM_Base_Start(&htim2);                   //启动定时器while(differ<0xffff-6)                            //补偿,判断{differ=__HAL_TIM_GET_COUNTER(&htim2);           //查询计数器的计数值}HAL_TIM_Base_Stop(&htim2);}
/* USER CODE END 1 */

好了!现在我们的基础已经搭好了,我们开始进入驱动编写过程。首先,我们可以把一次要发出的波形分成帧头 帧尾 帧分割 数据位四种,每种其实都是一段载波加一段低电平构成。
所以可以先把他们先写出来。另外三个只需要写死时间即可。但是数据位要区分写1还是0,所以writebit应该具有一个参数,我们得到了一个写bit位的函数以后肯定要继续封装,用来写1byte。其实整个协议就是帧头 六个字节 帧尾 分割帧 帧头 六个重复的字节 帧尾构成。所以我们可以继续封装一层,这样只需要提供一个数组就可以执行一次按键动作了。
talk is cheap show my code
ir_air.c文件代码


#include <stdio.h>
#include "tim.h"
#include "main.h"
#include "ir_air.h"//发送一段载波
void ir_send_single(uint16_t hightime,uint16_t lowtime)
{//这里默认你的对应tim16是可用的,所以请先调试好对应定时器的pwm功能HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);delayXus(hightime);HAL_TIM_PWM_Stop(&htim16,TIM_CHANNEL_1);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_RESET);delayXus(lowtime);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_SET);
}
//发送消息头命令
void ir_send_head(void)
{       uint16_t hightime = 4360;uint16_t lowtime = 4460;ir_send_single(hightime,lowtime);
}
//发送消息尾部命令
void ir_send_tail(void)
{       uint16_t hightime = 477;uint16_t lowtime = 0;ir_send_single(hightime,lowtime);
}
//发送重复消息分割符号命令
void ir_send_cut(void)
{       uint16_t hightime = 477;uint16_t lowtime = 5277;ir_send_single(hightime,lowtime);
}
//发送1bit
uint8_t ir_send_bit(uint16_t value)
{   uint8_t ret = 0;uint16_t hightime;uint16_t lowtime;if(0x00 == value){        hightime =478;//写0,载波478us,低电平600uslowtime = 600;}else if(0x01 == value){         hightime =478;//写1,载波478us,低电平1700uslowtime = 1700;}else{printf("ir_send_bit error!\r\n");ret  = 1;return ret;}ir_send_single(hightime,lowtime);return ret;
}
//发送1字节
uint8_t ir_send_byte(uint8_t value)
{   uint8_t temp;uint8_t ret = 0;temp = value;for (int i=0;i<8;i++){temp = (value>>i)&0x01;ret = ir_send_bit(temp);if(ret != 0){return ret;}}return ret;
}
//发送打开数据 25° 二级风 制冷模式
uint8_t ir_send_open(void)
{ir_send_head();ir_send_byte(0x4d);ir_send_byte(0xb2);ir_send_byte(0xf9);ir_send_byte(0x06);ir_send_byte(0x03);ir_send_byte(0xfc);ir_send_cut();ir_send_head();//协议规定要重复发一次ir_send_byte(0x4d);ir_send_byte(0xb2);ir_send_byte(0xf9);ir_send_byte(0x06);ir_send_byte(0x03);ir_send_byte(0xfc);ir_send_tail();return 0;}

ir _air.h代码块

#ifndef __IR_AIR_H__
#define __IR_AIR_H__
#include "tim.h"
#include "main.h"//发送一段载波
void ir_send_single(uint16_t hightime,uint16_t lowtime);
void ir_send_head(void);
void ir_send_tail(void);
uint8_t ir_send_bit(uint16_t value);
uint8_t ir_send_byte(uint8_t value);
uint8_t ir_send_open(void);
#endif  /* __IR_AIR_H__ */

注意事项4:
上面的代码在使用定时器的过程中:使用tim2存在一句:HAL_TIM_Base_Start(&htim2);
使用tim16存在一句:HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);
这一点很关键,我们设置好tim不代表他会跑起来,需要使用对应的模式的start语句开始定时器。更多信息请查看hal库代码和其他教程。这里只是提示大家注意检查。因为如果出现定时器跑的不对的情况,萌新可能不敢确定是调用错误还是设置错误。

软件部分就大体如此,作者追求简单的验证效果,并没有详细的探索全部的数据位代表的含义
抓取分析的过程文本如图
一帧数据包括两次传输,是重复传输。所以一次按键实际传送了6个有效字节。其中246字节是135字节的反码。包括信息的只有3字节。并且第一字节是固定的头。所以其实1 2字节是不变的。初步的结论是,第二字节代表不同风速,第五个字节前四位代表工作模式,后四位代表温度。
注意 这里的bit顺序是逻辑分析仪上的时间顺序!与代码中有效位顺序正好相反!

2.2硬件部分

必须承认,作者硬件资源及其有限,甚至没有一个红外发射管,只好将hellobug开发板上的红外管拆了下来,他的原理图如图

一个非常简单的电路,没什么好说的。不过作者手里的8050和电阻都是贴片封装的,非常小,于是决定借鸡生蛋,使用历史遗留pcb承载三极管和电阻。

左侧的两根排针要链接到红外发射管上,两个挨着的排针是GND,R13是10k的电阻,D1是100R。右上方的针脚连接到a6引脚即可,条件就是这么的艰苦,没办法:)

总结

其实模拟遥控器只是玩票之作。主要练习使用hal库、cubemx、逻辑分析仪的使用以及驱动的编写。希望不拘泥于一个知识点而是有一个整体的大局观。

破解红外发射-美的空调实战篇相关推荐

  1. 染成茜色的坂道破解技术内幕之实战篇

    染成茜色的坂道破解技术内幕之内功提高篇,成为真正的程序员 使用OllyICE打开染红 我的版本是染红1.00 版 先认识我们这个武器 OllyDbg 是一种具有可视化界面的 32 位汇编-分析调试器. ...

  2. s8050三极管经典电路_曝光一个产品级的红外发射电路

    作者:瑞生,来源:科技老顽童微信公众号:芯片之家(ID:chiphome-dy今天给大家一个产品级的红外发射电路.为什么说是产品级的?因为这个电路我已经在各类产品上见过多次!很多小伙伴学电子有一个误区 ...

  3. VBS带你领略脚本语言的快乐!(实战篇—死循环)

    用VBS让电脑欲哭无泪 前言    经过了前面几章对VBS操作的了解,我们顺利的进入了令人期待的实战篇(其实是恶搞片).恶搞,,,呸,实战篇我依然会对代码进行解析,但不会像讲基础那样细节,如果有听不懂 ...

  4. Android逆向之玩转Xposed模块以劫持登录为例(实战篇)

    上一篇文章<Android逆向之玩转Xposed模块以劫持登录为例(Demo篇)>自编自导了一款劫持登录的Xposed模块,如果仅满于破解自己的APP是多么的悲哀,毕竟市场上的app都是经 ...

  5. ESP32实现红外遥控 红外发射与接收实现原理

    文章目录 一,原理 1.1 概括 1.2,时钟 1.3,认识 item 1.4,发射/接收器 1.5 电路原理图 1.5.1,发射电路 1.5.2 ,接收电路 二,红外发射 2.1 整体的思路 2.2 ...

  6. 基于stm32f103红外遥控美的空调

    一.红外nec协议 红外遥控的编码目前广泛使用的是:NEC Protocol的PWM(脉冲宽度调制)和Philips RC-5 Protocol的PPM(脉冲位置调制),而美的空调大多采用nec协议的 ...

  7. linux下文件字符集转化实战篇

    linux下文件字符集转化实战篇 ------------------------------------------ 为什么转换编码,起因:分析应用程序日志,文件编码格式gb2312(含中文字符), ...

  8. thinkphp6 接收不到数据_单片机红外接收与红外发射

    1. 红外接收1.1 说明1.2 NEC协议1.3 关于红外接收的波形1.4 解码1.4 红外对射思考2. 红外发射2.1 红外发射管参数2.2 红外发射电路搭建2.3 程序设计2.5 实验结果2.4 ...

  9. 跟安全技术大师学习黑客攻防技术 ——《黑客攻防技术宝典:web实战篇》

    跟安全技术大师学习黑客攻防技术 --<黑客攻防技术宝典: web 实战篇> 随着网络技术的快速发展以及网络带宽的不断扩张, Web 应用程序几乎无处不在,渗透到社会的经济.文化.娱乐等各个 ...

最新文章

  1. Pythorch使用总览
  2. python中文读音ndarray-Python开发:NumPy学习(一)ndarray数组
  3. OSI模型和TCP/IP模型
  4. python视图函数是什么意思_Flask初学者:视图函数
  5. SQL Reverse函数
  6. SpringBoot + Mybatis-puls + ClickHouse增删改查入门教程
  7. clienttop,clientleft,scrolltop,scrollleft,offsettop,offsetleft全解析
  8. 今晚7点,NVIDIA专家深度解析全新推荐系统解决方案Merlin
  9. 对Boost.Asio中异步事件循环的理解
  10. tp5 保存图片背景黑色_少女心背景图 | 风环绕世界百圈,不如见你时心动
  11. Ubuntu 默认启动到字符界面
  12. 列标题 如何删除gridcontrol_Excel如何制作工资条?
  13. ubuntu清除dns缓存_如何在Ubuntu上清除DNS缓存
  14. Stata:gen命令中的group()函数的潜在风险
  15. 什么是机器学习,为什么它如此重要?
  16. 6. 小A的糖果(普及-)
  17. 【折半搜索-经典题目】中山纪念中学暑期游Day13——【GDOI2017模拟8.15】Buy
  18. ISAM2.h/ISAM2.cpp
  19. php两个手机号正则表达式_php 手机号码验证正则表达式
  20. 认证资料大全(二十一)------ SAIR认证列表

热门文章

  1. scis硬盘和SATA硬盘有什么区别
  2. PyCharm警告Redeclared ‘ ‘ defined above without usage的问题
  3. 如何下载MathType及踩的坑(详细)
  4. 在win7系统中安装 Outlook Express
  5. 2021年电工(初级)考试总结及电工(初级)模拟考试系统
  6. caged系统pdf_介孔二氧化硅纳米粒子药物递送系统研究进展-中国药科大学学报!.PDF...
  7. 关机之后长按8秒复位
  8. 字节出了一款设计神器,居然这么好用。外包又有icon、插画素材了
  9. Java中的List与Set转换
  10. Error 193:%1 不是合法的Win32 应用程序 查看程序是x86还是x64