STM32F1与STM32CubeIDE编程实例-NEC协议红外接收与解码

NEC协议红外接收与解码

1、红外遥控及红外遥控协议介绍

红外线遥控(IR Remote)是一种无线、非接触控制技术,具有抗干扰能力强、信息传输可靠、功耗低、成本低、易于实现等显着优点。 它被许多电子设备,尤其是家用电器广泛使用。

红外线遥控,越来越多地应用于计算机系统。 同类产品的红外遥控器可以有相同的遥控频率或遥控码,不会出现遥控信号“过门(cross-door)”的情况。 目前广泛使用的红外遥控代码:NEC协议的PWM(脉冲宽度调制)和飞利浦RC-5协议的PPM(脉冲位置调制)。本文使用的是NEC Protocol的PWM协议。

1.1 NEC协议介绍

1)NEC协议具有以下特点

  • 8位地址和8位指令长度;
  • 地址和命令的两次传输(保证可靠性)
  • PWM脉宽调制,由红外载波0”和“1”的占空比表示;
  • 载波频率为38Khz;
  • 位时间为 1.125ms 或 2.25ms。

2)NEC 代码的位定义

一个脉冲对应一个560us的连续载波。逻辑1传输需要2.25ms(56.5us脉冲+1687.5us低电平);逻辑0传输需要1.125ms(562.5us脉冲+562.5us低电平)。 遥控接收器接收到脉冲时为低电平,无脉冲时为高电平。 这样,我们在接收头收到的信号就是:逻辑1应该是560us低+1687.5us高,逻辑0应该是562.5us低+562.5us高。

在这里插入图片描述

3)NEC遥控指令格式

NEC遥控指令的数据格式为:同步对接、地址码、地址反转、控制码、控制反转。 同步码由一个 9ms 的低电平和一个 4.5ms 的高电平组成。 地址码、地址反转、控制码、控制反转都是8位数据格式。 按照低位在前,高位在后的顺序发送。 反码用于增加传输的可靠性(可用于验证)。即,传输过程如下:

每当按下一个键时,都会传输以下内容:-

  • 1)一个 9 ms 的超前脉冲
  • 2)4.5 毫秒的间隔
  • 3)8位地址
  • 4)地址的8位逻辑取反
  • 5)8位指令
  • 6)指令的8位逻辑逆
  • 7)最后一个 562.5µs 的脉冲串表示消息传输结束

在这里插入图片描述

1.2 NEC协议解码

解码任何 IR 协议一点都不难。 IR 接收器是一个光电二极管和前置放大器,可将 IR 光转换为电信号。 它将高电平有效信号转换为低电平有效信号。 信号脉冲也是连续的,如下图所示:

在这里插入图片描述

第一步:为了首先解码,我们必须等待输入引脚变低。 一旦引脚为低电平,我们将等待引脚变为高电平。 这应该需要大约 9 毫秒。假如,红外接收器的引脚连接端口为GPIOA,引脚为GPIO_Pin_1,则这一过程实现的代码如下:

1
2
3
while (HAL\_GPIO\_ReadPin (GPIOA, GPIO_PIN_1));   // 等待引脚切换成低电平
while (!(HAL\_GPIO\_ReadPin (GPIOA, GPIO_PIN_1))); // 等待引脚切换为高电平,9ms

第二步:再次等待引脚变低,这大约需要 4.5 毫秒。开始帧结束,开始接收数据

1
2
while (HAL\_GPIO\_ReadPin (GPIOA, GPIO_PIN_1));

最后,现在我们将读取 32 位并将它们存储在一个变量中。在这一过程中,将检查高代电平间隔的长度,然后是 562.5µs。 如果此间隔的长度大于 1.2 ms,则表示该位为“1”。 如果长度小于 1 ms,则该位为“0”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for (int i = 0; i < 32; i++) {
count = 0;
// 等待引脚变高,这是562.5us的低电平信号
while (!(HAL\_GPIO\_ReadPin(GPIOA, GPIO_PIN_1)))
;
// 计算引脚高电平时的间隔长度
while ((HAL\_GPIO\_ReadPin(GPIOA, GPIO_PIN_1))) {
count++;
dwt\_delay\_us(50);
}

if (count >= 25) { // 如果间隔超过 1.2 毫秒
code |= (1UL << (31 - i)); // 写 1
} else {
code &= ~(1UL << (31 - i)); // 写 0
}
}

2、红外接收器配置

开发环境搭建、系统时钟配置、调试配置及串口配置,请参考:

传感器配置如下:

在这里插入图片描述

3、驱动代码实现

1)基本定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/\*
\* ir\_remote.h
\*
\* Created on: May 1, 2022
\* Author: jenson
\*/

#ifndef \_\_IR\_REMOTE\_H\_\_
#define \_\_IR\_REMOTE\_H\_\_

typedef struct {
uint16\_t id;
GPIO_TypeDef \*GPIOx; // 总线端口
uint16\_t GPIO_Pin; // 总线引脚
uint32\_t decoded_data;
} ir\_remote\_t;
/\*\*
\* @brief 初始化
\* @params ir 红外接收设备对象
\*/
void ir\_remote\_init(ir\_remote\_t \*ir);

/\*\*
\*@brief 接收并解码红外设备数据
\*@params ir 红外接收设备对象
\*/
void ir\_remote\_receive\_data(ir\_remote\_t \*ir);

/\*\*
\* @brief 等待红外数据
\* @params ir 红外接收设备对象
\*/
void ir\_remote\_wait(ir\_remote\_t \*ir);

#endif /\* \_\_IR\_REMOTE\_H\_\_ \*/


2)NEC协议红外数据接收与解码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/\*
\* ir\_remote.c
\*
\* Created on: May 1, 2022
\* Author: jenson
\*/
#include "dwt/dwt\_delay.h"
#include "ir\_remote.h"

void ir\_remote\_init(ir\_remote\_t \*ir) {
dwt\_init();
ir->decoded_data = 0x00;
HAL\_GPIO\_ReadPin(ir->GPIOx, ir->GPIO_Pin);

}
static uint32\_t count;
void ir\_remote\_receive\_data(ir\_remote\_t \*ir) {
uint32\_t code = 0;
// NEC格式信号的 START序列从这里开始,
// 会有一个 9ms低电平信号 和 4.5ms 间高电平信号的脉冲
// 9ms 低电平信号
while (!(HAL\_GPIO\_ReadPin(ir->GPIOx, ir->GPIO_Pin)))
;
// 4.5ms高电平信号
while ((HAL\_GPIO\_ReadPin(ir->GPIOx, ir->GPIO_Pin)))
;
// START序列结束,开始接收数据
// 在 562.5us脉冲后检查间隔,
// 如果间隔为 562.5us,则该位表示“0”,
// 如果间隔在 1.6ms 左右,则该位为“1”

for (int i = 0; i < 32; i++) {
count = 0;
// 等待引脚变高,这是562.5us的低电平信号
while (!(HAL\_GPIO\_ReadPin(ir->GPIOx, ir->GPIO_Pin)))
;
// 计算引脚高电平时的间隔长度
while ((HAL\_GPIO\_ReadPin(ir->GPIOx, ir->GPIO_Pin))) {
count++;
dwt\_delay\_us(50);
}

if (count >= 25) { // 如果间隔超过 1.2 毫秒
code |= (1UL << (31 - i)); // 写 1
} else {
code &= ~(1UL << (31 - i)); // 写 0
}
}
ir->decoded_data = code;
// 处理需要至少延时100us左右,否则可能会下次导致解析结果不稳定
dwt\_delay\_ms(100);
}

void ir\_remote\_wait(ir\_remote\_t \*ir) {
while (HAL\_GPIO\_ReadPin(ir->GPIOx, ir->GPIO_Pin))
;
}

本次驱动使用了DWT模块的延时功能,关于DWT的延时功能使用,请参考前面的文章:

3)主程序

main.c文件中,添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include <stdio.h>
#include <ir\_remote/ir\_remote.h>
/\* USER CODE END Includes \*/

/\* USER CODE BEGIN PV \*/
ir\_remote\_t ir;
/\* USER CODE END PV \*/

int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_USART1\_UART\_Init();
/\* USER CODE BEGIN 2 \*/
ir.GPIO_Pin = IR_RECEIVER_Pin;
ir.GPIOx = IR_RECEIVER_GPIO_Port;
ir.id = 0;
ir\_remote\_init(&ir);
printf("\*\*\*\*STM32CubeIDE:IR Remote\*\*\*\*\r\n");
/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
ir\_remote\_wait(&ir);
ir\_remote\_receive\_data(&ir);
if (ir.decoded_data > 0) {
printf("recv-> hex:0x%x,dec:%u\r\n", ir.decoded_data,
ir.decoded_data);
}

}
/\* USER CODE END 3 \*/
}


运行结果如下:

在这里插入图片描述

文章来源: https://iotsmart.blog.csdn.net/article/details/125547224