STM32红外接收模块使用教程
红外遥控是一种比较常用的通讯方式,目前红外遥控的编码方式中,应用比较广泛的是NEC协议。NEC协议的特点如下:
- 载波频率为 38KHz
- 8位地址和 8位指令长度
- 地址和命令2次传输(确保可靠性)
- 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);
}
}
编写成功了。里面可能有许多瑕疵,望谅解。
作者:小探探