来源:公众号【鱼鹰谈单片机】
作者:鱼鹰Osprey
ID :emOsprey
夏天来了,空调成了必备,如何让空调定时器启动/关闭,或者人回来了自动开启空调?
首先要了解的是红外编、解码。
需要购买两个小模块,一个用于接收,一个用于发送,几块钱搞定。
另外需要一个示波器,或者逻辑分析仪,能看到高低电平即可,不需要很高的带宽。如果都没有,那么自己通过单片机也可自行处理解析高低电平数据。
5 V 供电,3.3 V 也能工作,可能距离短一点。
首先我们要了解自己的遥控器编码,简单一点,直接遥控器(型号 YAP0F3)对准接收器,可以看到接收模块灯亮了,同时逻辑分析仪可以清楚知道电平状态:
第一个数据:
第二个数据:
按下一次按键后全部发送数据展示:
使用 nec 解码器
可以看到,按下按键时,发送了两次数据,每次数据 8 字节。有可能不是标准 nes,因此可以看到后面的 4 字节无法解析。
可以看到,有几个元素:
起始位:
低电平持续时间 : 9000us (即 IO 翻转频率 38KHz,驱动红外发射管,此时接收管表现为低电平状态)
高电平持续时间: 4500us (不驱动红外发射管,设置 0 或 1 都可)
数据位定义(字节内,低位先发送,低字节先发):
bit:0
低电平持续时间 : 650 us
高电平持续时间: 650 us
bit:1
低电平持续时间: 650 us
高电平持续时间 : 1650 us
共 4*8 + 3 bit,后面的 3bit 为固定时间 0b010
然后是
连接码 1 bit:
低电平持续时间 : 650 us
高电平持续时间 : 20000us
之后跟随 32 bit 数据。
最后一个停止码:
低电平持续时间 : 650 us
高电平持续时间 : 40000us
一次数据传输结束。
然后又是一个起始码,重复上一次输出,但是数据有所不同,鱼鹰还没搞清楚为什么要发两次不同的码,有了解的道友可以留言讨论一下。
学习红外,都会看到 38Khz 载波,其实对于工程师来说,你只要知道,使用 PWM 38KHz 驱动红外发射管,接收管会显示低电平即可,剩下的就是如何组织数据和时序了。
下面附关键代码:
typedef enum {GREE_MODE_AUTO = 0, // 自动GREE_MODE_COOL = 1, // 制冷GREE_MODE_HUMIDIFICATION = 2, // 加湿GREE_MODE_FAN = 3, // 送风GREE_MODE_HEAT = 4, // 加热}GREE_MODE;typedef enum {GREE_FAN_SPEED_AUTO = 0,GREE_FAN_SPEED_1 = 1,GREE_FAN_SPEED_2 = 2,GREE_FAN_SPEED_3 = 3,}GREE_FAN_SPEED;typedef struct{union {uint32_t data;struct {GREE_MODE mode_flag:3; // 模式uint32_t on_off:1; // 开关uint32_t fan_speed:2; // 风速uint32_t swing_flap:1; // 扫风uint32_t sleep:1; // 睡眠uint32_t temprature:4; // 16°C = 0 30°C = 0111, =26°C-16°Cuint32_t timer:8; // 定时uint32_t humidification:1; // 加湿uint32_t lamplight:1; // 灯光uint32_t anion:1; // 负离子uint32_t save_electricity:1; // 节电uint32_t aeration:1; // 换气uint32_t fix_data:7; // 固定值? 0001010b ? 40,56 ?}bits;}first; // 后面有 3 bit 固定数据和连接码union {uint32_t data;struct {uint32_t fan_up_down:1; // 上下扫风uint32_t reserver_1:3; // 000buint32_t fan_left_right:1; // 左右扫风uint32_t reserver_2:3; // 000buint32_t display_temperature:2; // 温度显示uint32_t reserver_3:16;uint32_t energy_conservation:1; // 节能uint32_t reserver_4:1; //uint8_t sum2 :4; // checksum of the previous bytes (8-14)}bits;}second;}nec_gree_data_def;void nec_carrier(uint32_t bit, uint32_t time_us){if(bit) {GPIOC->BSRR = GPIO_PIN_14;hw_delay_us(time_us);}else {for(int i = 0; i < time_us/13; i++) { // 38 Khz,if(i &1) {GPIOC->BSRR = GPIO_PIN_14;}else {GPIOC->BRR = GPIO_PIN_14;}hw_delay_us(13);}}}void send_bit(uint32_t bit){nec_carrier(0, 650); // 687usnec_carrier(1, bit? 1620: 650); // 1619us}void nec_send(uint32_t data, uint32_t sec_data){nec_carrier(0, 8800); // 9.021msnec_carrier(1, 4500); // 5.143msfor(int i = 0;i < 32; i++){send_bit(data&1); // low bit fistdata >>= 1;}// 后面跟随三位 固定值send_bit(0);send_bit(1);send_bit(0);// 连接码nec_carrier(0, 560);nec_carrier(1, 20000);for(int i = 0; i < 32; i++){send_bit(sec_data&1);sec_data >>= 1;}nec_carrier(0, 560);}
关机代码(可以自己写优雅一点,方便阅读):
uint8_t tx_data[4] = {0x79, 0x0a, 00, 0x50}; // 开机,只需这个好像就可以。uint8_t tx_data_2[4] = {0x0, 0x00, 0x00, 0xD0};nec_gree_data_def gree_data;gree_data.first.data = *(uint32_t*)tx_data;gree_data.second.data = *(uint32_t*)tx_data_2;nec_send(gree_data.first.data, gree_data.second.data);// 后面这个不知道有啥用uint8_t tx_data3[4] = {0x79, 0x0a, 00, 0x70}; // 开机uint8_t tx_data4[4] = {0x0, 0x00, 0x30, 0x00};nec_gree_data_def gree_data2;gree_data2.first.data = *(uint32_t*)tx_data3;gree_data2.second.data = *(uint32_t*)tx_data4;nec_send(gree_data2.first.data, gree_data2.second.data);
开机代码(可以自己写优雅一点):
uint8_t data[4] = {0x71, 0x0a, 00, 0x50}; // 关机uint8_t data_2[4] = {0x0, 0x00, 0x00, 0x50};nec_gree_data_def gree_data3;gree_data3.first.data = *(uint32_t*)data;gree_data3.second.data = *(uint32_t*)data_2;nec_send(*(uint32_t*)data, *(uint32_t*)data_2);hw_delay_us(40000);uint8_t data3[4] = {0x71, 0x0a, 00, 0x70}; // 关机uint8_t data4[4] = {0x0, 0x00, 0x30, 0x80};nec_send(*(uint32_t*)data3, *(uint32_t*)data4);
网上找的校验码,发现和我手上的遥控器不一致,仅供参考:
uint8_t sum2 = ((uint8_t)gree_data.first.bits.mode_flag - 1) + gree_data.first.bits.temprature + 5 +gree_data.second.bits.fan_left_right + gree_data.first.bits.aeration +gree_data.second.bits.energy_conservation - gree_data.first.bits.on_off;
上面的数据解析可能有所不同,但大体不差,大家可以根据自己的情况修改。
由于校验值没法得出,因此算是一个小遗憾,不过能控制开关机就不错了。后面有时间可以参考:
https://github.com/crankyoldgit/IRremoteESP8266
另外为简单起见,没有使用 PWM,后续可以优化一下,同时没有学习功能,如果有的话,就可以轻松自动学习了,不用额外的逻辑分析仪了。
778