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
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!