来源:公众号【鱼鹰谈单片机】
作者:鱼鹰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°C
uint32_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; // 000b
uint32_t fan_left_right:1; // 左右扫风
uint32_t reserver_2:3; // 000b
uint32_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); // 687us
nec_carrier(1, bit? 1620: 650); // 1619us
}
void nec_send(uint32_t data, uint32_t sec_data)
{
nec_carrier(0, 8800); // 9.021ms
nec_carrier(1, 4500); // 5.143ms
for(int i = 0;i < 32; i++)
{
send_bit(data&1); // low bit fist
data >>= 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,后续可以优化一下,同时没有学习功能,如果有的话,就可以轻松自动学习了,不用额外的逻辑分析仪了。
END


往期精选:

请点下【♡】给小编加鸡腿
