✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
目录
[1.1 硬件清单](#1.1 硬件清单)
[1.2 接线方案](#1.2 接线方案)
[1.3 具体连接图](#1.3 具体连接图)
[1.4 实物接线图](#1.4 实物接线图)
[2.1 发送端代码(YS-IRTM)](#2.1 发送端代码(YS-IRTM))
[2.2 接收端代码(IR Receiver)](#2.2 接收端代码(IR Receiver))
[2.3 红外编码处理流程](#2.3 红外编码处理流程)
[3.1 性能测试](#3.1 性能测试)
[3.2 视频演示](#3.2 视频演示)
[4.1 NEC编码方式](#4.1 NEC编码方式)
[4.2 数据帧结构](#4.2 数据帧结构)
[4.3 扩展NEC协议](#4.3 扩展NEC协议)
[Q1: 如何提高红外通信的距离和稳定性?](#Q1: 如何提高红外通信的距离和稳定性?)
[Q2: 遇到解码错误或信号干扰怎么办?](#Q2: 遇到解码错误或信号干扰怎么办?)
(1)项目概述
本项目基于零知开发板系列(标准板和增强板)构建了一个完整的红外通信控制系统,实现了通过红外信号远程控制LED灯状态并在显示屏上实时显示控制信息的功能。发送端使用零知增强板通过YS-IRTM红外编解码模块发送编码后的红外信号。接收端使用零知标准板接收红外信号,解析后控制LED灯开关并在240×240分辨率的ST7789屏幕上显示接收信息。
红外通信模块
>YS-IRTM红外编解码模块:集成了红外发射和接收功能,支持多种红外协议,可通过串口进行控制
>IR Receiver红外接收器:常用的红外接收头,能够接收和解码38kHz载波的红外信号
(2)项目难点及解决方案
问题描述:原始红外编码(如0XFF6897)与YS-IRTM模块所需的编码格式(如A1 F1 00 FF 16)之间存在显著差异,需要建立准确的映射关系。
**解决方案:**通过实验测试确定两种编码格式之间的对应关系,创建映射表并在发送端实现格式转换功能。
一、硬件设计部分
1.1 硬件清单
组件 | 型号/规格 | 数量 |
---|---|---|
零知标准板 | STM32F103RBT6 | 1 |
零知增强板 | STM32F407VET6 | 1 |
YS-IRTM红外编解码模块 | 支持串口控制 | 1 |
IR Receiver红外接收器 | 38kHz | 1 |
ST7789显示屏 | 240×240分辨率 | 1 |
LED灯 | 5mm | 1 |
1.2 接线方案
发送端(零知增强板+YS-IRTM)
零知增强板引脚 | YS-IRTM模块引脚 | 功能 |
---|---|---|
3.3V | VCC | 电源 |
GND | GND | 地线 |
10 | TX | 数据接收 |
9 | RX | 数据发送 |
接收端(零知标准板)
零知标准板引脚 | 外设 | 功能 |
---|---|---|
3.3V | IR Receiver VCC | 电源 |
GND | IR Receiver GND | 地线 |
6 | IR Receiver OUT | 数据输出 |
2 | LED阳极 | LED控制 |
10 | ST7789 CS | 片选 |
9 | ST7789 DC | 数据/命令 |
8 | ST7789 RST | 复位 |
7 | ST7789 BL | 背光控制 |
11 | ST7789 SDA | 数据输入 |
13 | ST7789 SCL | 时钟 |
1.3 具体连接图
(1)发送端接线

将零知增强板上的11引脚用作软串口的TX、10引脚用作软串口的RX控制YS-IRTM红外编解码器收发数据
(2)接收端接线

1.4 实物接线图

二、软件代码详解
2.1 发送端代码(YS-IRTM)
1.软串口发送编码字节
cpp
#include <SoftwareSerial.h>
// 软串口引脚定义
#define SOFT_RX 10
#define SOFT_TX 11
// 解码后的编码映射 - 以字节数组形式存储
byte irCodes[10][5] = {
{0xA1, 0xF1, 0x00, 0xFF, 0x16}, // 0
{0xA1, 0xF1, 0x00, 0xFF, 0x0C}, // 1
{0xA1, 0xF1, 0x00, 0xFF, 0x18}, // 2
{0xA1, 0xF1, 0x00, 0xFF, 0x5E}, // 3
{0xA1, 0xF1, 0x00, 0xFF, 0x08}, // 4
{0xA1, 0xF1, 0x00, 0xFF, 0x1C}, // 5
{0xA1, 0xF1, 0x00, 0xFF, 0x5A}, // 6
{0xA1, 0xF1, 0x00, 0xFF, 0x42}, // 7
{0xA1, 0xF1, 0x00, 0xFF, 0x52}, // 8
{0xA1, 0xF1, 0x00, 0xFF, 0x4A} // 9
};
SoftwareSerial mySerial(SOFT_RX, SOFT_TX); // RX, TX
void setup() {
// 初始化硬件串口
Serial.begin(9600);
// 初始化软串口
mySerial.begin(9600);
Serial.println("YS-IRTM Controller Ready");
Serial.println("Send numbers 0-9 to transmit IR codes");
Serial.println("Send 'L' to enter learning mode");
}
void loop() {
// 处理硬件串口输入
if (Serial.available()) {
char c = Serial.read();
if (c >= '0' && c <= '9') {
int num = c - '0';
// 通过软串口发送编码字节
mySerial.write(irCodes[num], 5);
Serial.print("Sent code for number ");
Serial.print(num);
Serial.print(": ");
for (int i = 0; i < 5; i++) {
if (irCodes[num][i] < 0x10) Serial.print("0");
Serial.print(irCodes[num][i], HEX);
Serial.print(" ");
}
Serial.println();
}
else if (c == 'L' || c == 'l') {
// 进入学习模式
enterLearningMode();
}
}
// 检查是否有学习到的编码
readLearnedCode();
delay(100);
}
零知IDE串口调试助手发送对应的数字后,将NEC格式的编码数据通过软串口发送到红外发射头。发送字符'L'时进入学习模式读取新的NEC编码
2.读取NEC编码
cpp
// 进入学习模式
void enterLearningMode() {
Serial.println("Entering learning mode...");
Serial.println("Press a button on your remote control");
}
// 读取学习到的编码
void readLearnedCode() {
if (mySerial.available() >= 3) {
byte receivedData[3];
mySerial.readBytes(receivedData, 3);
Serial.print("Learned code: ");
for (int i = 0; i < 3; i++) {
if (receivedData[i] < 0x10) Serial.print("0");
Serial.print(receivedData[i], HEX);
Serial.print(" ");
}
Serial.println();
}
}
2.2 接收端代码(IR Receiver)
1.初始化定义
cpp
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <IRremote.h>
// 引脚定义
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 8
#define TFT_BL 7 // 背光控制引脚
#define LED_PIN 2 // 板载LED引脚
#define IR_RECV_PIN 6 // 红外接收引脚
// 创建ST7789显示对象
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// 红外接收对象
IRreceiver irrecv(IR_RECV_PIN);
IRdecode_results decode_result;
// 红外编码映射
unsigned long irCodes[10] = {
0xFF6897, 0xFF30CF, 0xFF18E7, 0xFF7A85, 0xFF10EF,
0xFF38C7, 0xFF5AA5, 0xFF42BD, 0XFF4AB5, 0XFF52AD
};
// 当前显示的数字
int currentNumber = -1;
void setup() {
// 初始化串口通信
Serial.begin(115200);
// 初始化LED引脚
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// 初始化ST7789显示屏
tft.init(240, 240);
tft.setRotation(1);
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH); // 开启背光
// 显示欢迎界面
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(3);
tft.setCursor(50, 100);
tft.print("Ready");
// 初始化红外接收
irrecv.enableIRIn();
Serial.println("System initialized. Waiting for IR input...");
Serial.println("Send numbers 0-9 via software serial to transmit IR codes");
}
2.红外接收数据处理
cpp
void loop() {
// 处理红外接收
if (irrecv.decode(&decode_result)) {
unsigned long value = decode_result.value;
Serial.print("Received IR code: 0x");
Serial.println(value, HEX);
// 检查是否为0-9的数字
for (int i = 0; i < 10; i++) {
if (value == irCodes[i]) {
displayNumber(i);
// 控制LED灯
if (i == 2) {
digitalWrite(LED_PIN, HIGH);
Serial.println("LED turned ON");
} else if (i == 3) {
digitalWrite(LED_PIN, LOW);
Serial.println("LED turned OFF");
}
break;
}
}
irrecv.resume(); // 接收下一个值
}
delay(100);
}
// 在屏幕上显示数字
void displayNumber(int number) {
if (number == currentNumber) return; // 避免重复绘制
currentNumber = number;
tft.fillScreen(ST77XX_BLACK);
// 绘制外圈
tft.fillRoundRect(5, 5, 235, 235, 30, 0X8410);
// 绘制内圈
tft.fillRoundRect(15, 15, 215, 215, 30, ST77XX_WHITE);
// 绘制数字
tft.setTextColor(ST77XX_ORANGE);
tft.setTextSize(20);
// 根据数字大小调整位置
int xPos = 75;
if (number == 1) xPos = 80;
tft.setCursor(xPos, 70);
tft.print(number);
Serial.print("Displaying number: ");
Serial.println(number);
}
将发送端的数字实时显示到接收端的显示屏,识别到数'2'和'3'时分别打开和关闭LED灯
2.3 红外编码处理流程

三、系统演示效果
3.1 性能测试
(1)红外信号学习与发送测试
通过YS-IRTM模块成功学习了10个数字按键的红外编码,学习过程稳定可靠。发送端使用串口发送对应的红外信号,测试数据如下:

数字键 | 原始编码 | YS-IRTM编码 | 发送成功率 |
---|---|---|---|
0 | 0xFF6897 | A1 F1 00 FF 16 | 100% |
1 | 0xFF30CF | A1 F1 00 FF 0C | 100% |
(2)控制设备测试
数字键2和3分别控制LED灯的开关,测试结果:
开关响应时间:<100ms <
控制准确率:100% <
3.2 视频演示
YS-IRTM红外编解码器集成灯控与显示系统
串口发送红外编码和遥控发送红外信号效果对比
四、NEC红外协议
4.1 NEC编码方式
NEC协议采用脉冲间隔的方式编码每一位数据,用不同数据位的时间间隔来表示不同的逻辑位。与众多单总线IC的通信方式很相似。NEC编码的载波频率为38KHz,对应的单脉冲周期约等于26.3us。

每一数据位起始于562.5µs的载波,约为21.25个38 kHz脉冲组成。 脉冲的占空比通常为1/4或1/3,以减少电流消耗:

(1)数据表示方式
NEC协议使用脉冲距离编码表示数据:
>逻辑"0":560μs高电平 + 560μs低电平
>逻辑"1":560μs高电平 + 1.68ms低电平

逻辑"0"为562.5µs的有效脉冲 + 562.5µs的空闲间隔,总时长为1.125ms。逻辑"1"为562.5µs的有效脉冲 + 1.6875ms的空闲间隔,总时长为2.25ms(为逻辑0的一倍)。
(2)重复码机制
当按键保持按下时,发送重复码:9ms高电平 + 2.25ms低电平 + 560μs高电平

>即使一直按住遥控器上的一个键,命令帧也只会发送一次。 只要按键保持按下状态,就会每110毫秒发送一次重复码。

4.2 数据帧结构
完整的NEC数据帧由 起始位 + 地址码 + 地址码反码 + 命令码 + 命令码反码 组成。

(1)起始位
每个序列均以9ms的脉冲(称为AGC脉冲)(是逻辑数据位使用的脉冲562.5us的16倍)开始。 接下来是4.5毫秒的空闲:(可以理解这一过程是每一帧的起始标志)
(2)地址码+命令码
起始位之后会传输4个字节共32Bit的数据位,分别是 地址码 + 地址码反码 + 命令码 + 命令码反码。字面上是4个字节,而实际只有2个字节有效,多余的2字节为冗余的反码。一方面可以用于校验是否出错。另一方面,因为总有相同数量的反码的存在,每帧的总消息时长保持恒定。
(3)结束位
结束位为末尾的562.5µs的有效脉冲。
4.3 扩展NEC协议
NEC协议因应用广泛,原有地址很快会被用完。
通过牺牲地址冗余,其地址范围从8位、256个扩展到16位、65536个,且无需改动协议其他属性。
不过这样扩展后,消息总时长不再恒定,取决于消息中1和0的总数;若想保持时长恒定,需让地址字段中逻辑1和逻辑0各为8个,此时地址最大数量会减至约13000个。
此外,协议仍保留命令冗余,每个地址仍可处理256个不同命令。
五、常见问题解答
Q1: 如何提高红外通信的距离和稳定性?
A: 可以通过以下方式提高红外通信性能:
使用更高功率的红外发射管、确保发射器和接收器之间没有障碍物,避免强光直射接收器、调整发射器的发射角度、增加红外发射管的数量(阵列)
Q2: 遇到解码错误或信号干扰怎么办?
A: 解决解码错误和信号干扰的方法:
检查电源稳定性,确保供电充足、增加软件去抖动机制,过滤干扰信号、使用错误检测和重发机制提高可靠性、调整接收器的灵敏度和滤波参数
项目资源:
红外编解码模块:YS-IRTM模块数据手册
NEC红外协议:NEC红外协议详解