单片机学习小组
直播中

尚文清

7年用户 1604经验值
私信 关注

求一种基于STM32与NFC的万能红外遥控器设计方案

怎样去设计一种基于STM32NFC的万能红外遥控器呢?其程序代码该怎样去实现呢?

回帖(1)

陈思伊

2022-1-25 11:02:12
wifi控制基于STM32与NFC的万能红外遥控器—毕业设计(1)

背景技术

      带有红外遥控器的家电产品给我们的生活带来了极大的便利,不过当这类设备越来越多,难免给我们带来了新的困扰。遥控器越来越多,非常容易弄混,或者丢的到处都是。在这个时候一款能够对家中各种红外遥控器发射的控制信号进行识别、存储和再现的遥控系统是多么的容易受到大家的青睐。并且该设备还能够联网通过手机APP控制,这无疑会极大的便利人们的生活。同时该设备还达到了一定控制环境变化的能力。
设计所完成的功能

      该设备能够接收并解码红外信号并能够长期存储,同时还可以将所存储的信号发送出去,从而模拟遥控器控制设备(最大支持200个红外按键,理论上是可无限增多的,和设计容量有关)。通过设计的手机APP。该模块具有学习、工作两种模式。
      进入学习模式后,APP按下需要设置的按键,再将对应遥控器按键按下,即可完成学习。工作模式下,手机APP就像一个真正的遥控器,你只需按下你已录入的按键,该设备就能控制你的家电了。
同时该设备还配备了环境监测模块及选接的智能控制开关,当周围环境不再舒适其会自动打开(可接空气净化器或加湿器),直到周围环境达到一定条件。
一、实施方法

      处理器采用STM32F103R大容量cortex-M3内核。其优点在于资料丰富、价格便宜、高性能、有多个串口和时钟、具有DMA功能。

1.1 STM32F103R设计:

      通过配置将PA9,PA10引脚定义为串口引脚,并将其设置为标准输入输出引脚,接好WIFI设备。定义TIM3信号捕获用于接收并解码发送来的红外信号,定义TIM2产生38kHz的PWM用于产生红外的载波。使能芯片自带的ADC模块用于环境监测。使能引脚的外部中断。
      该设备能够接收并解码红外信号并能够长期存储,同时还可以将所存储的信号编码发送,从而模拟遥控器控制设备。通过我们设计的手机APP,按键可一一对应相应遥控器码值,按下按键即可发送。空气质量信息会定时的通过WIFI模块传到我们的手机APP上进行显示,同时可通过中断系统控制引脚上的继电器(可接空气净化器或加湿器)。

1.2 红外模块设计:

      红外接收模块用于学习发射来的红外信号,红外发射模块用于发射。通过特定编程,使得该模块具有学习、工作两种模式(通过设计APP特定按键进行修改)。



  • 学习模式:在进入学习模式后,APP按下所需要设置的按键,然后将对应遥控器按键按下,对准我们的红外遥控器设备,APP上的按键就成功学习到了。该操作可重复执行。
  • 工作模式:工作模式下,手机APP就变成了一个遥控器,你只需按下你已录入的按键,红外遥控器就可以像你用家用遥控器一样了



二、代码实施的简单介绍

      首先我们主要对红外方面的代码做个介绍:
定时器4初始化函数,主要用于对红外遥控器引脚的初始化。

//红外遥控初始化
//设置IO以及定时器4的输入捕获
void Remote_Init(void)         
{  
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_ICInitTypeDef  TIM_ICInitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能PORTB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //TIM4 时钟使能


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;     //PB9 输入
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   //上拉输入
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB,GPIO_Pin_9); //初始化GPIOB.9


  TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出  
  TIM_TimeBaseStructure.TIM_Prescaler =(72-1);  //预分频器,1M的计数频率,1us加1.     
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式


  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
  TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;  // 选择输入端 IC4映射到TI4上
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;  //配置输入分频,不分频
  TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
  TIM_ICInit(TIM4, &TIM_ICInitStructure);//初始化定时器输入捕获通道


  TIM_Cmd(TIM4,ENABLE );  //使能定时器4
  NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  //TIM3中断
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级0级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
  TIM_ITConfig( TIM4,TIM_IT_Update|TIM_IT_CC4,ENABLE);//允许更新中断 ,允许CC4IE捕获中断
用于捕获红外信号到变量RmtRec中。主要通过模拟NFC的方式进行数据的接收。

//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留
//[4]:标记上升沿是否已经被捕获           
//[3:0]:溢出计时器
u8  RmtSta=0;      
u16 Dval;  //下降沿时计数器的值
u32 RmtRec=0; //红外接收到的数据         
u8  RmtCnt=0; //按键按下的次数   
//定时器4中断服务程序  
void TIM4_IRQHandler(void)
{         
if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)
{
  if(RmtSta&0x80)        //上次有数据被接收到了
  {
   RmtSta&=~0X10;       //取消上升沿已经被捕获标记
   if((RmtSta&0X0F)==0X00){RmtSta|=1<<6;} //标记已经完成一次按键的键值信息采集
   if((RmtSta&0X0F)<14)RmtSta++;//14
   else
   {
    RmtSta&=~(1<<7);     //清空引导标识
    RmtSta&=0XF0;      //清空计数器
   }              
  }           
}
if(TIM_GetITStatus(TIM4,TIM_IT_CC4)!=RESET)
{  
  if(RDATA)//上升沿捕获
  {
     TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Falling);      //CC4P=1 设置为下降沿捕获
   TIM_SetCounter(TIM4,0);       //清空定时器值
   RmtSta|=0X10;       //标记上升沿已经被捕获
  }else //下降沿捕获
  {
   Dval=TIM_GetCapture4(TIM4);     //读取CCR4也可以清CC4IF标志位
    TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Rising);    //CC4P=0 设置为上升沿捕获
   if(RmtSta&0X10)       //完成一次高电平捕获
   {
     if(RmtSta&0X80)//接收到了引导码
    {  
     if(Dval>300&&Dval<800)   //560为标准值,560us
     {
      RmtRec<<=1;     //左移一位.
      RmtRec|=0;     //接收到0   
     }else if(Dval>1400&&Dval<1800) //1680为标准值,1680us
     {
      RmtRec<<=1;     //左移一位.
      RmtRec|=1;     //接收到1
     }else if(Dval>2200&&Dval<2600) //得到按键键值增加的信息 2500为标准值2.5ms
     {
      RmtCnt++;      //按键次数增加1次
      RmtSta&=0XF0;    //清空计时器  
     }
     }else if(Dval>4200&&Dval<4700)  //4500为标准值4.5ms
    {
     RmtSta|=1<<7;     //标记成功接收到了引导码
     RmtCnt=0;      //清除按键次数计数器
    }      
   }
   RmtSta&=~(1<<4);
  }                        
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update|TIM_IT_CC4);      
}
获取接受到红外信号的解码信息,后期主要用于学习模式下的调用。

//处理红外键盘
//返回值:
//  0,没有任何按键按下
//其他,按下的按键键值.
u32 Remote_Scan(void)
{        
u32 sta=0;               //接收红外参数的有效值
    u8 t1,t2;  
if(RmtSta&(1<<6))//得到一个按键的所有信息了
{
   printf("标志寄存器=%d",RmtSta);  //数字模拟的标志寄存器
   printf("完整的数据=%d",RmtRec);  //真正的数据
     t1=RmtRec>>24;   //得到地址码
     t2=(RmtRec>>16)&0xff; //得到地址反码
      //if((t1==(u8)~t2)&&t1==REMOTE_ID)//检验遥控识别码(ID)及地址
  if(t1==(u8)(~t2)) //检验地址
     {
     printf("地址码=%d",t1);
         t1=RmtRec>>8;
         t2=RmtRec;
     printf("数据码=%d",t1);   
         if(t1==(u8)~t2)sta=RmtRec;//键值正确
     RmtSta&=~(1<<7);     //清空引导标识
     RmtSta&=0XF0;      //清空计数器  
  }   
  if((sta==0)||((RmtSta&0X80)==0))//按键数据错误/遥控已经没有按下了
  {
    RmtSta&=~(1<<6);//清除接收到有效按键标识
   //RmtCnt=0;  //清除按键次数计数器
  }
}  
    return sta;
}
红外发射函数通过定时器3来实现,符合NFC协议(NFC协议在此不再介绍,此函数主要模拟NFC协议进行发送)

#include "timer.h"
#include "led.h"
#include "delay.h"
//#include "adc.h"
#include "usart.h"


int js_count;
int flag;
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)//947 0
{
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
TIM_Cmd(TIM3, ENABLE);  //使能TIMx      
}


//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
if(js_count>0) js_count--;
if(flag==1) //如果全局变量标志位等于1,顺序执行
{
  GPIO_WriteBit(GPIOB,GPIO_Pin_5,(BitAction)((1-GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5)))); //向PD-12管脚每隔一定时间写与上次取反的状态值
}
else
{
  GPIO_ResetBits(GPIOB,GPIO_Pin_5);
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update );  //清除TIMx更新中断标志      
}


//红外发射函数
void hw_fs(u32 dt)
{
u8 i=0;
flag=0;//初始化管脚
js_count=683;//9ms 起始信号低电平
flag=1;
while(js_count); //等待
js_count=341;//4.5ms 起始信号高电平
flag=0;
while(js_count); //等待
for(i=0;i<32;i++)
{
  js_count=42;//低电平 常数 0.56ms
  flag=1;
  while(js_count); //等待
  if((dt&0x80000000)==0)
  {
   js_count=43;//0.565ms 发射 “0”
  }
  else
  {
   js_count=128;//1.69ms 发射 “1”
  }
  flag=0;
  while(js_count); //等待
  dt=dt<<1; //向左移动一位
}
js_count=20; //0.263ms
flag=1;
while(js_count); //等待
flag=0;
}
   

此设计方案已成功实现,由于代码量较大,后期持续更新,欢迎读者提问咨询。如果读者有意愿完成欢迎与我交流探讨并改进。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分