• 物联网ARM开发-3协议-单总线应用红外遥控


    前言:单总线类传感器-红外遥控的实现

    目录

    一、红外遥控工作原理讲解

    1、光的基础知识

    2、红外通信

    3、红外NEC协议详解

    二、红外遥控实例讲解

    1、实验目的:按下遥控按键,主机通过红外接收器接收到信号并解码,识别出按键的命令码,打印出对应的按键符号。

    2、Cubmx配置

    3、实现思路分析

    (1)协议分析:

    (2)功能分析

    (3)具体实现

    (4)实验演示和源码


     

    一、红外遥控工作原理讲解

    1、光的基础知识

    • 光的本质:光的本质是电磁波,其传播本质上是一种粒子振动。广义上,光是指所有的电磁波谱。狭义上的光是人类眼睛可以看见的一种电磁波,也称可见光。
    • 光的波长:是指波在一个振动周期内传播的距离。光的波长由光的频率以及传播的介质决定,光通过不同介质的时候,频率不变而波长发生改变
    • 光的颜色: 是由它的波长来决定的,各种颜色有各自的波长,人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫。
    • 可见光:光整个电磁波谱包括了无线电波、红外线、紫外线以及X射线等。它们的波长不同,其中波长在380~760纳米之间就是一般的可见光。依次为红、橙、黄、绿、青、蓝、紫。比紫光波长还短的光叫紫外线,比红光波长还长的光叫红外线。
    • 红外线:是波长介于微波与可见光之间的电磁波,波长在1mm到760纳米(nm)之间,比红光长的非可见光。    高于绝对零度(-273.15℃)的物质都可以产生红外线。现代物理学称之为热射线。我们把红光之外的辐射叫做红外线(紫光之外是紫外线),人的肉眼不可见。

    2、红外通信

    无线远程遥控技术:   又称为遥控技术,是指实现对被控目标的遥远控制,在工业控制、航空航天、家电领域应用广泛。

    红外遥控:     是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机和手机系统中。

    红外通讯:就是通过红外线传输数据。发射器发出红外信号,接收器接收到信号进行解析。

    红外遥控器:     红外遥控器是利用一个红外发光二极管,以红外光为载体来将按键信息传递给接收端的设备。红外光对于人眼是不可见的,因此使用红外遥控器不会影响人的视觉(可以打开手机摄像头,遥控器对着摄像头按,可以看到遥控器发出的红外光)。

    信号调制:     日常生活环境中有很多红外光源,太阳、蜡烛火光、白炽灯、甚至是我们的身体。这些红外光源都可能会对我们的接收设备产生干扰,为了屏蔽干扰,只接收有效信息,我们就需要用到调制。     通过调制我们可以把指定的数字信号转换为特定频率的红外光进行发送,调制载波频率一般在30khz到60khz之间,大多数使用的是38kHz

    红外接受器:    红外线接收器是一种可以接收红外信号并能独立完成从红外线接收到输出与TTL电平信号兼容的器件,体积和普通的塑封三极管差不多,适合于各种红外线遥控和红外线数据传输。

    信号解调:    解调就是将模拟信号转换成数字信号。红外接收器接收到外部发射器传过来的红外信号后,会按照固定的协议去解析信号,并转换成数字信号输出。

    3、红外NEC协议详解

    7b44464252ac474c97e7747d1abc7703.png

    二、红外遥控实例讲解

     

    1、实验目的:按下遥控按键,主机通过红外接收器接收到信号并解码,识别出按键的命令码,打印出对应的按键符号。

    所要用到的硬件资源如下:
    1) 红外接收头,遥控接收头在板子上,与MCU 的连接原理图如下
    2) 红外遥控器

    9bf92a338a4045f085bcd3600ba7b54f.png

     我们红外接收器连接的是PA8,一根总线实现通信协议。

    2、Cubmx配置

    使能led,使能168mhz时钟,定时器Tim5用于输入捕获,pa8外部中断配置

    19c46e1070284f3cab39bd3a362020f7.png

     2f97e9fb095648f3a8c4200d0ac052ee.png

    bf6fb9083f4c4c6c9587a9692f9cfc45.png 

     eff8c8e0e3ce4fab97cef32dac31d0dd.png

     2d95fa52d7eb4ae89b740f37ba62c373.png

    触发中断方式是上升沿下降沿都触发

    67d108d30c74471aa5afc8057c6cedb9.png

    3、实现思路分析

    7b44464252ac474c97e7747d1abc7703.png

    (1)协议分析:

    红外遥控传输的数据格式:地址码+地址反码+命令码+命令反码

    根据协议规定,每个地址码或命令码8位,一条数据一共32位数据。

    • 我们定义一个结构体Remote_Infrared_data_struct,定义4个成员来表示地址码和命令码。
    • 再定义一个共同体Remote_Infrared_data_union,定义2个成员,一个保存上面的结构体和完整32位数据。不同数据类型的数据可以使用共同的存储区域

    (2)功能分析

    开始拉低9ms,接着是一个4.5ms的高脉冲,通知器件开始传送数据了接着是发送4个8位二进制码,第一二个是遥控识别码(REMOTE_ID),第一个为正码(0),第二个为反码(255),接着两个数据是键值,第一个为正码第二个为反码.发送完后40ms,遥控再发送一个9ms低,2ms高的脉冲,表示按键的次数,出现一次则证明只按下了一次,如果出现多次,则可以认为是持续按下该键。

    处理红外接收

    定义一个标志位 bBitCounter 记录键盘帧的位数。每次信号进来都加1,代表进来的次数。通过bBitCounter次数和计数器时长来判定起始码和数据码。最后读取按键值。

    261fb691081b4e9d847d3fbde5888a1e.png​​​​​​​

    (3)具体实现

    RomoteInfrared.h

    1. #include "stm32f4xx_hal.h"
    2. #define Remote_Infrared_DAT_INPUT HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
    3. typedef struct _Remote_Infrared_data_struct //定义红外线接收到的数据结构体类型
    4. {
    5. uint8_t bKeyCodeNot; //按键的的ASIIC码值 按键反码
    6. uint8_t bKeyCode; //shift键按下标志 按键码
    7. uint8_t bIDNot; //断码标志位 用户反码
    8. uint8_t bID; //新键标志位 用户码
    9. }Remote_Infrared_data_struct;
    10. typedef union _Remote_Infrared_data_union //定义红外线接收到的数据结构体类型
    11. {
    12. Remote_Infrared_data_struct RemoteInfraredDataStruct; //上面定义的红外线接收到的数据结构体类型
    13. uint32_t uiRemoteInfraredData; // 记录 用户码用户反码按键码按键反码
    14. }Remote_Infrared_data_union;
    15. void Remote_Infrared_KEY_ISR(void);
    16. uint8_t Remote_Infrared_KeyDeCode(void);

    RomoteInfrared.c

    1. #include "RemoteInfrared.h"
    2. #define REPEAT_KEY 0xEE
    3. extern __IO uint32_t GlobalTimingDelay100us;
    4. extern __IO uint32_t GlobalTimingDelay100usTx;
    5. __IO uint32_t FlagGotKey = 0;
    6. __IO Remote_Infrared_data_union RemoteInfrareddata;
    7. /************************************************************************
    8. //处理红外接收
    9. -------------------------协议--------------------------
    10. 开始拉低9ms,接着是一个4.5ms的高脉冲,通知器件开始传送数据了
    11. 接着是发送4个8位二进制码,第一二个是遥控识别码(REMOTE_ID),第一个为
    12. 正码(0),第二个为反码(255),接着两个数据是键值,第一个为正码
    13. 第二个为反码.发送完后40ms,遥控再发送一个9ms低,2ms高的脉冲,
    14. 表示按键的次数,出现一次则证明只按下了一次,如果出现多次,则可
    15. 以认为是持续按下该键.
    16. *名称: Remote_Infrared_KEY_ISR(INT11_vect )
    17. *功能: INT0中断服务程序
    18. *参数: 无
    19. *返回: 无
    20. *************************************************************************/
    21. // 检测脉冲宽度最长脉宽为5ms
    22. const uint32_t TIME_DELAY_6MS = 60;
    23. const uint32_t TIME_DELAY_10MS = 100;
    24. //按键中断服务程序ISR
    25. void Remote_Infrared_KEY_ISR(void)
    26. {
    27. static __IO uint8_t bBitCounter = 0; //键盘帧位计数
    28. static __IO uint32_t bKeyCode = 0;
    29. bBitCounter++; //每次信号进来都加加,代表进来的次数
    30. if(bBitCounter == 1) // 开始拉低9ms
    31. {
    32. if(Remote_Infrared_DAT_INPUT) // 高电平无效
    33. {
    34. bBitCounter = 0;
    35. }
    36. else
    37. {
    38. GlobalTimingDelay100us = TIME_DELAY_10MS; //100赋给变量 ,这个变量会在中断函数里减
    39. }
    40. }
    41. else if(bBitCounter == 2) // 4.5ms的高脉冲
    42. {
    43. if(Remote_Infrared_DAT_INPUT)
    44. {
    45. if((GlobalTimingDelay100us > 2) && (GlobalTimingDelay100us < 18)) //GlobalTimingDelay100us检查到减到什么程度了 100-90应该是10 2-18范围内
    46. {
    47. GlobalTimingDelay100us = TIME_DELAY_6MS;
    48. }
    49. else
    50. {
    51. bBitCounter = 0;
    52. //printf(".");
    53. }
    54. }
    55. else
    56. {
    57. bBitCounter = 0;
    58. }
    59. }
    60. else if(bBitCounter == 3) // 4.5ms的高脉冲
    61. {
    62. if(Remote_Infrared_DAT_INPUT)
    63. {
    64. bBitCounter = 0;
    65. }
    66. else
    67. {
    68. if((GlobalTimingDelay100us > 5) && (GlobalTimingDelay100us < 20)) //起始码 4.5ms 5<60-45<20
    69. {
    70. GlobalTimingDelay100us = TIME_DELAY_6MS;
    71. //printf("引导码");
    72. }
    73. else if((GlobalTimingDelay100us > 32) && (GlobalTimingDelay100us < 46)) //重复码 2.25ms
    74. {
    75. //重复码
    76. bBitCounter = 0; //检测到结束信号 次数置0
    77. RemoteInfrareddata.uiRemoteInfraredData = bKeyCode; //把按键码保存到结构体
    78. //RemoteInfrareddata.uiRemoteInfraredData = REPEAT_KEY;
    79. bBitCounter = 0;
    80. FlagGotKey = 1;
    81. }
    82. else
    83. {
    84. bBitCounter = 0;
    85. //printf("%d&", GlobalTimingDelay100us);
    86. }
    87. }
    88. }
    89. else if(bBitCounter > 3 && bBitCounter < 68) //接收8位数据
    90. {
    91. if(Remote_Infrared_DAT_INPUT) //检测数据脉冲低电平的时间
    92. {
    93. if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58))
    94. {
    95. GlobalTimingDelay100us = TIME_DELAY_6MS;
    96. }
    97. else
    98. {
    99. bBitCounter = 0;
    100. //printf("#");
    101. }
    102. }
    103. else
    104. {
    105. if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58)) // '0' 0.56ms左右
    106. {
    107. GlobalTimingDelay100us = TIME_DELAY_6MS;
    108. bKeyCode <<= 1; // MSB First
    109. bKeyCode += 0x00;
    110. }
    111. else if((GlobalTimingDelay100us > 40) && (GlobalTimingDelay100us < 48)) //'1' 1.685ms左右
    112. {
    113. GlobalTimingDelay100us = TIME_DELAY_6MS;
    114. bKeyCode <<= 1; // MSB First
    115. bKeyCode += 0x01;
    116. }
    117. else
    118. {
    119. bBitCounter = 0;
    120. }
    121. }
    122. if(bBitCounter == 67)
    123. {
    124. RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
    125. bBitCounter = 0;
    126. FlagGotKey = 1;
    127. //printf("KeyCode = 0x%X", bKeyCode);
    128. }
    129. }
    130. else
    131. {
    132. bBitCounter = 0;
    133. //printf("KeyCode = 0x%X", bKeyCode);
    134. }
    135. }
    136. /************************************************************************
    137. *名称: unsigned char Remote_Infrared_KeyDeCode(unsigned char bKeyCode)
    138. *功能: PS2键盘解码程序
    139. *参数: bKeyCode 键盘码
    140. *返回: 按键的ASIIC码
    141. ************************************************************************/
    142. uint8_t Remote_Infrared_KeyDeCode(void)
    143. {
    144. // uint8_t Key = 0xFF;
    145. if (FlagGotKey == 1)//通码 0没按,1按了,外部会判断解析按没按并返回值
    146. {
    147. FlagGotKey = 0;//方便下次再进来
    148. if((RemoteInfrareddata.RemoteInfraredDataStruct.bID == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bIDNot) //校验用户码
    149. && (RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCodeNot)) //校验按键码
    150. {
    151. printf("\n\r IR Receive KeyCode = 0x%02X, ", RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode);
    152. switch(RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode) //提取按键码
    153. {
    154. case 0: printf("ERROR");
    155. break;
    156. case 162:printf("POWER");
    157. break;
    158. case 98:printf("UP");
    159. break;
    160. case 2:printf("PLAY");
    161. break;
    162. case 226:printf("ALIENTEK");
    163. break;
    164. case 194:printf("RIGHT");
    165. break;
    166. case 34:printf("LEFT");
    167. break;
    168. case 224:printf("VOL-");
    169. break;
    170. case 168:printf("DOWN");
    171. break;
    172. case 144:printf("VOL+");
    173. break;
    174. case 104:
    175. printf("1");
    176. HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
    177. break;
    178. case 152:printf("2");
    179. HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
    180. break;
    181. case 176:printf("3");
    182. break;
    183. case 48:printf("4");
    184. break;
    185. case 24:printf("5");
    186. break;
    187. case 122:printf("6");
    188. break;
    189. case 16:printf("7");
    190. break;
    191. case 56:printf("8");
    192. break;
    193. case 90:printf("9");
    194. break;
    195. case 66:printf("0");
    196. break;
    197. case 82:printf("DELETE");
    198. break;
    199. default:
    200. printf("Unknown key!");
    201. }
    202. }
    203. else
    204. {
    205. printf("\n\r ERR 0x%08X", RemoteInfrareddata.uiRemoteInfraredData);
    206. }
    207. }
    208. return RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode;
    209. }

    main.c

    1. #include "main.h"
    2. #include "stm32f4xx_hal.h"
    3. #include "tim.h"
    4. #include "usart.h"
    5. #include "gpio.h"
    6. /* USER CODE BEGIN Includes */
    7. #include
    8. #include "RemoteInfrared.h"
    9. /* USER CODE END Includes */
    10. /* Private variables ---------------------------------------------------------*/
    11. /* USER CODE BEGIN PV */
    12. /* Private variables ---------------------------------------------------------*/
    13. __IO uint32_t GlobalTimingDelay100us;
    14. /* USER CODE END PV */
    15. /* Private function prototypes -----------------------------------------------*/
    16. void SystemClock_Config(void);
    17. /* USER CODE BEGIN PFP */
    18. /* Private function prototypes -----------------------------------------------*/
    19. /* USER CODE END PFP */
    20. /* USER CODE BEGIN 0 */
    21. int fputc(int ch,FILE *p)
    22. {
    23. while(!(USART1->SR & (1<<7)));
    24. USART1->DR = (uint8_t)ch;
    25. return ch;
    26. }
    27. /* USER CODE BEGIN 0 */
    28. int fputc(int ch,FILE *p)
    29. {
    30. while(!(USART1->SR & (1<<7)));
    31. USART1->DR = (uint8_t)ch;
    32. return ch;
    33. }
    34. /* USER CODE END 0 */
    35. /**
    36. * @brief The application entry point.
    37. *
    38. * @retval None
    39. */
    40. int main(void)
    41. {
    42. /* USER CODE BEGIN 1 */
    43. /* USER CODE END 1 */
    44. /* MCU Configuration----------------------------------------------------------*/
    45. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    46. HAL_Init();
    47. /* USER CODE BEGIN Init */
    48. /* USER CODE END Init */
    49. /* Configure the system clock */
    50. SystemClock_Config();
    51. /* USER CODE BEGIN SysInit */
    52. /* USER CODE END SysInit */
    53. /* Initialize all configured peripherals */
    54. MX_GPIO_Init();
    55. MX_USART1_UART_Init();
    56. MX_TIM5_Init();
    57. /* USER CODE BEGIN 2 */
    58. /*设置SYSTICK为 100us中断一次 */
    59. HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/10000); //原本主频/1000得到的是1ms中断,因为我们需要用到计时器100us计数,所以我们除10000,得到的是100us计数1次
    60. HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
    61. HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
    62. printf("remote test");
    63. printf("\n\r");
    64. printf("\n\r-------------------------------------------------");
    65. printf("\n\r FS-STM32开发板 IR红外线接收实验程序");
    66. printf("\n\r 请将红外线接收头连接到开发板的对应接口");
    67. printf("\n\r 然后用红外遥控器进行控制,注意串口输出");
    68. printf("\n\r-------------------------------------------------");
    69. printf("\n\r----------------- 协议如下 ----------------------");
    70. printf("\n\r 首先是引导码: 开始拉低9ms,接着一个4.5ms的高脉冲.");
    71. printf("\n\r 引导码的作用是通知接收方器准备接收数据;");
    72. printf("\n\r 引导码之后是4个字节的二进制码, 其中前两个字节是");
    73. printf("\n\r 遥控识别码(ID), 第一个为正码,第二个为反码,");
    74. printf("\n\r 后两字节是键值, 第一个为正码,第二个为反码.");
    75. printf("\n\r 最后可能有持续按下通知, 上述数据发送完后如果刚才");
    76. printf("\n\r 的按键持续被按下, 则发送9ms低,2ms高的脉冲。");
    77. printf("\n\r---------------- 载波为38kHz --------------------");
    78. printf("\n\r 传输一个逻辑1需要2.25ms(560us低电平+1680us高电平)");
    79. printf("\n\r 传输一个逻辑0需要1.125ms(560us低电平+560us高电平)");
    80. printf("\n\r-------------------------------------------------");
    81. printf("\n\r----- 本实验在中断中检测接收IR红外线的数据 ------\n\r");
    82. /* USER CODE END 2 */
    83. /* Infinite loop */
    84. /* USER CODE BEGIN WHILE */
    85. while (1)
    86. {
    87. HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_RESET);
    88. HAL_Delay(1000);
    89. HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_SET);
    90. HAL_Delay(1000);
    91. Remote_Infrared_KeyDeCode();
    92. /* USER CODE END WHILE */
    93. /* USER CODE BEGIN 3 */
    94. }
    95. /* USER CODE END 3 */
    96. }
    97. /* USER CODE BEGIN 4 */
    98. void HAL_SYSTICK_Callback(void)
    99. {
    100. if(GlobalTimingDelay100us != 0)
    101. {
    102. GlobalTimingDelay100us--;
    103. }
    104. }
    105. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    106. {
    107. Remote_Infrared_KEY_ISR();
    108. }
    109. /* USER CODE END 4 */

    a4c1ce193f794d1d89b4c4a3398dc82e.png

    (4)实验演示和源码

    后期上传实验效果和源码

     

  • 相关阅读:
    数字孪生电力系统,可视化应用实现科学调度的电子设备
    什么是Jmeter?Jmeter使用的原理步骤是什么?
    解决Qt程序桌面图标有锯齿的问题
    Shell脚本语言用法详解(超详细~)
    多线程(一)——委托与多线程
    Spring5源码-监听器原理
    使用1688开放平台API接口获取商品详情信息
    『PyQt5-基本控件』| 14 主窗口的类型、创建以及代码如何实现?
    基于GAMS的电力系统优化分析
    电网变电站安全作业行为规范检测-绝缘手套佩戴检测图像数据集(VOC,YOLO两类标签,六类目标)
  • 原文地址:https://blog.csdn.net/m0_60718520/article/details/127580525