资讯详情

DIY:制作一个语音识别的空调遥控器

??夏天来了,空调简直就是我们的救命神器,但是我总是担心找不到遥控器,所以我花了一天一夜的时间用单片机做了一个语音识别空调遥控器,24小时放在空调下面,再也不怕遥控器找不到了。~除了遥控器最基本的功能外,还可以添加自己定义的功能,比如循环开半小时,然后关半小时,用于节电;或根据环境温度调节空调温度等~ Alt

1 红外遥控原理

??红外遥控是一种无线、非接触控制技术,具有抗干扰能力强、信息传输可靠、功耗低、成本低、易于实现等显著优点。广泛应用于许多电子设备,特别是家用电器,并越来越多地应用于计算机系统。 ?? 红外遥控发射电路由红外接收二极管、三极管或硅光电池组成,将红外发射器发射的红外光转换为相应的电信号,然后发送后放大器。 ??发射机一般由指令键(或操作杆)、指令编码系统、调制电路、驱动电路、发射电路等部件组成。当按下指令键或推动操作杆时,指令编码电路产生所需的指令编码信号,指令编码信号调制载波,然后通过发射电路向外发射调制的指令编码信号。 接收电路一般由接收电路、放大电路、调制电路、指令翻译电路、驱动电路、执行电路(机构)等组成。接收电路接收发射器发出的编码指令信号,放大后发送解调电路。解调电路解调已调制的指令编码信号,即恢复为编码信号。指令翻译器翻译编码指令信号,最后由驱动电路驱动执行电路,实现各种指令的操作控制(机构)。 ??目前,红外遥控编码应用广泛:NEC Protocol 的 PWM(脉冲宽度调制)和 Philips RC-5 Protocol 的 PPM(脉冲位置调制)。这里我抓取了我自己奥克斯空调遥控器的指令波形,发现使用的是NEC协议脉宽调制。 ??NEC 代码的位置定义:脉冲对应 560us 连续载波,逻辑 1 传输需要 2.25ms(560us脉冲 1680us 低电平),逻辑 0 的传输需要 1.125ms(560us 脉冲 560us 低电平)。当接收到脉冲时,遥控接收头为低电平,无脉冲时为高电平。这样,我们在接收头端收到的信号是:逻辑 1 应该是 560us 低 1680us 高,逻辑 0 应该是 560us 低 560us 高。数据均采用8bit传输方式,低位在前高位在后。 ??需要注意的是,接收器在这里解码的高电平实际上是灭绝的;低电平是发射器的亮起状态。而且发射器端部需要38KHz载波驱动不是简单的高电平驱动。必须注意这一点。

2 红外遥控器的生产工艺

2.1 解码遥控指令

??解码遥控指令是不必要的,但这一步可以加深对协议的理解,方便后续代码的编写。如果您可以在互联网上找到您复制的遥控器的协议代码,则可以省略此步骤。 ??首先,用逻辑分析仪抓取红外接收器解调的信号,大致了解协议格式。 ??例如,上图中红色长方形中的数据为0b1110110,这个数据是0,因为它处于低位。x6F,黄色长方形中的数据为0xE0.这一步主要是用单片机解码时验证结果的正确性。 ??根据不同功能下的指令差异,可以分析每个字节的每个代表。例如,我的AUX空调遥控器的命令分析如下。主流空调品牌协议应在网上找到。

2.2 编写单片机解码代码

??我的红外接收器和发射器是淘宝随便买的,单片机是华大的HC32L130,当然以自己选择,成本越低越好。因为驱动红外发射器的载波只需要38KHz,对单片机的时钟频率要求也不是很高。 ??考虑到协议是PWM调制,所以需要分析占空比来确定每个代表的含义,但其实这个人的含义只需要看高电平的时间,所以用PWM捕获捕获上升沿,捕获后立即将捕获类型改为下降沿捕获。除时钟频率外,两次捕获之间的计数器值为高电平时间。除了SYNC除了信号,每8个bit存储在数组中的字节数据buffer中。由于AUX遥控器为13byte收到13个指令byte然后打印主函数中接收到的数据并重置buffer。代码示例如下:

void PwmCaptureInit(void) { 
          stc_pcacfg_t  PcaInitStruct;     stc_gpio_cfg_t GpioInitStruct;      Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);     Sysctrl_SetPeripheralGate(SysctrlPeripheralPca, TRUE);         //PA07设置为PCA_CH1     GpioInitStruct.enDrv  = GpioDrvH;     GpioInitStruct.enDir  = GpioDirIn;     Gpio_Init(GpioPortA, GpioPin7, &GpioInitStruct);     Gpio_SetAfMode(GpioPortA,GpioPin7,GpioAf2);            PcaInitStruct.pca_clksrc = PcaPclkdiv32; //32MHz / 32 = 1MHz     PcaInitStruct.pca_cidl   = FALSE;     PcaInitStruct.pca_ecom   = PcaEcomDisble;   
    PcaInitStruct.pca_capp   = PcaCappEnable;        //上升沿捕获
    PcaInitStruct.pca_capn   = PcaCapnDisable;        //禁止下降沿捕获
    PcaInitStruct.pca_mat    = PcaMatDisable; 
    PcaInitStruct.pca_tog    = PcaTogDisable;  
    PcaInitStruct.pca_pwm    = PcaPwm8bitDisable;   
    PcaInitStruct.pca_epwm   = PcaEpwmDisable;   
    Pca_M1Init(&PcaInitStruct);    
	Pca_ClrItStatus(PcaCcf1);
    Pca_ConfModulexIt(PcaModule1, TRUE);
    EnableNvic(PCA_IRQn, IrqLevel3, TRUE);
	Pca_StartPca(TRUE);
}

uint8_t  remoteStatus = 0;
uint16_t captureValue = 0;		//下降沿时计数器的值
uint8_t  captureCount = 0;	//按键按下的次数
uint8_t cmdBuf[13] = { 
         0 };//每条命令有13bytes
uint8_t cmdNum = 0;//当前是第几条命令


uint8_t bitCnt = 0;
void Pca_IRQHandler(void)
{ 
        
	M0P_TIM0_MODE0->M0CR &= ~1;	//停止并清零定时器
	M0P_TIM0_MODE0->CNT = 0;
	
    if(Pca_GetItStatus(PcaCcf1) != FALSE)
    { 
        
		if(Gpio_GetInputIO(GpioPortA, GpioPin7) == TRUE)//捕获到上升沿后,标记捕获状态并切换为下降沿捕获
		{ 
        
			M0P_PCA->CCAPM1_f.CAPP = 0;
			M0P_PCA->CCAPM1_f.CAPN = 1;	//切换为下降沿捕获
			Pca_SetCnt(0);
			remoteStatus |= 0X10;
		}
		else	//捕获到下降沿,判断当前接收到的是什么指令码,并回到上升沿捕获
		{ 
        
			captureValue = Pca_GetCcap(PcaModule1);
			M0P_PCA->CCAPM1_f.CAPP = 1;
			M0P_PCA->CCAPM1_f.CAPN = 0;	//切换为上升沿捕获
			if(remoteStatus & 0X10)				//前面捕获到上升沿
            { 
        
                if(remoteStatus & 0X80) //当前命令存在合法的SYNC码头
                { 
        
                    if(captureValue > 300 && captureValue < 800)	//560us
                    { 
        
                        cmdBuf[cmdNum] >>= 1;					//接收到0,右移一位
						bitCnt++;
                    }
                    else if(captureValue > 1400 && captureValue < 1900)	//1680us
                    { 
        
                        cmdBuf[cmdNum]  >>= 1;					//接收到1,左移一位并存在最高位
                        cmdBuf[cmdNum]  |= 0x80;			
						bitCnt++;
                    }
                    else if(captureValue > 2200 && captureValue < 2700)	//2.5ms,按键次数增加
                    { 
        
                        captureCount++; 		
                        remoteStatus &= 0XF0;				//清空计时器
                    }
					if(bitCnt==8)
					{ 
        
						bitCnt = 0;
						cmdNum++;
					}
                }
                else if(captureValue > 4200 && captureValue < 4700)		//4.5ms SYNC
                { 
        
                    remoteStatus |= 0X80;				//SYNC状态位置1
                    captureCount = 0;					//清除按键次数计数器
                }
            }
            remoteStatus &= ~0x10;	//等待下一次上升沿捕获
		}
    }
	Pca_ClrItStatus(PcaCcf1);
	Pca_ClrItStatus(PcaCf);
	M0P_TIM0_MODE0->M0CR |= 1;	//定时器重新开始计数
}

2.3 单片机编码发送代码编写

  那么到这里就可以按照协议来发送我们的命令了。注意驱动红外二极管要用38KHz的载波驱动。因为这个载波频率不需要特别精准,所以我只是用软件模拟了一个方波。需要精准的PWM驱动可以用定时器/硬件PWM实现。

void RemoteGpioInit(void)
{ 
        
	stc_gpio_cfg_t    GpioInitStruct;
    DDL_ZERO_STRUCT(GpioInitStruct);
    
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
    
    //PB9作为红外输出data脚
    GpioInitStruct.enDrv  = GpioDrvH;
    GpioInitStruct.enDir  = GpioDirOut;
    Gpio_Init(GpioPortB, GpioPin9, &GpioInitStruct);
}


void IRSend_1(void)
{ 
        
	uint32_t cnt = 0x2C;
	while(cnt--)	//38KHz,560us
	{ 
        
		M0P_GPIO->PBOUT ^= 0x200;
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop( 

标签: 二极管用来发送脉冲二极管2c3

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台