STM32红外接收模块使用教程

  红外遥控是一种比较常用的通讯方式,目前红外遥控的编码方式中,应用比较广泛的是NEC协议。NEC协议的特点如下:

  1. 载波频率为 38KHz
  2. 8位地址和 8位指令长度
  3. 地址和命令2次传输(确保可靠性)
  4. PWM 脉冲位置调制,以发射红外载波的占空比代表“0”和“

一.我们先配置IO口和外部中断

#define  PB1
{
    .name   ="PB1",
    .port   =GPIOB,
    .pin    =GPIO_PIN_1,
    .Init   =GPIODevInit,
    .Write  =NULL,
    .Read   =GPIODevRead,
    .IrqHander =NULL,
    .next      =NULL    
}



#define    DEVPTIM10
{
    .name    ="TIM10 PWM10",
    .port    =TIM10,
    .channel =0,
    .status  =0,
    .Init    =TimerDevInit,
    .Start   =TimerDevStart,
    .Stop    =TimerDevStop,
    .GetCount =TimerDevGetCount,
    .Contoral  =TimerDevContoral,
    .Irqhander =NULL,
    .next      =NULL
}

二.实现端口的驱动

#include "error.h"
#include "libs.h"
#include "stm32f4xx_hal.h"                  // Device header
#include "dev_gpio.h"
#include "./drv_config/gpio_config.h"

static int GPIODevInit(struct GPIODev *Pdev);
static int GPIODevWrite(struct GPIODev *pdev,unsigned char status);
static int GPIODevRead(struct GPIODev *pdev);


static  GPIODevice gGPIODev[]={
    PB1
};

void GPIODevCelearce(void)
{
    unsigned int num=sizeof(gGPIODev)/sizeof(GPIODevice)
    for(int i=0;i<num;i++)
    {
        GPIODevInist(&gGPIODev);
    }
}


EXTI_HandleTypeDef hexti1={

	.Line =EXTI_LINE_1,
	.PendingCallback =NULL

};


static int GPIODevInit(struct GPIODev *Pdev)
{
    EXTI_ConfigTypeDef nConfig={
		
		.Mode=EXTI_MODE_INTERRUPT,
		.Trigger=EXTI_TRIGGER_RISING_FALLING
	};
	
	if(strstr(Pdev->name,"PB1"))
	{	
		hexti1.PendingCallback=Pdev->IrqHander;
		nConfig.Line=EXTI_LINE_1;
		nConfig.GPIOSel=EXTI_GPIOB;
		HAL_StatusTypeDef status =HAL_EXTI_SetConfigLine(&hexti1,&nConfig);
		if(HAL_OK!=status) return -EIO;	
	}

    return ESUCCESS;
}


static int GPIODevWrite(struct GPIODev *pdev,unsigned char status)
{
    if(NULL==PDev) return -EIO;
    if(status!=1&&status!=0)
    {
        return -EIO;
    }   
    HAL_GPIO_WritePin(PDev->port,GPIO->pin,(unsigned int)status);
    pdev->value=status;
    return ESUCCESS;

}
static int GPIODevRead(struct GPIODev *pdev)
{
    if(NULL==PDev) return -EIO;
    PDev->value=HAL_GPIO_Read(PDev->port,PDev->pin)

}


三.中断

#include "error.h"
#include "./drv_config/timer_config.h"
#include "libs.h"
#include "stm32f4xx_hal.h"                  // Device header
#include "drivers.h"
#include "tim.h"

static	int TimerDevInit(struct TimerDev *PDev);
static	int TimerDevStart(struct TimerDev *PDev);               //定时器的开启
static	int TimerDevStop(struct TimerDev *PDev);                   //定时器的关闭
static	int TimerDevGetCount(struct TimerDev *PDev);              //定时器计数值的获取
static  int TimerDrvControl(struct TimerDev *PDev,unsigned int cmd ,unsigned int cfg);

static volatile unsigned int gTIM10Count=0;
static TimerDevice gTimerDev[]={
#if
    DEVPTIM10
#endif

};

void TimerDrvCelare(void)
{
    unsigned int num=sizeof(gTimerDev)/sizeof(TimerDevice);
    for(int i=0;i<num;i++)
    {
        TimerDevInist(&gTimerdev);
    }

}

static	int TimerDevInit(struct TimerDev *PDev)
{
    if(NULL==PDev)  return -EIO;
    if(PDev->port==TIM10)
    {   
        TimerIrqHander[10]=PDev->IrqHander;
    }    
    retuen ESUCCESS;
}
static	int TimerDevStart(struct TimerDev *PDev)
{
    if(NULL==PDev) return -EIO;
    TIM_HandleTypeDef* htim=NULL;

    if(PDev->port==TIM10)
    {
        htim=&htim10;
        if(HAL_OK!=HAL_TIM_BASE_Start_IT(htime))
        return -EIO;
    }
     return ESUCCESS;
}
static	int TimerDevStop(struct TimerDev *PDev)                  //定时器的关闭
{
    if(NULL==PDev) return -EIO;
    TIM_HandleTypeDef* htim=NULL
    if(PDev->port==TIM10)
    {
        if(HAL_OK!=HAL_TIM_BAES_STOP_IT(htim))
        return -EIO;
    }
    return ESUCCESS;
}    
static	int TimerDevGetCount(struct TimerDev *PDev)             //定时器计数值的获取
{    
    if(NULL==PDev) return -EIO;
    unsigned int Count=0;
    if(PDev->port==TIM10)
    {
        Count=gTIM10Count;
    }
    return Count;
}
static  int TimerDrvControl(struct TimerDev *PDev,unsigned int cmd ,unsigned int cfg)
{
    if(NULL==PDev) return -EIO;
    TIM_HandleTypeDef* htim=NULL;
    unsigned int Freq=0;
    if(PDev->port==TIM10)
    {
        htim=&htim10;
        Freq=HAL_RCC_GetPCLK2Freq()*2;//获取总线时钟
    }
    if(htim==NULL) return -EIO;
    Freq=Freq/1000000
    switch(cmd)
    {
        case SET_PRESCALER_VALUE:
        {
			__HAL_TIM_SET_PRESCALER(htim,cfg);
			break;
        }
        case SET_CLKDIV_VALUE:
        {
			if(cfg==1)
			  __HAL_TIM_SET_CLOCKDIVISION(htim,TIM_CLEARINPUTPRESCALER_DIV1);
			else if(cfg==2)
			  __HAL_TIM_SET_CLOCKDIVISION(htim,TIM_CLEARINPUTPRESCALER_DIV2);
			else if(cfg==4)
			  __HAL_TIM_SET_CLOCKDIVISION(htim,TIM_CLEARINPUTPRESCALER_DIV4);
			break;
        }
		case SET_PERIOD_VALUE:
		{	
			//x>>y==x/2^y;x<<y=x*2^y
			unsigned int max_input=(0xFFFFFFFF)/freq+freq;
			unsigned int period=1;
			if(cfg>=max_input)
				period=cfg/1000*freq;
			else
				period=cfg*freq/1000;
			unsigned int prescaler =(period>>16)+1;//就要让时钟频率边慢去计数。判断要分频多少倍去计数period/65536+1=1282//分频系数//x>>16二进制右移16、
//			period=cfg*freq/1000;//假设传进来cfg位1秒=1*10^9*84/1000=84*10^6;//计数器最大值65536个数,是装不下的
			period=period/prescaler;//然后重制period的值,只有计数这莫多数之后,才是计算传递进来时间的时间period=65522HZ.也就是计数器计时折磨多频率位一秒
			__HAL_TIM_SET_PRESCALER(htim,prescaler-1);//以微秒为单位
			__HAL_TIM_SET_AUTORELOAD(htim,period-1);
			break;
		}
	}
	return ESUCCESS;   

    }    

}
 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)//传递的时一个句柄指针
{
    if(TIM10==htim->Instance)
	{	
		gTIM10Count++;
		irqHandle=TimerIrqHandler[10];
	}

}

四.红外设备

#include "devices.h"
#include "error.h"
#include "stm32f4xx_hal.h"                  // Device header
#include "Libs.h"
#include "dev_irda.h"


static RingBuffer *Pbuf =NULL;

static	int IRDADevInit(struct IRDADev *PDev);
static	int IRDADevRead(struct IRDADev *PDev);
static  int IRDADeccode(struct IRDADev *PDev);

/*********************  20键盘的码值表  *****************/
static const unsigned char KeyCode[20]={
0x45, 0x47, 0x44, 0x40, 0x43,
0x07, 0x15, 0x09, 0x16, 0x19,
0x0D, 0x0C, 0x18, 0x5E, 0x08,
0x1C, 0X5A, 0x42, 0X52, 0x4A
	
};

static const char KeyName[20][10]={
"Open",    "Menu",     "Test",     "+",    "Return",
"Back",    "Suspend",  "Forward",  "0",    "-",
"Cancle",  "1",        "2",        "3",    "4",
"5",       "6",        "7",        "8",    "9"
	
};

static IRDADevices IRDADev={
     .name   ="NEC IRDA",
     .key    =NULL,
     .Init   =IRDADevInit,
     .Read   =IRDADevRead
};

static IRDADev GetIRDA(void)
{
    return &IRDADev;
}

static void GetIrdaTrigerTime(void)
{
    TimerDevice *PTIM10=TimerDeviceFind("TIM10 PWM10");
    if(PTIM10==NULL) return -EIO;
    if(Pbuf==NULL) return -EIO;
    
    volatile unsigned int tick=PTIM10->GetCount(PTIM10);
    Pbuf->Write(Pbuf,(unsigned int*)&tick,sizeof(unsigned int));
}

static	int IRDADevInit(struct IRDADev *PDev)
{
    if(PDev==NULL) return -EIO;
    
    GPIODevice *PPB1=IOdeviceFind("PB1");
    if(PPB1==NULL) return -EIO;
    PPB1->IrqHanle=GetIrdaTrigerTime;
    if(ESUCCESS!=PPB1->Init(PPB1)) return -EIO;
    
    TimerDevice *PTIM10=TimerDeviceFind("TIM10 PWM10");
    if(PTIM10==NULL) return -EIO;
    if(ESUCCESS!=PTIM10->Init(PTIM10)) return -EIO;
    if(ESUCCESS!=PTim10->Contoral(PTIM10,SET_PERIOD_VALUE,10000)) return -EIO;//10us
    if(ESUCCESS!=PTIM10->Start(PTIM10)) return -EIO;
    
    Pbuf=RingBufferNew(sizeof(unsigned int)<<8);
    if(Pbuf==NULL) return -EIO;
    
    return ESUCCESS;
}

static  int IRDADeccode(struct IRDADev *PDev)
{
    unsigned int timeout=1000;
    unsigned int duty=0;
    unsigned char step=0;
    unsigned char PQ=0;
    unsigned int Tick[2]={0};
    
    unsigned int i=0;
    unsigned int TE[64]={0};
    unsigned int cnt=0;
	unsigned int value=0;
    while(timeout)
    {
        timeout--;
        if(sizeof(unsigned int)!=Pbuf->Read(Pbuf,(unsigned int *)&Tinck[PQ],sizeof(unsigned int)))
        {
            mdelay(1);
            continue;
        }
        PQ++;
        if(PQ==2)
        {
            duty=(Tick[1]-Tick[0])*10;
            Tick[0]=Tick[1];
            PQ=1;
        }
        switch(step)
        {
            /*红外遥控模块解码的思路
			*1.计算连续两个计数值之间的差值
			*2.根据差值与NEC编码协议来判断是否属于引导码
			*3.如果属于引导码继续计算判断是否属于连续码还是指令码
			*4.如果是指令码则继续读取32bit对应于64个脉冲计数值,计算差值
			*5.根据差值确定改为是0是1
			*6.组合成指令
			*7.根据指令找到相对应的键码
			*/
            case 0:
            {
                if(duty>=8500&&duty<=9500)//引导码
                {
                    step++;
                }
                break;
            }
            case 1:
            {
                if(duty>=4000&&duty<=5000)//指令码
                {
                    step++;
                }
                else if(duty>=2000&&duty<=2500)//连续码
                {
                    step=0;
                } 
                else {
                    step=0;
                }  
                break;  
            } 
            case 2:
            {
                timeout=1000;
                TE[i]=duty;
                i++;
                if(i==64)
                {
                    i=0;
                    step++;
                } 
                break;       
            }
            case 3:
            {
                for(unsigned char n=0;n<64;n+=2)
				{
					if(TempBuf[n]>=500&&TempBuf[n]<=600)
					{
						if(TempBuf[n+1]>=500 && TempBuf[n+1]<=600) //逻辑0(数据0)
						{
							//value不变化
						}
						else if(TempBuf[n+1]>=1000 && TempBuf[n+1]<=2000) //逻辑2
						{	
							value =value+(1<<cnt);
						}
					
					}
					cnt++;
				}
				step++;
				break;
            }  
            case 4:
			{
				/*解码*/
				timeout=1000;
				unsigned int sys_code=(value>>16&0xFF);
				step=0;
				PQ=0;
				return sys_code;
				break;
			}
			default:break;
        }
    }
    return -EIO;
}

static	int IRDADevRead(struct IRDADev *PDev)
{
    if(NULL==PDev) return -EIO;
    int code=IRDADeccode();
    if(code<0) return -EIO;
    for(int i=0;i<20;i++)
    {
        if(code==KeyCode[i])
        {
            PDev->key=(char *)&KeyNum[1][0];
            return ESUCCESS;
        }
    }
    return -EIO;
}

五.测试程序

#include "devices.h"
#include "error.h"
#include "libs.h"
#include "./irda/dev_irda.h"
#include <string.h>
#include "font.h"

static void clear_screen(unsigned short color);
void app_disirda_test(void)
{
	IODevReigest();
	SPIDevRegiest();
	UARTDevReigest();
	TimerDevRegiest();
	I2CDevReigest();
	
	IRDADevice *PIRAD=GETIRDA();
	if(NULL==PIRAD) return;
	if(ESUCCESS!=PIRAD->Init(PIRAD)) return;
	
	DisplayDevice *PDIS=GetST7789Device();
	if(NULL==PDIS) return;
	if(ESUCCESS!=PDIS->Init(PDIS)) return;
	clear_screen(0xFFFF);
	
	while(1)
	{
		if(ESUCCESS==PIRAD->Read(PIRAD))
		{
			clear_screen(0xFFFF);
			ShowStringEN(PIRAD->name,88,0,0x0000);
			ShowStringEN(PIRAD->key,120-strlen(PIRAD->key)*8/2,16,0x0000);
		}
	}

}


static void clear_screen(unsigned short color)
{
	DisplayDevice *ptdev =GetST7789Device();
	if(NULL==ptdev) return;
	unsigned short nbuf[240]={0};
	for(unsigned short i=0;i<240;i++)
	{
		nbuf[i]=color;
	}
	
	ptdev->FBBase=(unsigned short *)nbuf;
	for(unsigned short i=0;i<320;i++)
	{
		ptdev->SetDisplayWindow(ptdev,0,i,240,i+1);
		ptdev->Flush(ptdev);
	}
	
}

编写成功了。里面可能有许多瑕疵,望谅解。

作者:小探探

物联沃分享整理
物联沃-IOTWORD物联网 » STM32红外接收模块使用教程

发表评论