前言:单总线类传感器-红外遥控的实现
目录
一、红外遥控工作原理讲解
1、光的基础知识
2、红外通信
3、红外NEC协议详解
二、红外遥控实例讲解
1、实验目的:按下遥控按键,主机通过红外接收器接收到信号并解码,识别出按键的命令码,打印出对应的按键符号。
2、Cubmx配置
3、实现思路分析
(1)协议分析:
(2)功能分析
(3)具体实现
(4)实验演示和源码
无线远程遥控技术: 又称为遥控技术,是指实现对被控目标的遥远控制,在工业控制、航空航天、家电领域应用广泛。
红外遥控: 是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机和手机系统中。
红外通讯:就是通过红外线传输数据。发射器发出红外信号,接收器接收到信号进行解析。
红外遥控器: 红外遥控器是利用一个红外发光二极管,以红外光为载体来将按键信息传递给接收端的设备。红外光对于人眼是不可见的,因此使用红外遥控器不会影响人的视觉(可以打开手机摄像头,遥控器对着摄像头按,可以看到遥控器发出的红外光)。
信号调制: 日常生活环境中有很多红外光源,太阳、蜡烛火光、白炽灯、甚至是我们的身体。这些红外光源都可能会对我们的接收设备产生干扰,为了屏蔽干扰,只接收有效信息,我们就需要用到调制。 通过调制我们可以把指定的数字信号转换为特定频率的红外光进行发送,调制载波频率一般在30khz到60khz之间,大多数使用的是38kHz
红外接受器: 红外线接收器是一种可以接收红外信号并能独立完成从红外线接收到输出与TTL电平信号兼容的器件,体积和普通的塑封三极管差不多,适合于各种红外线遥控和红外线数据传输。
信号解调: 解调就是将模拟信号转换成数字信号。红外接收器接收到外部发射器传过来的红外信号后,会按照固定的协议去解析信号,并转换成数字信号输出。
我们红外接收器连接的是PA8,一根总线实现通信协议。
使能led,使能168mhz时钟,定时器Tim5用于输入捕获,pa8外部中断配置
触发中断方式是上升沿下降沿都触发
红外遥控传输的数据格式:地址码+地址反码+命令码+命令反码
根据协议规定,每个地址码或命令码8位,一条数据一共32位数据。
开始拉低9ms,接着是一个4.5ms的高脉冲,通知器件开始传送数据了接着是发送4个8位二进制码,第一二个是遥控识别码(REMOTE_ID),第一个为正码(0),第二个为反码(255),接着两个数据是键值,第一个为正码第二个为反码.发送完后40ms,遥控再发送一个9ms低,2ms高的脉冲,表示按键的次数,出现一次则证明只按下了一次,如果出现多次,则可以认为是持续按下该键。
处理红外接收
定义一个标志位 bBitCounter 记录键盘帧的位数。每次信号进来都加1,代表进来的次数。通过bBitCounter次数和计数器时长来判定起始码和数据码。最后读取按键值。
RomoteInfrared.h
#include "stm32f4xx_hal.h"
#define Remote_Infrared_DAT_INPUT HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
typedef struct _Remote_Infrared_data_struct //定义红外线接收到的数据结构体类型
{
uint8_t bKeyCodeNot; //按键的的ASIIC码值 按键反码
uint8_t bKeyCode; //shift键按下标志 按键码
uint8_t bIDNot; //断码标志位 用户反码
uint8_t bID; //新键标志位 用户码
}Remote_Infrared_data_struct;
typedef union _Remote_Infrared_data_union //定义红外线接收到的数据结构体类型
{
Remote_Infrared_data_struct RemoteInfraredDataStruct; //上面定义的红外线接收到的数据结构体类型
uint32_t uiRemoteInfraredData; // 记录 用户码用户反码按键码按键反码
}Remote_Infrared_data_union;
void Remote_Infrared_KEY_ISR(void);
uint8_t Remote_Infrared_KeyDeCode(void);
RomoteInfrared.c
#include "RemoteInfrared.h"
#define REPEAT_KEY 0xEE
extern __IO uint32_t GlobalTimingDelay100us;
extern __IO uint32_t GlobalTimingDelay100usTx;
__IO uint32_t FlagGotKey = 0;
__IO Remote_Infrared_data_union RemoteInfrareddata;
/************************************************************************
//处理红外接收
-------------------------协议--------------------------
开始拉低9ms,接着是一个4.5ms的高脉冲,通知器件开始传送数据了
接着是发送4个8位二进制码,第一二个是遥控识别码(REMOTE_ID),第一个为
正码(0),第二个为反码(255),接着两个数据是键值,第一个为正码
第二个为反码.发送完后40ms,遥控再发送一个9ms低,2ms高的脉冲,
表示按键的次数,出现一次则证明只按下了一次,如果出现多次,则可
以认为是持续按下该键.
*名称: Remote_Infrared_KEY_ISR(INT11_vect )
*功能: INT0中断服务程序
*参数: 无
*返回: 无
*************************************************************************/
// 检测脉冲宽度最长脉宽为5ms
const uint32_t TIME_DELAY_6MS = 60;
const uint32_t TIME_DELAY_10MS = 100;
//按键中断服务程序ISR
void Remote_Infrared_KEY_ISR(void)
{
static __IO uint8_t bBitCounter = 0; //键盘帧位计数
static __IO uint32_t bKeyCode = 0;
bBitCounter++; //每次信号进来都加加,代表进来的次数
if(bBitCounter == 1) // 开始拉低9ms
{
if(Remote_Infrared_DAT_INPUT) // 高电平无效
{
bBitCounter = 0;
}
else
{
GlobalTimingDelay100us = TIME_DELAY_10MS; //100赋给变量 ,这个变量会在中断函数里减
}
}
else if(bBitCounter == 2) // 4.5ms的高脉冲
{
if(Remote_Infrared_DAT_INPUT)
{
if((GlobalTimingDelay100us > 2) && (GlobalTimingDelay100us < 18)) //GlobalTimingDelay100us检查到减到什么程度了 100-90应该是10 2-18范围内
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
}
else
{
bBitCounter = 0;
//printf(".");
}
}
else
{
bBitCounter = 0;
}
}
else if(bBitCounter == 3) // 4.5ms的高脉冲
{
if(Remote_Infrared_DAT_INPUT)
{
bBitCounter = 0;
}
else
{
if((GlobalTimingDelay100us > 5) && (GlobalTimingDelay100us < 20)) //起始码 4.5ms 5<60-45<20
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
//printf("引导码");
}
else if((GlobalTimingDelay100us > 32) && (GlobalTimingDelay100us < 46)) //重复码 2.25ms
{
//重复码
bBitCounter = 0; //检测到结束信号 次数置0
RemoteInfrareddata.uiRemoteInfraredData = bKeyCode; //把按键码保存到结构体
//RemoteInfrareddata.uiRemoteInfraredData = REPEAT_KEY;
bBitCounter = 0;
FlagGotKey = 1;
}
else
{
bBitCounter = 0;
//printf("%d&", GlobalTimingDelay100us);
}
}
}
else if(bBitCounter > 3 && bBitCounter < 68) //接收8位数据
{
if(Remote_Infrared_DAT_INPUT) //检测数据脉冲低电平的时间
{
if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58))
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
}
else
{
bBitCounter = 0;
//printf("#");
}
}
else
{
if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58)) // '0' 0.56ms左右
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
bKeyCode <<= 1; // MSB First
bKeyCode += 0x00;
}
else if((GlobalTimingDelay100us > 40) && (GlobalTimingDelay100us < 48)) //'1' 1.685ms左右
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
bKeyCode <<= 1; // MSB First
bKeyCode += 0x01;
}
else
{
bBitCounter = 0;
}
}
if(bBitCounter == 67)
{
RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
bBitCounter = 0;
FlagGotKey = 1;
//printf("KeyCode = 0x%X", bKeyCode);
}
}
else
{
bBitCounter = 0;
//printf("KeyCode = 0x%X", bKeyCode);
}
}
/************************************************************************
*名称: unsigned char Remote_Infrared_KeyDeCode(unsigned char bKeyCode)
*功能: PS2键盘解码程序
*参数: bKeyCode 键盘码
*返回: 按键的ASIIC码
************************************************************************/
uint8_t Remote_Infrared_KeyDeCode(void)
{
// uint8_t Key = 0xFF;
if (FlagGotKey == 1)//通码 0没按,1按了,外部会判断解析按没按并返回值
{
FlagGotKey = 0;//方便下次再进来
if((RemoteInfrareddata.RemoteInfraredDataStruct.bID == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bIDNot) //校验用户码
&& (RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCodeNot)) //校验按键码
{
printf("\n\r IR Receive KeyCode = 0x%02X, ", RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode);
switch(RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode) //提取按键码
{
case 0: printf("ERROR");
break;
case 162:printf("POWER");
break;
case 98:printf("UP");
break;
case 2:printf("PLAY");
break;
case 226:printf("ALIENTEK");
break;
case 194:printf("RIGHT");
break;
case 34:printf("LEFT");
break;
case 224:printf("VOL-");
break;
case 168:printf("DOWN");
break;
case 144:printf("VOL+");
break;
case 104:
printf("1");
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
break;
case 152:printf("2");
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
break;
case 176:printf("3");
break;
case 48:printf("4");
break;
case 24:printf("5");
break;
case 122:printf("6");
break;
case 16:printf("7");
break;
case 56:printf("8");
break;
case 90:printf("9");
break;
case 66:printf("0");
break;
case 82:printf("DELETE");
break;
default:
printf("Unknown key!");
}
}
else
{
printf("\n\r ERR 0x%08X", RemoteInfrareddata.uiRemoteInfraredData);
}
}
return RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode;
}
main.c
#include "main.h"
#include "stm32f4xx_hal.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* USER CODE BEGIN Includes */
#include
#include "RemoteInfrared.h"
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
__IO uint32_t GlobalTimingDelay100us;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *p)
{
while(!(USART1->SR & (1<<7)));
USART1->DR = (uint8_t)ch;
return ch;
}
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *p)
{
while(!(USART1->SR & (1<<7)));
USART1->DR = (uint8_t)ch;
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
*
* @retval None
*/
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();
MX_TIM5_Init();
/* USER CODE BEGIN 2 */
/*设置SYSTICK为 100us中断一次 */
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/10000); //原本主频/1000得到的是1ms中断,因为我们需要用到计时器100us计数,所以我们除10000,得到的是100us计数1次
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
printf("remote test");
printf("\n\r");
printf("\n\r-------------------------------------------------");
printf("\n\r FS-STM32开发板 IR红外线接收实验程序");
printf("\n\r 请将红外线接收头连接到开发板的对应接口");
printf("\n\r 然后用红外遥控器进行控制,注意串口输出");
printf("\n\r-------------------------------------------------");
printf("\n\r----------------- 协议如下 ----------------------");
printf("\n\r 首先是引导码: 开始拉低9ms,接着一个4.5ms的高脉冲.");
printf("\n\r 引导码的作用是通知接收方器准备接收数据;");
printf("\n\r 引导码之后是4个字节的二进制码, 其中前两个字节是");
printf("\n\r 遥控识别码(ID), 第一个为正码,第二个为反码,");
printf("\n\r 后两字节是键值, 第一个为正码,第二个为反码.");
printf("\n\r 最后可能有持续按下通知, 上述数据发送完后如果刚才");
printf("\n\r 的按键持续被按下, 则发送9ms低,2ms高的脉冲。");
printf("\n\r---------------- 载波为38kHz --------------------");
printf("\n\r 传输一个逻辑1需要2.25ms(560us低电平+1680us高电平)");
printf("\n\r 传输一个逻辑0需要1.125ms(560us低电平+560us高电平)");
printf("\n\r-------------------------------------------------");
printf("\n\r----- 本实验在中断中检测接收IR红外线的数据 ------\n\r");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_SET);
HAL_Delay(1000);
Remote_Infrared_KeyDeCode();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
void HAL_SYSTICK_Callback(void)
{
if(GlobalTimingDelay100us != 0)
{
GlobalTimingDelay100us--;
}
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
Remote_Infrared_KEY_ISR();
}
/* USER CODE END 4 */
后期上传实验效果和源码