历史上的今天
返回首页

历史上的今天

今天是:2025年04月23日(星期三)

正在发生

2018年04月23日 | STM32 定时器实现红外遥控数据接收

2018-04-23 来源:eefocus

一、原理

1、红外发射协议

  • 红外通信的协议有很多种。这个实验使用的是NEC协议。这个协议采用PWM的方法进行调制,利用脉冲宽度来表示 0 和 1 。

  • NEC 遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码均是 8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性。因此,每帧的数据为 32 位,包括地址码,地址反码,控制码,控制反码。反码可用于解码时进行校验比对。

  • NEC码的位定义:一个脉冲对应 560us 的连续载波,一个逻辑 1 传输需要 2.25ms(560us 脉冲+1680us 低电平),一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平)。而遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,在接收头端收到的信号为:逻辑 1 应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高。


  • 红外数据的波形如下图:包括一个同步头和 32 帧数据。 
    这里写图片描述

  • 下图可看出,同步头为 9ms 低电平加上 4.5ms 高电平,控制码为 8 个 0,控制反码为 8 个 1。 
    这里写图片描述

2、定时器计数

  • 定时器就是按照一个特定的频率对计数值进行加一或减一操作,当数值溢出时则产生一个标志或中断。这里是用定时器计数产生一个周期性的中断。

3、实现方法

  • 利用定时器记录两个下降沿之间的时间,通过该时间判断是否是同步头信息、数据 1 或者数据 0。当检测到同步头,开始记录 32 个数据的时间值。 
    这里写图片描述

二、实现

1、配置 GPIO 口下降沿触发中断

  • 示例代码中使用 PA7 管脚,配置为上拉输入模式。

  • 选择下降沿触发,是因为红外接收管默认情况下保持高电平,接收到数据时从高电平转变为低电平。

  • 中断源选择为 EXTI_Line7 ,在库函数中对该中断源定义的服务函数为 EXTI9_5_IRQHandler(),也就是说外部中断 5 到 9 是 共用一个中断服务函数的。

  • 配置代码如下:


void IR_Pin_init()

{

  GPIO_InitTypeDef GPIO_InitStructure;

  EXTI_InitTypeDef EXTI_InitStructure;

  NVIC_InitTypeDef NVIC_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);


  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;

  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;

  GPIO_Init(GPIOA,&GPIO_InitStructure);


  EXTI_ClearITPendingBit(EXTI_Line7);

  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7); 

  EXTI_InitStructure.EXTI_Line=EXTI_Line7;

  EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;

  EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

  EXTI_InitStructure.EXTI_LineCmd=ENABLE;

  EXTI_Init(&EXTI_InitStructure); 


  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 

  NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; 

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  

  NVIC_Init(&NVIC_InitStructure);

}


2、配置定时器计数值


定时器使用的是 TIM2 通用定时器,模式为向上计数。在该模式中,计数器从 0 计数到自动加载值 (TIMx_ARR计数器的内容) ,然后重新从 0 开始计数并且产生一个计数器溢出事件。


示例函数接收两个参数,分别为预分频器的值和自动加载值。通过调整这两个参数,可以灵活地改变定时器的计数周期。例如在 TIM2 的默认时钟源 PCLK1 为96MHz时,使用语句 Tim2_UPCount_Init(SystemCoreClock/1000000-1,100-1); //0.1ms 进行初始化,可以每 0.1ms 产生一次中断。


示例代码如下:


void Tim2_UPCount_Init(u16 Prescaler,u16 Period)

{

  TIM_TimeBaseInitTypeDef TIM_StructInit;

  NVIC_InitTypeDef NVIC_StructInit;


  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);


  TIM_StructInit.TIM_Period=Period;

  TIM_StructInit.TIM_Prescaler=Prescaler;

  TIM_StructInit.TIM_ClockDivision=TIM_CKD_DIV1;

  TIM_StructInit.TIM_CounterMode=TIM_CounterMode_Up;

  TIM_StructInit.TIM_RepetitionCounter=0;

  TIM_TimeBaseInit(TIM2, &TIM_StructInit);


  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

  TIM_Cmd(TIM2, ENABLE);    

  TIM_ClearFlag(TIM2, TIM_FLAG_Update);


  NVIC_StructInit.NVIC_IRQChannel=TIM2_IRQn;

  NVIC_StructInit.NVIC_IRQChannelCmd=ENABLE;

  NVIC_StructInit.NVIC_IRQChannelPreemptionPriority=0;

  NVIC_StructInit.NVIC_IRQChannelSubPriority=1;

  NVIC_Init(&NVIC_StructInit);

}


3、定时器中断函数统计时间

  • 如上所说,定时器每 0.1ms 计数完成,产生中断,在中断函数中对标志位 ucTim2Flag 加 1,意味着时间过去了 0.1ms。

  • 时间标志位原型为 uint16_t ucTim2Flag; 。

  • 示例代码如下:


void TIM2_IRQHandler(void)

{

  TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

  ucTim2Flag++;

}


4、GPIO 中断函数中接收 32 位数据

  • 在下降沿触发的 IO 口中断函数中,需要实现统计两个下降沿之间的时间,并将其记录在数组中。

  • 下降沿第一次触发时,清除当前定时器中的计数值,以便统计时间。之后每一次下降沿触发就记录下当前计数值,然后再对其清零。如果该时间在同步头的时间区间内,对索引进行清零,表示重新开始接收数据。

  • 完整接收同步头和 32 个数据之后,表示接收完成。

  • 示例代码如下:


uint8_t irdata[33];   //用于记录两个下降沿之间的时间

bool receiveComplete; //接收完成标志位

uint8_t idx;          //用于索引接收到的数值

bool startflag;       //表示开始接收


void EXTI9_5_IRQHandler(void)

{

  uint16_t ir_time;

  if(startflag)

  {

      ir_time = ucTim2Flag; 

      if(ucTim2Flag < 150 && ucTim2Flag >= 50 ) // 接收到同步头

      {

          idx=0;  // 数组下标清零

      }


      irdata[idx] = ucTim2Flag;  // 获取计数时间

      ucTim2Flag = 0;            // 清零计数时间,以便下次统计

      idx++;                     // 接收到一个数据,索引加1


      if(idx==33)       // 如果接收到33个数据,包括32位数和以一个同步头

      {

          idx=0;

          ucTim2Flag = 0;

          receiveComplete = TRUE;

      }

  }

  else   // 下降沿第一次触发

  {

      idx = 0;

      ucTim2Flag = 0;

      startflag = TRUE;

  }


EXTI_ClearITPendingBit(EXTI_Line7);  // 清除中断标志

}


5、判断控制码值

  • 由于中断函数中接收并记录下的数据是两个下降沿之间的时间,并不是红外所发送的数据。因此需要根据红外协议,对 32 个时间进行判断,从而获得红外真正发送的数据。

  • 下面这个函数需要在红外完整接收数据后执行,可通过判断接收完成标志位 receiveComplete 来实现。

  • 示例代码如下:


uint8_t Ir_Server()

{

  uint8_t i,j,idx=1; //idx 从1 开始表示对同步头的时间不处理

  uint8_t temp;

  for(i=0; i<4; i++)

  {

      for(j=0; j<8; j++)

      {

          if(irdata[idx] >=8 && irdata[idx] < 15)   //表示 0

          {

              temp = 0;

          }

          else if(irdata[idx] >=18 && irdata[idx]<25) //表示 1

          {

              temp = 1;

          }

          remote_code[i] <<= 1;

          remote_code[i] |= temp;

          idx++;

      }

  }


  return remote_code[2];  // 该数组中记录的是控制码,每个按键不一样

  //for(idx=0; idx<4; idx++)

  //{

  //    printf("remote_code[%d] = %#x\n",idx,remote_code[idx]);

  //}

}


6、主函数

  • 在 main 函数中,对 IO 口和 定时器进行初始化。

  • 主循环中,通过判断接收完成标志位,对接收完成的按键控制码进行打印。

  • 示例代码如下:


void main()

{

  ...


  IR_Pin_init();

  Tim2_UPCount_Init(SystemCoreClock/1000000-1,100-1);


  while(1)

  {

      if(repeatEnable)

      {

          repeatEnable = FALSE;

          Ir_Server();

          printf("key_code = %#x\n",remote_code[2]);

      }

  }


}


三、演示

如下图为串口打印出接收的红外按键值信息: 
这里写图片描述

说明1:这只是实现红外接收的其中一种方法,网上还有一种比较常见的方法是利用下降沿触发,在中断中进行延迟,判断高电平持续时间以此来判断信号类别。个人感觉这不是一种很好的方法,因为在中断中进行延时会导致主函数得不到及时的处理。

说明2:在调试时,不要在中断处理中加入过多无关语句,例如打印语句,这会导致结果出错。


推荐阅读

史海拾趣

方向电子公司的发展小趣事

机顶盒,全称为数字视频变换盒,是连接电视机与外部信号源的关键设备,具有高度的专业性和广泛的应用性。从广义上讲,凡是能与电视机连接并处理音视频信号的网络终端设备均可视为机顶盒。它不仅能够接收来自有线电缆、卫星天线、宽带网络及地面广播的数字电视信号,还能通过内置的解码器将这些信号转换为电视机可识别的格式,从而呈现出高清乃至4K的超高清画质,大大提升了观看体验。

机顶盒不仅限于基本的电视信号接收功能,还集成了多种增值服务。例如,它提供电子节目指南,让用户轻松查找和预约节目;支持因特网网页浏览,实现网络购物、在线视频观看、游戏娱乐等多元化互动体验。随着智能化技术的发展,现代机顶盒还融入了语音助手、智能推荐等功能,进一步提升了用户的操作便捷性和内容个性化程度。

在技术层面,机顶盒的发展日新月异,不断向高清化、智能化方向迈进。5G技术的应用更是为机顶盒带来了更快的传输速度和更低的延迟,为用户提供了更为流畅的观影体验。此外,虚拟现实和增强现实技术的融入,也为机顶盒的未来发展打开了新的想象空间,将为用户带来更为沉浸式和互动式的观影享受。

综上所述,机顶盒作为现代家庭娱乐的重要组成部分,不仅极大地丰富了人们的电视观看体验,还通过不断的技术创新和服务升级,满足着用户日益多样化的需求。

意华(CZT)公司的发展小趣事

自1995年成立以来,意华(CZT)始终秉持“诚信”的合作态度和“创新”的发展思路。在公司初创阶段,面对激烈的市场竞争,意华坚持高质量的产品和服务,赢得了客户的信赖。同时,公司不断投入研发,推出了一系列具有创新性的电子产品和连接器,迅速在市场上树立了良好的口碑。

Bel Power Solutions公司的发展小趣事

随着技术的不断进步,Bel Power Solutions始终保持着对创新的热情。公司不断加大研发投入,积极引进先进技术和人才,推动产品的升级换代。其中,一项重要的技术突破是公司成功研发出具有高效能、高可靠性特点的电源转换技术。这一技术的推出,不仅提升了公司产品的竞争力,还为客户提供了更加优质、可靠的电源解决方案。同时,公司还根据市场需求,推出了多款定制化、个性化的电源产品,满足了不同客户的特殊需求。

Everett Charles Technologies (ECT)公司的发展小趣事

1965年,Everett Charles Technologies (ECT) 公司正式成立,标志着其在电子测试系统硬件与软件领域的起点。ECT的创始人凭借对电子技术的深刻理解和市场需求的敏锐洞察,成功开发出了一系列符合工业标准的产品,包括POGO探针、ValuGrid测试治具等。这些产品的推出,不仅奠定了ECT在电子测试领域的基础,也为公司的后续发展提供了强大的动力。

Eureka Microelectronics Inc公司的发展小趣事

为了支持公司的持续发展和业务扩张,Eureka成功完成了多轮融资。这些融资不仅为公司提供了充足的资金支持,也吸引了众多投资者的关注。借助这些资金,Eureka加大了在研发、生产、销售等方面的投入,进一步提升了公司的竞争力和市场地位。同时,公司也积极开展战略合作,与上下游企业建立了紧密的合作关系,共同推动产业链的发展。

ANOVA公司的发展小趣事

ANOVA公司的成功并非偶然,它始终站在技术创新的前沿。公司积极推动新通信技术、电池寿命和移动应用等方面的创新,不断推动电子行业的发展。ANOVA的研发团队不断探索新技术,致力于为客户提供最先进的解决方案,帮助客户在激烈的市场竞争中保持领先地位。

问答坊 | AI 解惑

matlab第四课-多项式乘法

两个主要是有conv指令完成的啦!…

查看全部问答>

Cortex-M3(版本2)比前代有哪些改进和提高?改进

http://infocenter.arm.com/help/t ... tex_m3_r2p0_trm.pdf 1.7.3. Differences in functionality between r1p1 and r2p0 In summary, the differences in functionality include: 1, Implementation time options have been added to select b ...…

查看全部问答>

三星S3C2440参考设计!

三星S3C2440参考设计!…

查看全部问答>

如何将读取数据改为异步读取

我用HIDLIB库做了个读取USB HID设备的VC6程序. 同步读取数据好像经常死机.         unsigned long lpNumberOfBytesRead=0;         unsigned char buffer[Receive_no], display[Receive_no*3+1]; &nbs ...…

查看全部问答>

Microsoft.WINDOWS.SERVER.V2003.IFD.DDK-ISO 如何安装?

Microsoft.WINDOWS.SERVER.V2003.IFD.DDK-ISO如何安装?…

查看全部问答>

怎么把PPC下面的程序移植到CE4.2上?

现在有一个程序是基于ppc2002,wince3.0,但是由于我没有装PPC的SDK所以导致我在EVC下无法编译,现在准备把程序移植到WINCE4.2上去,不牵涉到PPC了,请问需要做哪些工作才能让程序运行起来啊?…

查看全部问答>

VHDL求模运算

最近在写一个VHDL的年历+时钟程序,发现mod运算不能识别, if (year mod 400=0) or ((year mod 4=0) and (year mod 100 /=0)) then                         leap…

查看全部问答>

求PCI主模式下源代码

我只有在lattice下载的从模式源代码,现在想做主模式的。请有这方面资料的人给点帮助吧…

查看全部问答>

刚开始接触AVR,求教大虾AVR单片机的学习心得

刚开始接触AVR,求教大虾AVR单片机的学习心得…

查看全部问答>

G2553串口接收中断

我在用g2553写串口接收程序时,将串口接收的那个寄存器赋值给变量,之后就不进中断了,数据时用上位机一直发的,只能进一次是什么原因啊?用f149的时候不这样啊?忘大神指点!谢谢…

查看全部问答>