來(lái)源:公眾號(hào)【魚(yú)鷹談單片機(jī)】
作者:魚(yú)鷹Osprey
ID ? :emOsprey
夏天來(lái)了,空調(diào)成了必備,如何讓空調(diào)定時(shí)器啟動(dòng)/關(guān)閉,或者人回來(lái)了自動(dòng)開(kāi)啟空調(diào)?
首先要了解的是紅外編、解碼。
需要購(gòu)買(mǎi)兩個(gè)小模塊,一個(gè)用于接收,一個(gè)用于發(fā)送,幾塊錢(qián)搞定。
另外需要一個(gè)示波器,或者邏輯分析儀,能看到高低電平即可,不需要很高的帶寬。如果都沒(méi)有,那么自己通過(guò)單片機(jī)也可自行處理解析高低電平數(shù)據(jù)。
5 V 供電,3.3 V 也能工作,可能距離短一點(diǎn)。
首先我們要了解自己的遙控器編碼,簡(jiǎn)單一點(diǎn),直接遙控器(型號(hào) YAP0F3)對(duì)準(zhǔn)接收器,可以看到接收模塊燈亮了,同時(shí)邏輯分析儀可以清楚知道電平狀態(tài):
第一個(gè)數(shù)據(jù):
第二個(gè)數(shù)據(jù):
按下一次按鍵后全部發(fā)送數(shù)據(jù)展示:
使用 nec 解碼器
可以看到,按下按鍵時(shí),發(fā)送了兩次數(shù)據(jù),每次數(shù)據(jù) 8 字節(jié)。有可能不是標(biāo)準(zhǔn) nes,因此可以看到后面的 4 字節(jié)無(wú)法解析。
可以看到,有幾個(gè)元素:
起始位:
低電平持續(xù)時(shí)間?:?9000us?(即 IO 翻轉(zhuǎn)頻率 38KHz,驅(qū)動(dòng)紅外發(fā)射管,此時(shí)接收管表現(xiàn)為低電平狀態(tài))
高電平持續(xù)時(shí)間:?4500us ?(不驅(qū)動(dòng)紅外發(fā)射管,設(shè)置 0 或 1 都可)
數(shù)據(jù)位定義(字節(jié)內(nèi),低位先發(fā)送,低字節(jié)先發(fā)):
bit:0
低電平持續(xù)時(shí)間?: 650 us
高電平持續(xù)時(shí)間: 650 us
bit:1
低電平持續(xù)時(shí)間: 650 us
高電平持續(xù)時(shí)間?: 1650 us
共 4*8 + 3 bit,后面的 3bit 為固定時(shí)間 0b010
然后是
連接碼 1 bit:
低電平持續(xù)時(shí)間?: 650 us
高電平持續(xù)時(shí)間?: 20000us
之后跟隨 32 bit 數(shù)據(jù)。
最后一個(gè)停止碼:
低電平持續(xù)時(shí)間?: 650 us
高電平持續(xù)時(shí)間?: 40000us
一次數(shù)據(jù)傳輸結(jié)束。
然后又是一個(gè)起始碼,重復(fù)上一次輸出,但是數(shù)據(jù)有所不同,魚(yú)鷹還沒(méi)搞清楚為什么要發(fā)兩次不同的碼,有了解的道友可以留言討論一下。
學(xué)習(xí)紅外,都會(huì)看到 38Khz 載波,其實(shí)對(duì)于工程師來(lái)說(shuō),你只要知道,使用 PWM 38KHz 驅(qū)動(dòng)紅外發(fā)射管,接收管會(huì)顯示低電平即可,剩下的就是如何組織數(shù)據(jù)和時(shí)序了。
下面附關(guān)鍵代碼:
typedef?enum?{
? ? GREE_MODE_AUTO =?0, ? ? ? ? ? ??// 自動(dòng)
? ? GREE_MODE_COOL =?1, ? ? ? ? ? ??// 制冷
? ? GREE_MODE_HUMIDIFICATION =?2, ??// 加濕
? ? GREE_MODE_FAN =?3, ? ? ? ? ? ? ?// 送風(fēng)
? ? 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; ? ??// 開(kāi)關(guān)
? ? ? ? ? ??uint32_t? fan_speed:2; ?// 風(fēng)速
? ? ? ? ? ??uint32_t? swing_flap:1;?// 掃風(fēng)
? ? ? ? ? ??uint32_t? sleep:1; ? ? ?// 睡眠
? ? ? ? ? ??uint32_t? temprature:4;?// 16°C = 0 ?30°C = 0111, ? =26°C-16°C
? ? ? ? ? ??uint32_t? timer:8; ? ? ?// 定時(shí)
? ? ? ? ? ??uint32_t? humidification:1;?// 加濕
? ? ? ? ? ??uint32_t? lamplight:1; ?// 燈光
? ? ? ? ? ??uint32_t? anion:1; ?// 負(fù)離子
? ? ? ? ? ??uint32_t? save_electricity:1; ??// 節(jié)電
? ? ? ? ? ??uint32_t? aeration:1; ??// 換氣
? ? ? ? ? ??uint32_t? fix_data:7; ??// 固定值? 0001010b ? ? 40,56 ?
? ? ? ? }bits;
? ? }first;?// 后面有 3 bit 固定數(shù)據(jù)和連接碼
? ??union?{
? ? ? ??uint32_t?data;?
? ? ? ??struct?{
? ? ? ? ? ??uint32_t? fan_up_down:1; ? ?// 上下掃風(fēng)
? ? ? ? ? ??uint32_t? reserver_1:3; ? ??// 000b
? ? ? ? ? ??uint32_t? fan_left_right:1;?// 左右掃風(fēng)
? ? ? ? ? ??uint32_t? reserver_2:3; ? ??// 000b
? ? ? ? ? ??uint32_t? display_temperature:2;?// 溫度顯示
? ? ? ? ? ??uint32_t? reserver_3:16;
? ? ? ? ? ??uint32_t? energy_conservation:1; ??// 節(jié)能
? ? ? ? ? ??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);
}
關(guān)機(jī)代碼(可以自己寫(xiě)優(yōu)雅一點(diǎn),方便閱讀):
uint8_t?tx_data[4] = {0x79,?0x0a,?00,?0x50}; ??// 開(kāi)機(jī),只需這個(gè)好像就可以。
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);
// 后面這個(gè)不知道有啥用
uint8_t?tx_data3[4] = {0x79,?0x0a,?00,?0x70}; ??// 開(kāi)機(jī)
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);
開(kāi)機(jī)代碼(可以自己寫(xiě)優(yōu)雅一點(diǎn)):
uint8_t?data[4] = {0x71,?0x0a,?00,?0x50}; // 關(guān)機(jī)
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}; // 關(guān)機(jī)
uint8_t?data4[4] = {0x0,?0x00,?0x30,?0x80};
nec_send(*(uint32_t*)data3, *(uint32_t*)data4);
網(wǎng)上找的校驗(yàn)碼,發(fā)現(xiàn)和我手上的遙控器不一致,僅供參考:
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;
上面的數(shù)據(jù)解析可能有所不同,但大體不差,大家可以根據(jù)自己的情況修改。
由于校驗(yàn)值沒(méi)法得出,因此算是一個(gè)小遺憾,不過(guò)能控制開(kāi)關(guān)機(jī)就不錯(cuò)了。后面有時(shí)間可以參考:
https://github.com/crankyoldgit/IRremoteESP8266
另外為簡(jiǎn)單起見(jiàn),沒(méi)有使用 PWM,后續(xù)可以?xún)?yōu)化一下,同時(shí)沒(méi)有學(xué)習(xí)功能,如果有的話,就可以輕松自動(dòng)學(xué)習(xí)了,不用額外的邏輯分析儀了。